collapseos/doc/asm.txt

301 lines
9.0 KiB
Plaintext

# Assembling Z80 binaries
(All assembers in Collapse OS follow the same basic principles.
There are sections, below, for each supported architectures, but
you should read this first section first to be familiar with
those common, basic principles)
Words in the Z80 assembler, loaded with "5 LOAD" allow you to
assemble z80 binaries. Being Forth words, opcode assembly is a
bit different than with a typical assembler. For example, what
would traditionally be "ld a, b" would become "A B LDrr,".
Those opcode words, of which there is a complete list below, end
with "," to indicate that their effect is to write (,) the cor-
responding opcode.
The "argtype" suffix after each mnemonic is needed because the
assembler doesn't auto-detect the op's form based on arguments.
It has to be explicitly specified. "r" is for 8-bit registers,
"d" for 16-bit ones, "i" for immediate, "c" is for conditions.
Be aware that "SP" and "AF" refer to the same value: some 16-
bit ops can affect SP, others, AF. If you use the wrong argu-
ment on the wrong op, you will affect the wrong register.
Mnemonics having only a single form, such as PUSH and POP,
don't have argtype suffixes.
In addition to opcode words, some variables are also defined by
this program:
BIN( is the addr at which the compiled binary will live. It is
often 0.
ORG is H@ offset at which we begin spitting binary. Used to
compute PC. To have a proper PC, call "H@ ORG !" at the
beginning of your assembly process. PC is H@ - ORG + BIN(.
Labels are a convenient way of managing relative jump
calculations. Backward labels are easy. It is only a matter or
recording "HERE" and do subtractions. Forward labels record the
place where we should write the offset, and then when we get to
that point later on, the label records the offset there.
To avoid using dict memory in compilation targets, we
pre-declare label variables here, which means we have a limited
number of it. We have 4: L1, L2, L3, L4.
# Ad-hoc assembly
A frequent usage of the assembler is to cross-assemble a new
Collapse OS binary. However, it can also be used to assemble
code for immediate usage on the machine that assemble.
A frequent mistake when doing so is to forget to set BIN( and
ORG. BIN( must match your system's binary offset (you get get
it with "0 BIN+") even for the most simple of code because
otherwise, ";CODE" will jump to the wrong offset.
ORG is less critical, but can lead to problems if not set, so
you should take the habit of setting it with "H@ ORG !".
# Flow
There are 2 label types: backward and forward. For each type,
there are two actions: set and write. Setting a label is
declaring where it is. Words for this are BSET and FSET. It has
to be performed at the label's destination. Writing a label is
writing its offset difference to the binary result. It has to be
done right after a relative jump operation. Word for this are
BWR and FWR. Yes, those words are only for relative jumps.
For backward labels, set happens before write. For forward
labels, write happen before set. The write operation writes a
dummy placeholder, and then the set operation writes the offset
at that placeholder's address.
Important limitation: Flow words are broken when PC reaches
0x8000. The BREAK, word relies on that 15th bit as a flag.
Variable actions are expected to be called with labels in
front of them. Examples:
L1 BSET NOP, JR, L1 BWR ( backward jump )
JR, L1 FWR NOP, L1 FSET ( forward jump )
If you look at the code for those words, you'll notice a mys-
terious "1-". z80 relative jumps receives "e-2", that is, the
offset that *counts the 2 bytes of the jump itself*. Because we
set the label *after* the jump OP1 itself, that's 1 byte that is
taken care of. We still need to adjust by another byte before
writing the offset.
Can you use labels with JP, and CALL,? Yes, but only backwards
jumps, and in that case, you use the label's value directly.
Example: L2 @ CALL,
# Structured flow
z80a also has words that behave similarly to IF..THEN and
BEGIN..UNTIL.
On the IF side, we have IFZ, IFNZ, IFC, IFNC, and THEN,. When
the opposite condition is met, a relative jump is made to
THEN,'s PC. For example, if you have IFZ, a jump is made when
Z is unset.
There can be an ELSE, in the middle of an IF, and THEN,. When
present, IF, jumps to it when the condition is unmet. When the
condition is met, upon reaching the ELSE, we unconditionally
jump to the THEN,.
On the BEGIN,..AGAIN, side, it's a bit different. You start
with your BEGIN, instruction, and then later you issue a
JRxx, instr followed by AGAIN,. Exactly like you would do
with a label.
On top of that, you have the very nice BREAK, instruction,
which must also be preceded by a JRxx, and will jump to the
PC following the next AGAIN,. Examples:
IFZ, NOP, ELSE, NOP, THEN,
BEGIN, NOP, JR, AGAIN, ( unconditional )
BEGIN, NOP, JRZ, AGAIN, ( conditional )
BEGIN, NOP, JRZ, BREAK, JR, AGAIN, ( break off the loop )
# Z80 Instructions list
Letters in [] brackets indicate "argtype" variants. When the
bracket starts with ",", it means that a "plain" mnemonic is
available. For example, "RET," and "RETc," exist.
Note that assemblers in Collapse OS are incomplete and opcode
words were implemented in a "just-in-time" fashion, when needed.
r => A B C D E H L (HL)
d => BC DE HL AF/SP
c => CNZ CZ CNC CC CPO CPE CP CM
LD [rr, ri, di, (i)HL, HL(i), d(i), (i)d, rIXY, IXYr,
(DE)A, A(DE), (i)A, A(i)]
ADD [r, i, HLd, IXd, IXIX, IYd, IYIY]
ADC [r, HLd]
CP [r, i, (IXY+)]
SBC [r, HLd]
SUB [r, i]
INC [r, d, (IXY+)]
DEC [r, d, (IXY+)]
AND [r, i]
OR [r, i]
XOR [r, i]
OUT [iA, (C)r]
IN [Ai, r(C)]
JP [i, (HL), (IX), (IY)]
JR [, Z, NZ, C, NC]
PUSH POP
SET RES BIT
RL RLC SLA RLA RLCA
RR RRC SRL RRA RRCA
CALL RST DJNZ
DI EI EXDEHL EXX HALT
NOP RET [,c] RETI RETN SCF
Macros:
SUBHLd PUSH [0,1,Z,A] HLZ DEZ
LDDE(HL) OUT [HL,DE]
# 8086 assembler
Load with "30 LOAD". As with the Z80 assembler, it is incom-
plete.
Mnemonics are followed by argument types. For example, MOVri,
moves 8-bit immediate to 8-bit register.
'r' = 8-bit register 'x' = 16-bit register
'i' = 8-bit immediate 'I' = 16-bit immediate
's' = SREG register
Mnemonics that only have one signature (for example INT,) don't
have operands letters.
For jumps, it's special. 's' is SHORT, 'n' is NEAR, 'f' is FAR.
# 8086 Instructions list
r -> AL BL CL DL AH BH CH DX
x -> AX BX CX DX SP BP SI DI
s -> ES CS SS DS
[] -> [SI] [DI] [BP] [BX] [BX+SI] [BX+DI] [BP+SI] [BP+DI]
RET CLI STI HLT CLD STD NOP CBW REPZ REPNZ
LODSB LODSW CMPSB SMPSW MOVSB MOVSW SCASB SCASW STOSB STOSW
CALL J[Z,NZ,C,NC] JMP[s,n,r,f]
INC[r,x,[w],[b],[w]+,[b]+]
DEC[r,x,[w],[b],[w]+,[b]+]
POP[x,[w],[w]+]
PUSH[x,[w],[w]+,s]
MUL[r,x]
DIV[r,x]
XOR[rr,xx]
OR[rr,xx]
AND[rr,xx,ALi,AXI]
ADD[rr,xx,ALi,AXI,xi]
SUB[rr,xx,ALi,AXI,xi]
INT
CMP[rr,xx,r[],x[],r[]+,x[]+]
MOV[rr,xx,r[],x[],[]r,[]x,r[]+,x[]+,[]+r,[]+x,ri,xI,sx,rm,xm
mr,mx]
("1" means "shift by 1", "CL" means "shift by CL")
ROL[r1,x1,rCL,xCL]
ROR[r1,x1,rCL,xCL]
SHL[r1,x1,rCL,xCL]
SHR[r1,x1,rCL,xCL]
# AVR assembler
Load with "50 LOAD". As with the Z80 assembler, it is incom-
plete.
All mnemonics in AVR have a single signature. Therefore, we
don't need any "argtype" suffixes.
Registers are referred to with consts R0-R31. There is
X, Y, Z, X+, Y+, Z+, X-, Y-, Z- for appropriate ops (LD, ST).
XL, XH, YL, YH, ZL, ZH are simple aliases to R26-R31.
Branching works differently. Instead of expecting a byte to be
written after the naked op, branching words expect a displace-
ment argument.
This is because there's bitwise ORing involved in the creation
of the final opcode, which makes z80a's approach impractical.
This makes labelling a bit different too. Instead of expecting
label words after the naked branching op, we rather have label
words expecting branching wordref as an argument. Examples:
L2 ' BRTS FLBL! ( branch forward to L2 )
L1 ' RJMP LBL, ( branch backward to L1 )
# Model-specific constants
Model-specific constants must be loaded separately. Here is a
list of units:
- ATMega328P: B65-B66
Those units contain register constants such as PORTB, DDRB, etc.
Unlike many moder assemblers, they do not include bit constants.
Here's an example use:
DDRB 5 SBI,
PORTB 5 CBI,
R16 TIFR0 IN,
R16 0 ( TOV0 ) SBRS,
# AVR instructions list
OPRd (B53)
ASR COM DEC INC LAC LAS LAT LSR NEG POP PUSH
ROR SWAP XCH
OPRdRr (B54)
ADC ADD AND CP CPC CPSE EOR MOV MUL OR SBC
SUB
OPRdA (B54)
IN OUT
OPRdK (B55)
ANDI CPI LDI ORI SBCI SBR SUBI
OPAb (B55)
CBI SBI SBIC SBIS
OPNA (B56)
BREAK CL[C,H,I,N,S,T,V,Z] SE[C,H,I,N,S,T,V,Z] EIJMP ICALL
EICALL IJMP NOP RET RETI SLEEP WDR
OPb (B57)
BCLR BSET
OPRdb (B57)
BLD BST SBRC SBRS
Special (B57,B60)
CLR TST LSL LD ST
Flow (B58)
RJMP RCALL
BR[BC,BS,CC,CS,EQ,NE,GE,HC,HS,ID,IE,LO,LT,MI,PL,SH,TC,TS,VC,VS]
Flow macros (B61)
LBL! LBL, SKIP, TO, FLBL, FLBL! BEGIN, AGAIN? AGAIN, IF, THEN,