Jump to content
IGNORED

GCC for the TI


insomnia

Recommended Posts

There's no explicit support, but gcc is tremendously configurable and can easily be used to create code that uses the extra RAM pages provided by the SAMS (in much the same way that it can be used to create code for bankswitching carts, I created a thread on that last year explaining how I did it). All it would require is a little inline assembly to do the page switching (I believe page switching is CRU based on the SAMS? If it's 'memory mapped' like a cart you don't even need assembly).

 

Having said that, it would be awesome if the compiler could natively support bankswitched memory (both SAMS and latching carts), making it so that one wouldn't have to explicitly switch banks and copy state when running code in another bank (via so-called trampoline functions). The non-standard concept of 'far pointers' in the old DOS days was introduced to do exactly that. Not sure how feasible it is to add that the the tms9900 backend in gcc though :).

 

*edit* but the short answer is 'yes', you can use SAMS memory in gcc programs :).

Edited by TheMole
  • Like 1
Link to comment
Share on other sites

  • 3 months later...

Ok, so, How do I set this up to create flashrom99 compatible files?

Basically, you need to create a linker script (a text file with an .ld extension) that tells GCC the memory layout you're targeting. This is in and by itself not terribly hard, but it's a bit of an awkward syntax to work with: https://www.math.utah.edu/docs/info/ld_3.html

 

Using linker scripts, you can:

- create the required cart header

- locate executable code and constants in cart space (the .text section)

- locate variables in scratchpad (the .data and .bss sections)

 

If you give me a couple of days, I might be able to help out in creating that linker script, but I probably won't be able to get to it before the weekend.

  • Like 3
Link to comment
Share on other sites

  • 2 weeks later...

Should I be able to create a def in .asm, and then link to it and copy data from it in c code?

 

I tried the following in 'images.asm'

 

 

  def image
image:
  DATA >0001, >0002, >0003.... 

Then in my main.c I have:

 

 

extern const char* image;
...
vdpmemcpy(gSpritePat, image, 32);
Things all link, but at runtime it seems to copy garbage data.

 

By contrast, if I transform the pattern data to c and embed in an array it works...

 

const char image[32] = { 0x00, 0x01, .... };
...
vdpmemcpy(gSpritePat, image, 32); 
And life is happy...

 

The reason I ask, is that Magellan handily outputs patterns in .asm DATA statements... And it just seems that I should be able to do this.

 

I haven't broken down and opened the generated bin, and checked that the addresses are correct when in the ASM... I suppose that is next...

 

-M@

Link to comment
Share on other sites

I usually do the C includes, but yeah, you should be able to do what you are showing.

 

A quick test gave me similar results:

 

 

 DEF TESTDAT
TESTDAT DATA 1,2,3,4,5

 

// http://harmlesslion.com - visit the web page for contact info
//
// uses libti99
#include "vdp.h"
extern const char *TESTDAT;
int main() {
 // init the screen
 {
  int x = set_graphics(VDP_SPR_8x8);  // set graphics mode with 8x8 sprites
  charsetlc();       // get character set including lowercase
  vdpmemset(gColor, 0x10, 32);   // all colors to black on transparent
  vdpchar(gSprite, 0xd0);     // all sprites disabled
  VDP_SET_REGISTER(VDP_REG_MODE1, x);  // enable the display
  VDP_REG1_KSCAN_MIRROR = x;    // must have a copy of VDP Reg 1 if you ever use KSCAN
 }
 VDP_SET_REGISTER(VDP_REG_COL, 0x07);
 // display a build date in lieu of a version
 writestring(16,5,"Testing: ");
 faster_hexprint(TESTDAT[0]);
 faster_hexprint(TESTDAT[1]);
 faster_hexprint(TESTDAT[2]);
 faster_hexprint(TESTDAT[3]);
 faster_hexprint(TESTDAT[4]);
 for (; { }

 return 0;
}

 

The above did not work, instead of some variant of the data provided (I would have expected 0001000200) I got what looked like pointers (E0002483C0).

 

Looking at the assembly code, it showed this around the TESTDAT pointer:

 

 

 mov  @TESTDAT, r1
 movb *r1, r1

 

And therein lies the issue. TESTDAT is a variable that /contains/ a pointer, GCC expects memory to be backing it. It's not a memory address itself according to the way we defined it.

 

TESTDAT contains the pointer value >A0E4 in my build (per the map), which is the correct address for the data (looking at the debugger).

 

I'm not sure the /right/ answer to this, honestly. This works, at the cost of two extra bytes in RAM for the variable:

 

 

extern const char *TESTDAT;
const char *tt = &TESTDAT;

 

(then dereference 'tt' instead of TESTDAT). The generated code is efficient enough, tt is just generated with a DATA statement:

 

 

tt
 data TESTDAT

 

It does seem like there should be a better way... the problem is that TESTDAT is a constant value, not a variable. The number representing TESTDAT doesn't exist anywhere in the code, so it can't be dereferenced.

  • Like 1
Link to comment
Share on other sites

Ah, this works... some very ugly casting (so I wrapped it in a macro)

 

 

extern const char *TESTDAT;
#define DEREFLABEL(xx,off) *(((const char*)(&xx))+(off))

I then used it like this:

 

 faster_hexprint(DEREFLABEL(TESTDAT,0));
 faster_hexprint(DEREFLABEL(TESTDAT,1));
 faster_hexprint(DEREFLABEL(TESTDAT,2));
 faster_hexprint(DEREFLABEL(TESTDAT,3));
 faster_hexprint(DEREFLABEL(TESTDAT,4));
The resulting code was clean and exactly what we'd expect:

 

	li   r9, faster_hexprint
	movb @TESTDAT, r1
	bl   *r9
	movb @TESTDAT+1, r1
	bl   *r9
	movb @TESTDAT+2, r1
	bl   *r9
	movb @TESTDAT+3, r1
	bl   *r9
	movb @TESTDAT+4, r1
	bl   *r9
Edited by Tursi
  • Like 1
Link to comment
Share on other sites

You just saved me hours :) Thanks!

 

So now I have another experiment to run when I get home... It seems like TESTDAT is actually the first value in the block of data. So the mistake might be declaring it as a pointer.

 

I think this would work (if you don't want the second 'tt' pointer in your binary...:

 

extern const char TESTDAT;
#define TESTDAT_PTR (&TESTDAT)

...

vdpmemcpy(gSpritePat, TESTDAT_PTR, 32);
I suppose the generated assembly should be looked at to see which is friendlier.

 

-M@

Link to comment
Share on other sites

Ok, I think this is funny... if I declare the truth in my C 'extern' statement, it works naturally...

I have a 32 byte pattern referenced wth a DEF in an assembly file.

        def tst_vader
tst_vader:
        DATA >0000, >1030, >6043, >CEFD
        DATA >FDCE, >4360, >3010, >0000
        DATA >0000, >080C, >06C2, >73BF
        DATA >BF73, >C206, >0C08, >0000



Then in my .c code, I actually declare it to be an array of 32 bytes...

extern const unsigned char tst_vader[32];

Now in C, when the symbol tst_vader is referenced, it is a pointer to the beginning of the array of data. Which is exactly what I want.
It is a byte array, and can be indexed and referenced naturally in C.

vdpmemcpy(gSpritePat, tst_vader, 32);

This generated the following assembly code:

        li   r2, tst_vader
        li   r3, >20
        bl   @vdpmemcpy

which is exactly what I would expect.

-M@

  • Like 3
Link to comment
Share on other sites

  • 2 weeks later...

Heads up Taking the modulus of an unsigned char either results in undefined behavior, or a complete failure of gcc to parse the file. Took me two days to figure that one out. because sometimes it would compile, but behave erratically, other times not throwing errors about parse error at end of input or some-such. I'll try to make some code to reproduce the bug.

Link to comment
Share on other sites

  • 1 month later...
  • 3 weeks later...

Once again, it's patch time. They have been added to the first post of this thread

 

Life has been particularly distracting lately, so I haven't been able to spend as much time doing TI stuff as I would like.

 

I have finished a floating point library for ieee singe precision floats, and have been slowly building up the parts of libc which are OS-independent. I would still like to work with a later GCC version, but just haven't been able to get to it. The long-term plan is to write a new OS for the TI hardware, but that seems to be far in the future.

 

Anyway, here's the fixes I made while working on the other stuff:

 

Added compilation pass to better use post-increment addressing
Ensured word alignment for symbols
Removed optimization of tests against zero, they emitted unnecessary opcodes
Fixed 32-bit shift instructions
Fixed shift instructions to handle shift by zero bits
Fixed and instruction to use ANDI when appropriate
Added optimizations for shift of 32-bit value by 16 bits
Fixed multiply to prevent using MPY with an immediate operand

 

The test against zero code I removed was never called in a helpful manner. The idea was to take advantage of notes on register deaths to do some slightly faster, but destructive testing. Unfortunately, those notes are aways removed before the optimization could be used. The end result was to always insert a MOV to do testing, even if an earlier instruction already set the condition flags properly. We're better off without that junk.

 

The new compilation pass searches for places where pointer increments can be merged with an earlier use, resulting in more post-increment addressing. This can save a few instructions in certain use cases and is a fairly neat bit of code.

 

The multiply fix corrects a problem originally seen by TheMole this spring (Yikes! So long ago!), and was pretty straightforward.

 

The other fixes are pretty self-explanatory, and don't need much discussion.

 

One thing that didn't get added to this patch was a failed attempt to reorder the register usage to eliminate MOV instructions. If it worked, it could reduce most code by 7%, but in the end it was too difficult to avoid breaking 32-bit values or scrambling function arguments. Maybe I will get back to this at some point ant try again.

 

Several questions have been asked about GCC natively handling SAMS or paged memory, or far pointers in general. This is not really practical. There are too many different ways that vendors have extended the memory space to be able to support them all in the compiler. The right way to handle this is to make a library of functions to handle these extensions. I'm looking into finding some way to make this easier, but haven't put much time into that effort.

 

As always, If anyone finds any issues or sees a way to improve the generated code please let me know.

  • Like 4
Link to comment
Share on other sites

Several questions have been asked about GCC natively handling SAMS or paged memory, or far pointers in general. This is not really practical. There are too many different ways that vendors have extended the memory space to be able to support them all in the compiler. The right way to handle this is to make a library of functions to handle these extensions. I'm looking into finding some way to make this easier, but haven't put much time into that effort.

 

I don't think you have to worry about any other memory expansions than SAMS since this would account for about 95% of current users. The really cool thing would be to have a high level programming language that supported SAMS in a transparent manner. But I'm beginning to realize I have to write my own compiler to accomplish this goal.

  • Like 1
Link to comment
Share on other sites

Question about gcc on for the TI:

I have been adding to my silly tank game, and have finally gotten to the point where it won't load in emulation (js99er.net) and I noticed that the binary has exceeded 8192, weighing in at 8290. I noticed that other binaries on the SD card for the FR99 that I have contains binaries that are 8192, 16384, 32768, so I was wondering if the binary must be a multiple of 8K in size in order to work.

 

The question is - is the fix as easy as altering my Makefile to signal the assembler or linker to have it pad to 16384 or am I way off base?

Right now my LDFLAGS is set to:

 

LDFLAGS=\

--section-start .text=6000 --section-start .data=8320

 

Any help is appreciated!

Link to comment
Share on other sites

Question about gcc on for the TI:

 

I have been adding to my silly tank game, and have finally gotten to the point where it won't load in emulation (js99er.net) and I noticed that the binary has exceeded 8192, weighing in at 8290. I noticed that other binaries on the SD card for the FR99 that I have contains binaries that are 8192, 16384, 32768, so I was wondering if the binary must be a multiple of 8K in size in order to work.

 

The question is - is the fix as easy as altering my Makefile to signal the assembler or linker to have it pad to 16384 or am I way off base?

Right now my LDFLAGS is set to:

 

LDFLAGS=\

--section-start .text=6000 --section-start .data=8320

 

Any help is appreciated!

 

For programs to work in the FR99, they must be set up to run from ROM.

 

If your program can be assembled to an E/A5 program, that can be converted with Fred's Module Creator to a ROM binary up to 32KiB.

 

If you are producing a ROM binary, anything larger than 8KiB must use bank-switching. Each bank's code must start at >6000. You will need the trampoline code to do the necessary switching. There's plenty of help here once we know what you're trying to do. I have not set up bank-switching in GCC, but I'm sure others here have.

 

...lee

Edited by Lee Stewart
  • Like 1
Link to comment
Share on other sites

 

For programs to work in the FR99, they must be set up to run from ROM.

 

If your program can be assembled to an E/A5 program, that can be converted with Fred's Module Creator to a ROM binary up to 32KiB.

 

If you are producing a ROM binary, anything larger than 8KiB must use bank-switching. Each bank's code must start at >6000. You will need the trampoline code to do the necessary switching. There's plenty of help here once we know what you're trying to do. I have not set up bank-switching in GCC, but I'm sure others here have.

 

...lee

 

Thanks Lee. I haven't tried to assemble to an E/A5 program since (I thought) it's easier and faster to make edits to the code in my editor, save to disk, alt-tab to a terminal, type 'make' to build a ROM binary, alt-tab to the browser and upload to js99er.net using its Open Cartridge button. Test code, alt-tab back to the editor and lather, rinse, repeat.

 

Can an E/A5 binary be natively 32k without any trampolining or other gymnastics to do bank switching? I'd certainly like to learn the tricks that you guys have come up with, so I'll scour this thread some more in case it's been asked/answered before.

 

Thanks!

Link to comment
Share on other sites

  • 1 month later...
  • 1 month later...

Hey everyone, I've got some more patches.

 

At this point, the compiler is getting pretty mature, and finding a bug or a missing feature has become a rare event. I wanted to get these changes out since they work, and I'm not sure what else to work on.

 

Anyway, here's what's new in this patch:

 

Changes to GCC, patch 1.14

Tail call optimization

Confirmed support for the C++ language

 

Tail call optimization can remove the need for a stack in some cases, which is helpful for recursion or cases where a call is the last operation in a function. This is a common optimization strategy, and a quick Google search can be more informative tha what I could write here.

 

The other feature that has now been tested is support for the C++ language. This can sometimes be more useful than C for certain dessigns. This also implies that all the other languages GCC supports: Java, Fortran and Ada, should work also. Unfortunately, I don't know Java or Ada well enough to test properly, and I doubt that many people are waiting desperately to use Fortran.

 

I've included a "hello world" example in C++ in the first post of this thread, as well as the patch and a new version of the GCC installer that uses the latest patches.

 

I've looked into far pointers, and unfortunately, I can't make them work in the compiler. GCC only uses one size for pointers. If we used 32-bit far pointers, we would have to use 32 bits for ALL pointers, resulting in much less efficient code in the general case. We would lose the use of at least one register. The stack could be up to twice as large. Code size would be larger, and memory requirements would increase. In short, there's no real advantage. If far pointers are really needed, they will need to be emulated by function calls. I'm not happy about this, but that's where we stand.

 

I've also noticed that when using -O2 optimization, GCC likes to inline function calls as much as possible. If functions are not marked as static, they will be duplicated and not called in the output binary. This is done under the assumption that a non-inlined version will be needed for other code. So use static functions whenever possible.

 

If anyone has any problems, or finds a bug or improvement, please let me know.

  • Like 9
Link to comment
Share on other sites

  • 3 weeks later...

So now that Insomnia has built a good C compiler for the TI-99, (merci) it becomes possible to build small C for the TI-99.

This would allow you to create something that resembles C programs on the TI-99 itself.

It would not compile very fast (means glacial speed) and of course it would never generate the code quality that we get with this GCC implementation.

Small C V1.0 was a 20K binary on DOS so it's possible. In fact I would think that 9900 code would have higher semantic density than '86 code

so it should be a little smaller.

 

I notice that Small C 1.1 however bloated to 33Kbytes. Such a tragedy. ;-)

 

B

Link to comment
Share on other sites

I should have guessed as much or maybe check the programming resources page?

Amazing how much work people do for this old platform.

 

Have you every used it? I think I will have to get it and see what the code looks like.

 

...another day.

 

B

Link to comment
Share on other sites

I should have guessed as much or maybe check the programming resources page?

Amazing how much work people do for this old platform.

 

Have you every used it? I think I will have to get it and see what the code looks like.

 

...another day.

 

B

Yes I wrote a sprite definer program with it. It works just fine but the edit/save/compile/assemble/load/link/test/repeat cycle is a pain in the ass. Give me Forth any day!

 

https://youtu.be/Jf_m0_O5RXc

  • Like 3
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...