PROGRAM z80_assembler; {$V-,R+} { 05 April 1985 - Dap Z80 Assembler } CONST Null = #00; cr = #13; end_ch = #27; space = ' '; tab = #09; version = '[1.01] 10 October 1985'; TYPE hex = 0 .. 15; CONST value : ARRAY ['0' .. 'F'] OF Byte = ( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, { :;<=>?@ } 10, 11, 12, 13, 14, 15 ); digit : ARRAY [hex] OF Char = ( '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' ); TYPE registers = ( A, B, C, D, E, F, H, L, R, IV, AF, BC, DE, HL, IX, IY, PC, SP, null_reg ); mnemonics = ( ADC, ADD, AND_, BIT, CALL, CCF, CP, CPD, CPDR, CPI, CPIR, CPL, DAA, DEC, DI, DJNZ, EI, EX, EXX, HLT, IM, IN_, INC, IND, INDR, INI, INIR, JP, JR, LD, LDD, LDDR, LDI, LDIR, NEG, NOP, OR_, OTDR, OTIR, OUT, OUTD, OUTI, POP, PUSH, RES, RET, RETI, RETN, RL, RLA, RLC, RLCA, RLD, RR, RRA, RRC, RRCA, RRD, RST, SBC, SCF, SET_, SLA, SRA, SRL, SUB, XOR_, null_op ); OtherSymbols = ( DefineByte, DefineChar, DefineWord, Originate, Macro, EndMacro, EndAssembly, TheRadix, Equate, IncludeFile, PageSet, TitleSet, TypeSet, NullOther ); symbols = ( null_sym, identifier, number, operation, equal, str_data, comma, semicolon, period, location, colon, left_bp, right_bp, end_file, EndLine ); reg_str = String[ 2]; mnem_str = String[ 4]; val_str = String[ 16]; file_name = String[ 23]; _String = String[ 80]; big_str = String[255]; LabelStr = String[ 15]; LabelPtr = ^LabelType; LabelType = RECORD Name : LabelStr; Loc : Integer; Left : LabelPtr; Right : LabelPtr; END; PLocPtr = ^PatchLoc; PatchLoc = RECORD PLoc : Integer; Oprtion : Char; PAdj : Integer; OSet : Boolean; TwoBytes : Boolean; PNext : PLocPtr END; PatchPtr = ^Patch; Patch = RECORD PName : LabelStr; FixLoc : PLocPtr; LeftPatch : PatchPtr; RightPatch : PatchPtr END; VAR ops : ARRAY [mnemonics] OF mnem_str; reg : ARRAY [registers] OF reg_str; line : big_str; ch_pos : Byte; radix : Byte; ch : Char; io_error : Integer; PosCnt : Integer; { Position Counter } GenFile : FILE OF Byte; in_name : file_name; in_file : Text; Labels : LabelPtr; Patches : PatchPtr; PROCEDURE init_ops; BEGIN { init_ops } ops[ADC ] := 'ADC'; ops[ADD ] := 'ADD'; ops[AND_] := 'AND'; ops[BIT ] := 'BIT'; ops[CALL] := 'CALL'; ops[CCF ] := 'CCF'; ops[CP ] := 'CP'; ops[CPD ] := 'CPD'; ops[CPDR] := 'CPDR'; ops[CPI ] := 'CPI'; ops[CPIR] := 'CPIR'; ops[CPL ] := 'CPL'; ops[DAA ] := 'DAA'; ops[DEC ] := 'DEC'; ops[DI ] := 'DI'; ops[DJNZ] := 'DJNZ'; ops[EI ] := 'EI'; ops[EX ] := 'EX'; ops[EXX ] := 'EXX'; ops[HLT ] := 'HALT'; ops[IM ] := 'IM'; ops[IN_ ] := 'IN'; ops[INC ] := 'INC'; ops[IND ] := 'IND'; ops[INDR] := 'INDR'; ops[INI ] := 'INI'; ops[INIR] := 'INIR'; ops[JP ] := 'JP'; ops[JR ] := 'JR'; ops[LD ] := 'LD'; ops[LDD ] := 'LDD'; ops[LDDR] := 'LDDR'; ops[LDI ] := 'LDI'; ops[LDIR] := 'LDIR'; ops[NEG ] := 'NEG'; ops[NOP ] := 'NOP'; ops[OR_ ] := 'OR'; ops[OTDR] := 'OTDR'; ops[OTIR] := 'OTIR'; ops[OUT ] := 'OUT'; ops[OUTD] := 'OUTD'; ops[OUTI] := 'OUTI'; ops[POP ] := 'POP'; ops[PUSH] := 'PUSH'; ops[RES ] := 'RES'; ops[RET ] := 'RET'; ops[RETI] := 'RETI'; ops[RETN] := 'RETN'; ops[RL ] := 'RL'; ops[RLA ] := 'RLA'; ops[RLC ] := 'RLC'; ops[RLCA] := 'RLCA'; ops[RLD ] := 'RLD'; ops[RR ] := 'RR'; ops[RRA ] := 'RRA'; ops[RRC ] := 'RRC'; ops[RRCA] := 'RRCA'; ops[RRD ] := 'RRD'; ops[RST ] := 'RST'; ops[SBC ] := 'SBC'; ops[SCF ] := 'SCF'; ops[SET_] := 'SET'; ops[SLA ] := 'SLA'; ops[SRA ] := 'SRA'; ops[SRL ] := 'SRL'; ops[SUB ] := 'SUB'; ops[XOR_] := 'XOR'; ops[null_op] := '' END; { init_ops } PROCEDURE init_reg; BEGIN { init_reg } reg[A ] := 'A'; reg[B ] := 'B'; reg[C ] := 'C'; reg[D ] := 'D'; reg[E ] := 'E'; reg[F ] := 'F'; reg[H ] := 'H'; reg[L ] := 'L'; reg[R ] := 'R'; reg[IV] := 'I'; reg[AF] := 'AF'; reg[BC] := 'BC'; reg[DE] := 'DE'; reg[HL] := 'HL'; reg[IX] := 'IX'; reg[IY] := 'IY'; reg[PC] := 'PC'; reg[SP] := 'SP'; reg[null_reg] := '' END; { init_reg } PROCEDURE usage; BEGIN { usage } WriteLn; WriteLn ('Usage:'); WriteLn; WriteLn (' Z80 [.ASM],[filename][.COM],[filename][.LST],[filename][.CRF][;]'); WriteLn; WriteLn (' ie: Z80 test,,A:test;'); WriteLn; Halt END; { usage } FUNCTION upper_ch ( ch : Char ) : Char; BEGIN { upper_ch } IF ch IN ['a' .. 'z'] THEN ch := Chr (Ord (ch) - Ord ('a') + Ord ('A') ); upper_ch := ch END; { upper_ch } FUNCTION upper_str ( s : big_str ) : big_str; VAR i : Byte; BEGIN { upper_str } FOR i := 1 TO Length (s) DO s[i] := upper_ch (s[i] ); upper_str := s END; { upper_str } PROCEDURE Error ( Message : _String ); BEGIN { Error } WriteLn; WriteLn (Message); Halt END; { Error } PROCEDURE initialize; VAR i : Integer; GenName : File_Name; ErrorNum : _String; BEGIN { initialize } WriteLn; WriteLn ('Z80 Assembler ', version); IF ParamCount < 1 THEN usage; in_name := upper_str (ParamStr (1) ); IF (Pos ('.ASM', in_name) = 0) AND (Pos ('.', In_Name) = 0) THEN in_name := in_name + '.ASM'; Assign (in_file, in_name); {$I-} Reset (in_file); io_error := IoResult; {$I+} Str (Io_Error, ErrorNum); IF io_error <> 0 THEN Error ('Unable to open ' + in_name + ' due to I/O error #' + ErrorNum); GenName := Copy (In_Name, 1, Pos ('.', In_Name) ) + 'Bin'; Assign (GenFile, GenName); Rewrite (GenFile); init_ops; init_reg; ch := ' '; line := ''; ch_pos := 0; radix := 10; PosCnt := 0; { Position Counter } Labels := Nil; Patches := Nil; END; { initialize } FUNCTION val_radix ( s : val_str; rdx : Byte ) : Integer; VAR i : Integer; BEGIN { val_radix } i := 0; s := upper_str (s); WHILE Length (s) > 0 DO BEGIN i := i * rdx + value[s[1] ]; Delete (s, 1, 1) END; val_radix := i END; { val_radix } FUNCTION str_radix ( i, wide : Integer; rdx, pwr : Byte ) : val_str; VAR r : Real; s : val_str; FUNCTION power ( x : Real; y : Byte ) : Real; BEGIN { power } IF y = 0 THEN x := 1 ELSE WHILE y > 1 DO BEGIN x := x * x; y := y - 1 END; power := x END; { power } BEGIN { str_radix } s := ''; IF i < 0 THEN BEGIN r := power (256.0, pwr) + i; WHILE r > 0.0 DO BEGIN i := Trunc (r - Int (r / rdx) * rdx); r := Int (r / rdx); s := digit[i] + s END END ELSE WHILE i > 0 DO BEGIN s := digit[i MOD rdx] + s; i := i DIV rdx; END; WHILE Length (s) < wide DO s := '0' + s; str_radix := s END; { str_radix } PROCEDURE get_line; BEGIN { get_line } ReadLn (in_file, line); ch_pos := 0 END; { get_line } PROCEDURE get_ch; BEGIN { get_ch } ch_pos := ch_pos + 1; IF ch_pos <= Length (line) THEN ch := line[ch_pos] ELSE IF Eof (in_file) THEN ch := end_ch ELSE BEGIN get_line; ch := cr END END; { get_ch } FUNCTION next_ch : Char; BEGIN { next_ch } IF ch_pos < Length (line) THEN next_ch := line[ch_pos + 1] ELSE next_ch := cr END; { next_ch } PROCEDURE parser; VAR start_ch : Char; TempOpCh : Char; chars : big_str; ident : LabelStr; LabelId : LabelStr; num : String[16]; p_radix : Byte; CurChPos : Byte; sym : symbols; PROCEDURE get_symbol; BEGIN { get_symbol } sym := null_sym; chars := ''; ident := ''; num := ''; REPEAT get_ch UNTIL NOT (ch IN [space, tab] ); IF ch IN ['A' .. 'Z', 'a' .. 'z'] THEN { Identifier } BEGIN sym := identifier; ident := ch; WHILE next_ch IN ['A' .. 'Z', 'a' .. 'z', '0' .. '9', '_'] DO BEGIN get_ch; ident := ident + ch END END ELSE IF ch IN ['0' .. '9'] THEN { Number } BEGIN sym := number; num := ch; WHILE next_ch IN ['0' .. '9', 'A' .. 'F', 'a' .. 'f'] DO BEGIN get_ch; num := num + ch END; IF next_ch IN ['H', 'O', 'Q', 'h', 'o', 'q'] THEN BEGIN get_ch; CASE ch OF 'H', 'h' : p_radix := 16; { Hexidecimal } 'O', 'o', 'Q', 'q' : p_radix := 8; { Octal } END END ELSE CASE num[Length (num) ] OF 'B', 'b' : BEGIN p_radix := 2; { Binary } Delete (num, Length (num), 1) END; 'D', 'd' : BEGIN p_radix := 10; { Decimal } Delete (num, Length (num), 1) END ELSE p_radix := radix END END ELSE IF ch IN ['/', '+', '-', '*'] THEN { Arith } sym := operation ELSE IF ch IN ['[', '('] THEN { Memory or parenthesis in expression } sym := left_bp ELSE IF ch IN [']', ')'] THEN sym := right_bp ELSE IF ch IN ['''', '"'] THEN { String or Char } BEGIN sym := str_data; start_ch := ch; get_ch; WHILE NOT (ch IN [start_ch, cr] ) DO BEGIN chars := chars + ch; get_ch END; IF ch = cr THEN Error ('Strings must not exceed current line.') END ELSE IF ch = ':' THEN { Label } sym := colon ELSE IF ch = ',' THEN { Seperator } sym := comma ELSE IF ch = '.' THEN { Special commands } sym := period ELSE IF ch = ';' THEN { Comment -- ignore rest of line } BEGIN sym := semicolon; WriteLn; Write (Copy (Line, Ch_Pos, Length (Line) ) ); ch_pos := Length (line) END ELSE IF ch = '$' THEN { Current location value } sym := location ELSE IF ch = '=' THEN { EQU -- another form } sym := equal ELSE IF ch = end_ch THEN { End of file } sym := end_file ELSE IF Ch = Cr THEN { End of line } Sym := EndLine END; { get_symbol } FUNCTION check_op : mnemonics; VAR op_is : mnemonics; id : String[31]; BEGIN { check_op } op_is := ADC; id := upper_str (ident); WHILE (ops[op_is] <> id) AND (op_is < null_op) DO op_is := Succ (op_is); check_op := op_is END; { check_op } FUNCTION check_reg : registers; VAR reg_is : registers; id : String[31]; BEGIN { check_reg } reg_is := A; id := upper_str (ident); WHILE (reg[reg_is] <> id) AND (reg_is < null_reg) DO reg_is := Succ (reg_is); check_reg := reg_is END; { check_reg } FUNCTION Others : OtherSymbols; VAR id : String[31]; BEGIN { Others } id := upper_str (ident); IF (id = 'DB') OR (id = 'DEFB') OR (id = 'DEFBYTE') THEN { Define byte data } Others := DefineByte ELSE IF (id = 'DC') OR (id = 'DEFC') OR (id = 'DEFCHAR') THEN { Define char data } Others := DefineChar ELSE IF (id = 'DM') OR (id = 'DEFM') OR (id = 'DEFMEM') THEN { Define char data } Others := DefineChar ELSE IF (id = 'DW') OR (id = 'DEFW') OR (id = 'DEFWORD') THEN { Define word data } Others := DefineWord ELSE IF id = 'ORG' THEN { Originate code at this address } Others := Originate ELSE IF id = 'MACRO' THEN { Indicate this is a macro } Others := Macro ELSE IF id = 'ENDM' THEN { End of macro } Others := EndMacro ELSE IF id = 'END' THEN { End of assembly text file } Others := EndAssembly ELSE IF id = 'RADIX' THEN { Default base for all numbers } Others := TheRadix ELSE IF id = 'EQU' THEN { Set identifier to be equal to this value } Others := Equate ELSE IF id = 'INCLUDE' THEN { Use the text from the following file name } Others := IncludeFile ELSE IF id = 'PAGE' THEN { Either force page break or set page height, width } Others := PageSet ELSE IF id = 'TITLE' THEN { Use the follow as the title line on assembler listing } Others := TitleSet ELSE IF id = 'TYPE' THEN { Force use of incompatible types, ie. BYTE < WORD } Others := TypeSet ELSE Others := NullOther END; { Others } PROCEDURE Generate ( Code : _String ); VAR Loc : Byte; OrV : Byte; BEGIN { Generate } FOR Loc := 1 TO Length (Code) DO BEGIN OrV := Ord (Code[Loc] ); Write (GenFile, OrV); PosCnt := PosCnt + 1 END END; { Generate } PROCEDURE ParseMnemonic ( OpIs : Mnemonics ); CONST Skip = #00; VAR Value : Integer; Sym2 : Symbols; Ident2 : String[31]; Num2 : String[16]; PROCEDURE AddLoc ( VAR ALoc : PLocPtr; NLoc : PLocPtr ); BEGIN { AddLoc } IF ALoc = Nil THEN Aloc := NLoc ELSE AddLoc (ALoc^.PNext, NLoc) END; { AddLoc } PROCEDURE AddPatch ( VAR APatch : PatchPtr; Both : Boolean; Id : LabelStr; AOffset : Boolean; OprCh : Char; PAValue : Integer ); VAR TPatch : PatchPtr; TPLoc : PLocPtr; BEGIN { AddPatch } IF APatch = Nil THEN BEGIN New (TPLoc); WITH TPLoc^ DO BEGIN PLoc := PosCnt; Oprtion := OprCh; PAdj := PAValue; OSet := AOffset; TwoBytes := Both; PNext := Nil END; New (TPatch); WITH TPatch^ DO BEGIN PName := Id; FixLoc := TPLoc; LeftPatch := Nil; RightPatch := Nil END; APatch := TPatch END ELSE IF Id < APatch^.PName THEN AddPatch (APatch^.LeftPatch, Both, Id, AOffset, OprCh, PAValue) ELSE IF Id > APatch^.PName THEN AddPatch (APatch^.RightPatch, Both, Id, AOffset, OprCh, PAValue) ELSE BEGIN New (TPLoc); WITH TPLoc^ DO BEGIN PLoc := PosCnt; Oprtion := OprCh; PAdj := PAValue; OSet := AOffset; TwoBytes := Both; PNext := Nil END; AddLoc (APatch^.FixLoc, TPLoc) END END; { AddPatch } PROCEDURE ViaLabel ( LeadIn : _String; Both : Boolean; Id : LabelStr; AOffset : Boolean; OprCh : Char; PAValue : Integer ); BEGIN { ViaLabel } Write (' Via label [', Id, ']'); Generate (LeadIn); AddPatch (Patches, Both, Id, AOffset, OprCh, PAValue); Generate (Null); IF Both THEN Generate (Null) END; { ViaLabel } PROCEDURE OperLabel ( LeadIn : _String; Both : Boolean; Id : LabelStr; AOffset : Boolean ); BEGIN { OperLabel } Get_Symbol; { Operation | ? } IF Sym <> Operation THEN ViaLabel (LeadIn, Both, Id, AOffset, '+', 0) ELSE BEGIN TempOpCh := Ch; Get_Symbol; { Number } IF Sym <> Number THEN Write ('Number expected'); ViaLabel (LeadIn, Both, Id, AOffset, TempOpCh, Val_Radix (Num, P_Radix) ) END END; { OperLabel } PROCEDURE DoReg1 ( LeadIn : Char; StartOp : Byte ); BEGIN { DoReg1 } IF LeadIn <> Skip THEN Generate (LeadIn); CASE Check_Reg OF A : Generate (Chr (StartOp - 0) ); B : Generate (Chr (StartOp - 7) ); C : Generate (Chr (StartOp - 6) ); D : Generate (Chr (StartOp - 5) ); E : Generate (Chr (StartOp - 4) ); H : Generate (Chr (StartOp - 3) ); L : Generate (Chr (StartOp - 2) ); END END; { DoReg1 } PROCEDURE DoONCR ( StartOp : Byte ); BEGIN { DoONCR } CASE Sym OF Identifier : DoReg1 (#$CB, StartOp); Left_Bp : BEGIN Get_Symbol; IF Sym <> Identifier THEN Error ('Op code expected') ELSE CASE Check_Reg OF HL : Generate (#$CB + Chr (StartOp - 1) ); IX : BEGIN Get_Symbol; IF Sym <> Operation THEN Error ('+ Expected') ELSE Get_Symbol; Generate (#$DD + #$CB + Chr (Val_Radix (Num, P_Radix) ) + Chr (StartOp - 1) ) END; IY : BEGIN Get_Symbol; IF Sym <> Operation THEN Error ('+ Expected') ELSE Get_Symbol; Generate (#$FD + #$CB + Chr (Val_Radix (Num, P_Radix) ) + Chr (StartOp - 1) ) END END; Get_Symbol { Right_BP } END END END; { DoONCR } PROCEDURE DoOR; BEGIN { DoOR } Sym := Sym2; Ident := Ident2; Num := Num2; CASE OpIs OF AND_ : IF Check_Reg IN [A .. L] THEN DoReg1 (Skip, $A7) ELSE IF Sym = Number THEN Generate (#$E6 + Chr (Val_Radix (Num, P_Radix) ) ) ELSE ViaLabel (#$E6, False, Ident, False, '+', 0); CALL : IF Sym = Number THEN Generate (#$CD + Chr (Lo (Val_Radix (Num, P_Radix) ) ) + Chr (Hi (Val_Radix (Num, P_Radix) ) ) ) ELSE ViaLabel (#$CD, True, Ident, False, '+', 0); CP : IF Check_Reg IN [A .. L] THEN DoReg1 (Skip, $BF) ELSE IF Sym = Number THEN Generate (#$FE + Chr (Val_Radix (Num, P_Radix) ) ) ELSE ViaLabel (#$FE, False, Ident, False, '+', 0); DEC : CASE Check_Reg OF A : Generate (#$3D); B : Generate (#$05); BC : Generate (#$0B); C : Generate (#$0D); D : Generate (#$15); DE : Generate (#$1B); E : Generate (#$1D); H : Generate (#$25); HL : Generate (#$2B); IX : Generate (#$DD + #$2B); IY : Generate (#$FD + #$2B); L : Generate (#$2D); SP : Generate (#$3B); END; IM : CASE Val_Radix (Num, P_Radix) OF 0 : Generate (#$ED + #$46); 1 : Generate (#$ED + #$56); 2 : Generate (#$ED + #$5E); END; INC : CASE Check_Reg OF A : Generate (#$3C); B : Generate (#$04); BC : Generate (#$03); C : Generate (#$0C); D : Generate (#$14); DE : Generate (#$13); E : Generate (#$1C); H : Generate (#$24); HL : Generate (#$23); IX : Generate (#$DD + #$23); IY : Generate (#$FD + #$23); L : Generate (#$2C); SP : Generate (#$33); END; JP : IF Sym = Number THEN Generate (#$C3 + Chr (Lo (Val_Radix (Num, P_Radix) ) ) + Chr (Hi (Val_Radix (Num, P_Radix) ) ) ) ELSE ViaLabel (#$C3, True, Ident, False, '+', 0); JR : IF Sym = Number THEN Generate (#$18 + Chr (Val_Radix (Num, P_Radix) ) ) ELSE ViaLabel (#$18, False, Ident, True, '+', 0); OR_ : IF Check_Reg IN [A .. L] THEN DoReg1 (Skip, $B7) ELSE IF Sym = Number THEN Generate (#$F6 + Chr (Val_Radix (Num, P_Radix) ) ) ELSE ViaLabel (#$F6, False, Ident, False, '+', 0); POP : CASE Check_Reg OF AF : Generate (#$F1); BC : Generate (#$C1); DE : Generate (#$D1); HL : Generate (#$E1); IX : Generate (#$DD + #$E1); IY : Generate (#$FD + #$E1); END; PUSH : CASE Check_Reg OF AF : Generate (#$F5); BC : Generate (#$C5); DE : Generate (#$D5); HL : Generate (#$E5); IX : Generate (#$DD + #$E5); IY : Generate (#$FD + #$E5); END; RET : IF Ident = 'C' THEN { Carry } Generate (#$D8) ELSE IF Ident = 'M' THEN { Minus } Generate (#$F8) ELSE IF Ident = 'NC' THEN { No Carry } Generate (#$D0) ELSE IF Ident = 'NZ' THEN { Not Zero } Generate (#$C0) ELSE IF Ident = 'P' THEN { Plus } Generate (#$F0) ELSE IF Ident = 'PE' THEN { Plus & Equal } Generate (#$E8) ELSE IF Ident = 'PO' THEN Generate (#$E0) ELSE IF Ident = 'Z' THEN { Zero } Generate (#$C8) ELSE Error (' Conditional expected for RET'); RL : IF Check_Reg IN [A .. L] THEN DoReg1 (#$CB, $17); RLC : IF Check_Reg IN [A .. L] THEN DoReg1 (#$CB, $07); RR : IF Check_Reg IN [A .. L] THEN DoReg1 (#$CB, $1F); RRC : IF Check_Reg IN [A .. L] THEN DoReg1 (#$CB, $0F); RST : CASE Val_Radix (Num, P_Radix) OF $00 : Generate (#$C7); $08 : Generate (#$CF); $10 : Generate (#$D7); $18 : Generate (#$DF); $20 : Generate (#$E7); $28 : Generate (#$EF); $30 : Generate (#$F7); $38 : Generate (#$FF); END; SLA : IF Check_Reg IN [A .. L] THEN DoReg1 (#$CB, $27); SRA : IF Check_Reg IN [A .. L] THEN DoReg1 (#$CB, $2F); SRL : IF Check_Reg IN [A .. L] THEN DoReg1 (#$CB, $3F); SUB : IF Check_Reg IN [A .. L] THEN DoReg1 (Skip, $97) ELSE IF Sym = Number THEN Generate (#$D6 + Chr (Val_Radix (Num, P_Radix) ) ) ELSE ViaLabel (#$D6, False, Ident, False, '+', 0); XOR_ : IF Check_Reg IN [A .. L] THEN DoReg1 (Skip, $AF) ELSE IF Sym = Number THEN Generate (#$EE + Chr (Val_Radix (Num, P_Radix) ) ) ELSE ViaLabel (#$EE, False, Ident, False, '+', 0); END END; { DoOR } PROCEDURE DoOM_Sub ( LeadIn : Char; OpByte : Byte ); BEGIN { DoOM_Sub } CASE Check_Reg OF HL : IF LeadIn = Skip THEN Generate (Chr (OpByte) ) ELSE Generate (LeadIn + Chr (OpByte) ); IX : BEGIN Get_Symbol; { Operation } Get_Symbol; { Offset } IF Sym = Number THEN IF LeadIn = Skip THEN Generate (#$DD + Chr (OpByte) + Chr (Val_Radix (Num, P_Radix) ) ) ELSE Generate (#$DD + LeadIn + Chr (Val_Radix (Num, P_Radix) ) + Chr (OpByte) ) ELSE IF LeadIn = Skip THEN ViaLabel (#$DD + Chr (OpByte), False, Ident, True, '+', 0) ELSE BEGIN ViaLabel (#$DD + LeadIn, False, Ident, True, '+', 0); Generate (Chr (OpByte) ) END END; IY : BEGIN Get_Symbol; { Operation } Get_Symbol; { Offset } IF Sym = Number THEN IF LeadIn = Skip THEN Generate (#$FD + Chr (OpByte) + Chr (Val_Radix (Num, P_Radix) ) ) ELSE Generate (#$FD + LeadIn + Chr (Val_Radix (Num, P_Radix) ) + Chr (OpByte) ) ELSE IF LeadIn = Skip THEN ViaLabel (#$DD + Chr (OpByte), False, Ident, True, '+', 0) ELSE BEGIN ViaLabel (#$DD + LeadIn, False, Ident, True, '+', 0); Generate (Chr (OpByte) ) END END END; Get_Symbol { Right_BP } END; { DoOM_Sub } PROCEDURE DoOM; BEGIN { DoOM } CASE OpIs OF AND_ : DoOM_Sub (Skip, $A6); CALL : Error ('Conditional expected'); CP : DoOM_Sub (Skip, $BE); DEC : DoOM_Sub (Skip, $35); IM : Error ('Should be numeric'); INC : DoOM_Sub (Skip, $34); JP : DoOM_Sub (Skip, $E9); JR : Error ('Conditional expected'); OR_ : DoOM_Sub (Skip, $B6); POP : Error ('Word register expected'); PUSH : Error ('Word register expected'); RET : Error ('Conditional expected'); RL : DoOM_Sub (#$CB, $16); RLC : DoOM_Sub (#$CB, $06); RR : DoOM_Sub (#$CB, $1E); RRC : DoOM_Sub (#$CB, $0E); RST : Error ('Should be numeric'); SLA : DoOM_Sub (#$CB, $26); SRA : DoOM_Sub (#$CB, $2E); SRL : DoOM_Sub (#$CB, $3E); SUB : DoOM_Sub (Skip, $96); XOR_ : DoOM_Sub (Skip, $AE) END END; { DoOM } PROCEDURE DoArith; PROCEDURE DoRegPair ( LeadIn : Char; OpByte : Byte ); BEGIN { DoRegPair } IF LeadIn <> Skip THEN Generate (LeadIn); Get_Symbol; { Comma } Get_Symbol; { Register } CASE Check_Reg OF BC : Generate (Chr (OpByte + $00) ); DE : Generate (Chr (OpByte + $10) ); HL : Generate (Chr (OpByte + $20) ); SP : Generate (Chr (OpByte + $30) ); END END; { DoRegPair } BEGIN { DoArith } CASE OpIs OF ADC : CASE Check_Reg OF A : BEGIN Get_Symbol; { Comma } Get_Symbol; { Reg | data | Memory } IF Check_Reg IN [A .. L] THEN DoReg1 (Skip, $8F) ELSE IF Sym = Number THEN Generate (#$CE + Chr (Val_Radix (Num, P_Radix) ) ) ELSE IF Sym = Left_BP THEN BEGIN Get_Symbol; { HL | IX | IY } DoOM_Sub (Skip, $8E) END ELSE ViaLabel (#$CE, False, Ident, False, '+', 0) END; HL : DoRegPair (#$ED, $4A) ELSE Error ('Illegal register') END; ADD : CASE Check_Reg OF A : BEGIN Get_Symbol; { Comma } Get_Symbol; { Reg | data | Memory } IF Check_Reg IN [A .. L] THEN DoReg1 (Skip, $87) ELSE IF Sym = Number THEN Generate (#$C6 + Chr (Val_Radix (Num, P_Radix) ) ) ELSE IF Sym = Left_BP THEN BEGIN Get_Symbol; { HL | IX | IY } DoOM_Sub (Skip, $86) END ELSE ViaLabel (#$C6, False, Ident, False, '+', 0) END; HL : DoRegPair (Skip, $09); IX : DoRegPair (#$DD, $09); IY : DoRegPair (#$FD, $09) ELSE Error ('Illegal register') END; SBC : CASE Check_Reg OF A : BEGIN Get_Symbol; { Comma } Get_Symbol; { Reg | data | Memory } IF Check_Reg IN [A .. L] THEN DoReg1 (Skip, $9F) ELSE IF Sym = Number THEN Generate (#$DE + Chr (Val_Radix (Num, P_Radix) ) ) ELSE IF Sym = Left_BP THEN BEGIN Get_Symbol; { HL | IX | IY } DoOM_Sub (Skip, $9E) END ELSE ViaLabel (#$DE, False, Ident, False, '+', 0) END; HL : DoRegPair (#$ED, $42) ELSE Error ('Illegal register') END END END; { DoArith } PROCEDURE DoConditions; VAR DoJRIt : Boolean; PROCEDURE HandleAddress ( OpByte : Byte; Adrs : Char ); VAR DoIt : Boolean; I : Integer; BEGIN { HandleAddress } DoIt := True; IF Ident = 'C' THEN Generate (Chr (OpByte + $00) ) ELSE IF Ident = 'M' THEN Generate (Chr (OpByte + $20) ) ELSE IF Ident = 'NC' THEN Generate (Chr (OpByte - $08) ) ELSE IF Ident = 'NZ' THEN Generate (Chr (OpByte - $18) ) ELSE IF Ident = 'P' THEN Generate (Chr (OpByte + $18) ) ELSE IF Ident = 'PE' THEN Generate (Chr (OpByte + $10) ) ELSE IF Ident = 'PO' THEN Generate (Chr (OpByte + $08) ) ELSE IF Ident = 'Z' THEN Generate (Chr (OpByte - $10) ) ELSE BEGIN Write (' Address '); DoIt := False; LabelId := Ident; Get_Symbol; { Operation | ? } IF Sym <> Operation THEN ViaLabel (Adrs, True, LabelId, False, '+', 0) ELSE BEGIN TempOpCh := Ch; Get_Symbol; { Number } ViaLabel (Adrs, True, LabelId, False, Ch, Val_Radix (Num, P_Radix) ) END END; IF DoIt THEN BEGIN Get_Symbol; { Comma } Get_Symbol; { Address } I := Val_Radix (Num, P_Radix); Generate (Chr (Lo (I) ) + Chr (Hi (I) ) ) END END; { HandleAddress } BEGIN { DoConditions } CASE OpIs OF CALL : HandleAddress ($DC, #$CD); JP : IF Sym = Left_BP THEN BEGIN Get_Symbol; CASE Check_Reg OF HL : Generate (#$E9); IX : Generate (#$DD + #$E9); IY : Generate (#$FD + #$E9); END; Get_Symbol { Right_BP } END ELSE HandleAddress ($DA, #$C3); JR : BEGIN DoJRIt := True; IF Ident = 'C' THEN Generate (#$38) ELSE IF Ident = 'NC' THEN Generate (#$30) ELSE IF Ident = 'NZ' THEN Generate (#$20) ELSE IF Ident = 'Z' THEN Generate (#$28) ELSE BEGIN DoJRIt := False; OperLabel (#18, False, Ident, True) END; IF DoJRIt THEN BEGIN Get_Symbol; { Comma } Get_Symbol; { Number } IF Sym = Number THEN Generate (Chr (Val_Radix (Num, P_Radix) ) ) ELSE ViaLabel ('', False, Ident, True, '+', 0) END END END END; { DoConditions } PROCEDURE DoIN; VAR FinishUp : Boolean; BEGIN { DoIN } FinishUp := True; CASE Check_Reg OF A : BEGIN FinishUp := False; Get_Symbol; { Comma } Get_Symbol; { Number | Label | Left_BP } IF Sym = Number THEN Generate (#$DB + Chr (Val_Radix (Num, P_Radix) ) ) ELSE IF Sym = Left_BP THEN BEGIN Generate (#$ED + #$78); Get_Symbol; { C } Get_Symbol { Right_BP } END ELSE { Must be a label ! } BEGIN Write (' IN '); ViaLabel (#$DB, False, Ident, False, '+', 0) END END; B : Generate (#$ED + #$40); C : Generate (#$ED + #$48); D : Generate (#$ED + #$50); E : Generate (#$ED + #$58); H : Generate (#$ED + #$60); L : Generate (#$ED + #$68); END; IF FinishUp THEN BEGIN Get_Symbol; { Comma } Get_Symbol; { Left_BP } Get_Symbol; { C } Get_Symbol { Right_BP } END END; { DoIN } PROCEDURE DoOUT; BEGIN { DoOUT } IF Sym = Number THEN BEGIN Generate (#$D3 + Chr (Val_Radix (Num, P_Radix) ) ); Get_Symbol; { Comma } Get_Symbol { A } END ELSE IF Sym = Left_BP THEN BEGIN Get_Symbol; { C } Get_Symbol; { Right_BP } Get_Symbol; { Comma } Get_Symbol; { Register } CASE Check_Reg OF A : Generate (#$ED + #$79); B : Generate (#$ED + #$41); C : Generate (#$ED + #$49); D : Generate (#$ED + #$51); E : Generate (#$ED + #$59); H : Generate (#$ED + #$61); L : Generate (#$ED + #$69); END END ELSE BEGIN Write (' OUT '); ViaLabel (#$D3, False, Ident, False, '+', 0); Get_Symbol; { Comma } Get_Symbol { A } END END; { DoOUT } PROCEDURE HandleLD; VAR SetPatch : Boolean; I : Integer; OffValue : Integer; PROCEDURE DoRegs ( Reg1, Reg2 : Byte ); BEGIN { DoRegs } Get_Symbol; { Comma } Get_Symbol; { Reg | data | Memory } IF Check_Reg IN [A .. L] THEN DoReg1 (Skip, Reg1) ELSE IF Sym = Number THEN Generate (Chr (Reg2) + Chr (Val_Radix (Num, P_Radix) ) ) ELSE IF Sym = Left_BP THEN BEGIN Get_Symbol; { HL | IX | IY } DoOM_Sub (Skip, Reg1 - 1) END ELSE ViaLabel (Chr (Reg2), False, Ident, False, '+', 0) END; { DoRegs } PROCEDURE DoPairs ( RegIs : Registers; Adrs, Dta : Char ); VAR CleanUp : Boolean; Send : _String; BEGIN { DoPairs } CleanUp := False; Send := Dta; Get_Symbol; { Comma } Get_Symbol; { Number | Left_BP | Label } IF Sym = Left_BP THEN BEGIN CleanUp := True; Get_Symbol; { Number | Label } CASE RegIs OF HL : Send := Adrs; IX : Send := #$DD + Adrs; IY : Send := #$FD + Adrs; ELSE Send := #$ED + Adrs END END; IF Sym = Number THEN BEGIN I := Val_Radix (Num, P_Radix); Generate (Send + Chr (Lo (I) ) + Chr (Hi (I) ) ) END ELSE OperLabel (Send, True, Ident, False); IF CleanUp AND (Sym <> EndLine) THEN Get_Symbol { Right_BP } END; { DoPairs } PROCEDURE DoHXY ( LeadIn : Char ); PROCEDURE HandleSkip ( Ch : Char ); BEGIN { HandleSkip } IF LeadIn = Skip THEN Generate (Ch) ELSE Generate (Ch + Chr (I) ) END; { HandleSkip } BEGIN { DoHXY } IF LeadIn = Skip THEN BEGIN Get_Symbol; { Right_BP } Get_Symbol; { Comma } Get_Symbol { Register | Number | Label } END ELSE BEGIN Generate (LeadIn); Get_Symbol; { + } Get_Symbol; { Label | Number } IF Sym = Number THEN I := Val_Radix (Num, P_Radix) ELSE I := 0; Get_Symbol; { Right_BP } Get_Symbol; { Comma } Get_Symbol { Register | Number | Label } END; CASE Check_Reg OF A : HandleSkip (#$77); B : HandleSkip (#$70); C : HandleSkip (#$71); D : HandleSkip (#$72); E : HandleSkip (#$73); H : HandleSkip (#$74); L : HandleSkip (#$75) ELSE IF Sym = Number THEN IF LeadIn = Skip THEN Generate (#$36 + Chr (Val_Radix (Num, P_Radix) ) ) ELSE Generate (#$36 + Chr (I) + Chr (Val_Radix (Num, P_Radix) ) ) ELSE IF LeadIn = Skip THEN OperLabel (#$36, False, Ident, True) ELSE OperLabel (#$36 + Chr (I), False, Ident, True) END END; { DoHXY } BEGIN { HandleLD } IF Sym = Left_BP THEN BEGIN Get_Symbol; { Register | Label | Number } CASE Check_Reg OF BC : BEGIN Get_Symbol; { Right_BP } Get_Symbol; { Comma } Get_Symbol; { A } Generate (#$02) END; DE : BEGIN Get_Symbol; { Right_BP } Get_Symbol; { Comma } Get_Symbol; { A } Generate (#$12) END; HL : DoHXY (Skip); IX : DoHXY (#$DD); IY : DoHXY (#$FD) ELSE BEGIN SetPatch := False; IF Sym = Number THEN I := Val_Radix (Num, P_Radix) ELSE BEGIN SetPatch := True; LabelId := Ident; I := 0 END; Get_Symbol; { Right_BP | Operation } IF Sym = Operation THEN BEGIN TempOpCh := Ch; Get_Symbol; { Number } OffValue := Val_Radix (Num, P_Radix); Get_Symbol { Right_BP } END; Get_Symbol; { Comma } Get_Symbol; { Reg } CASE Check_Reg OF A : Generate (#$32); BC : Generate (#$ED + #$43); DE : Generate (#$ED + #$53); HL : Generate (#$22); IX : Generate (#$DD + #$22); IY : Generate (#$FD + #$22); SP : Generate (#$ED + #$73); END; IF SetPatch THEN IF OffValue > 0 THEN ViaLabel ('', True, LabelId, False, TempOpCh, OffValue) ELSE ViaLabel ('', True, LabelId, False, '+', 0) ELSE Generate (Chr (Lo (I) ) + Chr (Hi (I) ) ) END END END ELSE CASE Check_Reg OF A : BEGIN Get_Symbol; { Comma } Get_Symbol; { Reg | data | Left_BP | Label } IF Check_Reg IN [A .. L] THEN DoReg1 (Skip, $7F) ELSE IF Check_Reg = R THEN Generate (#$ED + #$5F) ELSE IF Sym = Number THEN Generate (#$3E + Chr (Val_Radix (Num, P_Radix) ) ) ELSE IF Sym = Left_BP THEN BEGIN Get_Symbol; { HL | IX | IY | BC | DE } IF Check_Reg IN [HL, IX, IY] THEN DoOM_Sub (Skip, $7E) ELSE IF Sym = Number THEN BEGIN I := Val_Radix (Num, P_Radix); Generate (#$3A + Chr (Lo (I) ) + Chr (Hi (I) ) ) END ELSE IF Check_Reg IN [BC, DE] THEN CASE Check_Reg OF BC : Generate (#$0A); DE : Generate (#$1A) END ELSE OperLabel (#$3A, True, Ident, False); Get_Symbol { Right_BP } END ELSE ViaLabel (#$3E, True, Ident, False, '+', 0) END; B : DoRegs ($47, $06); BC : DoPairs (BC, #$4B, #$01); C : DoRegs ($4F, $0E); D : DoRegs ($57, $16); DE : DoPairs (DE, #$5B, #$11); E : DoRegs ($5F, $1E); H : DoRegs ($67, $26); HL : DoPairs (HL, #$2A, #$21); IV : BEGIN Get_Symbol; { Comma } Get_Symbol; { A } Generate (#$ED + #$47) END; IX : DoPairs (IX, #$2A, #$21); IY : DoPairs (IY, #$2A, #$21); L : DoRegs ($47, $06); R : BEGIN Get_Symbol; { Comma } Get_Symbol; { A } Generate (#$ED + #$4F) END; SP : BEGIN Get_Symbol; { Comma } Get_Symbol; { Reg | Number | Left_BP } IF Sym = Left_BP THEN BEGIN Get_Symbol; IF Sym = Number THEN BEGIN I := Val_Radix (Num, P_Radix); Generate (#$ED + #$7B + Chr (Lo (I) ) + Chr (Hi (I) ) ) END ELSE OperLabel (#$ED + #$7B, True, Ident, False); Get_Symbol { Right_BP } END ELSE IF Sym = Number THEN BEGIN I := Val_Radix (Num, P_Radix); Generate (#$31 + Chr (Lo (I) ) + Chr (Hi (I) ) ) END ELSE IF Check_Reg IN [HL, IX, IY] THEN DoOM_Sub (Skip, $F9) ELSE ViaLabel (#$31, True, Ident, False, '+', 0) END END END; { HandleLD } BEGIN { ParseMnemonic } Get_Symbol; IF OpIs = LD THEN HandleLD 4) ); ShowLabels (Right) END END; { ShowPatches } PROCEDURE FixPatches ( APatch : PatchPtr ); VAR Value : Integer; { Address or Data of Label } OValue : Integer; BEGIN { FixPatches } IF APatch <> Nil THEN WITH APatch^ DO BEGIN FixPatches (LeftPatch); IF NOT FoundLabel (Labels, PName, OValue) THEN Error ('Unable to locate label ' + PName) ELSE BEGIN WriteLn; Write ('':2, PName); REPEAT WITH FixLoc^ DO BEGIN Value := OValue; IF OSet THEN Value := Value - PLoc - 1; CASE Oprtion OF '+' : Value := Value + PAdj; '-' : Value := Value - PAdj; '*' : Value := Value * PAdj; '/' : Value := Value DIV PAdj END; SetValue (PLoc, Value, TwoBytes) END; FixLoc := FixLoc^.PNext UNTIL FixLoc = Nil END; FixPatches (RightPatch) END END; { FixPatches } BEGIN { z80_assembler } initialize; parser; WriteLn; WriteLn; WriteLn ('Labels'); ShowLabels (Labels); WriteLn; WriteLn; WriteLn ('Second pass'); FixPatches (Patches); WriteLn; WriteLn ('End assembly'); Close (GenFile) END { z80_assembler }.