Not really sure where to put this, so I'll put it here. There's been some interest as to how I'm using SDCC to program
for the TI-84+CSE, so I figured I'd make a post about it.

1. Install SDCC. I will be using SDCC version 3.4.0 (according to sdcc --version)
I'm not really sure how to do this on Windows. On Arch I was able to run 'pacman -S sdcc' and everything just worked.

2. Create a project folder. Go get a copy of ti84pcse.inc and put it in the project folder.

3. To follow this tutorial, get Brass and put it in your project folder

4. Make yourself a C file called sdcc_test.c. This is a good test file to make sure that you've got something running

Code:

//You can't write the code for any functions above main(), or else
//the top function defined will run instead of main(). The top
//function defined in your file must be the code you want to run
//when your program starts. I think a custom crt0 file would allow
//you to avoid this problem
void disp_hl(unsigned int);
void getkey();

//Define variables curRow/curCol to exist at specific addressed
//These are the addresses of the actual OS curRow/curCol for _DispHL
//If you get tired of typing 'unsigned' before all your chars,
//there's a compiler for '--funsigned-char' which will make all
//chars unsigned by default, unless you explicity write 'signed'
__at(0x8459) unsigned char curRow;
__at(0x845A) unsigned char curCol;

//Can be 'int main' but nothing is using your return value anyway
void main(void)
{
    //Set output position on screen
    curRow = 2;
    curCol = 3;

    //Should display 32325
    disp_hl(32325);

    //Wait for key press
    getkey();
}

//Really you should write this in it's own assembly file
//and declare it as extern up here. I'm just defining
//it like this so everything fits in one file
//
//an 'int' is 2 bytes.
void disp_hl(unsigned int x)
{
    //x should be in HL
    //We reference it below to avoid compiler warnings
    x;
__asm
    push hl
    rst 0x28
    .dw 0x453D ;B_CALL(_ClrScrnFull)
    pop hl
    rst 0x28
    .dw 0x44FE ;B_CALL(_DispHL) on CSE
__endasm;
}

//does a B_CALL(_GetKey) to pause.
//Doesn't return the key value
void getkey() {
__asm
    rst 0x28
    .dw 0x495D ;B_CALL(_GetKey)
__endasm;
}


5. Make a wrapper assembly program called sdcc_test_wrapper.asm (this is how I'm doing things right now, there are
alternatives) Here's a nostub assembly program that should assemble with Brass. It expects your output binary it's
including to be called sdcc_test.bin (we'll get to that later), and it also expects a ti84pcse.inc file in the same
folder as it.

Code:

.binarymode TI8X
.variablename "SDCCTEST"
.nolist
.global
#include "ti84pcse.inc"
.endglobal
.list
    .org UserMem - 2
    .db tExtTok, tAsm84CCmp
.incbin "sdcc_test.bin"


6. Compile your C code! You should be able to use the following command.

Code:

sdcc -mz80 --code-loc 42507 --no-std-crt0 --data-loc 0 --reserve-regs-iy --max-allocs-per-node 30000 sdcc_test.c

Let's go over the arguments!
-mz80 : Tells SDCC to compile for the z80 processor

--code-loc 42507 : think of this like '.org' in assembly. It tells SDCC where your code will be. 42507 = UserMem on the CSE

--no-std-crt0 : SDCC includes a standard 'crt0' wrapper around the output by default. You can make your own (which I think
I'll need to do later for reasons I'll mention later) if you want and provide it as well, but this by itself is enough
to get SDCC to output just the data for the code we've written ourselves.

--data-loc 0 : I'm not really sure what this does honestly. I don't even know if it's necessary. I recommend reading the
official documentation if you're curious

--reserve-regs-iy : This makes it so SDCC will never touch the IY register, which is important if you do anything touching
system flags. You can omit this if you make sure to set IY to flags before b_calling something that might use it, and
you make sure to set IY to flags before returning

--max-allocs-per-node 30000 : I'm not entirely sure exactly what this affects in the compiler. The higher it is, the
longer your code will take to compile, but the more optimized it will be. the default is 3000. I've kept it at 30000
because it was the first number I tried, and it made the code significantly more optimized (much smaller file size, even
when optimizing for speed). Maybe try experimenting to find a good tradeoff between compilation speed and optimization.

sdcc_test.c : The C file you're compiling.

This will generate a bunch of intermediary files as a result of the compilation, but the main one we care about is
sdcc_test.ihx. This is a file containing your code in intel hex format. To get this into a raw binary, I use the hex2bin
program with the following command which will generate sdcc_test.bin:

Code:

#A60B is also UserMem
hex2bin -s A60B sdcc_test.ihx


I'm not sure what alternatives there are to hex2bin. Really you can just write your own if you want, the intel hex
format isn't that complicated.

7. Finally, we can assemble the wrapper assembly program we wrote earlier:

Code:

#just 'Brass.exe' on windows
mono Brass.exe sdcc_test_wrapper.asm SDCCTEST.8xp


Transfer SDCCTEST.8xp to an emulator or your hardware and you should be able to run it. It should look something like this:


As promised a few additional details:
► If you define global variables, they will be inserted as empty space in your program BEFORE main(). This means
that the first time your program runs you'll NOP sled down to main(), but if you're using a shell with writeback there's
no telling what will happen the next time it runs. I think that you can jump past them to main() if you provide a custom
replacement for the standard crt0 file. I have not tried doing this yet as I haven't needed to. I've instead defined all
my global variables to be located in the typical freeram locations assembly programmers know and love, using the __at()
syntax shown in the above sample C file.

► If you want to use or display a string whose value will never change, declare it globally and declare it const.
Otherwise SDCC will generate some nasty code to construct the string on the fly when used. Doing this sort of thing
should be familiar to Axe devs. the 'const' thing goes for ANY global data you define which you are not going to change.
All 'const' globals are inserted AFTER your code in the resulting assembly instead of BEFORE.
Example: const char SOME_STR[] = "Hello World!";

► If you want to write custom assembly routines and call them from C, the SDCC manual has a good reference on how to do
this (link to manual PDF below). However there are a few gotchas. The manual will mention that parameters are passed by
writing the values to fixed memory addresses unless a function is marked 'reentrant', but this is not true of the z80
compiler. The z80 compiler ALWAYS passes values on the stack. Also, if you (or routines you call) modify IX, you must
restore it's value before returning because SDCC uses IX to maintain the stack frame. The manual also has a section with
details related specifically to z80 assembly routines, the most important bit of information is: Return 1 byte values in
L, 2 byte values in HL, 4 byte values in DEHL.

► Local variables are stored on the stack. Be careful of declaring a lot of local variables, having a deep call stack,
calling a function recursively too much, or really anything that would cause a lot of memory allocation on the stack.
The stack, by default, does not really have much space available to it. If you DO need to do these things, look into
moving the stack somewhere else (I've moved it to saveSScreen before with no problems). If you want to do this, make
sure you know EXACTLY what you're doing though. Any code which swaps out the page of ram you put the stack on will
probably crash the calculator. Moving the stack pointer is not something to be taken lightly.

► The SDCC manual PDF can be found here: http://sdcc.sourceforge.net/doc/sdccman.pdf

Big thanks to AHelper for answering my questions related to the assembler in IRC, and for making
this thread a while ago which helped me figure out a few
things I didn't figure out from reading the manual. You can also read a bit about the whole 'crt0' thing there too,
though I don't know how relevant it is as I haven't really read it myself.
If you want a head-start on making a DCS-friendly crt0, check out my older B&W thread: http://www.cemetech.net/forum/viewtopic.php?t=8482
contra/deeph did some experiments using C for the 83/83+ as well over at yAronet. deeph started porting GBA Lib 2 (which apparently is unreleased? I think deeph knew the author of GBA Lib) and got some interesting stuff done.
for some reason


Code:
mono Brass.exe sdcc_test_wrapper.asm SDCCTEST.8xp


isnt working for me, as mono isn't recognized by the system.
martmists wrote:
for some reason

mono Brass.exe sdcc_test_wrapper.asm SDCCTEST.8xp

isnt working for me, as mono isn't recognized by the system.

What operating system do you have? Are you sure you have mono installed?
Windows 10 home edition, not sure if mono is installed, checking now.
martmists wrote:
Windows 10 home edition, not sure if mono is installed, checking now.

You don't need mono, mono is for non-windows platforms.


Code:
brass sdcc_test_wrapper.asm SDCCTEST.8xp


Should work just fine. Hope this helps! Smile
brass isnt recognized.
martmists wrote:
brass isnt recognized.

Do you have brass in the current directory your are executing the command in?
no, where is the bin folder located? (ASUS tends to not put everything in path)
martmists wrote:
no, where is the bin folder located? (ASUS tends to not put everything in path)

You need to download brass from here: http://www.benryves.com/bin/brass/
Maybe these things should be added to the list of requirements

It works now btw Very Happy
martmists wrote:
Maybe these things should be added to the list of requirements


From the first post:

Unknownloner wrote:
3. To follow this tutorial, get Brass and put it in your project folder


Anywho, what seems to be the problem? Did you follow all the steps to the letter, or did you jump around?
Whoops, must've missed that one >_<
martmists wrote:
Whoops, must've missed that one >_<


Ah well. Smile Anywho, great work! Takes some getting used to. Welcome to Cemetech, by the way martmists! Have you introduced yourself in this thread?: https://www.cemetech.net/forum/viewtopic.php?t=4925
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 1
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement