Jump to content
IGNORED

GCC for the TI


insomnia

Recommended Posts

@jedimatt42
I wonder whether my problem is not the struct or something else. Is there a way to compile with no optimization?

For the sake of finding the root cause I have tried #pragma pack(1), -fpack-struct=1, __attribute__(__packed__), __attribute__((packed)) in several combinations. I still get the bug with item->_tile wrongly compiled in the first occurrence of the line below (but not on the second)

_XL_DRAW(item->_x,item->_y,item->_tile,item->_color);
Link to comment
Share on other sites

I have found an ugly work around to get the correct behavior: it is enough to assign item->_tile to a variable before invoking the command. The following function is correctly compiled. I wish very much @insomnia or anyone else capable of fixing the bugs, could fix these bugs.
 

void handle_item(register Item* item)
{
    if(item->_active)
    {
        // TODO: Necessary for GCC for TI99
        uint8_t item_tile = item->_tile;
        
        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 with item->_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;
            }
        }
    }   
}

 

Link to comment
Share on other sites

One more bug to report (not related to mod, nor struct).
Remark: I am currently using @Tursi library to display strings and to write into screen memory to display single characters in graphics mode 1.
In my Cross Snake game (https://github.com/Fabrizio-Caruso/CROSS-LIB/tree/master/src/games/snake) I want to display a list of vertical items (redefined characters) with a string next to them with the following code:

        for(i=0;i<NUMBER_OF_STRINGS;++i)
        {
            _XL_DRAW(XSize/2-6,YSize/8+LINE_OFFSET+LINE_SKIP*i, images[NUMBER_OF_STRINGS-i], image_colors[NUMBER_OF_STRINGS-i]);
            _XL_SET_TEXT_COLOR(_XL_WHITE);
            _XL_PRINT(XSize/2-4,YSize/8+LINE_OFFSET+LINE_SKIP*i, (char *) strings[NUMBER_OF_STRINGS-1-i] );
        }

where _XL_DRAW is a macro that uses vdpmemset (from Tursi's libti99) and correctly produces a character at the expected position and where _XL_PRINT is a macro that uses gotoxy and cprintf (from Tursi's libti99) and does not produce any string in this code snipet but it otherwise works fine elsewhere.
Remark: The very same code gets compiled and produces the expected result on all targets.

The attached picture shows the problem (tiles are display but no string to their right is displayed) 

 

 

image.png

Link to comment
Share on other sites

@Tursi I don't think it is your lib but I leave it up to you to judge whether we should consider something wrong in it.
I think it is the compiler doing something really wrong.

If I change the original version (working on all compilers except this mod of GCC):
 

        for(i=0;i<NUMBER_OF_STRINGS;++i)
        {
            _XL_DRAW(XSize/2-6,YSize/8+LINE_OFFSET+LINE_SKIP*i, images[NUMBER_OF_STRINGS-i], image_colors[NUMBER_OF_STRINGS-i]);
            _XL_SET_TEXT_COLOR(_XL_WHITE);
            _XL_PRINT(XSize/2-4,YSize/8+LINE_OFFSET+LINE_SKIP*i, (char *) strings[NUMBER_OF_STRINGS-1-i] );
        }

with the following code, where I have only added a nonsensical extra line with _XL_PRINT(0,i,""):

        for(i=0;i<NUMBER_OF_STRINGS;++i)
        {
            _XL_DRAW(XSize/2-6,YSize/8+LINE_OFFSET+LINE_SKIP*i, images[NUMBER_OF_STRINGS-i], image_colors[NUMBER_OF_STRINGS-i]);
            _XL_SET_TEXT_COLOR(_XL_WHITE);
            _XL_PRINT(XSize/2-4,YSize/8+LINE_OFFSET+LINE_SKIP*i, (char *) strings[NUMBER_OF_STRINGS-1-i] );
            _XL_PRINT(0,i,"");
        }

then it works but the extra nonsensical line has to have the "i" for-loop variable in it in order to "fix" the issue, e.g., _XL_PRINT(0,0,"") does not fix the issue.

Edited by Fabrizio Caruso
Link to comment
Share on other sites

5 hours ago, Fabrizio Caruso said:

then it works but the extra nonsensical line has to have the "i" for-loop variable in it in order to "fix" the issue, e.g., _XL_PRINT(0,0,"") does not fix the issue.

Probably compiler, is 'i' a char instead of an int?

 

Link to comment
Share on other sites

I will note the data you are passing to your macros is pretty complex stuff. I'm not even going to try to parse those lines.

 

While it's 100% legal, legibility suffers and as you are learning, it's a good way to trip up bugs with less-tested compilers. ;) But when you need to present code to help authors with bugs, you should aim for legibility over compactness. That's a case where I'd be breaking out the math to temporary variables which both simplifies the work the compiler is doing and makes it easier to home in on where the fault may be occurring. But maybe that's just cause I've worked with a /lot/ of buggy compilers. ;)

 

 

 

  • Like 4
Link to comment
Share on other sites

This might be worth exploring @Fabrizio Caruso

 

 

 

It is fresh and young and the author is actively working on it. And using it to cross compile a lot of otherwise 8-bit c code.

 

I think I must be as successful with this tms9900-gcc because I program to it and the 16bit cpu. I've gotten used to reading the generated assembly as a first step when debugging, and I think I have been conditioned to use 16bit variables, parameters and return values.

 

I think insomnia was building gcc for the purpose of writing a bespoke 9900 OS, so he just may not have experienced these bugs.

 

Anyway, keep the bug reports coming! 

  • Like 3
Link to comment
Share on other sites

@jedimatt42 Yes, I know about the new compiler. I have mentioned it in one of my posts. I will give it a try.
Nevertheless it is a pity that GCC is no longer maintained ? @insomnia any hope of some bug-fixes??

@jedimatt42 I just found one more bug but it a random one (like some of the other bugs). Hard to isolate and reproduce.
One expression gets the wrong value by 1 only in some cases. So maybe a dirty (carry bit).

 

#if defined(BUGGY_HORIZ_DRAW)
DRAW_HORIZONTAL_LINE(1+(XSize>>1)-(innerHorizontalWallLength>>1), YSize/2, innerHorizontalWallLength);
#else
DRAW_HORIZONTAL_LINE((XSize>>1)-(innerHorizontalWallLength>>1), YSize/2, innerHorizontalWallLength);
#endif

The very same (XSize>>1)-(innerHorizontalWallLength>>1) expression is correctly evaluated elsewhere in the code.

I have tried to compile with no optimization but -O0 does not work and -O1 does not fix this issue.

Link to comment
Share on other sites

  • 11 months later...
On 3/12/2019 at 1:58 PM, jedimatt42 said:

 

There is an enlightening thread somewhere on bank switching that details the calling convention the most I've seen..... I've probably been learning assembly mostly from looking at the generated code when my C goes wrong.. :)

 

R10 is used as the stack pointer...

BL is used to call a function. and RT to return (b *R11)

when a function is called it expects arguments on registers R1, R2, R3, etc... until they don't fit...

 

An .asm file that defines an address to code can be called as a function, but you'll have to declare a prototype in C for that symbol...

 

so for a function in a .asm referenced as 'myasm' that takes zero arguments, and returns none:

 

 

extern void myasm();

 

 

If you want to be able to pass arguments in a few registers, expect them in order starting with R1... not sure how many it'll use... upto R1 through R9 I think, after that things get put on the stack by the caller.

 

 

extern void myasm(int argR1, int argR2, int argR3);

 

 

It seems that since the instruction set prefers the most significant byte for char type args, that passing an 8 bit type will be in the high side of a register...

 

 

extern void myasm(char argR1); // will put the 8 bit value in R1 << 8 (if that makes sense) 

 

 

return value is returned in R1. Same rules for 8 bit values or 16 bit values..

 

 

 

extern int myasm();

 

 

I believe it is the responsibility of the function to 'pop' the stack for any 'pushing' it has done... So R10 should be restored if moved... the stack grows down... so pushing is subtracting 2 from the R10, and storing the value in that location. pop is read, and increment.

 

Things get more complicated if you have a large number of arguments and the system resorts to the stack to pass them.

 

-M@

I feel like a time traveler here, observing these past posts, trying to get up to speed before asking questions, since I presume my answers will come (in time). But this response by jeddimatt42 is one of the most succinct and wonderful responses to this question. His memory cartridge-based memory test utility is the first program I got running (pulled down from gitlab). With a little work and a few iterations I had it stripped it all down to a simple hello world. I have yet to successfully build anything other than cartridges and am wanting to construct an EA5 image that can be used by XB2.9 call mload, or even E/A option 5 itself. I'm assuming as I travel time through this thread I'll see the help I'm looking for. I anticipate a large number of questions once I get to page 28 (am on 20 now). Thanks.

  • Like 1
Link to comment
Share on other sites

Alas I have reached my perception of current time and found the end of this currently 28 page entry. Yikes. I still need to compile any remaining questions and have several examples to try before bothering you all. I have two cartridge examples that work — memory test and a hello world, from culling down the memory test. I want to get an ea5 program running and ideally a program that can be started from xB 2.9 moload or similar.

Link to comment
Share on other sites

1 hour ago, mrvan said:

Alas I have reached my perception of current time and found the end of this currently 28 page entry. Yikes. I still need to compile any remaining questions and have several examples to try before bothering you all. I have two cartridge examples that work — memory test and a hello world, from culling down the memory test. I want to get an ea5 program running and ideally a program that can be started from xB 2.9 moload or similar.

Another example here: https://github.com/peberlein/turmoil

It looks like I have a linker script for creating EA5 program as well as cartridge. It requires elf2ea5 and ea5split programs, which I think can be found in this thread.

  • Like 1
Link to comment
Share on other sites

2 hours ago, PeteE said:

Another example here: https://github.com/peberlein/turmoil

It looks like I have a linker script for creating EA5 program as well as cartridge. It requires elf2ea5 and ea5split programs, which I think can be found in this thread.

Oh, thank you, PeteE. That looks very interesting. I'm looking for something that will allow for the options like this does. I'm using the ccureau / tms9900-gcc project that provides GCC patch 1.19 with libti99 and I just hacked in the ea5split program this evening. I have successfully now built an EA5 program (with text and speech) as well as the libti99 testlib that generates 3 EA5 files. I have some cleanup to do to keep it all straight and will further study what you've done and incorporate it. I've been in the SW industry for many years and know very well that plagiarism is often the best way to learn and get things done.

 

I've been reading these 28 pages over the course of about 3 days, an hour or two here and there. It was painful at times, including some rather bad manners on the part of some posters. That made me wonder if that's why our GCC guru has gone silent due to some lack of appreciation.  I hope he will return. Anyway it has been the examples that have helped the most. 

 

Thanks again for making me aware of your linker script. 

  • Like 1
Link to comment
Share on other sites

@mrvan @insomnia I am also using this GCC mod. The main problem with it is that it is bugged and the original developer of the mod (insomnia) has not updated and corrected the many bugs.
In this thread I have listed several bugs. I know it is a home-brew experimental compiler. I am not complaining... but it could be improved.
I am stilling hoping that he will eventually show up.
There is also an alternative C compiler (https://github.com/EtchedPixels/pcc-tms9995), which is actively developed and probably maintained by a very strong developer.

 

Link to comment
Share on other sites

5 hours ago, Fabrizio Caruso said:

@mrvan @insomnia I am also using this GCC mod. The main problem with it is that it is bugged and the original developer of the mod (insomnia) has not updated and corrected the many bugs.
In this thread I have listed several bugs. I know it is a home-brew experimental compiler. I am not complaining... but it could be improved.
I am stilling hoping that he will eventually show up.
There is also an alternative C compiler (https://github.com/EtchedPixels/pcc-tms9995), which is actively developed and probably maintained by a very strong developer.

 

Well, there can be some hope in this. I took a look at this alternative C compiler and built/installed it. When compiling an empty shell (int main (int argc, char *argv[]) { return 0; }, it complains about not finding libSystem. Have you successfully built with this compiler? My compilation command was simply pcc hello.c. What did you do?

Link to comment
Share on other sites

I'm looking at the location of the stack typically defined by *crt*.asm files in all the examples I've found. The cartridges seem to place the stack at 8400h and EA5 programs at 4000h, growing downward. That provides 1K and 4K, respectively. Is there any reason not to use 8400h for both cartridges and EA5 programs, assuming 1K of stack is sufficient? Are there any gotchas, such as perhaps XB 2.9 or E/A use particular areas for storing things? I'm looking for the conventions used, now that I understand the layout of memory. Goodness why did TI split the 32KB memory expansion into two pieces?!#? Really it's all part of the fun targeting this highly constrained yet powerful environment. Also, I don't see anything obvious that ensures the stack doesn't overflow. Is that just part of the fun as well?

 

I commonly use XB 2.9 with CALL MLOAD(FILE, 1) to load and run the EA5 programs. I have no idea how XB maps memory, really except that I can see it provides more than 32K to XB programs, around 24KB for program space and 12KB for stack. What conventions is XB using for memory mapping? I'd like to do compatible things :-).

 

And further, for context, I'm considering developing a simple heap manager that provides malloc and free, with coalescence of free space, etc. Obviously that needs to be located somewhere--any thoughts where best? The region 2000h-4000h seems pretty good, with the need to make sure the stack and heap pointers don't cross.

Link to comment
Share on other sites

I generally use the lower memory expansion for the gcc stack. 2000-3FFF. The initial value of the gcc stack pointer would be 4000 as it is decremented first before the address is used to hold data.

 

8400 is the top of the 256 byte section of memory. There is no ram below 8300. This is where the registers live for the CPU. The CPU only has a program counter, workspace pointer, and status register. The things we call R0-R15 are not actually CPU registers, but offsets from the workspace pointer. 

 

The console ROM GPL interpreter uses 83E0-83FF as it's workspace. The ROM interrupt service routine uses 83C0-83DF.  So if you have the stack there, you must keep the interrupts disabled. And even then you have a maximum 230 stack bytes, as 32 bytes are needed for at least one workspace.

  • Like 1
Link to comment
Share on other sites

On 3/31/2023 at 6:11 PM, jedimatt42 said:

I generally use the lower memory expansion for the gcc stack. 2000-3FFF. The initial value of the gcc stack pointer would be 4000 as it is decremented first before the address is used to hold data.

 

8400 is the top of the 256 byte section of memory. There is no ram below 8300. This is where the registers live for the CPU. The CPU only has a program counter, workspace pointer, and status register. The things we call R0-R15 are not actually CPU registers, but offsets from the workspace pointer. 

 

The console ROM GPL interpreter uses 83E0-83FF as it's workspace. The ROM interrupt service routine uses 83C0-83DF.  So if you have the stack there, you must keep the interrupts disabled. And even then you have a maximum 230 stack bytes, as 32 bytes are needed for at least one workspace.

Thanks jedimatt42. I miscalculated the 8400 region at 1K so yes not very much stack, so I'll avoid that. It seems one might get away with 2-3KB for dynamic allocation in the 2000-4000 range. I suppose one can simply allocate everything needed on the stack, in main, to keep everything allocated and then pass points to that area of the stack as needed. 2-3KB RAM for dynamic allocation is not much.

 

I got the sense that you are, or have, developed an OS. Is the OS running out of the ROM and programs as EA5 loaded as needed? Do you have an EA5 loader for chaining programs?

Link to comment
Share on other sites

2 hours ago, mrvan said:

I miscalculated the 8400 region at 1K so yes not very much stack, so I'll avoid that. It seems one might get away with 2-3KB for dynamic allocation in the 2000-4000 range.

Do you even need dynamic allocation?  You can always declare "global" variables outside of functions, which will be stored in the .data segment if initialized with data, or .bss segment if initialized to zero.  Variables which are initialized in this way are copied from the .text/.ctors segment to the .data segment or zeroed in the .bss segment in expansion RAM during the C runtime initialization step (crt0.c).

 

Turmoil avoids using expansion RAM so that's why its top-of-stack is initialized to 8400.  There's enough space in 256 bytes for the 32-byte workspace, a bunch of global variables, and the stack.  The function nesting doesn't go very deep, so the stack never overwrites the data.

 

If you do need a dynamic allocation malloc/free functions, I have some code that I wrote a couple months ago and could share it with you.

  • Like 3
Link to comment
Share on other sites

7 hours ago, PeteE said:

Do you even need dynamic allocation?  You can always declare "global" variables outside of functions, which will be stored in the .data segment if initialized with data, or .bss segment if initialized to zero.  Variables which are initialized in this way are copied from the .text/.ctors segment to the .data segment or zeroed in the .bss segment in expansion RAM during the C runtime initialization step (crt0.c).

 

Turmoil avoids using expansion RAM so that's why its top-of-stack is initialized to 8400.  There's enough space in 256 bytes for the 32-byte workspace, a bunch of global variables, and the stack.  The function nesting doesn't go very deep, so the stack never overwrites the data.

 

If you do need a dynamic allocation malloc/free functions, I have some code that I wrote a couple months ago and could share it with you.

Thank you for this response. Yes, I’d like to see your implementation. I really don’t have much of a clue how many of us are actively developing on the ti99, and in C, but it’d be really nice to know what we’ve all written in terms of libraries so I don’t reinvent the wheel :-). Am I missing an area I can find such libraries? I spent the late evening and this morning implementing long and ulong bit division and modulo since it’s not in our gcc implementation. Don’t get me wrong it’s soothing but I’d rather work on other things that are missing.

  • Like 1
Link to comment
Share on other sites

1 hour ago, TheMole said:

 

I'd be interested in seeing this, and see how it compares to what I came up with (here: https://github.com/themole-ti/libmalloc99 )

Your library appears to be well written and similar to what I was thinking to write. Knowing you and PeteE have already done so, I’ll find something else. There’s a lot of interesting memory mapping going on that I need to look at what I’ve been copying from the original libti99 or perhaps the memory test utility. I believe I captured the best of both but need to look at yours a bit.you have a great Makefile as well.

  • Thanks 1
Link to comment
Share on other sites

Thanks. I know we do have a handful of people using gcc on the TI, but I'm not aware of a repository of libraries that is maintened anywhere. I'm aware of libti99 and the soundplayer both from Tursi that he maintains on his own site.

 

I actually think it's a good idea though, I'd be happy to contribute if someone sets up such a repository.

Edited by TheMole
Link to comment
Share on other sites

On 4/3/2023 at 6:53 PM, TheMole said:

I'd be interested in seeing this, and see how it compares to what I came up with (here: https://github.com/themole-ti/libmalloc99 )

It's almost the same as yours, one-word overhead per allocation, except doesn't use a free list.  Each block length is signed, positive number indicates allocated, and negative number is free.  Blocks are scanned sequentially when searching first-fit during malloc, and adjacent free blocks are coalesced during that search.  Freeing a block set the block length word negative.  I had the idea that it could straddle both low and high expansion ram by setting the heap from 0x2000 to 0xffff and permanently allocating a block from 0x4000 to 0x9fff.

 

I wish I had remembered yours when I started and saved myself some time.  I even "liked" your announcement post.

  • Like 3
Link to comment
Share on other sites

I'm still coming up to speed with GCC, and have several times bumped up against the dreaded link errors that indicate some function, that should be a GCC built-in function, can't be found. I'm learning to take these in stride although one can vary rapidly dive down the rabbit hole where one gives one function and it asks for two or three more. Just try / and % on long...  Having conquered those by Google Fu and brute force, I thought to close on adding floats to the printf functions. Boy did I step in it with that. First using xargs required the float to be a double. Casting that then required another built in so I went to work on Google determining the floating point/real standards used (Radix 100 8 byte). No problem. But getting into this I'm not seemingly receiving formats that are valid, such as positive values, such as 1.0203... start with 3F F0. The exponent should be 0 (or 40h since there's an offset). The first byte suggests a number smaller than 1, and the second byte should only be valid for a negative number since the first two values will be 2s complemented. I've been bit a handful of times by the compiler bugs re char so trying to trust this compiler and not just blame it before exhausting all attempts to debug. 

 

Anyone have experience using doubles? I'm only trying to display them.

 

And for others that might be wondering, I'm still planning to work the malloc/free and other functions.

  • Like 1
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...