Jump to content
IGNORED

GCC for the TI


insomnia

Recommended Posts

I've never found strings to pull in memcpy, but... GCC /does/ make assumptions about the standard library you have, so I'm not surprised that something is. For example, I implemented a printf in libti99, but then I found that if you code printf() with no variables, gcc generates puts() instead (by deafult).

 

libti99 has gotten a little cluttered, and fills multiple voids now. Eventually I do hope to clean it up and make it more stable.

  • Like 1
  • Thanks 2
Link to comment
Share on other sites

Thanks guys.

 

I like the sound of that. It makes a compiler where you are really in control.

The reason I asked was I made this linker for my own amusement that links EA3 object and lets you RUN (B) , CALL (BL) or BLWP into the external code from Forth.

It's fun to see it work and for the first time in my life I understand a bit about what linkers actually do. :) 

Maybe C99 is compatible?  I will take a look.

Or maybe I should learn to parse the ELF file format... hmm. ;) 

 

EDIT:  Maybe not. :)  262 page specification document.  :o

Link to comment
Share on other sites

You can get the gcc linker to output flat binaries and a text file with the symbol table, with those two things you should be able to write a relatively simple tool that combines the information in those two files in to an EA3 object file. I think it might even be possible to write a linker script to output something that converts easily to EA3?

Link to comment
Share on other sites

1 hour ago, TheMole said:

You can get the gcc linker to output flat binaries and a text file with the symbol table, with those two things you should be able to write a relatively simple tool that combines the information in those two files in to an EA3 object file. I think it might even be possible to write a linker script to output something that converts easily to EA3?

Thanks for that info. 

I was thinking similarly.  I used a DOS Forth system in the 90s that took the .OBJ and the .MAP file and allowed linking external language code into the Forth environment.

Forth traditionally didn't play nice in the sandbox with other languages in the old days.  Not true today with commercial systems.

I am just keeping my old brain running so making something like this work on the 99 is good challenge.

 

 

Link to comment
Share on other sites

I already have a tool that converts binary files to EA#3, though it's not terribly flexible (it only outputs absolute data). I used it for some XB stuff way back in the day.

 

I dunno. Offered if it's useful. While I use OBJ2BIN a lot, BIN2OBJ I haven't looked at in many years. ;)

 

https://github.com/tursilion/tiobj2bin

 

Edited by Tursi
Link to comment
Share on other sites

3 hours ago, Tursi said:

I already have a tool that converts binary files to EA#3, though it's not terribly flexible (it only outputs absolute data). I used it for some XB stuff way back in the day.

 

I dunno. Offered if it's useful. While I use OBJ2BIN a lot, BIN2OBJ I haven't looked at in many years. ;)

 

https://github.com/tursilion/tiobj2bin

 

Thanks Tursi.

I will give it a whirl. 

Link to comment
Share on other sites

  • 5 months later...

I wanted to use GCC for the TMS9900 on my Mac.

 

Seems simple enough, especially as we have install-mac.sh script which I think was posted by @PeteE a few years ago. So I first grabbed the installation script included in the first message of this topic, as it includes the necessary patch files for gcc-4.4.0 and binutils-2.19.1. Then copied install-mac.sh and tried to run it. I tested this on two Macs, one Intel based running Catalina and one running M1 Pro Monterey. Of course, while the installation script is cool, it did not work as expected. I tried to keep notes of what I did to make this work, so that I finally could compile and install binutils, as well as gcc. I used Force Command as the test project at the end, although I have not tested if the generated binary works yet...

 

Problem #1: libiberty, which is separately contained in both binutils and gcc, would not compile. The reason is that there were no prototypes for malloc and free defined. I tracked it down to STDC_HEADERS not being defined.

Fix #1: Modify build/binutils-2.19.1/libiberty/config.in so that instead of #undef STDC_HEADERS I have #define STDC_HEADERS 1. This needs to be done for both copies of libiberty/config.in, within binutils and gcc build directories.

/* Define to 1 if you have the ANSI C header files. */
//EPEP #undef STDC_HEADERS
#define STDC_HEADERS 1

 

Problem #2: MPFR and GMP libraries required to compile GCC.

Fix #2: Two things, install MPFR using

brew install mpfr

(yes you need homebrew)

And then edit install-mac.sh to have the right paths to homebrew stuff. This varied depending the macOS version in my case, with Catalina its like this:

../configure --prefix $PREFIX --target=tms9900 --enable-languages=c --with-gmp=/opt/local/ --with-mpfr=/opt/local

 

Problem #3: The newer compilers are strict about prototypes, and some are missing from tms9900-protos.h

Fix #3: Edit build/gcc-4.4.0/gcc/config/tms9900/tms9900-protos.h and add to the end:

void tms9900_asm_output_dwarf_delta (FILE *file, int size, const char *lab1, const char *lab2);
void tms9900_asm_output_dwarf_offset (FILE *file, int size, const char * lab, section *base);
int tms9900_address_type(rtx op, enum machine_mode mode ATTRIBUTE_UNUSED);

 

Problem #4: (Only for Apple Silicon running Monterey): It appears the old GCC-4.4.0 does not include some platform specific file for arm64. Not surprising. As a result, the variable host_hooks is not defined anywhere, although it is declared. So you end up with a linker complaint.

Fix #4: As I was lazy, instead of understanding how the platform specific thing should be done, I just added the missing definition to the file: build/gcc-4.4.0/gcc/ggc-common.c

const struct host_hooks host_hooks = HOST_HOOKS_INITIALIZER;

 

After the above changes, if my notes are correct, both binutils and gcc can be compiled. However:

Problem #5: The make install step fails with both gcc and binutils, since on macOS the command gcc invokes clang, and it does not support the -print-multi-os-directory option. This seems to be a bit silly, since invoking gcc with this option really only prints the current directory, i.e. period. Probably in some other setup it does something more meaningful.

Fix #5: I just brute forced this by editing libtool.m4 files in the root directories of gcc and binutils like this:

# EPEP  lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
  lt_multi_os_dir=.

Obviously the first line above is just to comment out to the existing command, and replace it with the output directly...

As a related problem, I think I also needed to edit the makefile at build/binutils-2.19.1/libiberty/Makefile to do the same thing. I believe this Makefile should be autogenerated, but not quite sure. Keeping all notes straight was a bit tough, as I had to try to build so many times, but anyway I edited the said Makefile at line 357 like this:

# MULTIOSDIR = `$(CC) $(LIBCFLAGS) -print-multi-os-directory`
MULTIOSDIR = .

So the same simplification.

After those changes the make install commands in install-mac.sh succeeded, as the -print-multi-os-directory stuff was not used anymore.

 

Testing with Force Command compilation

After having a seemingly working toolchain, and adding it to PATH, I ran make for force command. It almost worked. Turns out the builtin implementation of echo command differs from Linux echo, as does sed command. Both are used to do some GPL compilation. Two things are needed to fix this:

brew install gnu-sed

and the I edited force command's Makefile, the line activating XGA99 as follows:

$(FNAME)G.bin: gpl-boot.g99 $(FNAME).elf
        python3 $(XGA99) -D "CART=$(shell echo '>\c' ; grep _cart mapfile | gsed 's/^\s*0x0*\([0-9a-f]*\) *_cart/\1/')" -o $@ $<

There are two changes above: the original "echo -n '>'" command is replaced with "echo '>\c' and the sed command is activated with gsed to use the GNU sed. Without these changes running XGA99 will not work, as xga99.py crashes since the value of CART is some really long string instead of a hexadecimal numeric value.

 

I guess it goes without saying that you need to understand a bit what you are doing if you want to follow these steps... In my case I had to run the compilation and install steps multiple times, so I needed to read and understand what install-mac.sh is doing. Also occasionally I had clean the build directories, since the configure etc. scrips cache all kinds of things. install-mac.sh is still super helpful, as it does a lot of groundwork.

 

EDIT: I discovered that the arm64 build of the compiler does not really work, it crashes with internal compiler error. So need to dig in more.

Edited by speccery
  • Like 6
Link to comment
Share on other sites

Where can I find ea5split? Is it by @lucien2 ? Doing searches in GitHub only give a dozen Makefiles using it. Searches here only indicate that it can be found in this thread, but I am at a loss where, it does not appear to be in the first post where everything else is... Everyone keeps talking about and using it, but it seems to be one of those mythical pieces of software which I cannot find. Sorry this is probably an obvious question.

 

EDIT: While I couldn't find it in the mighty Internet, I realised that perhaps I should search my own drive. Lo and behold, there it was, in a dropbox folder. I have now idea where I have got it, but it appears problem is solved.

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

See these excellent instructions on installing GCC-TMS9900 which also includes how to install EA5SPLIT:

 

### EA5SPLIT

This utility will convert the potentially large EA5 file produced by `elf2ea5` into 
a sequence of 8K PROGRAM image files as is usually required by EA5 loaders. It also 
converts the files to FIAD in TIFILES format making them immediately usable on a TIPI 
or HDX, or in classic99 emulation.

Download it:

```
cd $WORK
wget https://atariage.com/forums/applications/core/interface/file/attachment.php?id=261992 -O ea5split3.zip
```

Build it:

```
unzip ea5split3.zip
cd ea5split
make
```

It produced executable `ea5split`. Copy that to the same bin directory as the others:

```
cp ea5split ~/tms9900gcc/bin/
```

  • Like 2
  • Thanks 1
Link to comment
Share on other sites

Thank you @xahmol and @TheMole

 

Well I did some a bit strange and perhaps useless work, but at least I learned more about elf format and adaptation to TI: 

 

I compiled GCC on my Linux box to test things a bit. In this thread there were a couple of example programs by @lucien2 if I got the handle right. One of the example programs was "RUSH HOUR". I tried to compile and run it on js99er.net as a cartridge. The build process worked, and the program launched under emulation. The software seemed to work, but I could not figure out how play the game, the screen seemed too empty. After mulling over the source code and checking things out in the debugger, I realised that there was no graphics data in VDP memory. After a bit more digging I realised that all initialised data was missing. The .data section did not get initialized but was all zeros, at least on emulator. However, crt0_cart.asm had code to init the .data section, but as I looked at it, the init data pointers at ROM address >6018 were all empty. So, rather than trying to find the right tool (yeah so smart) I checked out the elf2cart source code I had, and realised that indeed it only handles the .text section. So I hacked it so that it also includes .data section initialisation data into the ROM image and then patches the table at >6018 accordingly. I am sure this was somewhat pointless, since there must some piece of code that already does this, but whatever, it was not much work and like I said I learned a bit more about elf file structure.

 

 

ep_elf2cart.zip

 

Edited by speccery
  • Like 5
Link to comment
Share on other sites

  • 3 weeks later...

Hi everyone!
Sorry I am asking again a newbie question here but I swear I have been busy with many non-TI99-related projects.
I am using GCC to compile my game(s) whcih I link it with crt0_ea5.o and Tursi's CONIO implementation with this messy Makefile:
https://github.com/Fabrizio-Caruso/CROSS-LIB/blob/master/src/games/chase/makefiles.chase/Makefile.gcc_tms9900_targets

GCC produces an ELF file. This cannot be loaded into an emulator.
So I use elf2ea5 to have something loadable through option 5 in the Editor/Assembler with Classic99.
The binary file is smaller than 8k. I am not sure that I have to process it with ea5split or not.
In either case the Editor/Assembler fails to load it and returns "I/O ERROR CODE 7".
What am I doing wrong?
 

xctbelf xchase.ea5.bin

Link to comment
Share on other sites

On 3/31/2022 at 4:48 PM, Fabrizio Caruso said:

Hi everyone!
Sorry I am asking again a newbie question here but I swear I have been busy with many non-TI99-related projects.
I am using GCC to compile my game(s) whcih I link it with crt0_ea5.o and Tursi's CONIO implementation with this messy Makefile:
https://github.com/Fabrizio-Caruso/CROSS-LIB/blob/master/src/games/chase/makefiles.chase/Makefile.gcc_tms9900_targets

GCC produces an ELF file. This cannot be loaded into an emulator.
So I use elf2ea5 to have something loadable through option 5 in the Editor/Assembler with Classic99.
The binary file is smaller than 8k. I am not sure that I have to process it with ea5split or not.
In either case the Editor/Assembler fails to load it and returns "I/O ERROR CODE 7".
What am I doing wrong?
 

xctbelf 10.08 kB · 3 downloads xchase.ea5.bin 4.08 kB · 7 downloads

xchase.ea5.bin looks like an EA5 file, but it is raw... not on in a DSK image or in a FIAD.  I think ea5split will wrap it in a TIFILES file... or use something like xdm99.py to import it into a FIAD:

xdm99.py -T xchase.ea5.bin -t -f PROGRAM -n XCHASE -o XCHASE

 

( -T : into FIAD, -t : use tifiles type of fiad instead of v9t9, -f PROGRAM : set the ti file type to PROGRAM IMAGE, -n XCHASE : set the name inside the tifiles header, -o XCHASE : output the TIFILES FIAD to file named XCHASE )

 

after I do that I get: XCHASE

 

and that I am able to load in Classic99 via Editor Assembler Option 5, I get a black screen, with a blinking cursor near the bottom right.  If I push some buttons things show up on the screen and move around.

  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...

I fear I may have found some big bug in this version of GCC... ?

I can compile my own C games with more than a dozen cross compilers and a couple of native compilers with no problem.
With this modded GCC I have managed to compile a simplified version of one of my games which avoids some of the expressions that cause errors but once I try to compile a full game I get unexpected errors.

 

When I use this modded GCC for TI99/4a I am getting this error in different places:

internal compiler error: output_operand: invalid express
Please submit a full bug report,



For example it fails at this line:

        return !(level%5);


Has this bug been addressed and fixed somehow?
 

Link to comment
Share on other sites

Well, I triggered exactly the same bug also using the % operand:

https://atariage.com/forums/topic/164295-gcc-for-the-ti/?do=findComment&comment=4785108

 

Not sure if your variable is a char or an int, but solved in my code by changing from a char to an int.

Maybe caused by the CPU being 16 bit instead of 8 bit?

 

Anyway: was to lazy to file a bug as my fix was that simple.

Edited by xahmol
  • Like 2
Link to comment
Share on other sites

There is an even worse bug related to the modulo.
Some modulo expressions are compiled but wrong code is produced.
For example in my code:

    uint8_t missileLevel(void)
    {
        return ((level%5)==3) || (level==5);
    }    

is compiled by, for level=1 (level is uint8_t), it returns 1 (true), which is very wrong.

Is there any hope these bugs may be fixed one day? Is anyone maintaining this mod?
 

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

Unfortunately I also have to report a bug that is not related to the modulo... ?

This bug seems to be related to struct but it could be something going wrong in the optimizer.

My game Cross Horde (which is written in C with my universal library Cross-Lib: https://github.com/Fabrizio-Caruso/CROSS-LIB) works perfectly fine on about 200 different computers, consoles and other devices when compiled with more than a dozen very different cross-compilers and native compilers (including recent versions of GCC) but this GCC version produces a partially broken binary in this part of the code:
 

void handle_item(register Item* item)
{
    if(item->_active)
    {
        if(item->_y<BOW_Y)
        {
            _XL_DELETE(item->_x,item->_y);
            if(main_loop_counter&1)
            {
                ++(item->_y);
            }
            #if !defined(_XL_NO_COLOR)
                    // TODO: GCC for TI99 does not display the correct tile
                    _XL_DRAW(item->_x,item->_y,item->_tile,item->_color);
            #else
                _XL_DRAW(item->_x,item->_y,item->_tile,0);
            #endif
        }
        else
        {
            if(item->_counter&1)
            {
                _XL_DELETE(item->_x,item->_y);
            }
            else
            {
                #if !defined(_XL_NO_COLOR)
                _XL_DRAW(item->_x,item->_y,item->_tile,item->_color);
                #else
                _XL_DRAW(item->_x,item->_y,item->_tile,0);
                #endif
            }
 
            if(item->_x==(bow_x>>1)+(bow_x&1))
            {
                item->_effect();
                _XL_PING_SOUND();
                item->_active=0;
            }
            display_bow();
            --(item->_counter);
            if(!(item->_counter))
            {
                item->_active=0;
            }
        }
    }   
}

where the *first* occurrence of
 

_XL_DRAW(item->_x,item->_y,item->_tile,item->_color);

seems to be miscompiled with 'item->tile' not returning the right value.

The visible buggy behavior during game play is that objects dropped by killed zombies are not shown while they are falling but they do get displayed afterwards by the second occurrence of the very same line, which is correctly compiled (i.e., the dropped item is displayed once it has reached y coordinate BOW_Y).

P.S.: You may try to compile Cross Chase and Cross Horde for the TI99 with Cross-Lib with:
xl chase ti99
xl horde ti99
or by using the respective Makefiles.
You may also have to adapt the Makefiles.

Edited by Fabrizio Caruso
Link to comment
Share on other sites

@jedimatt42 I cannot use any non-standard C89 code or anything that is not supported by all other cross-compilers for vintage systems. The very goal of Cross-Lib (https://github.com/Fabrizio-Caruso/CROSS-LIB) is to provide a universal abstraction layer, i.e., you code in C89 and use my APIs and you can massively cross-compile your games with more than a dozen compilers to get your game for 200 different targets while still using the *very same* code with no adaptation.

On the other hand, I have to exclude anything wrong I might be doing such as for example passing the wrong options to the compiler/linker.

What should I pass in the .data=.... option. Does .data stand for the C stack? I have seen examples with .data=2080. Does this mean that variables are in the tiny scratchpad memory and not in the memory expansion? If that is the case how can I have more memory for the variables and avoid running out of memory?

Some of the options I have tried are in:
https://github.com/Fabrizio-Caruso/CROSS-LIB/blob/master/src/games/horde/Makefile.horde#L2293

Edited by Fabrizio Caruso
Link to comment
Share on other sites

The .data segment is writable memory with initial values. 

The .bss segment is writable memory without initial values

The .text segment is stuff that can probably be read-only if well respected. 

 

The C stack is stored in R10, and is usually set with a little assembly in your equivalent of a crt0.asm or equivalent. I usually set it to the top of the lower memory expansion. On a 4A, the 32K ram is divided into memory from 0x2000 to 0x3fff ( an 8k chunk ) and then 0xA000 to 0xFFFF, the remaining 24k chunk.  So a: li sp,>4000   will set that... sp is a gcc alias on the 4A for R10.


as for where you want .data to live, if I'm building for a cartridge, I set it to 0xA000, and just let .bss follow, presuming you require memory expansion... .text then goes in 0x6000 (but then I end up extracting data from the elf and stuffing it into the cartridge, and copying into the ram during init in the program entrypoint (crt.o) -- there were some examples of that in the initial examples from Insomnia.

... 

If I'm building to load off disk and run entirely in ram expansion, then I set .text to 0xA000, and set .data to 0x2000 ... this gives 24k for code. And 8k for your stack and global variables.

 

Ok, I see, and recognize that example... I've been using a more complex linkfile, but you don't need that yet... so.. .data is still data... I don't believe there is any value in it being 0x2040 instead of just 0x2000... so data fills in from the bottom of lower memory expansion, and stack grows down from the top of lower memory expansion. 

 

 

I don't see options for this on the command line anywhere... surely if you are trying to abstract everything you can make concessions like:

 

...
#ifdef __TI99__   // you'll have to define this somewhere.
  #define STRUCT_ATTRS __attribute__(__packed__)
#else
  #define STRUCT_ATTRS  
#endif

...

struct STRUCT_ATTRS mystruct {
 ...
};

 

I see you are making extensive use of the #ifdef already... what's one more param... If you want your users to define their own structs, you can go further and require they use macros for that...

 

#ifndef _MACHINES_H
#define _MACHINES_H

#ifdef __TI99__
  #define DEF_STRUCT struct __attribute__(__packed__)
#else
  #define DEF_STRUCT struct
#endif
  
#endif // _MACHINES_H
...
  
#include <machines.h>

DEF_STRUCT myStruct {
  ...
};

 

I don't expect the just use C89 goal will fly forever.

 

  • Like 2
Link to comment
Share on other sites

I don't remember recommending packed structures... I usually minimize the use of chars in my gcc code because, as has been found, it's still not 100%. It's worlds better than it was, but there are still some issues.

 

I don't know if @insomnia is still updating this?

 

The problem is that the way the TI handles 8 bits of data is somewhat inconsistent, and GCC struggles with providing enough hints to generate the code. For instance, if you use a MOVB with an address, you get exactly the byte you asked for, but if you use it with a register you get the most significant byte (rather than the least). This makes conversion between char and int for promotion and the like very easy to get confused - you have to try and remember if your 8-bit value is in the most significant byte or least significant byte of a register at any given time, and move it to the correct position for whatever operation you need to do next.

 

I know when I attempted this way back before Insomnia succeeded, the char handling was absolutely killing me. I think it I were to ever try it again I might just pretend chars don't exist and internally promote everything to 16 bits. ;)

 

  • Like 2
Link to comment
Share on other sites

9 minutes ago, Tursi said:

I don't remember recommending packed structures...

You didn't recommend it, but you used it in libti99, and when I struggled, I just copied the example I had that was working.

 

packed structures do cause some other problems that I have observed, accessing 16bit values inside them always generates code that has to compose 2 bytes even if it is aligned, cause it might not be. 

  • Like 1
Link to comment
Share on other sites

@jedimatt42, Ideally I would prefer a compiler option instead of changing the language. Maybe GCC has one for packed structs.
I use hundreds of macros in the library but the (user's) game code is supposed to look like C89 + my APIs and the only directives that can be used there are not about the language itself but only about some of the abstract target features (size of screen, presence of colors, sounds, etc.) or in some cases about enabled/disabled game features.

The problem with GCC is not just structs. Modulo seems to be badly broken with both compiler's errors or worse wrong behavior. I see this in the first two games I have compiled. I fear I am going to have to patch all of them to work around this issue.

Has anyone addressed the modulo issue, as well? Has anyone found work arounds?

In the future, I will give a shot at the new mod of PCC by Alan Cox to see if it can handle my games better than GCC.

Edited by Fabrizio Caruso
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...