637 lines
27 KiB
Plaintext
637 lines
27 KiB
Plaintext
========================================
|
|
Gigatron Control Language (GCL) and vCPU
|
|
========================================
|
|
|
|
GCL is a compiled low-level language for writing simple games and
|
|
applications for the Gigatron TTL microcomputer, without bothering
|
|
the programmer with the harsh timing requirements of the hardware
|
|
platform.
|
|
|
|
vCPU is the interpreted 16-bit virtual processor running in the dead
|
|
time of the video/sound loop.
|
|
|
|
The two are closely tied together, and we sometimes we mix them up.
|
|
Technically still, "GCL" is the source language or notation, while
|
|
"vCPU" is the virtual CPU, or interpreter, that is executing compiled
|
|
GCL instructions.
|
|
|
|
vCPU's advantages over native 8-bit Gigatron code are:
|
|
1. you don't need to think about video timing with everything you do
|
|
2. operations are 16-bits, and
|
|
3. programs can run from RAM.
|
|
Its disadvantage is that vCPU is slower than native code.
|
|
|
|
The main menu, `Snake', `Mandelbrot', `Tiny BASIC v2' and `WozMon'
|
|
are pure GCL programs. These programs still use generic support
|
|
ROM functions implemented as native code: the so-called SYS functions.
|
|
|
|
`Racer', `Pictures' and `Loader' are hybrid programs, meaning that
|
|
they have application-specific SYS functions and/or data in ROM.
|
|
|
|
`Bricks' and `Tetronis' are vCPU programs written in a vCPU assembler,
|
|
not using GCL at all.
|
|
|
|
---------------
|
|
Example program
|
|
---------------
|
|
|
|
gcl1 {GCL version}
|
|
{Approximate BASIC equivalent}
|
|
{Function to draw binary value as pixels}
|
|
|
|
[def {10 GOTO 80}
|
|
$4448 D= {Middle of screen} {20 D=$4448: REM MIDDLE OF SCREEN}
|
|
[do
|
|
C [if<0 15 else 5] D. {30 IF C<0 POKE D,15 ELSE POKE D,5}
|
|
C C+ C= {40 C=C+C}
|
|
D 1+ D= {50 D=D+1}
|
|
-$4458 D+ if<0 loop] {60 IF D<$4458 THEN 30}
|
|
ret {70 RETURN}
|
|
] Plot=
|
|
|
|
{Compute largest 16-bit Fibonacci number and plot it on screen}
|
|
|
|
[do
|
|
0 A= {80 A=0}
|
|
1 B= {90 B=1}
|
|
[do
|
|
A B+ C= {100 C=A+B}
|
|
B A= C B= {110 A=B: B=C}
|
|
if>0 loop] {120 IF B>0 THEN 100}
|
|
Plot! {130 GOSUB 20}
|
|
loop] {140 GOTO 80}
|
|
|
|
Compiled to vCPU instructions this program looks like this:
|
|
|
|
RAM address
|
|
| encoding
|
|
| | vCPU instruction
|
|
| | | operand
|
|
| | | | GCL notation
|
|
| | | | |
|
|
V V V V V
|
|
0200 cd 29 DEF $022b [def
|
|
0202 11 48 44 LDWI $4448 $4448
|
|
0205 2b 30 STW $30 D=
|
|
[do
|
|
0207 21 32 LDW $32 C
|
|
0209 35 53 0e BGE $0210 [if<0
|
|
020c 59 0f LDI $0f 15
|
|
020e 90 10 BRA $0212 else
|
|
0210 59 05 LDI 5 5]
|
|
0212 f0 30 POKE $30 D.
|
|
0214 21 32 LDW $32 C
|
|
0216 99 32 ADDW $32 C+
|
|
0218 2b 32 STW $32 C=
|
|
021a 21 30 LDW $30 D
|
|
021c e3 01 ADDI 1 1+
|
|
021e 2b 30 STW $30 D=
|
|
0220 11 a8 bb LDWI $bba8 -$4458
|
|
0223 99 30 ADDW $30 D+
|
|
0225 35 53 28 BGE $022a if<0
|
|
0228 90 05 BRA $0207 loop]
|
|
022a ff RET ret]
|
|
022b 2b 34 STW $34 Plot=
|
|
[do
|
|
022d 59 00 LDI 0 0
|
|
022f 2b 36 STW $36 A=
|
|
0231 59 01 LDI 1 1
|
|
0233 2b 38 STW $38 B=
|
|
[do
|
|
0235 21 36 LDW $36 A
|
|
0237 99 38 ADDW $38 B+
|
|
0239 2b 32 STW $32 C=
|
|
023b 21 38 LDW $38 B
|
|
023d 2b 36 STW $36 A=
|
|
023f 21 32 LDW $32 C
|
|
0241 2b 38 STW $38 B=
|
|
0243 35 56 46 BLE $0248 if>0
|
|
0246 90 33 BRA $0235 loop]
|
|
0248 cf 34 CALL $34 Plot!
|
|
024a 90 2b BRA $022d loop]
|
|
|
|
As you can see, there is a near perfect one-to-one relation between
|
|
GCL words and vCPU instructions.
|
|
|
|
BTW: To make a visual distinction between 16-bits code and native
|
|
code, we always captalize vCPU instructions while we write 8-bits
|
|
Gigatron code in lower case.
|
|
|
|
----------
|
|
Deployment
|
|
----------
|
|
|
|
Translation from GCL to vCPU is done offline. So the Gigatron runs
|
|
vCPU applications. There are other ways to generate vCPU programs,
|
|
for example through at67's assembler. There are two ways to get a
|
|
vCPU program into the Gigatron. One is by sending it into RAM through
|
|
the input port using the Loader application. The other is to store
|
|
the object code in the ROM disk portion of the EPROM. The file
|
|
format for exchange of vCPU programs is GT1. This format is described
|
|
in a different document: GT1-files.txt
|
|
|
|
-----------------
|
|
Programming model
|
|
-----------------
|
|
|
|
The programming model is accumulator oriented: there is no implicit
|
|
expression stack. vCPU does have a call stack however. Programs
|
|
are typically made of many small function definitions followed by
|
|
a main loop. The functions usually operate on global variables,
|
|
although it is also possible to put variables on the stack.
|
|
|
|
The virtual registers are 16-bits (except vSP) and reside in the
|
|
zero page.
|
|
|
|
vAC is the virtual accumulator, used by instructions as operand
|
|
and/or result
|
|
|
|
vPC is the virtual program counter. Programs run from RAM. When
|
|
executing, vPC auto-increments just its low byte, so it wraps
|
|
around on the same RAM page. (Code normally doesn't "use" this.)
|
|
Function calls can go across pages. vPC gets incremented by 2
|
|
BEFORE fetching the next instruction, except after CALL and RET.
|
|
|
|
vLR is the link register. It points to the instruction after the
|
|
most recent CALL instruction. This is used to return after
|
|
making a function call. When nesting functions, vLR should
|
|
be saved on the stack with PUSH and restored with POP.
|
|
|
|
vSP is the stack pointer. The stack lives in the zero page top and
|
|
grows down.
|
|
|
|
Program variables are 16-bit words and typically hold a number or
|
|
a pointer. Variables normally reside in the zero page. Arbitrary
|
|
memory can be addressed as bytes and words using 16-bit pointers.
|
|
|
|
---------------------
|
|
Notes on GCL notation
|
|
---------------------
|
|
|
|
GCL draws most of its inspiration from two notable mini-languages:
|
|
SWEET16 (Apple II) and FALSE (Amiga). There is a little bit of a
|
|
FORTH influence as well. There are also similarities with UNIX dc(1)
|
|
and Altair VTL-2, although those are coincidental.
|
|
|
|
GCL programs are written as a long sequence of words without much
|
|
structure enforced by the compiler. Most words map directly to a
|
|
single vCPU instruction and therefore also encode an embedded operand
|
|
(e.g. `1' means `LDI 1' and `2+' means `ADDI 2'). Many words operate
|
|
on both vAC and some variable or constant. Sequences can be grouped
|
|
with () {} or [], each of which has its own meaning. Spaces and
|
|
newlines simply separate words. Use indentation for clarity. There
|
|
is no need for spaces around the grouping characters []{}().
|
|
|
|
Constants are decimal, or hexadecimal when preceded with '$'.
|
|
Constants can be preceded by '-' or '+' (note: -$1000, not $-0000).
|
|
|
|
For convenience, symbols predefined by the system can be referenced
|
|
by prefixing a backslash, e.g. '\fontData'. This can be used anywhere
|
|
where a constant is expected. It also may be preceded by a sign.
|
|
|
|
Variable names start with an alphanumeric character and are case
|
|
sensitive. The overview below uses the following conventions:
|
|
'i' indicates an 8-bit integer constant.
|
|
'ii' indicates a 16-bit integer constant.
|
|
'X' indicates a named variable, allocated globally on the zero page.
|
|
'_C' indicates a named constant, or label, from the system's symbol table
|
|
|
|
--------------------
|
|
Meaning of GCL words
|
|
--------------------
|
|
|
|
Basics
|
|
------
|
|
{...} Comments ignored by machine. Comments can be nested
|
|
i ii Load integer constant into vAC, e.g. 1, +1972, -$be05
|
|
X= Store vAC into variable X
|
|
X Load variable X into vAC
|
|
X+ X- Add/subtract variable X to/from vAC
|
|
i+ i- Add/subtract small constant to/from vAC
|
|
i<< Shift vAC i bits left
|
|
i& i| i^ Logical AND/OR/XOR vAC with small constant
|
|
X& X| X^ Logical AND/OR/XOR vAC with variable X
|
|
|
|
Advanced
|
|
--------
|
|
<X, >X, Load low/high byte of variable X
|
|
<X. >X. Store vAC as byte into the low/high byte of variable X
|
|
<X++ >X++ Increment low/high byte of variable X
|
|
<i++ >i++ Increment byte at address i/i+1
|
|
|
|
System
|
|
------
|
|
*=ii Set compilation address (default is $200)
|
|
_C=* Label definition. Refer to its value as \_C
|
|
_C Treat system symbol `C' as a GCL variable (e.g. _sysFn=)
|
|
|
|
Memory
|
|
------
|
|
X, X. Read/write unsigned byte at memory address pointed at by X
|
|
X; X: Read/write word at memory address pointed at by X
|
|
peek deek Read byte/word from memory address pointed at by vAC
|
|
i, i. Read/write unsigned byte at zero page address i
|
|
i; i: Read/write word at zero page address i
|
|
i?? Table lookup from ROM address vAC+i (ROM page must be prepared)
|
|
|
|
Structured programming
|
|
----------------------
|
|
[...] Code block, used with `if', `else', `do', `loop'
|
|
def Define function or inline data: load next vPC in vAC,
|
|
then jump to end of the current block
|
|
if>0 Continue executing code if vAC>0, otherwise jump to
|
|
end of the block (or past an optional matching `else')
|
|
Conditions are `=0' `>0' `<0' `<=0' `>=0' `<>0'
|
|
else When encountered, skip rest of code until end of block
|
|
do Mark the start of a loop
|
|
loop Jump back to matching `do' (which may be in an outer block)
|
|
if>0loop Optimization for `if>0' + `loop' (works with all conditions)
|
|
|
|
Subroutines
|
|
-----------
|
|
X! Jump to function pointed at by X, store old vPC in vLR
|
|
ret Jump to vLR, to return from a leaf function. Non-leaf
|
|
functions should use `pop' + `ret' as return sequence
|
|
push Push vLR onto stack, for entering a non-leaf function
|
|
pop Remove top of stack and put value in vLR
|
|
i!! Call native code pointed at by sysFn, not exceeding i cycles
|
|
call Jump to function pointed at by vAC
|
|
|
|
Stack variables
|
|
---------------
|
|
i-- i++ Subtract/add constant value from/to stack pointer
|
|
%i %i= Load/store stack variable at relative byte offset i
|
|
|
|
Data
|
|
----
|
|
#i #<ii Inline byte value i, silently truncated to lowest 8 bits
|
|
#>ii Inline high byte value (most often used with labels)
|
|
`Hello`world! Inline ASCII test. Replace backquotes with spaces
|
|
` A single inline backquote (ASCII 96)
|
|
##ii ##_C Inline word value, little-endian
|
|
#@_C PC relative offset (6502-style)
|
|
|
|
Directives
|
|
----------
|
|
zpReset=i Start allocating GCL variables from address i (default=$30)
|
|
execution=ii Start execution at address ii (default is first segment >= $200)
|
|
|
|
Versioning
|
|
----------
|
|
gcl0x gcl1 GCL version. `x' denotes experimental/extended versions
|
|
|
|
We foresee three versions of GCL: gcl0x, gcl1 and gcl2.
|
|
|
|
gcl0x is what we used to make the built-in applications of ROM v1.
|
|
It is still evolving, sometimes in backward incompatible ways.
|
|
|
|
gcl1 will be the final update in notation once we've settled on
|
|
what GCL should really look like. gcl0x has some inconsistencies
|
|
in notation that are confusing. Some aren't easy to resolve while
|
|
maintaining its spirit. We won't take this step easily.
|
|
|
|
gcl2 will add a macro system. The parenthesis are reserved for that.
|
|
|
|
-----------------
|
|
Program structure
|
|
-----------------
|
|
|
|
GCL translation is single-pass and doesn't have a linking stage.
|
|
Therefore a typical GCL program has a setup phase followed by a
|
|
main loop. In the setup phase, variables are initialized with
|
|
values and references to functions or data. When running, function
|
|
calling is done through those variables. vCPU is optimized for
|
|
function calling in that way. Long functions that don't fit in a
|
|
page should be split into multiple functions. When a program gets
|
|
bigger than a single segment or page, the setup phase will have to
|
|
hop over to the next page. The clearest way to do that is as follows:
|
|
|
|
{Start compilation at $200}
|
|
[def ... ret] Function1=
|
|
[def ... ret] Function2=
|
|
$300 call
|
|
{Continue compilation at $300}
|
|
$300:
|
|
[def ... ret] Function3=
|
|
[def ... ret] Function4=
|
|
$400 call
|
|
{Continue compilation at $400}
|
|
$400:
|
|
[def ... ret] Function5=
|
|
[do ... loop] {Main loop}
|
|
|
|
This can be done in a slightly more space-efficient way by using
|
|
"ret" and vLR. This saves 1 byte per page, but is cryptic. Also you
|
|
can't do any function calls in the setup phase as that uses vLR.
|
|
|
|
{Start compilation at $200. (vLR=$200)}
|
|
[def ... ret] Function1=
|
|
[def ... ret] Function2=
|
|
>_vLR++ ret {Increment high byte of _vLR and go there}
|
|
{Continue compilation at $300}
|
|
$300:
|
|
[def ... ret] Function3=
|
|
[def ... ret] Function4=
|
|
>_vLR++ ret
|
|
{Continue compilation at $400}
|
|
$400:
|
|
[def ... ret] Function5=
|
|
[do ... loop] {Main loop}
|
|
|
|
----------------------------------
|
|
Common pitfalls in GCL programming
|
|
----------------------------------
|
|
|
|
- Forgetting `ret' at the end of a function block
|
|
- Calling functions from a leaf function (forgetting `push' and `pop')
|
|
- Forgetting that all named variables are global
|
|
- Calling a function before the last page when also using vLR hopping in setup
|
|
- Misspelling a variable (Tip: inspect the symbol table output)
|
|
- Renaming a function, but not where it is called
|
|
|
|
-----------------
|
|
Future extensions
|
|
-----------------
|
|
|
|
The current GCL version is 'gcl0x'. The 'x' suffix intends to warn
|
|
that incompatible changes happen without notice or version number
|
|
change. Once we believe the notation is stable and some weird
|
|
quirks removed, we plan to rename it 'gcl1'.
|
|
|
|
Having stated that, GCL was primarily the bootstrapping notation
|
|
for writing the first vCPU applications in ROMv1. It has brought
|
|
us all the way to Tiny BASIC. In the meantime a more traditional
|
|
assembler has emerged, and we have a C compiler. So we might not
|
|
develop GCL much further, as it has served its purpose.
|
|
|
|
Some gcl1 changes we're still pondering about:
|
|
|
|
Consistency Especially surrounding ,.:; and \ (backslash)
|
|
Symptoms:
|
|
- i: and i= do the same thing (store vAC on zero-page)
|
|
- X: is a DOKE to the address in var X, but there is
|
|
no notation to POKE/DOKE using a zero page address i
|
|
- ii: sets the compilation address, it isn't a DOKE
|
|
- Therefore we can't create code in the zero page
|
|
_C=123 Constant naming
|
|
&X &<X &>X Get an variable's address. Should treat < and > as part
|
|
of the name instead of the prefix part of the operator
|
|
*i Treat as the name of a GCL variable with address i
|
|
i-- i++ Replace with something that associates with stack: %++i %--i ?
|
|
() Compile time expressions? Macros? (128+1) -> 129.
|
|
Can could escape to Python for evaluation.
|
|
E.g. Color=(Blue+1)
|
|
|
|
See GitHub issue #80 for a more detailed discussion.
|
|
https://github.com/kervinck/gigatron-rom/issues/80
|
|
|
|
-------------------
|
|
Deprecated features
|
|
-------------------
|
|
|
|
i= Alias for i: (was often used as \symbol=)
|
|
i# Original notation for #i
|
|
X<++ X>++ Original notation for <X++ >X++
|
|
i<++ i>++ Original notation for <i++ >i++
|
|
ii: Original notation for *=ii
|
|
i% i%= Original notation for %i %i=
|
|
i! Original notation for i!! (will only be depricated in gcl1)
|
|
i? Original notation for i?? (will only be depricated in gcl1)
|
|
|
|
----------------------
|
|
vCPU instruction table
|
|
----------------------
|
|
|
|
The vCPU interpreter has 34 core instructions. Each opcode is just
|
|
a jump offset into the interpreter code page to the code that
|
|
implements its behavior. Most instructions take a single byte
|
|
operand, but some have two and others none.
|
|
|
|
Mnem. Encoding #C Description
|
|
----- --------- -- -----------
|
|
ST $5E DD 16 Store byte in zero page ([D]=vAC&256)
|
|
STW $2B DD 20 Store word in zero page ([D],[D+1]=vAC&255,vAC>>8)
|
|
STLW $EC DD 26 Store word in stack frame ([vSP+D],[vSP+D+1]=vAC&255,vAC>>8)
|
|
LD $1A DD 18 Load byte from zero page (vAC=[D])
|
|
LDI $59 DD 16 Load immediate small positive constant (vAC=D)
|
|
LDWI $11 LL HH 20 Load immediate word constant (vAC=$HHLL)
|
|
LDW $21 DD 20 Word load from zero page (vAC=[D]+256*[D+1])
|
|
LDLW $EE DD 26 Load word from stack frame (vAC=[vSP+D]+256*[vSP+D+1])
|
|
ADDW $99 DD 28 Word addition with zero page (vAC+=[D]+256*[D+1])
|
|
SUBW $B8 DD 28 Word subtraction with zero page (vAC-=[D]+256*[D+1])
|
|
ADDI $E3 DD 28 Add small positive constant (vAC+=D)
|
|
SUBI $E6 DD 28 Subtract small positive constant (vAC-=D)
|
|
LSLW $E9 28 Shift left ('ADDW vAC' will not work!) (vAC<<=1)
|
|
INC $93 DD 16 Increment zero page byte ([D]++)
|
|
ANDI $82 DD 16 Logical-AND with small constant (vAC&=D)
|
|
ANDW $F8 DD 28 Word logical-AND with zero page (vAC&=[D]+256*[D+1])
|
|
ORI $88 DD 14 Logical-OR with small constant (vAC|=D)
|
|
ORW $FA DD 28 Word logical-OR with zero page (vAC|=[D]+256*[D+1])
|
|
XORI $8C DD 14 Logical-XOR with small constant (vAC^=D)
|
|
XORW $FC DD 26 Word logical-XOR with zero page (vAC^=[D]+256*[D+1])
|
|
PEEK $AD 26 Read byte from memory (vAC=[vAC])
|
|
DEEK $F6 28 Read word from memory (vAC=[vAC]+256*[vAC+1])
|
|
POKE $F0 DD 28 Write byte in memory ([[D+1],[D]]=vAC&255)
|
|
DOKE $F3 DD 28 Write word in memory ([[D+1],[D]],[[D+1],[D]+1]=vAC&255,vAC>>8)
|
|
LUP $7F DD 26 ROM lookup, needs trampoline in target page (vAC=ROM[vAC+D])
|
|
BRA $90 DD 14 Branch unconditionally (vPC=(vPC&0xff00)+D)
|
|
BCC $35 CC DD 28 Test vAC and branch conditionally. CC can be
|
|
EQ=$3F, NE=$72, LT=$50, GT=$4D, LE=$56, GE=$53
|
|
CALL $CF DD 26 Goto address but remember vPC (vLR,vPC=vPC+2,[D]+256*[D+1]-2)
|
|
RET $FF 16 Leaf return (vPC=vLR-2)
|
|
PUSH $75 26 Push vLR on stack ([vSP-2],v[vSP-1],vSP=vLR&255,vLR>>8,vLR-2)
|
|
POP $63 26 Pop address from stack (vLR,vSP=[vSP]+256*[vSP+1],vSP+2)
|
|
ALLOC $DF DD 14 Create or destroy stack frame (vSP+=D)
|
|
SYS $B4 DD 20+ Native function call using at most 2*T cycles, D=270-max(14,T)
|
|
HALT $B4 $80 inf Halt vCPU execution
|
|
DEF $CD DD 26 Define data or code (vAC,vPC=vPC+2,(vPC&0xff00)+D)
|
|
|
|
#C is the number of clocks or cycles, counted back-to-back.
|
|
So this number includes advancing vPC, fetch, dispatch and execute.
|
|
|
|
The formulas are pseudo-formal. Better take them with a grain of
|
|
salt. Still: 'DD' or 'D' are intended to represent a single byte.
|
|
'LL HH' is intended to represent a 16-bit word in little-endian
|
|
order. '[X]' is intended to represent a RAM location, as in the
|
|
native instruction set. 'vAC=D' is intended to mean that the (16-bits)
|
|
vAC gets the (unsigned) 8-bit value D, and with that clearing its
|
|
high 8 bits in the process.
|
|
|
|
----------------------------------------
|
|
Experimental vCPU instructions in DEVROM
|
|
----------------------------------------
|
|
|
|
See also this thread: https://forum.gigatron.io/viewtopic.php?f=4&t=136
|
|
|
|
Mnem. Encoding #C Description
|
|
----- --------- -- -----------
|
|
CALLI $85 LL HH 28 Goto immediate address and remember vPC (vLR,vPC=vPC+3,$HHLL-2)
|
|
CMPHS $1f DD 28 Adjust high byte for signed compare (vACH=XXX)
|
|
CMPHU $97 DD 28 Adjust high byte for unsigned compare (vACH=XXX)
|
|
|
|
Changed cycle times
|
|
-------------------
|
|
LD $1A DD 22 (was 18)
|
|
INC $93 DD 20 (was 16)
|
|
ANDI $82 DD 22 (was 16)
|
|
DEF $CD DD 24 (was 26)
|
|
|
|
-------------------------------------------
|
|
Mapping from vCPU instructions to GCL words
|
|
-------------------------------------------
|
|
|
|
Summary of prefixes:
|
|
Operators
|
|
Word variables: X *i X X= X, X. X; X: X& X| X^ X+ X-
|
|
Byte variables: <X <*i X, X. X++
|
|
>X >*i
|
|
Addresses or constants &X i i& i| i^ i+ i-
|
|
|
|
Instruction Variable word Constant word Keyword Comment
|
|
----------- ------------- ------------- ------- -------
|
|
LDI <ii >ii i
|
|
LDWI ii
|
|
LDW X was i; and \C; (keep?)
|
|
STW X= i: i= deprecated
|
|
PEEK peek alias for _vAC,
|
|
LDW+PEEK X,
|
|
POKE X.
|
|
DEEK deek alias for _vAC;
|
|
LDW+DEEK X;
|
|
DOKE X:
|
|
LD <X >X <*i >*i was i, (keep?)
|
|
ST <X= >X= <*i= >*i= was i. (keep?)
|
|
INC <X++ >X++ <*i++ >*i++ change to i++
|
|
LDLW %i was i%
|
|
STLW %i= was i%=
|
|
ALLOC i-- i++ XXX should become i%- i%+
|
|
CALL X! *i! call alias for _vAC!
|
|
DEF def
|
|
POP pop
|
|
PUSH push
|
|
RET ret
|
|
BRA else loop
|
|
BCC if...
|
|
ANDW X& *i&
|
|
ORW X| *i|
|
|
XORW X^ *i^
|
|
ADDW X+ *i+
|
|
SUBW X- *i-
|
|
ANDI &X& i&
|
|
ORI &X| i|
|
|
XORI &X^ i^
|
|
ADDI &X+ i+
|
|
SUBI &X- i-
|
|
LSLW i<< alias for i*LSLW
|
|
LSRW i>> XXX if we can add LSRW to vCPU
|
|
SYS i!! was i!
|
|
LUP i?
|
|
|
|
------------------------
|
|
Idioms and coding tricks
|
|
------------------------
|
|
|
|
255& Clear vAC high byte
|
|
|
|
255| 255^ Clear vAC low byte (there is no 'ANDWI' instruction)
|
|
|
|
\vACH, Move vAC high byte to low (vAC>>=8)
|
|
|
|
\vACH. 255| 255^ Move vAC low byte to high (vAC<<=8)
|
|
|
|
255^ 1+ Negate vAC low byte
|
|
|
|
>_vAC++ Increment vAC high byte
|
|
|
|
128- 128- Decrement vAC high byte
|
|
|
|
128^ 128- Sign extend vAC (provided the high byte is zero)
|
|
|
|
a b- [if>=0 ...] if a >= b ... But this breaks in case of overflow!
|
|
(For example consider a = 30000 and b = -5000)
|
|
|
|
a b^ [if<0 b 1| else a b-] Checking if the operands are of opposite sign first
|
|
[if>=0 ...] makes the comparison safe from overflow.
|
|
|
|
[def ... ret] fAddr= Defining function whose address is saved into var fAddr
|
|
fAddr! Calling said function with CALL (v4 method)
|
|
|
|
[def _fSym=* ... ret] Defining a function whose address is given by symbol fSym
|
|
\fSym! Calling said function with CALLI (v5 and above)
|
|
|
|
|
|
--------------
|
|
SYS extensions
|
|
--------------
|
|
|
|
Addresses of SYS functions that are part of the ABI:
|
|
|
|
00ad SYS_Exec_88 Load serialized vCPU code from ROM and execute
|
|
04a7 SYS_Random_34 Get random number and update entropy
|
|
0600 SYS_LSRW1_48 Shift right 1 bit
|
|
0619 SYS_LSRW2_52 Shift right 2 bits
|
|
0636 SYS_LSRW3_52 Shift right 3 bits
|
|
0652 SYS_LSRW4_50 Shift right 4 bits
|
|
066d SYS_LSRW5_50 Shift right 5 bits
|
|
0687 SYS_LSRW6_48 Shift right 6 bits
|
|
04b9 SYS_LSRW7_30 Shift right 7 bits
|
|
04c6 SYS_LSRW8_24 Shift right 8 bits
|
|
06a0 SYS_LSLW4_46 Shift left 4 bits
|
|
04cd SYS_LSLW8_24 Shift left 8 bits
|
|
04e1 SYS_VDrawBits_134 Draw 8 vertical pixels
|
|
06c0 SYS_Unpack_56 Unpack 3 bytes into 4 pixels
|
|
04d4 SYS_Draw4_30 Copy 4 pixels to screen memory
|
|
00f4 SYS_Out_22 Write byte to hardware OUT register
|
|
00f9 SYS_In_24 Read byte from hardwar IN register
|
|
|
|
Added in ROM v2:
|
|
|
|
0b00 SYS_SetMode_v2_80 Set video mode 0..3
|
|
0b03 SYS_SetMemory_v2_54 Set 1..256 bytes of memory to value
|
|
|
|
Added in ROM v3:
|
|
|
|
0b06 SYS_SendSerial1_v3_80 Send data out over game controller port
|
|
0c00 SYS_Sprite6_v3_64 Draw sprite of 6 pixels wide and N pixels high
|
|
0c40 SYS_Sprite6x_v3_64 Draw sprite mirrored in X direction
|
|
0c80 SYS_Sprite6y_v3_64 Draw sprite upside down
|
|
0cc0 SYS_Sprite6xy_v3_64 Draw sprite mirrored and upside down
|
|
|
|
Added in ROM v4:
|
|
|
|
0b09 SYS_ExpanderControl_v4_40 Set register in I/O and RAM expander
|
|
0b0c SYS_Run6502_v4_80 Execute MOS 6502 code until BRK
|
|
0b0f SYS_ResetWaveforms_v4_50 Setup sound tables in page 7
|
|
0b12 SYS_ShuffleNoise_v4_46 Shuffle noise table in page 7
|
|
0b15 SYS_SpiExchangeBytes_v4_134 Send and receive bytes
|
|
|
|
Added in ROM v5a:
|
|
|
|
00ef SYS_ReadRomDir_v5_80 Navigate ROM directory
|
|
|
|
Added in ROM v6:
|
|
|
|
009e SYS_Multiply_s16_v6_66 16 bits multiplication (from at67)
|
|
00a1 SYS_Divide_s16_v6_80 15 bits unsigned division (from at67)
|
|
00e6 SYS_ScanMemory_v6_50 Search byte in memory region
|
|
00e9 SYS_CopyMemory_v6_80 Copy memory region
|
|
00ec SYS_CopyMemoryExt_v6_100 Copy memory across RAM extension banks
|
|
0b18 SYS_ReceiveSerial1_v6_32 Receive byte on input port (for the loader)
|
|
|
|
Application specific SYS calls in ROM v1 that are -not- part of the ABI:
|
|
|
|
SYS_Read3_40 (Pictures)
|
|
SYS_LoaderProcessInput_48 (Loader, renamed SYS_ReceiveSerial1)
|
|
SYS_LoaderNextByteIn_32 (Loader)
|
|
SYS_LoaderPayloadCopy_34 (Loader)
|
|
SYS_RacerUpdateVideoX_40 (Racer)
|
|
SYS_RacerUpdateVideoY_40 (Racer)
|
|
|
|
Retro-actively retired from ABI:
|
|
SYS_Reset_36 Soft reset (use vReset instead)
|
|
|
|
For details on arguments, side-effects and return values, please
|
|
refer to the comments in the ROM source files (Core/ROMxxx.py).
|
|
|
|
-- End of document --
|