Just throw out a tutorial C for the ti83/84 calcs using SDCC! (Intended for linux)


1. Getting the needed tools

SDCC is a C compiler that targets many CPUs including the z80. The compiler is taking on major changes right now and fails to compile even its own libraries. So for now, I am showing how to do stuff with SDCC revision 6489. Make sure that you have gcc and boost headers installed. You can get the sources for SDCC with:


Code:
svn co http://sdcc.svn.sf.net/svnroot/sdcc/trunk/sdcc/ sdcc -r 6489


Once it grabs the source files, navigate into the sdcc/ directory created and run


Code:
./configure --disable-mcs51-port --disable-r2kk-port --disable-z180-port --disable-ds390-port --disable-ds400-port --disable-pic14-port --disable-pic16-port --disable-hc08-port


You could optionally add in


Code:
--prefix=/usr


if you want it to be installed to /usr/* and not /usr/local/*. Now, configure may fail if you don't have the needed dependencies. Linux distros are different, so I can't give specific help on all. Once you have configure finished, go ahead and run


Code:
make


This may take a while depending on your system. The first compile takes a lot of time as it does testing on the z80 and gameboy z80 ports. Once it finished, do


Code:
su -c 'make install'


You should now be able to do


Code:
sdcc --version


If that works, then you need to get hex2bin from http://downloads.sourceforge.net/project/hex2bin/hex2bin/Hex2bin-1.0.8.tar.bz2?r=http%3A%2F%2Fsourceforge.net%2Fprojects%2Fhex2bin%2Ffiles%2Fhex2bin%2F&ts=1322251921&use_mirror=iweb

Extract the archive somewhere and do


Code:
su -c 'make install'


You don't need to do make. When you have hex2bin installed, you can proceed onto writing programs! (Don't get rid of your sdcc directory, you will need it)

2. Targeting and Using SDCC

SDCC is different from other C compilers. With GCC, you compile sources into binaries, usually no extension. SDCC compiles into .ihx files. These are not binary files, but can be made into them using hex2bin. It converts an .ihx to a .bin. That can be used with binpac8x or similar program to make a .8xp. Also, gcc -c will output .o files, sdcc will output .rel files.

SDCC has no concept of what it will run on. It is setup to make operating systems. How it formats programs is determined by a crt0.rel file. This file can be found in sdcc/device/lib/z80. Now, the .rel is the compiled form. The crt0, written in assembly, is stored in crt0.s. For simplicity, .s is a assembly source file.

Opening up the file, you will see that the assembly used is a different dialect. The assembler, ASxxxx (SDCC installs it as sdasz80), changes how numbers are written and a few other things. For simplicity, here is the given crt0.s file with comments:


Code:
        .module crt0
          .globl   _main

   .area   _HEADER (ABS)
   ;; Reset vector
   .org    0 ; This is where the below code starts
   jp   init ; Starts the program
        ; Interrupts for an OS
   .org   0x08
   reti
   .org   0x10
   reti
   .org   0x18
   reti
   .org   0x20
   reti
   .org   0x28
   reti
   .org   0x30
   reti
   .org   0x38
   reti
   ; Getting ready to work as OS
   .org   0x100
init:
   ;; Stack at the top of memory.
   ld   sp,#0xffff

        ;; Initialise global variables
        call    gsinit
   call   _main
   jp   _exit

   ;; Ordering of segments for the linker.
   .area   _HOME
   .area   _CODE
        .area   _GSINIT
        .area   _GSFINAL

   .area   _DATA
   .area   _BSEG
        .area   _BSS
        .area   _HEAP

        .area   _CODE
   ; Not sure what this does...
__clock::
   ld   a,#2
        rst     0x08
   ret
   ; This is called when main() returns.
_exit::
   ;; Exit - special code to the emulator
   ld   a,#0
        rst     0x08
1$:
   halt
   jr   1$

   ; This initializes global/static variables
        .area   _GSINIT
gsinit::

        .area   _GSFINAL
        ret


Now, you can see that it is setup to work for an OS. Also, you see that .* statements use numbers like 0x100, but the rest use them as #*. I don't know why they are different, but you should at least know how to use numbers in assembly. #__ is for a regular number, like #10. #0x__ is for a hexidecimal number, like #0xA. #0b__ is for a binary number, and #. is for the current position. This file is compiled by using


Code:
sdasz80 -p -g -o crt0.rel crt0.s


That .rel would be used with SDCC to use as your header.

SDCC by default uses the premade header. If you have your own and made a .rel file, you can use


Code:
--no-std-crt0 crt0.rel


or whatever your .rel file is called. This is just a flag to SDCC, so lets see how to actually use SDCC


Code:
sdcc --mz80 --no-std-crt0 --std-sdcc99 --data-loc 0 --code-loc {start of code segment} -o {output}.ihx {crt0}.rel {source}


This will take in a .c source and your .rel and make an .ihx.
You can also call SDCC using -c to use multiple sources like gcc, outputting .rel files along the way.

3. Targeting TIOS

Now, to target TIOS, you will need to remake the crt0.s. I don't develop for TIOS, but here is my stab at a crt0:


Code:
; tios_crt0.s - TIOS assembly program header
   .module crt
   .globl _main
   .area _HEADER (ABS)
   .org #0x9D93
   .dw #0x6DBB
   call gsinit
   jp _main
   .org 0x9D9B
   .area _HOME
   .area _CODE
   .area _GSINIT
   .area _GSFINAL
   .area _DATA
   .area _BSEG
   .area _BSS
   .area _HEAP
   .area _CODE
   ;; Twice ???

__clock::
   ld a,#2
   ret ; needed somewhere...
   
   .area _GSINIT
gsinit::
   .area _GSFINAL
   ret


There it is, a simple TIOS header. Compile using


Code:
sdasz80 -p -g -o tios_crt0.rel tios_crt0.s


and you should be good to start C coding!

Now, if you start coding now, you will be lacking all TIOS calls. You need to make glue functions to connect C code with TIOS. Here is an example program that I made:


Code:
void main(void)
{
   ClrLCDFull();
   Pen = 0;
   PutS("Hello World!");
   NewLine();
}


Right away, this would not compile, as we have 3 functions and a variable not known. Now, SDCC doesn't know what a B_CALL is, so you have to make a function yourself in assembly Wink Here is how to do ClrLCDFull:


Code:
void ClrLCDFull() __naked
{
   __asm
      push ix
      rst #0x28
      .dw #4540
      pop ix
      ret
   __endasm;
}


A few things to note. First, __naked means that SDCC shouldn't treat the function as C function. SDCC would normally setup IX so that it can access variables, but that isn't needed. The ret is needed because __naked really means SDCC doesn't touch it.

__asm and __endasm; are used to inline asm code. Simply that. SDCC doesn't make way for your code, so that's why I made this a function and not in the main function. SDCC backs up registers before calling functions.

So, let me add in the rest:


Code:
void ClrLCDFull() __naked
{
   __asm
      push ix
      rst #0x28
      .dw #0x4540
      pop ix
      ret
   __endasm;
}
void NewLine() __naked
{
   __asm
      push ix
      rst #0x28
      .dw #0x452E
      pop ix
      ret
   __endasm;
}
void Puts(char *s)
{

}


Now, you can see that the first 2 functions didn't use parameters at all. PutS needs one. This is simple to do in asm, just ld hl,(text), but SDCC doesn't use registers for that, it uses the stack. This also requires you to understand how SDCC uses IX.

C uses the stack for variables. Local variables are added to the stack and calling functions pushes variables to the stack. (It isn't just SDCC, the language is that way Smile ) In order to access variables, it uses offsets from IX to get to the very quickly. If you have a char buffer[50];, that is on the stack, being a local variable. It uses IX on that, as doing pop/push or hl (hl pointing on the stack) would be too much.

Ix is setup by SDCC by starting and ending a function like:


Code:
void function()
{
   __asm
      push ix
      ld ix,#0
      add ix,sp
   _endasm;
   
   // Code here

   __asm
      ; Adds this on a return
      ; When using local variables:
      ld sp,ix
      ; always adds this
      pop ix
      ret
   __endasm;
}


A char as the first parameter would be, from the layout above, 4(ix). Note that _(ix) is how ASxxxx uses offsets. So, for our PutS function:


Code:
void PutS(char *s) // non-naked, let SDCC do its thing
{
   s; // Stop SDCC from saying 'Unused variable', but
   //    outputs no code for that.

   __asm
      ld l,4(ix)
      ld h,5(ix)
      rst #0x28
      .dw #0x450A
   __endasm;
   
   // Note that there was no ret above.
   // If we manually return from inline asm,
   // SDCC will ignore it and not restore IX
}


Now, we have all of our functions, we are all set to go... almost. Remember Pen = 0;? This is variable in TIOS, but unlike functions, it is much easier to access!


Code:
__at 0x86D7 unsigned int Pen;


Simple as that. The final program code:


Code:
// Woo no includes!!!

__at 0x86D7 unsigned int Pen;

void ClrLCDFull() __naked
{
   __asm
      push ix
      rst #0x28
      .dw #0x4540
      pop ix
      ret
   __endasm;
}
void NewLine() __naked
{
   __asm
      push ix
      rst #0x28
      .dw #0x452E
      pop ix
      ret
   __endasm;
}
void PutS(char *s) // non-naked, let SDCC do its thing
{
   s; // Stop SDCC from saying 'Unused variable', but
   //    outputs no code for that.

   __asm
      ld l,4(ix)
      ld h,5(ix)
      rst #0x28
      .dw #0x450A
   __endasm;
   
   // Note that there was no ret above.
   // If we manually return from inline asm,
   // SDCC will ignore it and not restore IX
}

void main()
{
   ClrLCDFull();
   Pen = 0;
   PutS("Hello World!");
   NewLine();
}


You can run this from TIOS, as from a shell, the program will return right away. Once you compile this program, you can use hex2bin to get a .bin file to use with binpac8x (from the DCS SDK, but you can use something else). This is my compile script:


Code:
sdasz80 -p -g -o tios_crt0.rel tios_crt0.s
sdcc --no-std-crt0 --code-loc 40347 --data-loc 0 --std-sdcc99 -mz80 --reserve-regs-iy -o tios.ihx tios_crt0.rel tios.c
hex2bin tios.ihx
./binpac8x.py -O SDCC tios.bin tios.8xp


You can run it with Asm(prgmSDCC, and make sure mathprint is off.

4. SDCC with GlassOS

GlassOS is an OS that provides a perfect C environment. A builtin libc is provided along with hardware drivers, USB access, ion functions, and more. The OS is pending a release. Once the OS has been released, a full SDK will be made available along with code examples and explanations on already existing games and programs (calculator, grayscale game and a classic arcade game).

4.1. Writing code that uses GlassOS libraries and kernel calls
todo

4.2. Preparing a binary for sending to the calc
Unlike TIOS, GlassOS doesn't encapsulate data into special formats. For example, to send a program to the calc, you simply need to send the raw binary data over.

When compiling, you get a .ihx as the final result. Using hex2bin, you can create a .bin of the .ihx. For example, if you compile a program named pak, you can use

Code:
hex2bin pak.ihx
and pak.bin will be created. That file can be sent to the calc and will be read as a program/library.

4.3. Files required to run programs on-calc
GlassOS's kernel, when starts, spawns the Launcher process. That program is in charge of displaying programs to the user in a quick and organized way. Simply putting binaries on the calc will not let them be organized on their own. Launcher uses .desktop files (limited versions) to know about programs. There are two directories that tell the OS about programs on the calc, /usr/share/applications and /usr/share/types. .../applications contains all of the .desktop files, and .../types contains files that describe filetype associations. /usr/bin contains the actual binaries. When sending a program to the calc, a program must have at minimum a binary and a .desktop file. Here is a typical layout of a package for the calc:

Code:
package.gop:
/files
  /usr
    /bin
      :: pak.bin
    /share
      /applications
        :: pak.desktop
/control

The above would be a minimal layout of a package, which is read by the PC software and the individual files are sent to the calc using the FSA protocol.

The pak.desktop would contain:

Code:
Exec=/usr/bin/pak.bin
Category=Other
Name=Pak!

This will cause Launcher to show an entry 'Pak!' under the Other category. If you want to manually send a program to GlassOS, you can use the File Browser in glassLink to manually send these two files to the calc. Once the files are on the calc, the Launcher must be restarted, and can be done by killing the Launcher process. This limitation will be addressed.

4.4. Making packages for the package manager
todo

5. More with Inline Assembly

todo

6. Accessing the Hardware Ports

SDCC lets you access hardware ports like an unsigned char. To access the ASIC version port, you can do:


Code:
__sfr __at 0x15 Port_ASIC_Version;


You can read and write to it just like a normal variable. You can see a nice header with ports at http://glassos.svn.sf.net/svnroot/glassos/trunk/src/include/global.h.

Questions, comments? Ask away! (Don't forget about COS for making an OS from scratch in C)
Wow, thanks for posting this, this is excellent! I've long insulted SDCC for its bloated code; perhaps this will give me a chance to test out its latest versions and see how the optimization has been improved. An excellent guide indeed, and I look forward to you fleshing it out further!
Yup. The only reason I say to use this revision (which is old) is because the new optimising code ... doesn't work. printf fails on "%02d" and printing floats, the port access isn't working, etc.
Since the GlassOS RC1 sdk is out, I will update the above tutorial for using it soon...
For PutS(), it looks like you can use the C variable attribute, unused, to suppress warnings about it. TuxPaint, for instance, does this:
Code:
#define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
And then, it is used like this:
Code:
/* ... */
int fisheye_get_tool_count(magic_api * api);
/* ... */
int fisheye_get_tool_count(magic_api * api ATTRIBUTE_UNUSED)
{
  return 1;
}
/* ... */
So, the prototype doesn't have it, and the actual function does. Unless SDCC is less standard than gcc Razz
From what I see, __attribute__ is gcc-specific and SDCC doesn't support it as it isn't based off of gcc. The above method is what is used by the devs in the sdcclib code. The variables being mentioned like that doesn't generate code - they are simply ignored when assembly is being generated.
I seem to be having issues with the modified crt0:

$ sdasz80 -p -g -o tios_crt0.rel tios_crt0.s
tios_crt0.s: cannot open.
removing

what should I do about this?
Seems like it would be worthwhile to write a z80 backend for llvm and use Clang.
that actually sounds like a really good idea.

I wish I was good enough at assembly to do that. Razz
willrandship wrote:
that actually sounds like a really good idea.

I wish I was good enough at assembly to do that. Razz


I'm pretty sure you don't need to know much assembly to write the backend - just the register layouts and the instruction formats + timings.
ZeroCrash wrote:
I seem to be having issues with the modified crt0:

$ sdasz80 -p -g -o tios_crt0.rel tios_crt0.s
tios_crt0.s: cannot open.
removing

what should I do about this?
If you wait patiently, I'm sure AHelper0 will come and respond. In the meantime, have a topic to introduce yourself in: Introduce Yourself.
KermMartian wrote:
ZeroCrash wrote:
I seem to be having issues with the modified crt0:

$ sdasz80 -p -g -o tios_crt0.rel tios_crt0.s
tios_crt0.s: cannot open.
removing

what should I do about this?
If you wait patiently, I'm sure AHelper0 will come and respond. In the meantime, have a topic to introduce yourself in: Introduce Yourself.


I duplicated that error message by having the file permissions set so that I cannot access it. (Assuming you are using *nix) chmod 775 tios_crt0.s will fix permission issues, but make sure that you can also write files in the folder. Also make sure that the file is in the same directory when you run the command.

Hope that helps Smile
Can you make a tutorial for Windows?
  
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