Based on LLVM toolchain, generating eZ80 ADL code and fasmg eZ80 assembler and linker.
Install the CE Toolchain - see [getting started](Getting Started — CE C/C Toolchain documentation) page. The tool-chain can be installed on Windows, Linux or MacOS. Follow the instructions to complete the install, and test that the toolchain works by compiling some of the examples for the TI calculator.
To adapt the the tool-chain for Agon, for this proof of concept, a number of files should be replaced with the files from the repository (GitHub - pcawte/AgDev: Port to Agon Light of TI 84 plus CE Toolchain). Clone the repository and copy over the top of the CE toolchain. For example, if the CE Toolchain is installed in AgDev and the downloaded repository in GitHub/AgDev copy the files over the top using:
cp -R -f GitHub/AgDev/ AgDev
Which should work for MacOS / Linux - not sure the equivalent for windows.
This follows the same make approach as the original CE Toolchain, see bottom of the getting [started page](Getting Started — CE C/C Toolchain documentation). The build process had been modified to stop on the generation of the .bin
file. This is the Agon Light executable.
I recommend to use:
make clean
make V=1
In the relevant: example, test or any other directory created at the same level. The makefile should be edited to contained the desired name of the .bin executable.
04/06/2023: initial proof of concept completed
06/06/2023: hosted on github
09/06/2023: missing compile run-time routines added (these previously called routines in CE ROM)
12/06/2023:
-
correct operation of malloc verified - comments added to
sbrk.src
as part of this -
crt0.src
updated to initialise stack -
makefile.mk
-
updated to include program name in the program header
-
default locations of stack and BSS adjusted
-
13/06/2023:
-
missing string and long jump routes added (these previously called routing in the CE ROM)
strcasecmp
removed from the library - this previously called the CE ROM. Use the alternativestrncasecmp
.
-
puts
modified to output CR / LF pair rather than just LF -
getchar
updated to echo character as per standard C
18/06/2023:
-
stdio library updated to work on Agon
-
This is a simple, non-buffered library - it may have performance issues writing large volumes as in many cases does a system call for each character.
-
fseek
only implements SEEK_SET (search from beginning of file). Other modes are not implemented, because requiresftell
functionality (see below). -
ftell
is not implemented as functionality is not exposed via MOS
-
19/06/2023:
- C-runtime modified to include a work-around for emulator hanging
- Config parameter in
makefile.mk
corrected.PREFER_OS_LIBC
changed toNO
20/06/2023:
- C-runtime workaround reversed as issue now resolved in the emulator.
- Development code for
scanf.c
included prior to tidying up and moving into libc
23/06/2023:
-
stdio library updated to include:
ftell
,clearerr
,remove
andrewind
-
fseek
updated to allow SEEK_SET and SEEK_CUR - note SEEK_END is not supported. If this required, close and reopen the file in append mode. -
additional error codes added in
errno.h
24/06/2023:
-
fseek
updated to support SEEK_END -
bug it
gets_s
fixed - was assuming buffer was 1 shorter than it was, so input of 1 character, with buffer size 2 previously did not work. -
test program updated (
tests/src/fileio.c
) to test functionality of stdio
25/06/2023:
scanf
andsscanf
moved into standard library (libc)
30/06/2023:
-
bug in
strncpy.src
which used a ZDS pseudo op that is not implemented in fasmg assembler. Replaced by individual assembly instructions. This problem may occur in other libraries copied from ZDS. For the moment, just fixed in this one location. -
C-runtime
crt0.src
updated to added back functionality previously removed as part of the proof of concept-
Supports the following C-functions:
-
atexit()
,on_exit()
- which register functions to be called at the end of the program -
exit()
,_Exit()
,abort()
- which exits the programme in various ways
-
-
Supports initialisers, constructors, destructors, finalisers (needs testing)
-
Added extensive commenting
-
-
Included a simple example of a user defined type (complex) which overloads a number of operators.
-
Changed default location of BSS & heap to 0x080000 to 0x09ffff
02/07/2023:
-
mos_api updated:
-
unit32_t getsysvar_time()
function added -
typedef
RTC_DATA
created for the format of data returned fromgetsysvar_rtc()
-
getsysvar_rtc()
updated tovolatile RTC_DATA *getsysvar_rtc()
to reflect that the returned pointer refers to a value that is dynamically changed by MOS - and shouldn't there be optimised. Changed to return typeRTC_DATA *
-
Added example under
tests/mos_time
-
-
clock_t clock()
function implemented using the Agon timer. Other RTC related functions are in the process of being ported.
03/07/2023:
-
Corrected
lib/crt/fpftol.src
to remove ZDS pseudo op not supported by fasmg and removed other ZDS assembler features that are different in fasmg -
Corrected
lib/crt/fpultof.src
to remove ZDS assembler directives that are different in fasmg -
Ported time functions:
difftime()
,mktime()
,time()
,localtime()
,gmtime()
,asctime()
,ctime()
-
Updated comments in
mos_api.h
to note thatgetsysvar_rtc()
data is only updated whenmos_getrtc()
is called. This is taken account of intime()
.
04/07/2023:
- Dhrystone benchmark added under full
ansibench/dhrystone
.
09/07/2023:
-
Support for output re-direction added for stdout. Cannot currently be done from the command line (todo), but can be done programmatically using
freopen()
.-
freopen()
added -
puts()
andprintf()
updated to callputchar()
instead of directly callingoutchar()
-
putchar()
modified to either calloutchar()
offputc()
depending on whether output redirected -
fputc()
modified to calloutchar()
when outputting tostdout
orstderr
to avoid any danger of function call loops -
fclose()
modified to allow stdout to be closed if it has been redirected -
stdio.h
andfiles.c
modified to support input / output redirection -
linker.script
updated to includefreopen()
-
Test / demonstration program added under
test/stdout-redirect
-
10/07/2023:
-
Support for input re-direction added for stdin. Cannot currently be done from the command line (todo), but can be done programmatically using
freopen()
.-
getchar()
- callsinchar()
to get the character andoutchar()
to echo the character (even if the output has been redirected). If output has not been re-directed callsfgetc()
and does not echo the character. -
gets_s
- callsgetchar()
if input has not been redirected (line are terminated with CR). Callsfgets()
of input has been redirected (lines are terminated with CR/LF pair). -
fgetc
- callsmos_fgetc()
unless called onstdin
when callsinchar()
and echos withoutchar()
- avoids callinggetchar()
so that no risk of function call loops -
Test / demonstration program added under
test/stdin-redirect
-
-
Bug fixed previously if a program was run again from the command line without re-loading it, stdin, stdout and stderr were not reset.
-
Added re-run handling in
crt0.src
(currently very simplistic as only initialises stdio) -
_stdio_init()
function added to files.c. This is only called fromcrt0.src
and is not declared in any header file.
-
-
Added fasmg quick reference guide (from Assembling eZ80 code with flat assembler g - Cemetech | Forum | Your Projects [Topic]) - but formatted to make readable / intelligible.
12/07/2023:
-
puts()
modified to print "(NULL)" if called with a null pointer. Code shortened. Defines 2 static strings which can be used elsewhere. Note there is still an issue withprintf()
.-
__null_literal_str
as "(NULL)" -
__end_of_line_str
as CR/LF pair
-
13/07/2023:
-
fopen()
modified to correct apparent bug in MOS. Now seeks to end of file when file opened for append ("a"). Previously wrote from the beginning. Now writes from the end. -
gets_s()
corrected to also output LF after CR pressed by the user. -
puts()
corrected was outputting LF/CR not CR/LF -
gets_s()
corrected to strip CR/LF pair if used with input redirected
14/07/2023:
- support for input / output redirection added on the MOS command line, e.g.
RUN &040000 <in.txt >out.txt
. Enabled by includingLDHAS_ARG_PROCESSING = 1
in the makefile. - This also supports quoting of command line arguments with double quotes.
tests/args
updated to demonstrate the effect of quotingtests/quote
added to demonstrate input / output redirection from the MOS command line
16/07/2023:
linker_script
corrected to includestrncmp
, which was missing.
17/07/2023:
-
And support for text and binary file modes - text mode has LF translation for input and output. Binary does not translate. At same time at support for unget of one character.
- Definition of
FILE
changed instdio.h
andfile.c
to store mode and unget character in FILE struct. Note, need to recompile the whole of stdio library because of change in definition of FILE.
- Definition of
-
Translate LF (
'\n'
) to CR/LF when writing to console and file. For files this is done by default, but not if the file is opened in binary mode.-
putchar.src
modified to do translation when stdio is not redirected -
puts.src
modified to no longer send CR/LF at end of line - now just sends LF and allowsputchar
to do the translation. -
fputc.c
modified to translate LF to CR/LF if FILE was opened in text mode -
fputs.c
modified to no longer send CR/LF at end of line - now just sends LF and allowsfputc
to do the translation. -
getchar.src
modified to do translation of CR to LF and supportungetc()
. Note that console keyboard only generates CR and not CR/LF pair. -
gets_s.c
modified - as translation is now done bygetchar()
, only needs to look for LF. -
fgetc.c
modified to do translation of CR/LF pair to LF and supportungetc()
-
fgets.c
modified - as translation is now done byfgetc()
, only needs to look for LF.
-
-
fprintf()
andvfprintf()
functions added innanoprintf.c
- CE toolchain option to use built in system printf function removed. Aliasing of names in
printf.src
no longer used. Naming simplified innanoprintf.c
. Linker optionHAS_PRINTF
no longer does anything. Entry forprintf.src
removed fromlinker_script
- CE toolchain option to use built in system printf function removed. Aliasing of names in
-
ungetc()
function added - can only unget one characterfseek()
,fflush()
all modified to clear the character pushed back on the stream.
-
fseek()
modified to allow seeking on redirectedstdin
,stdout
orstderr
. -
ftell()
modified to allow calling on redirectedstdin
,stdout
orstderr
. -
maptab.c
updated to include all white space characters. Is used by isspace() function. Previously it only had the actual space character listed as white space, causing problems forscanf()
.isspace.src
comments added
18/07/2023
-
setjmp.src
andlongjmp.src
corrected to fix bugs in conversion from ZDS to fasmg assembler - moved public statements inside section and replace ZDS pseudo ops. -
strstr.src
corrected to replace ZDS pseudo op -
memcmp.src
added tolinker_script
- was previously missing
22/07/2023
-
crt0.src
andsbrk.src
updated to fix reset start of heap if program is rerun. Previously if a program was rerun the start of heap used by malloc was not reset - potentially leading to not enough memory for malloc. -
crt0.src
updated to optimise the initialisation of BSS -
README.md
updated to included documentation on VDP and VDU commands -
mos_api.h
updated to include a structure for manipulating SYSVAR -
tests/malloc
updated to demonstrate correct functioning ofmalloc()
-
tests/vdu
added to demonstrate use of VDU commands - part of developing VDP/VDU wrapper library
23/07/2023
-
Started process of converting VDP/VDU demo to a library with creation of
vdp_vdu.c
andvdp_vdu.h
- although still intests/vdu
directory.- Added
tests/big-bitmap
to demonstrate the load of large bit maps from a file - this works on the emulator, but not on actual Agon Light HW, where there does not seem to be enough available memory in VDP (not sure of the reason for this).
- Added
24/07/2023
crt0.src
problem fixed with return value fromexit()
or_Exit()
not being properly returned to MOS
25/07/2023
-
Exit handler added to print error / exit message at end of program. This is required if program returns non-zero, as MOS interprets this as one of various file errors.
-
Added in
crt0.src
-
If not required (to slightly shorted the code) can be disabled setting
LDHAS_EXIT_HANDLER:=0
inmakefile
makefile.mk
has been updated to include the exit handler by default
-
stdlib.h
updated to include 3 exit codes-
EXIT_SUCCESS
-
EXIT_FAILURE
-
EXIT_ABORT
-
-
Added test/exit to test/demonstrate the functionality
-
26/07/2023
-
scanf
library updated-
Handling of backspace, cursor left and control-C during input
-
Added
fscanf()
-
Updated
tests/scanf
to include test offscanf()
-
-
gets_s()
updated to handle backspace, cursor left and control-C during input
01/08/2023
-
Agon-HW.md
file started to collect documentation on Agon hardware- Section on UART0 / VDP interrupt with a focus on understanding keyboard input
-
lib/agon/intagon.src
created - intercepts Agon UART0 interrupt to maintain an array of bits for key presses (one bit per key). This allows multiple key presses to be detected at the same time -
vdp_vdu
library created for using VDP graphics via the VDU commands-
vdp_bell()
-
vdp_cursor_left()
-
vdp_cursor_right()
-
vdp_cursor_down()
-
vdp_cursor_up()
-
vdp_clear_screen()
-
vdp_clear_graphics()
-
vdp_cursor_home()
-
vdp_cursor_tab()
-
vdp_set_
text_colour()
-
vdp_mode()
-
vdp_graphics_origin()
-
vdp_get_scr_dims()
-
vdp_logical_scr_dims()
-
vdp_cursor_enable()
-
vdp_move_to()
-
vdp_line_to()
-
vdp_point()
-
vdp_triangle()
-
vdp_circle_radius()
-
vdp_circle()
-
vdp_select_bitmap()
-
vdp_draw_bitmap()
-
vdp_load_bitmap()
-
vdp_load_bitmap_file()
-
vdp_load_sprite_bitmaps()
-
vdp_create_sprite()
-
vdp_select_sprite()
-
vdp_move_sprite_to()
-
vdp_move_sprite_by()
-
vdp_show_sprite()
-
vdp_hide_sprite()
-
vdp_next_sprite_frame()
-
vdp_prev_sprite_frame()
-
vdp_nth_sprite_frame()
-
vdp_activate_sprites()
-
vdp_refresh_sprites()
-
vdp_reset_sprites()
-
-
vdp_key
library created for accessing VDP keyboard data to maintain a bit map of key presses (can be simultaneous)-
vdp_key_init():
initialise the VDP keyboard routines, which hook into the UART0 interrupt handler and maintains an arrayvdp_key_bits[32]
of up/down status per key. -
vdp_key_reset_interrupt()
: resets the original interrupt vector. This does not normally need to be explicitly as it is registered to be call at the end of the program viaatexit()
. It can however be safely called. -
vdp_set_key_event_handler()
: sets a handler function to be called when a key is pressed. It is passed a KEY_EVENT -
vdp_update_key_state()
: this should be called frequently in the main game loop, otherwise events might be missed - there is in any case a small chance of this (could consider integrating into the interrupt routine). It is the routine that tiggers the event handler. -
vdp_check_key_press()
: checks for a key press by examiningvdp_key_bits[]
.
-
-
linker_script
: updated to include the above 3 items and remove some CE items left over from the original version. -
tests/sprite
added to demonstrate sprites and keyboard handling
02/08/2023
-
Added sprite related functions
-
vdp_clear_sprite()
-
vdp_add_sprite_bitmap()
-
-
Header files updated to include
extern "C"
for correct linking from C-
vdp_key.h
-
vdp_vdu.h
-
-
Corrected typo in
crt0.src
in conditional compilation of clearing BSS
03/08/2023
- Added
vdp_solid_bitmap()
function
10/08/2023
-
TI-84 CE specific library items (under
lib/ce
) disabled inlinker_script
. Other items shifted tolib/agon
. Note some items might rely on HW timing so not give accurate results.-
atomic functions: should work, but not checked
-
delay, sleep functions: rely on CPU clock cycle timing. Will run, but results will be off due to different clock speed
-
random functions: okay
-
zx0 & zx7 compression: okay
-
16/08/2023
- Corrected typo in this file
17/08/2023
-
vdp_key.h
andvdp_vdu.h
moved toagon
subdirectory ininclude
folder -
Sprite C library header files stored in
Sprite
subdirectory ininclude
folder -
sprite-demos
folder added withinvaders
demo
-
Testing / validation
-
Check for ZDS pseudo ops in any assembly language source files copied from ZDS
-
Review and improved efficiency / size of crt0.src and resulting binary - including the use of compile time options
-
For stdio remove stuff inherited from CE Toolchain (there is nothing used, but some remnants in the various files)
-
Change printf %s with NULL pointers to do something sensible
-
Close files that have not been closed on
exit()
as MOS does not close them and runs out of file handles - plus this is part of the C standard
-
Complete vdp_vdu library - for example, need to add routines for colours and palette
-
Add command line support for redirection of stderr
-
Add simulation of pipes
-
Port / replace / expand library where necessary to the Agon Light. Including:
- investigate benefits of buffering for stdio.
- VDP/VDU library
-
Create own repository that is not dependent on CE Toolchain
- Clean out the CE specific stuff
-
Test / proof of concept for other LLVM front-ends. See, for example, GitHub - maddymakesgames/Rust-CE: A proof-of-concept of rust on the ti-84 ce, which is a proof of concept for running Rust on the TI-84-CE calculator.
The rest of this file documents the process of porting the CE Toolchain to the Agon Light. This includes the process of discovery with respect to how the CE Toolchain works and the Agon Light, and the steps / decisions made in the porting process. This intended as reminder for myself and a guide to others who follow in my steps.
The Agon Light is based on the Zilog eZ80 processor. The eZ80 has a 24 bit address space supporting 16 megabytes of memory, compared to the 64 kilobytes of the original Z80. The eZ80 has two modes of operation, the standard Z80 mode, which has 16-bit registers making it easy to address 64k of memory, but requiring the use of "banking" to access more than 64k of memory. The other, ADL (address data long), mode of operation extends the registers to 24 bits, making the whole address space readily accessible.
From the point of view of assembly language programming both Z80 and ADL mode are very similar, making it easy to convert programmes from Z80 to ADL mode.
When we consider high-level programming languages, there are a number available for the Z80, but they are limited to 64k of memory or have awkward bank switching methods to access greater memory. Consequently it is very interesting to have a available high-level languages that make use of the ADL mode to allow use of the full 16M address space.
Considering the C-programming languages, there are a number of Z80 C-compilers available. To date, the Agon community has focussed on two:
-
Zilog ZDS II development environment which can produce eZ80 ADL code. This was the original set of tools used by the developers of Agon, but it is closed source, runs on Windows only, and only supports the data C89 standard
-
SDCC (small devices C-compiler) this is a popular choice for 8-bit computers, and adapting this for Agon has been a focus of a number of people in the Agon computer. This is a good compile for Z80, but it only support Z80 and not ADL mode.
As an alternative, open-source compiler that can produce ADL code there is the [CE C/C toolchain](Welcome to the CE C/C Toolchain! — CE C/C Toolchain documentation). This has been developed for the TI 84 Plus CE calculator that is based on the eZ80 processor and has a quite a reasonably sized community. There is a well developed toolchain, which is based on eZ80 versions of LLVM compile and fasmg assembler. It produces ADL code with 24 bit pointers, 24 bit integers, 32 bit long, 16 bit short, 32 bit float. There is also quite an extensive library for C and C programs.
Although this toolchain is focused heavily optimised for the TI calculator, work has become to adept it to support the Agon Light. At the moment a proof of concept has been achieved to demonstrate the feasibility of this by being able to compile and run a "Hello, world!" example, extend also to print the command line. This is an excellent first step. Further work is required to adapt the CE toolchain's extensive library to the Agon Light. The toolchain also supports C , this has not been tested on the Agon.
Install the CE Toolchain - see [getting started](Getting Started — CE C/C Toolchain documentation) page. The tool-chain can be installed on Windows, Linux or MacOS. Follow the instructions to complete the install, and test that the toolchain works by compiling some of the examples for the TI calculator.
To adapt the the tool-chain for Agon, for this proof of concept, a number of files should be replaced with the files from the repository (GitHub - pcawte/AgDev: Port to Agon Light of TI 84 plus CE Toolchain). Clone the repository and copy over the top of the CE toolchain. For example, if the CE Toolchain is installed in AgDev and the downloaded repository in GitHub/AgDev copy the files over the top using:
cp -R -f GitHub/AgDev/ AgDev`
Which should work for MacOS / Linux - not sure the equivalent for windows.
In the AgExamples/hello_world
issue the make
command (use V=1 not necessary but provides much more details on the commands used):
make V=1
The make clean
command can be used to delete the results of previous compilations and thereby force a recompilation.
There are other examples in the test directory which can be built in the same way.
The build process goes through the following steps:
-
Compilation of .c source files to LLVM bitcode (.bc) using
ez80-clang
-
Linking of LLVM bitcode using
ez80-link
. This includes link time optimisation -
Generation of eZ80 assembly code (.src) for the source programmes using
ez80-clang
-
Assembling and linking of the of the generated assembly code (from step 3) with the libraries and compiler run-time using
fasmg
- this includes building the executable targeted at a specific memory location. This is the main part of the build process that needs to be adjusted.
The compiler if successful produces the final "DEMO.bin". This should be transferred to the Agon for testing.
On the Agon:
*load DEMO.bin
*run &040000 Hello AgDev
Should produce the following output:
Hello World!
Arguments: 3
- argc: 3
- argv[0]: HELLO.BIN
- argv[1]: Hello
- argv[2]: AgDev
make V=1
ez80-clang -MD -c -emit-llvm -nostdinc
-isystem /AgDev/include -Isrc -fno-threadsafe-statics
-Xclang -fforce-mangle-main-argc-argv
-mllvm -profile-guided-section-prefix=false
-DNDEBUG -g0 -Wall -Wextra -Oz
'src/main.c' -o 'obj/src/main.c.bc'
Compiles:
- src/main.c
Generates output:
-
obj/src/main.c.bc
-
obj/src/main.c.d
The .bc files is an intermediate file in LLVM bitcode
The .d file is a dependencies list for main.c.bc, for the source main.c and various include (.h) files
ez80-link
'/AgDev/AgExamples/hello_world/obj/src/main.c.bc'
-o '/AgDev/AgExamples/hello_world/obj/lto.bc'
Links and optimises:
- obj/src/main.c.bc
Generates output:
- obj/lto.bc
ez80-clang -S
-mllvm -profile-guided-section-prefix=false
-Wall -Wextra -Oz
'/AgDev/AgExamples/hello_world/obj/lto.bc'
-o '/AgDev/AgExamples/hello_world/obj/lto.src'
Generates assembly language for
- obj/lto.bc
Outputs eZ80 assembly language to
- obj/lto.src
fasmg -n '/AgDev/meta/ld.alm'
-i 'DEBUG := 0'
-i 'HAS_PRINTF := 1'
-i 'HAS_LIBC := 1'
-i 'HAS_LIBCXX := 1'
-i 'PREFER_OS_CRT := 0'
-i 'PREFER_OS_LIBC := 1'
-i 'include "/AgDev/meta/linker_script"'
-i 'range .bss $080000 : $09ffff'
-i 'locate .header at $040000'
-i map
-i 'source "obj/icon.src",
"/AgDev/lib/crt/crt0.src",
"obj/lto.src"'
-i 'library "/AgDev/lib/libload/fatdrvce.lib",
"/AgDev/lib/libload/fileioc.lib",
"/AgDev/lib/libload/fontlibc.lib",
"/AgDev/lib/libload/graphx.lib",
"/AgDev/lib/libload/keypadc.lib",
"/AgDev/lib/libload/msddrvce.lib",
"/AgDev/lib/libload/srldrvce.lib",
"/AgDev/lib/libload/usbdrvce.lib"'
bin/DEMO.bin
Source files:
-
lib/crt/crt0.src: C runtime (this has been customised for Agon)
-
obj/lto.src: previously generated eZ80 assembly language for all the source files
Files generated
-
DEMO.bin
-
DEMO.map
The .bin files has no location information in it, but is not position independent (e.g. has absolute jumps in it, etc.). It has an assumed location and needs to be loaded by the OS to the correct location.
The .map files contains information on the locations of code / data sections and the global labels. The entry point to the program is at __start
which is at the beginning of the .init
section.
-
bin
-
examples
-
include
-
lib
-
ce
-
crt
, which includes-
crt0.src
: the C-compiler runtime / startup & exit routines -
many other runtime functions for basic arithmetic on the basic types, etc. From a quick inspection most of these should not need to be modified. There are likely a few that need to be changed, for example:
os.src
: which seems to include addresses of functions in the CE ROM which would need to be replaced or copied somehow
-
-
libc
-
libcxx
-
libload
-
-
meta
-
commands.alm
: defines some macros for the fasmg eZ80 assembly process. Don't need to change anything here -
ez80.alm
: configuration for fasmg to assemble eZ80 code. Don't need to change anything here -
ld.alm
: main configuration file for the fasmg assembler. Nothing needs to be changed here. Includescommands.alm
and thenez80.alm
-
linker_script
: included by fasmg as part of the assemblers linking process. Probably changes will need to be made here although it is possible that it is generic enough and just need to update the underlying libraries to the Agon target. -
makefile.mk
: this is included as part of the make process. It is the main place where configuration of the make compilation, assembly and linking is configured. It is included by the individual project makefile usinginclude $(shell cedev-config --makefile)
This has been edited to remove generation of icon and 8xp output files.
-
Things to be changed:
-
makefile.mk: main makefile includes information of the location in memory of the program and data structures, libraries, etc.
-
crt0.src: C-compiler runtime / startup & exit routings
-
Various libraries - this needs to be investigated further, starting with something simple and then adding complexity
Modified to:
-
Don't include an icon - this was a specific TI calculator thing
-
Don't generate an 8xp file from the binary. This is a specific TI 84 format for transferring to the calculator
Updated with Agon specific addresses / memory locations:
-
INIT_LOC = 040000: executable location - should change to 0B0000 for MOS binary executable that can be run from the command line (not sure what else needs to be changed)
-
BSSHEAP_LOW = 080000: this seems to work for a small programme - but not sure what should really be used
-
BSSHEAP_HIGH = 09FFFF: as above
-
STACK_HIGH = 0AFFFF: this seems to work - but not sure what should really be used - or even if this really needs to be set. For the moment this has been removed from the fasmg flags and whatever stack that is provided by MOS is used.
See details below for memory layout.
This has been re-written based on a combination of code from the reverse engineered CE Toolchain version and some of the original [Agon projects](GitHub - breakintoprogram/agon-projects: Official AGON QUARK Firmware: Projects) which used the Zilog toolchain, some of the optimisations used in the CE Toolchain have been removed for clarity. Note that the Zilog assembler, fasmg assembler used by CE toolkit and ez80asm used by many in the Agon community all have different formats and different waves of organising program sections, etc. Part of the challenge of this proof of concept has been in understanding the fasmg assembler.
From the MOS documentation, the memory layout for the Agon is as follows:
- &000000 - &01FFFF: MOS (Flash ROM)
- &040000 - &0BDFFF: User RAM
- &0B0000 - &0B7FFF: Storage for loading MOS star command executables off SD card
- &0BC000 - 0BFFFFF: Global heap and stack
Consequently we assume that the memory from 040000h to 0AFFFFh is useable for programs compiled with the AgDev toolchain (this can be adjusted)
The layout of sections of the program is in the following order with each section immediately following the proceeding one:
-
section .header
: 040000h -
section .init
: 040045h (this can be referenced by the label__start
) -
section .text
: this is where the main program code is located. The code is entered by calling_main
or___main_argc_argv
depending on whethermain(void)
ormain(int argc, char *argv[])
is used. Note that is almost certainly not at the beginning of of .text section. -
section .data
: contains static and global variables defined in the program code. -
section .rodata
: contains constant data, such as string literals in the program code.
There is then a gap before:
-
section .bss
: contains uninitialised global and static variables. This is zeroed as part of the as part of the C-runtime setup (by crt0). -
heap
: this is used by malloc to programmatically allocate memory. This grows upwards towards the stack. -
stack
: used for storing return addresses, parameters for function calls and local variables. This grows downwards towards the heap.
The layout in memory can be checked by examining the .map
file generated as part of the final linking process.
The location of .bss, heap and stack are configured in the makefile.mk
which controls the build process:
BSSHEAP_LOW ?= 080000
BSSHEAP_HIGH ?= 09FFFF
STACK_HIGH ?= 0AFFFF
The layout can be checked by examining the following labels in the .map
file.
___low_bss = 080000
___len_bss = 000000
___heaptop = 09FFFF
___heapbot = 080000
__stack = 0AFFFF
Memory in heap is managed by the malloc
function . The malloc used is from Zilog, the comments state:
/* This implements a straight forward version of malloc, allocating */
/* memory between the first unused address in RAM (__heapbot) and */
/* the current stack pointer; the stack pointer being initialized to */
/* the top of RAM (__heaptop). */
Note however, in this AgDev Toolchain, ___heaptop
is set up independently of the stack, so that the heap should not overwrite the stack (unless insufficient space has been reserved for the stack).
These are short assembly language subroutines that are output as part of the LLVM compiler eZ80 code generation (see discussion here jacobly0/llvm-project#10). There are a number of these not included in the CE Toolkit, instead it calls routines in the CE ROM. The list of compile runtimes is contained in the file: llvm/lib/Target/Z80/Z80ISelLowering.cpp
which is part of the eZ80 LLVM distribution. This list the calling convention to use. There is no detailed documentation on function of each, but in most cases it can be guessed from the naming. These are thought to derive from the original Zilog compiler distribution. There is documentation for each function at the start of the Zilog files - but it is not clear whether any functionality has been changed. Given that the routines in the CE ROM were most likely compiled using the Zilog compiler, one could reasonably assume that the routines still used in the ROM are the same as the original Zilog routines.
The compiler-rt routines that are include are in the C runtime directory lib/crt
of the CE Toolchain. The call address of the routines were the ROM is used is listed in lib/crt/os.src
, see condensed version below:
__fadd := 000270h
__fcmp := 000274h
__fdiv := 000278h
__fmul := 000288h
__fsub := 000290h
__ftol := 00027Ch
__ftoul := __ftol
__dtol := __ftol
__dtoul := __ftoul
__imul_b := 000150h
__indcall := 00015Ch
__ishl_b := 000178h
__ishrs_b := 000180h
__ishru_b := 000188h
__itol := 000194h
__ltof := 000284h
__ltod := __ltof
__setflag := 000218h
__sshl_b := 000244h
__sshrs_b := 00024Ch
__sshru_b := 000254h
__stoi := 000260h
__stoiu := 000264h
__ultof := 000280h
__ultod := __ultof
Contains mainly eZ80 ADL assembly (.src) files.
Items mentioned have been checked - see details. Others have not been checked.
-
Public functions are part of the externally available library
-
Weak functions are internal to the library
-
crt0 (public): updated to target Agon - sufficient for the proof of concept, will need to be developed further to support the full library and C (constructors, destructors, etc. - this was in the original crt0 - needs to be added back in and tested)
-
inchar.src (weak): updated to input from MOS rst 008h GETKEY command
-
outchar.src (weak): updated to output to VDP by MOS rst 010h
-
putchar.src (public): okay. Calls outchar (which needs to be modified)
-
getchar.src (public): updated to remove bug now typecasts from unsigned char to int (the CE version typecasts from signed char - which is contrary to the C-standard) . Call inchar to get character and outchar to echo on console
In general the libc library has been derived from a number of sources:
-
Floating point comes from Zilog ZDS. Note that the some additional functions have been added such as hyperbolic trig functions, cubic root, etc. These use calculations from other floating point functions.
-
Sort and search from Zilog ZDS, including bsearch and qsort
-
Standard IO: uses nanoprintf
- scanf and sscanf have been added based on the Zilog ZDS code
-
File IO: re-written / adapted from CE-Toolchain
-
Time
-
Memory management from Zilog ZDS. Includes free, malloc, realloc
-
Exception handling
-
String conversion from Zilog ZDS. Includes: strtof, strtol, strtoll, strtoul, strtoull
In the CE Toolchain, many of the string library routines that are part of libc are called in the CE ROM, as listed in the lib/libc/os.src
file. Condensed version below:
_longjmp := 000098h
_memchr := 00009Ch
_memcmp := 0000A0h
_memcpy := 0000A4h
_memmove := 0000A8h
_setjmp := 0000B8h
_strcat := 0000C0h
_strchr := 0000C4h
_strcmp := 0000C8h
_strcpy := 0000CCh
_strcspn := 0000D0h
_strlen := 0000D4h
_strncat := 0000D8h
_strncmp := 0000DCh
_strcasecmp := 021E3Ch
_strncpy := 0000E0h
_strpbrk := 0000E4h
_strspn := 0000ECh
_strstr := 0000F0h
_strtok := 0000F4h
These are all available in the Zilog distribution as assembly source code, apart from:
-
strcasecmp
: this is not part of standard libc - and has been deleted. There is, however,strncasecmp
. -
strtok
: available as c source code
All of these with the exception of strcasecmp
have been added to the AgDev toolchain as part of libc .src files.
In the CE Toolchain the file IO functions are wrappers around the fileioc library. This library is CE specific and needs to be replaced. There is already provision in the CE library for doing this, see the CE Toolchain documentation: Using File I/O Functions . Note that the stdio.h
file has been modified so that there is no need for a CUSTOM_FILE_FILE
as referred to in the CE documentation.
The library has been modified to use Agon MOS calls and includes:
-
fopen
,fclose
-
fputc
,fputs
-
fgetc
,fgets
-
feof
,ferror
,fflush
-
fread
,frwite
-
fseek
,rewind
,ftell
-
clearerr
-
remove
And includes files
-
files.c
-
stdio.h
On the Agon there are two different APIs for accessing the FatFS - MOS routines and FatFS routines. Having examined the MOS source code, the MOS routines are simple wrappers around the FatFS routines, just managing / storing the FatFS file descriptors in a table (supports 8 open files). Consequently it makes sense just to use the MOS routines.
The stdio library has been modified to differentiate between text and binary files.
-
When files are opened, they are assumed to be texted, unless binary is specified in the mode flags (see
fopen()
documentation). -
stdin, stdout and stderr are assumed to be text - if reopened via
freopen()
, it is possible to change to binary.
For text files, a translation is done between standard C, where '\n' (LF) is the end of a line and the operating system, which requires CR/LF pair at the end of a line. This applies to the following functions:
-
putchar()
,puts()
-
fputc()
,fputs()
-
printf()
,fprintf()
- but notsprintf()
Note that the following functions do not differentiate between text and binary files:
-
fread()
,frwite()
-
fseek()
,ftell()
This is very limited, mainly just C wrapper around C libraries
Contains .lib library files - needs to be investigated further
See Zilog application note "an0333 - Calling C from asm.pdf"
Only IX register and stack need to be preserved by called functions.
Arguments are pushed from last to first corresponding to the C prototype. In eZ80, 3 bytes are always pushed to the stack regardless of the actual size. However, the assembly function must be careful to only use the valid bytes that are pushed. For example, if a short type is used, the upper byte of the value pushed on the stack will contain arbitrary data. This table lists the locations relative to sp from within the called function. Note that sp [0,2]
contains the return address.
C/C Type | Size | Stack Location |
---|---|---|
char | 1 byte | sp [3] |
short | 2 bytes | sp [3,4] |
int | 3 bytes | sp [3,5] |
long | 4 bytes | sp [3,6] |
long long | 8 bytes | sp [3,10] |
float | 4 bytes | sp [3,6] |
double | 4 bytes | sp [3,6] |
pointer | 3 bytes | sp [3,5] |
Note that eZ80 is little endian - i.e. the least significant byte is stored first.
This table lists which registers are used for return values from a function. The type’s sign does not affect the registers used, but may affect the value returned. The LSB is located in the register on the far right of the expression, e.g. E:UHL
indicates register L
stores the LSB.
C/C Type | Return Register |
---|---|
char | A |
short | HL |
int | UHL |
long | E:UHL |
long long | BC:UDE:UHL |
float | E:UHL |
double | E:UHL |
pointer | UHL |
From the MOS documentation:
- Commands can be abbreviated with a dot, so
DELETE myfile
andDEL. myfile
are equivalent. - Commands are case-insensitive and parameters are space delimited.
- In the syntax description, optional parameters are written as
<param>
- The dot (.) character can be used as a substitution for an optional numeric parameter
- Default LOAD and RUN address is set to 0x040000
- Numbers are in decimal and can be prefixed by '&' for hexadecimal.
- Addresses are 24-bit, unless otherwise specified
&000000 - &01FFFF
: MOS (Flash ROM)&040000 - &0BDFFF
: User RAM&0B0000 - &0B7FFF
: Storage for loading MOS star command executables off SD card&0BC000 - 0BFFFFF
: Global heap and stack
- The RUN command checks a header embedded from byte 64 of the executable and can run either Z80 or ADL mode executables
- MOS will also search the
mos
folder on the SD card for any executables, and will run those like built-in MOS commands
Programs are called from MOS using the the _exec24()
function built into MOS :
-
This function will call the 24 bit address and keep the eZ80 in ADL mode.
-
The called function must do a RET and take care of preserving registers
-
On entry, HL contains the address of the command line parameter string - used for argc / argv processing. There is 256 bytes for storage of the command line including the terminating '
\0'
. -
Save AF, BC, DE, IX and IY registers
-
Return with HL=0
On exit from a program, MOS interprets the value in HL as a "file error", with the following being defined (0=OK, 1=Error accessing SC card, etc):
"OK",
"Error accessing SD card",
"Assertion failed",
"SD card failure",
"Could not find file",
"Could not find path",
"Invalid path name",
"Access denied or directory full",
"Access denied",
"Invalid file/directory object",
"SD card is write protected",
"Logical drive number is invalid",
"Volume has no work area",
"No valid FAT volume",
"Error occurred during mkfs",
"Volume timeout",
"Volume locked",
"LFN working buffer could not be allocated",
"Too many open files",
"Invalid parameter",
"Invalid command",
"Invalid executable",
Consists of the following:
File IO:
-
fopen()
,freopen(),
fclose()
-
fputc()
,fputs()
-
fgetc()
,ungetc()
,fgets()
-
feof()
,ferror()
,fflush()
-
fread()
,fwrite()
-
fseek()
,rewind()
,ftell()
-
clearerr()
-
remove()
Stdin / stdout IO:
-
putchar()
,puts()
-
getchar()
,gets_s()
Formatted output
-
printf()
(andvprintf()
) -
sprintf()
(andvsprintf()
) -
snprintf()
(andvsnprintf()
)
Formatted input
-
scanf()
-
sscanf()
Can redirect output by using freopen()
on stdout
or stderr
:
-
putchar()
- outputs tooutchar()
unless the output is redirected, in which case outputs tofputc()
-
puts()
- callsputchar()
-
printf()
(andvprintf()
) - callsnpf_putc_std()
, which callsputchar()
innanoprintf.c
-
fputc()
- callsmos_fputc()
unless called onstdout
when callsoutchar()
- avoids callingputchar()
so that no risk of function call loops
Can redirect input by using freopen()
on stdin
:
-
getchar()
- callsinchar()
to get the character andoutchar()
to echo the character (even if the output has been redirected). If output has not been re-directed callsfgetc()
and does not echo the character. -
gets_s()
- callsgetchar()
if input has not been redirected (line are terminated with CR). Callsfgets()
of input has been redirected (lines are terminated with CR/LF pair). -
scanf()
- callsgetchar()
inuscan.c
(doesn't need updating) -
fgetc()
- callsmos_fgetc()
unless called on stdin when callsinchar()
and echos withoutchar()
- avoids callinggetchar()
so that no risk of function call loops
Requires FILE *
, which is a pointer to a file handle returned by fopen
and passed to the file IO routines to indicate the file the action is to be performed upon.
Other related files:
stdio.h
- normal header files, which defines the various functions and the typedef for FILE
files.c
- instantiates the storage for files handles including: stdout, stderr, stdin.
The following standard file handles are defined:
-
stdout
- default output -
stderr
- default output for error message -
stdin
- default input
MOS does not implement input / output redirection, so by default these all use the console.
Two options are available for command line processing.
This is automatically included if the main function is defined as
int main( int argc, char* argv[] )
the splits the command line up using space as a delimiter. The command line options are available in the argv[]
array as normal.
This is optionally included if the makefile includes:
LDHAS_ARG_PROCESSING = 1
This supports
-
Quoting with double quotes
-
Input / output redirection
-
>out_file.txt
- redirects stdout toout_file.txt
, creating a new file -
>>out_file.txt
- redirects stdout toout_file.txt
, appending to the end of the file -
<in_file.txt
- redirects stdin to be fromin_file.txt
-
TODO: add redirection for stderr
-
The VDP (video display processor) accepts a text stream from MOS, acting like a text / graphics terminal. The text stream can contain:
-
Normal text
-
Escape sequences / commands to control the display and send graphics
When results are returned by MOS as a result of sending a command, these are stored in the SYSVARs and not returned directly in response to the command. The response is asynchronous, to check that a result has been returned:
-
Set
vpd_pflags
inSYSVAR
to zero -
Issue the VDU command
-
Wait for the relevant bit into in
vpd_pflags
to be set - see<mos_api.h>
for the bit masks
Commands can be sent by:
-
putch()
- single character (this is not part of the C standard library, on MS-DOS systems it is defined in<conio.h>
-
mos_puts()
- multi-character string
Both of which output directly to MOS/VDP - note that they are not part of STDIO library and not subject to CR/LF translation or redirection.
For current documentation on VDU commands, see the Agon Console8 Documentation
These can be called by sending the appropriate control characters using putch()
or mos_puts()
as mentioned.
Convenience functions for many of these are supplied in AgDev.
For example, to change the screen MODE to 3, the C call vdp_mode(3);
will send 22,3
as single bytes to the output, equivalent to putch(22);putch(3);
For a list of the commands see include/agon/vdp_vdu.h
//===-- Z80ISelLowering.cpp - Z80 DAG Lowering Implementation -------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the interfaces that Z80 uses to lower LLVM code into a
// selection DAG.
//
//===----------------------------------------------------------------------===//
setLibcall(RTLIB::ZEXT_I16_I24, "_stoiu", CallingConv::Z80_LibCall );
setLibcall(RTLIB::SEXT_I16_I24, "_stoi", CallingConv::Z80_LibCall );
setLibcall(RTLIB::SEXT_I24_I32, "_itol", CallingConv::Z80_LibCall );
setLibcall(RTLIB::NOT_I16, "_snot", CallingConv::Z80_LibCall );
setLibcall(RTLIB::NOT_I24, "_inot", CallingConv::Z80_LibCall );
setLibcall(RTLIB::NOT_I32, "_lnot", CallingConv::Z80_LibCall );
setLibcall(RTLIB::NOT_I64, "_llnot", CallingConv::Z80_LibCall );
setLibcall(RTLIB::AND_I16, "_sand", CallingConv::Z80_LibCall );
setLibcall(RTLIB::AND_I24, "_iand", CallingConv::Z80_LibCall );
setLibcall(RTLIB::AND_I32, "_land", CallingConv::Z80_LibCall );
setLibcall(RTLIB::AND_I64, "_lland", CallingConv::Z80_LibCall );
setLibcall(RTLIB::OR_I16, "_sor", CallingConv::Z80_LibCall );
setLibcall(RTLIB::OR_I24, "_ior", CallingConv::Z80_LibCall );
setLibcall(RTLIB::OR_I32, "_lor", CallingConv::Z80_LibCall );
setLibcall(RTLIB::OR_I64, "_llor", CallingConv::Z80_LibCall );
setLibcall(RTLIB::XOR_I16, "_sxor", CallingConv::Z80_LibCall );
setLibcall(RTLIB::XOR_I24, "_ixor", CallingConv::Z80_LibCall );
setLibcall(RTLIB::XOR_I32, "_lxor", CallingConv::Z80_LibCall );
setLibcall(RTLIB::XOR_I64, "_llxor", CallingConv::Z80_LibCall );
setLibcall(RTLIB::SHL_I8, "_bshl", CallingConv::Z80_LibCall_AB);
setLibcall(RTLIB::SHL_I16, "_sshl", CallingConv::Z80_LibCall );
setLibcall(RTLIB::SHL_I16_I8, "_sshl_b", CallingConv::Z80_LibCall_AC);
setLibcall(RTLIB::SHL_I24, "_ishl", CallingConv::Z80_LibCall );
setLibcall(RTLIB::SHL_I24_I8, "_ishl_b", CallingConv::Z80_LibCall_AC);
setLibcall(RTLIB::SHL_I32, "_lshl", CallingConv::Z80_LibCall_L );
setLibcall(RTLIB::SHL_I64, "_llshl", CallingConv::Z80_LibCall );
setLibcall(RTLIB::SRA_I8, "_bshrs", CallingConv::Z80_LibCall_AB);
setLibcall(RTLIB::SRA_I16, "_sshrs", CallingConv::Z80_LibCall );
setLibcall(RTLIB::SRA_I16_I8, "_sshrs_b", CallingConv::Z80_LibCall_AC);
setLibcall(RTLIB::SRA_I24, "_ishrs", CallingConv::Z80_LibCall );
setLibcall(RTLIB::SRA_I24_I8, "_ishrs_b", CallingConv::Z80_LibCall_AC);
setLibcall(RTLIB::SRA_I32, "_lshrs", CallingConv::Z80_LibCall_L );
setLibcall(RTLIB::SRA_I64, "_llshrs", CallingConv::Z80_LibCall );
setLibcall(RTLIB::SRL_I8, "_bshru", CallingConv::Z80_LibCall_AB);
setLibcall(RTLIB::SRL_I16, "_sshru", CallingConv::Z80_LibCall );
setLibcall(RTLIB::SRL_I16_I8, "_sshru_b", CallingConv::Z80_LibCall_AC);
setLibcall(RTLIB::SRL_I24, "_ishru", CallingConv::Z80_LibCall );
setLibcall(RTLIB::SRL_I24_I8, "_ishru_b", CallingConv::Z80_LibCall_AC);
setLibcall(RTLIB::SRL_I32, "_lshru", CallingConv::Z80_LibCall_L );
setLibcall(RTLIB::SRL_I64, "_llshru", CallingConv::Z80_LibCall );
setLibcall(RTLIB::CMP_I32, "_lcmpu", CallingConv::Z80_LibCall_F );
setLibcall(RTLIB::CMP_I64, "_llcmpu", CallingConv::Z80_LibCall_F );
setLibcall(RTLIB::CMP_I16_0, "_scmpzero", CallingConv::Z80_LibCall_F );
setLibcall(RTLIB::CMP_I24_0, "_icmpzero", CallingConv::Z80_LibCall_F );
setLibcall(RTLIB::CMP_I32_0, "_lcmpzero", CallingConv::Z80_LibCall_F );
setLibcall(RTLIB::CMP_I64_0, "_llcmpzero",CallingConv::Z80_LibCall_F );
setLibcall(RTLIB::SCMP_I32, "_lcmps", CallingConv::Z80_LibCall_F );
setLibcall(RTLIB::SCMP_I64, "_llcmps", CallingConv::Z80_LibCall_F );
setLibcall(RTLIB::SCMP, "_setflag", CallingConv::Z80_LibCall_F );
setLibcall(RTLIB::NEG_I16, "_sneg", CallingConv::Z80_LibCall );
setLibcall(RTLIB::NEG_I24, "_ineg", CallingConv::Z80_LibCall );
setLibcall(RTLIB::NEG_I32, "_lneg", CallingConv::Z80_LibCall );
setLibcall(RTLIB::NEG_I64, "_llneg", CallingConv::Z80_LibCall );
setLibcall(RTLIB::ADD_I32, "_ladd", CallingConv::Z80_LibCall );
setLibcall(RTLIB::ADD_I32_I8, "_ladd_b", CallingConv::Z80_LibCall_AC);
setLibcall(RTLIB::ADD_I64, "_lladd", CallingConv::Z80_LibCall );
setLibcall(RTLIB::SUB_I32, "_lsub", CallingConv::Z80_LibCall );
setLibcall(RTLIB::SUB_I64, "_llsub", CallingConv::Z80_LibCall );
setLibcall(RTLIB::MUL_I8, "_bmulu", CallingConv::Z80_LibCall_BC);
setLibcall(RTLIB::MUL_I16, "_smulu", CallingConv::Z80_LibCall );
setLibcall(RTLIB::MUL_I24, "_imulu", CallingConv::Z80_LibCall );
setLibcall(RTLIB::MUL_I24_I8, "_imul_b", CallingConv::Z80_LibCall_AC);
setLibcall(RTLIB::MUL_I32, "_lmulu", CallingConv::Z80_LibCall );
setLibcall(RTLIB::MUL_I64, "_llmulu", CallingConv::Z80_LibCall );
setLibcall(RTLIB::SDIV_I8, "_bdivs", CallingConv::Z80_LibCall_BC);
setLibcall(RTLIB::SDIV_I16, "_sdivs", CallingConv::Z80_LibCall_16);
setLibcall(RTLIB::SDIV_I24, "_idivs", CallingConv::Z80_LibCall );
setLibcall(RTLIB::SDIV_I32, "_ldivs", CallingConv::Z80_LibCall );
setLibcall(RTLIB::SDIV_I64, "_lldivs", CallingConv::Z80_LibCall );
setLibcall(RTLIB::UDIV_I8, "_bdivu", CallingConv::Z80_LibCall_BC);
setLibcall(RTLIB::UDIV_I16, "_sdivu", CallingConv::Z80_LibCall_16);
setLibcall(RTLIB::UDIV_I24, "_idivu", CallingConv::Z80_LibCall );
setLibcall(RTLIB::UDIV_I32, "_ldivu", CallingConv::Z80_LibCall );
setLibcall(RTLIB::UDIV_I64, "_lldivu", CallingConv::Z80_LibCall );
setLibcall(RTLIB::SREM_I8, "_brems", CallingConv::Z80_LibCall_AC);
setLibcall(RTLIB::SREM_I16, "_srems", CallingConv::Z80_LibCall_16);
setLibcall(RTLIB::SREM_I24, "_irems", CallingConv::Z80_LibCall );
setLibcall(RTLIB::SREM_I32, "_lrems", CallingConv::Z80_LibCall );
setLibcall(RTLIB::SREM_I64, "_llrems", CallingConv::Z80_LibCall );
setLibcall(RTLIB::UREM_I8, "_bremu", CallingConv::Z80_LibCall_AC);
setLibcall(RTLIB::UREM_I16, "_sremu", CallingConv::Z80_LibCall_16);
setLibcall(RTLIB::UREM_I24, "_iremu", CallingConv::Z80_LibCall );
setLibcall(RTLIB::UREM_I32, "_lremu", CallingConv::Z80_LibCall );
setLibcall(RTLIB::UREM_I64, "_llremu", CallingConv::Z80_LibCall );
setLibcall(RTLIB::UDIVREM_I24, "_idvrmu", CallingConv::Z80_LibCall );
setLibcall(RTLIB::UDIVREM_I32, "_ldvrmu", CallingConv::Z80_LibCall );
setLibcall(RTLIB::CTLZ_I8, "_bctlz", CallingConv::Z80_LibCall_AC);
setLibcall(RTLIB::CTLZ_I16, "_sctlz", CallingConv::Z80_LibCall );
setLibcall(RTLIB::CTLZ_I24, "_ictlz", CallingConv::Z80_LibCall );
setLibcall(RTLIB::CTLZ_I32, "_lctlz", CallingConv::Z80_LibCall );
setLibcall(RTLIB::CTLZ_I64, "_llctlz", CallingConv::Z80_LibCall );
setLibcall(RTLIB::POPCNT_I8, "_bpopcnt", CallingConv::Z80_LibCall_AC);
setLibcall(RTLIB::POPCNT_I16, "_spopcnt", CallingConv::Z80_LibCall );
setLibcall(RTLIB::POPCNT_I24, "_ipopcnt", CallingConv::Z80_LibCall );
setLibcall(RTLIB::POPCNT_I32, "_lpopcnt", CallingConv::Z80_LibCall );
setLibcall(RTLIB::POPCNT_I64, "_llpopcnt", CallingConv::Z80_LibCall );
setLibcall(RTLIB::BITREV_I8, "_bbitrev", CallingConv::Z80_LibCall_AC);
setLibcall(RTLIB::BITREV_I16, "_sbitrev", CallingConv::Z80_LibCall );
setLibcall(RTLIB::BITREV_I24, "_ibitrev", CallingConv::Z80_LibCall );
setLibcall(RTLIB::BITREV_I32, "_lbitrev", CallingConv::Z80_LibCall );
setLibcall(RTLIB::BITREV_I64, "_llbitrev", CallingConv::Z80_LibCall );
setLibcall(RTLIB::BSWAP_I32, "_lbswap", CallingConv::Z80_LibCall );
setLibcall(RTLIB::BSWAP_I64, "_llbswap", CallingConv::Z80_LibCall );
setLibcall(RTLIB::ADD_F32, "_fadd", CallingConv::Z80_LibCall_L );
setLibcall(RTLIB::SUB_F32, "_fsub", CallingConv::Z80_LibCall_L );
setLibcall(RTLIB::MUL_F32, "_fmul", CallingConv::Z80_LibCall_L );
setLibcall(RTLIB::DIV_F32, "_fdiv", CallingConv::Z80_LibCall_L );
setLibcall(RTLIB::REM_F32, "_frem", CallingConv::Z80_LibCall_L );
setLibcall(RTLIB::NEG_F32, "_fneg", CallingConv::Z80_LibCall_L );
setLibcall(RTLIB::CMP_F32, "_fcmp", CallingConv::Z80_LibCall_F );
setLibcall(RTLIB::FPTOSINT_F32_I32, "_ftol", CallingConv::Z80_LibCall_L );
setLibcall(RTLIB::FPTOSINT_F32_I64, "_ftoll", CallingConv::Z80_LibCall );
setLibcall(RTLIB::FPTOUINT_F32_I32, "_ftoul", CallingConv::Z80_LibCall_L );
setLibcall(RTLIB::FPTOUINT_F32_I64, "_ftoull", CallingConv::Z80_LibCall );
setLibcall(RTLIB::SINTTOFP_I32_F32, "_ltof", CallingConv::Z80_LibCall_L );
setLibcall(RTLIB::SINTTOFP_I64_F32, "_lltof", CallingConv::Z80_LibCall );
setLibcall(RTLIB::UINTTOFP_I32_F32, "_ultof", CallingConv::Z80_LibCall_L );
setLibcall(RTLIB::UINTTOFP_I64_F32, "_ulltof", CallingConv::Z80_LibCall );
setLibcall(RTLIB::ADD_F64, "_dadd", CallingConv::Z80_LibCall );
setLibcall(RTLIB::SUB_F64, "_dsub", CallingConv::Z80_LibCall );
setLibcall(RTLIB::MUL_F64, "_dmul", CallingConv::Z80_LibCall );
setLibcall(RTLIB::DIV_F64, "_ddiv", CallingConv::Z80_LibCall );
setLibcall(RTLIB::REM_F64, "_drem", CallingConv::Z80_LibCall );
setLibcall(RTLIB::NEG_F64, "_dneg", CallingConv::Z80_LibCall );
setLibcall(RTLIB::CMP_F64, "_dcmp", CallingConv::Z80_LibCall_F );
setLibcall(RTLIB::FPEXT_F32_F64, "_ftod", CallingConv::Z80_LibCall );
setLibcall(RTLIB::FPROUND_F64_F32, "_dtof", CallingConv::Z80_LibCall );
setLibcall(RTLIB::FPTOSINT_F64_I32, "_dtol", CallingConv::Z80_LibCall );
setLibcall(RTLIB::FPTOSINT_F64_I64, "_dtoll", CallingConv::Z80_LibCall );
setLibcall(RTLIB::FPTOUINT_F64_I32, "_dtoul", CallingConv::Z80_LibCall );
setLibcall(RTLIB::FPTOUINT_F64_I64, "_dtoull", CallingConv::Z80_LibCall );
setLibcall(RTLIB::SINTTOFP_I32_F64, "_ltod", CallingConv::Z80_LibCall );
setLibcall(RTLIB::SINTTOFP_I64_F64, "_lltod", CallingConv::Z80_LibCall );
setLibcall(RTLIB::UINTTOFP_I32_F64, "_ultod", CallingConv::Z80_LibCall );
setLibcall(RTLIB::UINTTOFP_I64_F64, "_ulltod", CallingConv::Z80_LibCall );c
From: https://www.cemetech.net/forum/viewtopic.php?t=13913&start=0
Each line is a precedence level, except where specified otherwise, operators within a precedence level are evaluated ltr.
Ops | Meaning |
---|---|
Logical expressions | |
~ | unary logical not, arguments can also be |
&, | | binary logical conjunction and disjunction |
All the operators below this line can only have basic expressions as arguments | |
=, <, >, <=, >=, <>, eq, eqtype, relativeto | Comparison, <> is not equal, eq is like = but returns false for uncomparable operands, eqtype returns true if both operands are the same (algebraic, string, float) type, relativeto returns true if both operands are comparable (differ by a constant) |
defined, used | true if the basic expression to the right is entirely defined, true if the variable name to the right is used |
Basic expressions | |
lengthof, elementsof, float, trunc | length of a string in characters, length of a poly in terms, int to float, float to int |
sizeof, elementof, scaleof, metadataof | rtl size associated with a label, poly op idx for element, scale, metadata idx op poly |
not, bsf, bsr | complement, index of lowest set bit, index of highest set bit |
shl, shr, bswap | shifts, byte swap (second arg is the size of the value to swap, in bytes) |
xor, and, or | bitwise |
mod | remainder |
*, / | multiplication, division |
, - | addition, subtraction (includes unary ops) |
string | converts a number to a string (strings are usually converted to numbers implicitly, or explicitly with a unary operator) |
-
<name>#<name>
; concatenates two names into a single identifier, but each side may get replaced individually if it matches a parameter name -
$
; current pc -
$$
; base of the current output section (address passed to last org) -
$@
; address after last non-reserved data -
$%
; offset within output file, does not work with tiformat.inc -
$%%
; offset after last non-reserved data within output file, does not work with tiformat.inc -
%t
; assembly timestamp -
__file__
; current file -
__line__
; current line
-
org <basic expr>
; start a new output area to appear next in the file and assembled starting at address<basic expr>
-
section <basic expr>
; same as org but do not output reserved bytes, does not work with tiformat.inc -
virtual [<basic expr>]
; start a new output area that does not get output to the file end virtual ; restore the previous output area -
<name>::
; creates an area label that references the current output area -
load <name>[ : <size>] from [<area label> : ]<addr>
; loads<size>
(defaults tosizeof <addr>
) outputted bytes from address<addr>
in output area<area label>
(defaults to current output area) and store in variable<name>
-
store <name>[ : <size>] at [<area label> : ]<addr>
; stores<size>
(defaults tosizeof <addr>
) bytes to address<addr>
in output area<area label>
(default to current output area) and store in variable<name>
-
db <basic expr 1>[, <basic expr 2>...]
; define 1-byte values -
rb <basic expr>
; reserve<basic expr>
1-byte locations -
dw <basic expr 1>[, <basic expr 2>...]
; define 2-byte values -
rw <basic expr>
; reserve<basic expr>
2-byte locations -
dl <basic expr 1>[, <basic expr 2>...]
; define 3-byte values -
rl <basic expr>
; reserve<basic expr>
3-byte locations -
dd <basic expr 1>[, <basic expr 2>...]
; define 4-byte values -
rd <basic expr>
; reserve<basic expr>
4-byte locations -
emit|dbx <size> : <basic expr 1>[, <basic expr 2>...]
; define<size>
-byte values;
If any reserve or definition is preceded by a name with no colon, that name gets defined as a label to the first item, with a sizeof the item size
Inside any definition
<basic expr 1> rep <basic expr 2>
; repeats<basic expr 2>
<basic expr 1>
times, to include more than one value in the repetition, enclose them with <>
-
<name> equ <anything>
; define an arbitrary text substitution for a symbol -
<name> reequ <anything>
; same as equ but don't discard previous value -
define <name> <anything>
; same as equ, but is not checked for recursive substitutions until use -
redefine <name> <anything>
; same as define but don't discard previous value -
<name> = <basic expr>
; assign a value to a symbol, discarding current value -
<name> =: <basic expr>
; assign a value to a symbol, remembering the current value -
<name> := <basic expr>
; assign a value to a constant symbol only once, attempts to redefine will error, therefore it can always be forward referenced -
<name>:
; like<name> := $
-
label <name>[ : <size>][ at <addr>]
; defines a constant symbol with size at address (defaults to $) -
restore <name 1>[, <name 2>...]
; restore the previously remembered value for each symbol
Namespaces can be created by assigning anything to any symbol
-
namespace <name 1>
; switches to, but does not create, the namespace associated with the symbol -
All new symbols
<name 2>
defined in here can be referenced outside the namespace block with <name 1>.<name 2> -
end namespace
-
macro <name> [<param 1>[, <param 2>...]]
; defines a macro, remembering the current contents. Macro body, parameters get substituted with their values every time the macro is executed -
end macro
-
purge <name>
; restores the previously remembered contents of a macro -
struc <name> [(<label name>) ][<param 1>[, <param 2>...]]
; defines a labeled macro, remembering the current contents. Macro body, parameters get substituted with their values every time the macro is executed, Both.
and<label name>
refer to the label -
end struc
-
restruc <name>
; restores the previously remembered contents of a labeled macro
Macro and struc args can be suffixed with *
to mean required, :
to give a default value if not specified, and &
on the last argument means it takes on the value of all the remaining arguments
-
local <name 1>[, <name 2>...]
; makes the specified symbols local to the current macro or struc invocation -
esc macro ...
; exactly like macro, but does not require an extra end macro to end the enclosing macro -
esc end macro
; exactly like end macro, but does not close the enclosing macro even if there was no opening macro
-
[else ]if <cond expr>
; run these commands if<cond expr>
is true, with else only if the previous command was false or didn't match -
[else ]match <pattern>, <anything>
; run these command if<pattern>
matches<anything>
, with else only if the previous command was false or didn't match -
[else ]rmatch <pattern>, <anything>
; run these command if<pattern>
matches<anything>
, discarding context, with else only if the previous if was false or match didn't match -
else
; run these commands if previous if was false or match didn't match -
end if|match|rmatch
; ends if, match, or rmatch, use whichever was used last (before the optional else) -
while <cond expr>
; run these commands while<cond expr>
is true -
end while
-
repeat <basic expr>[, <name 1> : <basic expr 1>[, <name 2> : <basic expr 2>...]]
; run these commands<basic expr>
times, symbols start at their initial value and go up by one each iteration -
end repeat
-
iterate|irp <name>[, <first>[, <second>...]]
; run these commands -
end iterate|irp
-
irpv <name 1>, <name 2>
; run these commands with name 1 equal to each remembered value of name 2, starting from the oldest, only works with define/equ -
end irpv
Inside all looping commands:
-
%
; current iteration starting from 1 -
%%
; total iterations -
indx <basic expr>
; switches to a different iteration index -
break
; break out of loop
-
include '<file>'
; assembles<file>
at the current location in the source -
file '<file>'[ : <start>[, <size>]]
; outputs<size>
(defaults to entire file) bytes from a file starting at byte<start>
(defaults to beginning) -
display <basic expr 1>[, <basic expr 2>...]
; outputs strings as strings and numbers as characters to stdout -
eval <basic expr 1>[, <basic expr 2>...]
; same syntax as display, evaluates the concatenation of all the arguments as assembly code at the current location in the source -
err <basic expr 1>[, <basic expr 2>...]
; same syntax as display, displays a custom error the causes assembly to fail if this is the last pass -
assert <cond expr>
; causes assembly to fail if is false on the last pass