# 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,