TITLE CP/M UG UK Z80 Assembler ;----------------------------------------------- ; ; Z80ASM - Z80 RESIDENT ASSEMBLER ; ; ORIGINALLY WRITTEN IN 8080 ASSEMBLY ; LANGUAGE FOR ASSEMBLY BY THE CP/M ; ASSEMBLER. NOW IN ZILOG MNEMONICS ; SO THAT IT CAN ASSEMBLE ITSELF! ; ALL STILL IN 8080 COMPATIBLE CODE ; FOR THOSE RARE (RAIR?) PEOPLE WITH ; 8080 & 8085 CHIPS. ; ;----------------------------------------------- ; VER: EQU 28 ;VERSION NUMBER ; ;----------------------------------------------- ; ; Original - LEHMAN CONSULTING SERVICES ; (C) COPYRIGHT 1977 ; ; US User Group, Vol 16 ; ;----------------------------------------------- ; ; Modified by Ray Halls ; August 1982 ; for CP/M Users Group (UK) ; ; i) Correction to OP-CODE handling ; ii) Addition of Console Output and ; Print File for Listing pass ; iii) Additional error reporting ; iv) Expansion of DEFB facilities ; ; v) More comprehensive Relative dis- ; placement computation ; vi) Accurate assembly of Labels ; vii) Unsigned 16-bit by 8-bit divide ; added to expression handling ; ; October 1982 ; ; Bugs in NEG and I/O Opcode Routines ; fixed. ; ;----------------------------------------------- ; ; Modified even more by Neil Harrison ; January 1983 ; ; i) added DB,DW,DS as equivalent to ; DEFB,DEFW,DEFS ; ii) permitted full CP/M 2.x drive ; specification A: to P:, except for ; list option A: to O:, P being used ; to direct listing to printer ; iii) forced errors & error count to ; always display on console ; iv) Made divide into true 16bit by 16 ; bit operation ; v) Added "modulo" function using '%' ; vi) Added IF, ELSE, ENDIF conditional ; pseudo ops ; vii) Increased HEX file record size ; to 24 bytes to reduce disk usage. ; viii) Added option to convert spaces ; to tabs in PRN and printer output. ; This greatly reduces PRN file size. ; ix) Added symbol table overflow check ; x) Added conditional assembly option ; for Z80 processor. Principally used ; in place of MOVE subroutine. Where ; convenient MOVE replaced by LDIR, an ; 8080 subroutine which has the same ; register usage as the Z80 instruction. ; xi) Added sorted symbol table ; xii) Added LIST pseudo-op ; xiii) Added TITLE pseudo-op ; xiv) Moved all one time initialisation ; code into the input buffer to save ; space. ; xv) Expanded expression evaluation to ; include: .NOT.,.MOD.,.SHR.,.SHL.,.EQ. ; .NE.,.GT.,.GE.,.LT.,.LE.,.LOW.,.HIGH. ; "." is no longer valid in labels! Use ; "_" instead, e.g. LAB_01 ; xvi) Limited symbol table searches to ; avoid confusion with opcodes. ; xvii) Fixed a bug in the end of file ; code. ; ;=============================================== ; CR: EQU 13 LF: EQU 10 TAB: EQU 9 QUOTE: EQU 27H ; LIST NOCOND ; FALSE: EQU 0 ;FICTION TRUE: EQU .NOT. FALSE ;STRANGER THAN FICTION ; Z80: EQU FALSE ;TRUE IF WE CAN USE Z80 INSTRUCTIONS ; ORG 100H ; BEGIN: JP Z80ASM ;CP/M ENTRY POINT BRANCH ; INBUF: ;1K BUFFER FOR SOURCE INPUT ; ; TO SAVE SPACE WE STORE MESSAGES WHICH ARE USED ; ONLY BEFORE DISK INPUT STARTS IN THE INPUT BUFFER. ; THEY ARE OVERWRITTEN ONCE THE ASSEMBLY STARTS. ; VER1: EQU VER/10+'0' ;MOST SIGNIFICANT VERSION DIGIT VER2: EQU VER.MOD.10+'0' ;LEAST SIGNIFICANT VERSION DIGIT ; VSNMSG: DB 'Zilog/Mostek Z80 Assembler' DB ' Version ',VER1,'.',VER2,' (' IF .NOT. Z80 DB '8080/8085/' ENDIF DB 'Z80 CPU)',CR,LF,LF,'$' ; SM1: DB CR,LF,'Source (.ZSM) file not found',CR,LF,'$' SM2: DB CR,LF,'Unable to create/open object (.HEX) file',CR,LF,'$' SM3: DB CR,LF,'Unable to create/open output (.PRN) file',CR,LF,'$' ; ASMEXT: DB 'ZSM' HEXEXT: DB 'HEX' PRNEXT: DB 'PRN' ; ; INIT FIELDS AND SET UP FILES ; Z80ASM: LD SP,STACK ;LOCAL STACK LD HL,(6) ;GET MEMORY SIZE LD L,0 ;PAGE BOUNDARY LD DE,-16 ;SPARE ROOM FOR ONE SYMBOL ENTRY ADD HL,DE ;CALCULATE MAX MEMORY ADDRESS LD (MAXMEM),HL ;AND SAVE FOR LATER LD DE,VSNMSG ;SIGN ON MESSAGE CALL WMSG ;DISPLAY ON CONSOLE LD HL,SYM ;GET ADDRESS OF SYMBOL TABLE LD (SYMPT),HL LD HL,(SYMBEG) LD (SYMPTR),HL ;SET UP POINTERS CALL SETUP ;PROCESS FILE NAME, PARAMETERS, ETC. LD A,MAXLNE+1 LD (CURLNE),A ;SET UP FOR HEAD OF FORM ON FIRST PRINT XOR A LD (PASSNO),A ;INDICATE PASS 1 LD HL,0 LD (PC),HL ;SET UP DEFAULT PROGRAM ORIGIN JP NEXT ;AND GO TO MAIN LOOP TITLE Z80 Assembler - File I/O initialisation ; ; SETUP - SET UP FILES FOR Z80ASM ; COMMAND SYNTAX AS FOR 'ASM' ; ; INPUT FROM CONSOLE IS FILENAME.ABC ; ; A= SOURCE (.ZSM) FILE DRIVE NAME ; B= (.HEX) FILE DESTINATION DRIVE NAME OR Z ; C= (.PRN) FILE DESTINATION DRIVE NAME ; OR P, X, Y, OR Z FOR LISTING ; SETUP: LD DE,FCB1 ;SET UP SOURCE FCB LD HL,5CH LD BC,33 IF Z80 LDIR ;GET DEFAULT FCB ELSE CALL LDIR ;GET DEFAULT FCB ENDIF LD DE,FCB1+9 LD HL,ASMEXT LD BC,3 IF Z80 LDIR ;SET UP EXTENSION ELSE CALL LDIR ;SET UP EXTENSION ENDIF ; ; HEX FILE ; LD DE,FCB2 ;SET UP HEX FILE FCB LD HL,5CH LD BC,33 IF Z80 LDIR ;GET DEFAULT FCB ELSE CALL LDIR ;GET DEFAULT FCB ENDIF LD DE,FCB2+9 LD HL,HEXEXT LD BC,3 IF Z80 LDIR ;SET UP EXTENSION ELSE CALL LDIR ;SET UP EXTENSION ENDIF ; ; PRN FILE ; LD DE,FCB3 ;SET UP PRN FILE FCB LD HL,5CH LD BC,33 IF Z80 LDIR ;GET DEFAULT FCB ELSE CALL LDIR ;GET DEFAULT FCB ENDIF LD DE,FCB3+9 LD HL,PRNEXT LD BC,3 IF Z80 LDIR ;SET UP EXTENSION ELSE CALL LDIR ;SET UP EXTENSION ENDIF ; ; HEADER LINE MESSAGE ; LD HL,FCB1+1 LD DE,HOFNAM LD BC,8 IF Z80 LDIR ;GET FILE NAME ELSE CALL LDIR ;GET FILE NAME ENDIF ; ; DRIVE SELECTS ; LD A,(5CH+9) CP ' ' ;TEST FOR DEFAULT DRIVE JP Z,SETUPA SUB 40H ;MAKE DRIVE CODE CP 10H ;TEST VALID JP C,SETUPB ;OK SETUPA: LD A,(5CH) ;FETCH DEFAULT SETUPB: LD DE,FCB1 ;SET SOURCE DRIVE LD (DE),A CALL OPNFIL JP Z,SETER1 ;BRANCH IF SOURCE FILE NOT FOUND LD HL,5CH+9 ;FILE EXTENSION LD A,(HL) ;TEST FOR SOURCE/DEFAULT CP ' ' JP NZ,SETUPC ;DRIVE SPECIFIED LD A,(5CH) ;SET UP DEFAULT LD (HL),A INC HL LD (HL),A ;SET DEFAULT HEX DESTN SETUPC: LD A,(5CH+11) ;FETCH LIST FLAG CP ' ' ;DEFAULT ? JP NZ,SETUPL LD A,(5CH) ;FETCH DEFAULT SETUPL: LD (LFLAG),A ;SET UP LISTING FLAG CP 'O' ;LISTING TO FILE OR OUTPUT ? JP NC,SETUPH ;TRY HEX OUTPUT SUB 40H ;MAKE DRIVE CODE CP 0FH ;VALID ? JP C,SETUPP ;OTHERWISE DEFAULT DESTN LD A,(5CH) ;FETCH DEFAULT SETUPP: LD DE,FCB3 LD (DE),A CALL DELFIL LD DE,FCB3 CALL CREFIL JP Z,SETER3 ;BRANCH IF UNABLE TO CREATE LD DE,FCB3 CALL OPNFIL JP Z,SETER3 ;BRANCH IF UNABLE TO OPEN SETUPH: LD A,(5CH+10) LD (HFLAG),A ;SET UP HEX FLAG CP 'Z' ;TEST FOR SKIP HEX JP Z,SETUP1 ;BRANCH IF NO HEX FILE SETUP SUB 40H ;MAKE DRIVE CODE CP 10H ;VALID ? JP C,SETUPD ;OTHERWISE DEFAULT DESTN LD A,(5CH) ;FETCH DEFAULT SETUPD: LD DE,FCB2 LD (DE),A CALL DELFIL ;DELETE FILE LD DE,FCB2 CALL CREFIL JP Z,SETER2 ;BRANCH IF UNABLE TO CREATE LD DE,FCB2 CALL OPNFIL JP Z,SETER2 ;BRANCH IF UNABLE TO OPEN SETUP1: LD HL,INBUF+1024 LD (IBP),HL XOR A LD (OBP2),A ;SET UP POINTERS RET ;AND GET OUT OF HERE ; SETER1: LD DE,SM1 JP SETERR ;MSG AND REBOOT ; SETER2: LD DE,SM2 JP SETERR ;MSG AND REBOOT ; SETER3: LD DE,SM3 SETERR: CALL WMSG JP 0 ;REBOOT ; ORG INBUF+1024 ;INPUT BUFFER SPACE OUTBUF: DS 128 ;.PRN OUTPUT BUFFER OUTBF2: DS 128 ;.HEX OUTPUT BUFFER ; FCB1: DB 0 ;FILE CONTROL BLOCK (SOURCE) DS 32 ;REMAINING AREA FCB2: DB 0 ;FILE CONTROL BLOCK (HEX) DS 32 FCB3: DB 0 ;FILE CONTROL BLOCK (PRN) DS 32 IBP: DS 2 ;INPUT BUFFER POINTER (FCB1) OBP: DB 0 ;OUTPUT BUFFER POINTER (FCB3) OBP2: DS 1 ;OUTPUT BUFFER POINTER (FCB2) IBUF: DW INBUF ;INPUT BUFFER ADDRESS OBUF: DW OUTBUF ;OUTPUT BUFFER ADDRESS (FCB3) OBUF2: DW OUTBF2 ;OUTPUT BUFFER ADDRESS (FCB2) TITLE Z80 Assembler - Console, Printer and File I/O ; ; CP/M EQUATES ; BDOS: EQU 5 BOOT: EQU 0 ; ; ENTRY - USED TO CALL BDOS - SAVES AND RESTORES REGISTERS ; ENTRY: PUSH BC PUSH DE PUSH HL CALL BDOS POP HL POP DE POP BC RET ; ; WLINE - WRITE LINE TO LIST DEVICE ; HL -> BUFFER, TERM = LF ; WLINE: LD A,(HL) AND 7FH ;MASK OFF TOP BIT LD E,A LD C,5 CALL ENTRY LD A,(HL) CP LF RET Z INC HL JP WLINE ; ; CLINE - WRITE LINE TO CONSOLE ; HL -> BUFFER, TERM = LF ; CLINE: LD A,(HL) AND 7FH ;MASK OFF TOP BIT LD E,A LD C,2 CALL ENTRY LD A,(HL) CP LF RET Z INC HL JP CLINE ; ; WMSG - WRITE MESSAGE TO CONSOLE ; DE -> MSG, TERM-'$' ; WMSG: LD C,9 JP ENTRY ; ; DSKSEL - SELECT DISK ; DRIVE NUMBER IN A REG ; DSKSEL: LD E,A LD C,14 JP ENTRY ; ; OPNFIL - OPEN FILE CONTROL BLOCK ; REEL # AND NR ARE SET TO 00H ; DE -> FCB ; Z=0 SUCCESS, Z=1 FAILURE ; OPNFIL: PUSH HL LD HL,12 ADD HL,DE LD (HL),0 ;ZERO REEL # LD C,15 PUSH DE CALL ENTRY POP DE CP 255 POP HL RET Z PUSH HL LD HL,32 ADD HL,DE LD (HL),0 ; ZERO NR POP HL RET ; ; CLSFIL - CLOSE FILE CONTROL BLOCK ; DE -> FCB ; Z=0 SUCCESS, Z=1 ERROR ; CLSFIL: LD C,16 CALL ENTRY INC A RET ; ; DELFIL - DELETE FILE ; DE -> FCB ; Z=0 SUCCESS, Z=1 ERROR ; DELFIL: LD C,19 CALL ENTRY INC A ;CP 255 RET ; ; DREAD - READ DISK SECTOR ; DE -> FCB ; Z=0 ERROR OR EOF, Z=1 NORMAL ; DREAD: LD C,20 CALL ENTRY OR A RET ; ; DWRITE - WRITE DISK SECTOR ; DE -> FCB ; Z=0 ERROR, Z=1 NORMAL ; DWRITE: LD C,21 CALL ENTRY OR A RET ; ; CREFIL - CREATE FILE ; REEL # ASSUMMED TO BE SET ; ; DE -> FCB ; Z=0 NORMAL, Z=1 ERROR ; CREFIL: LD C,22 CALL ENTRY INC A ;CP 255 RET ; ; DMASET - SET DMA ADDRESS ; BUFFER ADDRESS IN DE ; DMASET: LD C,26 JP ENTRY ; IF .NOT. Z80 ; ; MOVE (DE) <= (HL) LEN IN BC, JUST LIKE Z80 LDIR ; LDIR: LD A,(HL) LD (DE),A INC HL INC DE DEC BC LD A,B OR C JP NZ,LDIR RET ENDIF ; ; CMPCHR - COMPARE STRINGS (HL) : (DE) LEN IN B ; CMPCHR: PUSH HL PUSH DE PUSH BC ;SAVE REGS EX DE,HL CMPCH0: LD A,(DE) CP 'a' ;CONVERT TO U/C JP C,CMPCH1 ;(USED TO BE DONE BY CP 'z'+1 ;A CALL TO CASCVRT JP NC,CMPCH1 ;NOW DONE IN LINE AND 5FH ;TO SAVE TIME) CMPCH1: CP (HL) JP NZ,CMPCH2 INC HL INC DE DEC B JP NZ,CMPCH0 CMPCH2: POP BC POP DE POP HL ;RESTORE REGS RET ; ; CONVERTS L/C IN SOURCE TO U/C BUT LEAVES ; COMMENTS AND MESSAGES ALONE ; CASCVRT:CP 'a' RET C CP 'z'+1 RET NC AND 5FH RET ; ; CMPHD - COMPARE HL:DE ; CMPHD: LD A,H CP D RET NZ LD A,L CP E RET ; ; GNB - GET NEXT BYTE (FCB1) ; GNB: PUSH HL PUSH DE LD HL,(IBP) LD DE,INBUF+1024 CALL CMPHD ;SEE IF END OF BUFFER POP DE POP HL JP NZ,GNB0 ;BRANCH IF NOT AT END OF BUFFER PUSH HL PUSH DE PUSH BC ;GET 1K BLOCK FROM DISK LD HL,INBUF LD (GNBPTR),HL ;SET UP POINTER FOR DMASET LD B,8 ;NUMBER OF 128 BYTE SECTORS IN 1K BUFFER GNB0A: PUSH BC LD HL,(GNBPTR) EX DE,HL CALL DMASET ;SET UP DMA ADDRESS LD DE,FCB1 CALL DREAD ;GO READ A SECTOR LD HL,(GNBPTR) LD DE,128 ADD HL,DE LD (GNBPTR),HL ;UPDATE POINTER POP BC DEC B JP NZ,GNB0A ;BRANCH BACK IF MORE TO DO POP BC POP DE ;RESTORE REGS LD HL,INBUF LD (IBP),HL POP HL ;SET UP NEXT DATA POINTER GNB0: PUSH HL LD HL,(IBP) LD A,(HL) INC HL LD (IBP),HL POP HL ;GET BYTE RET ; GNBPTR: DS 2 ;TEMP POINTER FOR BUFFERING ; ; WNB - WRITE NEXT BYTE (FCB3) ; BYTE IN A REG ; WNB: PUSH HL PUSH DE PUSH AF LD A,(OBP) CP 80H JP NZ,WNB0 LD DE,OUTBUF CALL DMASET LD DE,FCB3 CALL DWRITE JP Z,WNB00 ;BRANCH IF OK LD DE,WNBERR ;OR PRINT AN ERROR CALL WMSG JP BOOT ; WNB00: XOR A WNB0: LD E,A LD D,0 INC A LD (OBP),A LD HL,OUTBUF ADD HL,DE POP AF LD E,A ;SAVE FULL 8 BITS AND 7FH ;STRIP MSB LD (HL),A LD A,E ;GET 8 BIT VALUE BACK POP DE POP HL RET ; WNBERR: DB CR,LF,'OUTPUT FILE WRITE ERROR (DISK MAY BE FULL)',CR,LF,'$' ; ; WNB2 - WRITE NEXT BYTE (FCB2) ; WNB2: PUSH HL PUSH DE PUSH AF LD A,(OBP2) CP 80H JP NZ,WNB20 LD DE,OUTBF2 CALL DMASET LD DE,FCB2 CALL DWRITE JP Z,WNB200 ;BRANCH IF ALL OK LD DE,WNBERR CALL WMSG JP BOOT ; WNB200: XOR A WNB20: LD E,A LD D,0 INC A LD (OBP2),A LD HL,OUTBF2 ADD HL,DE POP AF LD (HL),A POP DE POP HL RET ; ; CNV2HX - CONVERT CONTENTS OF A REG TO HEX CHARACTERS ; AND PLACE IN BUFFER POINTED TO BY HL ; CNV2HX: PUSH BC LD B,A RRCA RRCA RRCA RRCA AND 0FH CALL CNV2H LD A,B POP BC AND 0FH CNV2H: CP LF JP C,CNV2H1 ADD 7 CNV2H1: ADD '0' LD (HL),A INC HL RET TITLE Z80 Assembler - Common Data Area ;----------------------------------------------- ; COMMON DATA AREA ;----------------------------------------------- ; ORG $/16+1 ;MUST START ON 'LINE' BOUNDARY ORG $*16 ;MUST START ON 'LINE' BOUNDARY ; HDRBUF: DS 16 ;MUST FOLLOW ORG REC: DS 80H ;MUST FOLLOW HDRBUF OBJ: DS 32 ;OBJECT CODE BUFFER OBJCNT: DS 1 ;LEN OF DATA IN OBJ INST: DS 80H ;CURRENT INSTRUCTION (OR DATA FROM DB) EOM: DB 0 ;END OF BUFFER CHARACTER (FOR WLINE) IDBUF: DS 16 ;CURRENT ID INTBUF: DS 2 ;RETURNED VALUE FROM INT ROUTINE PC: DS 2 ;CURRENT PROGRAM COUNTER LEN: DS 1 ;LEN OF CURRENT INSTRUCTION LEN2: DS 2 ;FOR DEFS VAL: DS 2 ;RETURN FROM EVAL ROUTINE PTR1: DS 2 ;POINTS TO NEXT CHAR IN REC PASSNO: DS 1 ;CURRENT PASS 0= PASS1 FF= PASS2 CURLNE: DS 1 ;CURRENT LINE NUMBER FOR PAGING OUTPUT LBLADR: DS 2 ;LAST LBL ADDR IN SYM (FOR EQU) EQUFLG: DS 1 ;IF NON-ZERO EQUVAL IS USED INSTEAD OF PC FOR PRINT EQUVAL: DS 2 ;VALUE OF LAST EQU ENDADR: DS 2 ;EXPRESSION VALUE ON END STATEMENT EFLG: DS 1 ;END OF PROGRAM FLAG (TO ALLOW PRINTING OF END STMT) OPCODE: DS 2 ;CURRENT OPCODE FROM SYMBOL TABLE SAVVAL: DS 2 ;SAVED CONTENTS OF VAL LFLAG: DS 1 ;LISTING FLAG:'A-D' = PRN FILE DESTN DRIVE ;'Z' = NO LISTING; 'X' = LISTING TO SCREEN ;'Y' = LISTING TO SCREEN, ERRORS ECHOED TO PRINTER ;'P' = LISTING TO PRINTER HFLAG: DS 1 ;HEX OBJ FLAG 'Z' = NO HEX OBJ, 'A - D') = HEX OBJ DESTN DRIVE ERRFLG: DS 1 ;ERROR CHARACTER FOR THIS LINE ERCNT: DS 1 ;NO OF ERRORS COUNT (BCD FORMAT) TEMP: DS 2 ;TEMP 2 BYTE AREA SYMPTR: DS 2 ;ADDRESS OF NEXT SYMBOL TABLE ENTRY SYMPT: DS 2 ;ADDRESS OF BEGINNING OF SYMBOL TABLE MAXMEM: DS 2 ;MAXIMUM USABLE MEMORY ADDRESS PCFLAG: DS 1 ;PC RELATIVE VALUE IN EVAL PCEVAL: DS 1 ;SET IF '$' (PC) INVOLVED IN LAST EVAL UFLAG: DS 1 ;UNDEFINED FLAG FROM EVAL, 0 = ALL OK, 1 OR >1 = UNDEFINED EVFLGS: DS 1 ;FLAG FIELD FROM LAST SYMLUK IXFLAG: DS 1 ;INDEX DISPLACEMENT BEING CALCULATED INFFLG: DS 1 ;INFORMATION BYTE FLAG TABS: DB 0FFH ;TRUE IF WE WANT COMPRESS SPACES TO TABS IN LISTING SYMBLS: DB 0FFH ;TRUE IF WE WANT A SYMBOL TABLE LSTFLG: DB 0FFH ;TRUE IF WE WANT A LISTING CNDFLG: DB 0FFH ;TRUE IF WE WANT FALSE CONDITIONALS IFLIST: DB 0 ;SET TRUE TO SUPPRESS LISTING ON IF, ELSE, ENDIF ; WHEN "LIST NOCOND" IS CURRENT. NOLIST: DB 0 ;SET TO TRUE TO AVOID LISTING TITLE, FORM, PAGE, EJECT TITLEB: DB CR,LF ;TITLE BUFFER (80 CHARS + CR,LF) DS 80 TITLE Z80 Assembler - Expression Evaluation ; ; EXPRESSION EVALUATOR ; ; EVALUATE AN EXPRESSION. IF A SYMBOL IS FOUND, ; SEARCH SYMBOL TABLE FROM CONDITIONALS ONWARDS ; ONLY, (I.E. OMIT OPCODES & REG NAMES) ; EVALNEW:LD HL,SYMNXT ;START OF NEW SYMBOLS LD (SYMPT),HL ;SO NO CONFLICTS WITH OPCODES & REGISTER NAMES CALL EVAL LD HL,SYM LD (SYMPT),HL ;PUT IT BACK THE WAY IT WAS RET ; ; EVALUATE AN EXPRESSION. IF A SYMBOL IS FOUND, ; SEARCH FROM REGISTERS ONWARDS ONLY, (I.E. ; OMIT OPCODES) ; EVALREG:LD HL,REGS LD (SYMPT),HL ;SO NO CONFLICTS WITH REGISTER NAMES CALL EVAL LD HL,SYM LD (SYMPT),HL ;PUT IT BACK THE WAY IT WAS RET ; ; EVALUATE AN EXPRESSION. IF A SYMBOL IS FOUND, ; SEARCH FROM CONDIDTIONALS ONWARDS ONLY, (I.E. ; OMIT OPCODES & REG NAMES) ; EVALCND:LD HL,CONDS LD (SYMPT),HL ;SO NO CONFLICTS WITH REGISTER NAMES CALL EVAL LD HL,SYM LD (SYMPT),HL ;PUT IT BACK THE WAY IT WAS RET ; ; EVALUATE AN EXPRESSION. IF A SYMBOL IS FOUND, ; SEARCH USING DEFAULT SYMBOL TABLE POINTER IN ; (SYMPT) ; ; VALID OPERATORS ARE: +, -, * AND / ; ; VALID ELEMENTS ARE: ID'S, NUMBERS, AND '$' FOR PC ; EVAL: XOR A LD (OP),A ;DEFAULT OP TO + LD (PCFLAG),A ;RESET PC RELATIVE FLAG LD (PCEVAL),A ;RESET PC EVALUATION FLAG LD (EVFLGS),A ;RESET FLAGS FROM LAST SEARCH LD HL,0 LD (VAL),HL ;SET VAL TO 0000 EVAL1: CALL GNC ;GET NEXT ELEMENT OR OPERATOR CP '0' JP C,EVAL3 CP '9'+1 JP C,EVAL9 ;BRANCH IF NUMBER EVAL3: CP '$' JP Z,EVAL11 ;BRANCH IF PC REF CP 'A' JP C,EVAL5 CP 'Z'+1 JP C,EVAL13 ;BRANCH IF ID EVAL5: CP '(' JP Z,EXPERR ;BRACKETS ILLEGAL CP '+' JP Z,EVAL22 CP '-' JP Z,EVAL24 CP '*' JP Z,EVAL25 CP '/' JP Z,EVAL26 CP '%' ;MODULO JP Z,EVL26A CP '=' ;EQUALS? JP Z,EVL26B CP '>' ;GREATER THAN? JP Z,EVL26C CP '<' ;LESS THAN? JP Z,EVL26D CP 27H JP Z,EVL29 ;PROCESS SINGLE BYTE QUOTED STRING CP '.' JP Z,EXOPS ;EXTENDED OPS START WITH '.' RET ;GET OUT OF HERE WE ARE DONE ; ; PROCESS NUMBER ; EVAL9: CALL BACKUP CALL INT JP EVAL28 ; ; PROCESS '$' REF ; EVAL11: LD HL,(PC) LD (INTBUF),HL LD A,(PCFLAG) ;FETCH PC RELATIVE VALUE FLAG INC A LD (PCFLAG),A ;SET FLAG LD A,0FFH ;SET FLAG TO SAY '$' REF FOUND LD (PCEVAL),A JP EVAL28 ; ; PROCESS ID ; EVAL13: CALL BACKUP CALL ID CALL BACKUP ;BACKUP SO AS TO NOT IGNORE OPERATOR OR COMMA LD HL,IDBUF CALL SYMLUK JP NZ,EVAL19 ;BRANCH IF UNDEFINED LD A,(PCFLAG) ;FETCH PC RELATIVE FLAG INC A LD (PCFLAG),A ;SET FLAG TO MARK AS PC-RELATIVE LD A,(HL) LD (INTBUF),A INC HL LD A,(HL) LD (INTBUF+1),A JP EVAL28 ; EVAL19: LD HL,0 ;PROCESS UNDEFINED ID LD (INTBUF),HL ;SET VALUE TO 0000 LD A,(UFLAG) OR 1 LD (UFLAG),A ;SET UNDEFINED FLAG JP EVAL28 ; ; PROCESS OPERATORS ; EVAL22: LD A,1 ;+ JP EVAL27 ; EVAL24: LD A,2 ;- JP EVAL27 ; EVAL25: LD A,3 ;* JP EVAL27 ; EVAL26: LD A,4 ;/ JP EVAL27 ; EVL26A: LD A,5 ;% OR .MOD. JP EVAL27 ; EVL26B: LD A,12 ;= OR .EQ. JP EVAL27 ; EVL26C: LD A,16 ;> OR .GT. JP EVAL27 ; EVL26D: LD A,14 ;< OR .LT. JP EVAL27 ; EVAL27: LD (OP),A JP EVAL1 ; ; DECODE EXTENDED OPERATORS ; delimited by "." at each end ; EXOPS: PUSH HL PUSH DE PUSH BC ;SAVE REGS LD DE,IDBUF+1 LD HL,IDBUF LD BC,15 LD (HL),' ' IF Z80 LDIR ;BLANK ID BUF ELSE CALL LDIR ;BLANK ID BUF ENDIF LD HL,IDBUF CALL GNC CP '.' JP Z,ID5 EXOPS1: CALL CASCVRT ;CONVERT L/C TO U/C LD (HL),A INC HL PUSH HL LD HL,(PTR1) LD A,(HL) INC HL LD (PTR1),HL POP HL CP '.' JP NZ,EXOPS1 ; LD HL,EXTOPS ;EXTENDED OPERANDS LD (SYMPT),HL ;SO NOT CONFLICT WITH REG NAMES LD HL,IDBUF CALL SYMLUK LD A,(HL) ;GET OP NUMBER LD (OP),A LD HL,SYM LD (SYMPT),HL ;PUT IT BACK THE WAY IT WAS POP BC POP DE POP HL LD A,(UFLAG) ;VALID LABEL ? OR A JP Z,EVAL1 ; EXOPER: LD A,'L' LD (ERRFLG),A JP EVAL1 ; EXTOPS: DB 7,'NOT',6,0,0 DB 7,'MOD',5,0,0 DB 7,'SHR',7,0,0 DB 7,'SHL',8,0,0 DB 7,'AND',9,0,0 DB 6,'OR',10,0,0 DB 7,'XOR',11,0,0 DB 6,'EQ',12,0,0 DB 6,'NE',13,0,0 DB 6,'LT',14,0,0 DB 6,'LE',15,0,0 DB 6,'GT',16,0,0 DB 6,'GE',17,0,0 DB 7,'LOW',18,0,0 DB 8,'HIGH',19,0,0 DB 0 ; ; PROCESS VALUE USING CURRENT OPERATOR ; ; VAL = VAL OP INTBUF ; EVAL28: LD A,(EVFLGS) ;FETCH LABEL TYPE AND 20H ;MASK SGL REGISTER LD A,(OP) JP Z,EVL280 ;CHECK IF OPERATOR OR A ;ANY OPERATOR ? JP NZ,EXPERR ;BOMB IF NOT ; EVL280: LD HL,OPRET ;WHERE TO RETURN TO AFTER FUNCTION PUSH HL ;PUT RETURN ADDRESS ON STACK ADD A ;GET OPERAND NUMBER*2 LD E,A ;INEX INTO TABLE WITH IT LD D,0 LD HL,OPTAB ;TABLE OF OPERATION ADDRESES ADD HL,DE LD E,(HL) INC HL LD D,(HL) PUSH DE ;WE ARE GOING THERE VIA A RETURN ;SO PUSH ADDRESS ON STACK LD HL,(VAL) ;NOW GET THE OPERANDS EX DE,HL LD HL,(INTBUF) RET ;AND GO TO IT! ; OPRET: LD (VAL),HL JP EVAL1 ; ; OPERATOR DISPATCH TABLE ; OPTAB: DW BADD ;0:DEFAULT ADD DW BADD ;1:ADD DW BSUB ;2:SUBTRACT DW BMULT ;3:MULTIPLY DW BDIV ;4:DIVIDE DW BMOD ;5:MODULO DW UNOT ;6:UNARY NOT DW BSHR ;7:SHIFT RIGHT DW BSHL ;8:SHIFT LEFT DW BAND ;9:LOGICAL AND DW BOR ;10:LOGICAL OR DW BXOR ;11:LOGICAL EXCLUSIVE OR DW BEQ ;12:EQUAL DW BNE ;13:NOT EQUAL DW BLT ;14:LESS THAN DW BLE ;15:LESS THAN OR EQUAL DW BGT ;16:GREATER THAN DW BGE ;17:GREATER THAN OR EQUAL TO DW ULOW ;18:UNARY LOW BYTE (MOD 256) DW UHIGH ;19:UNARY HIGH BYTE (SHR 8) ; ; ADD ; BADD: ADD HL,DE LD A,(IXFLAG) OR A RET Z ;NOT INDEX, SO O.K LD A,H ;FETCH VAL MS BIT OR A ;SHOULD BE BLANK FOR INDEX JP NZ,VALERR ;ERROR LD A,L ;FETCH LS BIT OR A ;SET SIGN FLAG RET P ;RETURN IF + VALERR: LD A,'V' LD (ERRFLG),A RET ; ; SUBTRACT ; BSUB: CALL SBHLDE ;HL=DE-HL LD A,(IXFLAG) OR A RET Z ;NOT INDEX, SO O.K LD A,H ;FETCH VAL MS BIT INC A ;SHOULD BE 0FFH FOR -INDEX JP NZ,VALERR ;ERROR LD A,L ;FETCH LS BIT OR A ;SET SIGN FLAG JP P,VALERR ;ERROR IF +VE RET ; SBHLDE: LD A,E ;HL=DE-HL SUB L LD L,A LD A,D SBC H LD H,A RET ; ; MULTIPLY: HL <= HL * DE (16 BITS ONLY) ; BMULT: PUSH BC PUSH DE LD B,D LD C,E EX DE,HL LD HL,0 BMULT1: LD A,C OR B JP Z,BMULT2 ADD HL,DE DEC BC JP BMULT1 ; BMULT2: POP DE POP BC RET ; ; DIVIDE ; ; DIVIDES 16-BIT DIVIDEND BY 16-BIT DIVISOR ; ON ENTRY: DE = DIVIDEND, HL = DIVISOR ; ON RETURN: HL = QUOTIENT, REMAINDER = DE ; BDIV: LD A,H ;TEST FOR 0 DIVISOR OR L JP NZ,BDIV0 ;JP IF NOT /0 LD A,'Z' LD (ERRFLG),A ;FLAG DIVIDE BY 0 ERROR SCF ;MARK 0 DIVISOR AS ERROR RET ;..AND EXIT ; BDIV0: EX DE,HL LD BC,0 BDIV1: LD A,L ;LOW BYTE OF DIVIDEND SUB E LD L,A LD A,H SBC D LD H,A JP C,BDIV2 INC BC JP BDIV1 ; BDIV2: ADD HL,DE ;HL=REMAINDER ;BC=RESULT EX DE,HL ;DE=REMAINDER LD L,C LD H,B ;HL=RESULT XOR A ;ENSURE NO ERROR RET ; EXPERR: LD A,'E' LD (ERRFLG),A ;FLAG EXPRESSION ERROR RET ;EXIT THIS LINE ; ; MODULO ; BMOD: CALL BDIV EX DE,HL ;DE HAS MODULO RET ; ; PROCESS SINGLE BYTE IN QUOTES ; EVL29: LD HL,(PTR1) LD A,(HL) ;GET CHARACTER INC HL CP 27H JP NZ,EVL29A ;NOT DOUBLE QUOTES LD A,(HL) ;FETCH NEXT CHAR INC HL EVL29A: LD (INTBUF),A LD A,(HL) ;NEXT CHAR SUB 27H ;1 OR 2 BYTES? JP Z,EVL29B ;ONLY 1 LD A,(HL) ;FETCH SECOND BYTE INC HL ;PTR TO TRAILING QUOTES EVL29B: LD (PTR1),HL LD (INTBUF+1),A ;MAKE 2 BYTE VALUE CALL GNC ;BYPASS TRAILING QUOTE JP EVAL28 ;AND GO PROCESS VALUE NORMALLY ; ; UNOT - LOGICAL COMPLEMENT ; UNOT: LD A,D OR E JP NZ,EXPERR LD A,H ;COMPLEMENT RESULT CPL LD H,A LD A,L CPL LD L,A RET ; ; BLE - LESS THAN OR EQUAL ; BLE: CALL SBHLDE JP C,BLE1 ;IF LEFT .LT. RIGHT LD A,H OR L LD HL,0FFFFH RET Z ;IF LEFT .EQ. RIGHT INC HL RET BLE1: LD HL,0FFFFH RET ; ; BLT - LESS THAN ; BLT: CALL SBHLDE LD HL,0FFFFH ;PRESET TRUE RET C ;IF LEFT .LT. RIGHT INC HL RET ; ; BEQ - EQUAL ; BEQ: CALL SBHLDE LD A,H OR L LD HL,0FFFFH RET Z ;IF LEFT .EQ. RIGHT INC HL RET ; ; BGT - GREATER THAN ; BGT: CALL SBHLDE JP C,BGT1 ;IF LEFT .LT. RIGHT LD A,H OR L RET Z ;IF LEFT .EQ. RIGHT LD HL,0FFFFH RET BGT1: LD HL,0 RET ; ; BGE - GREATER THAN OR EQUAL ; BGE: CALL SBHLDE LD HL,0 ;PRESET FALSE RET C ;IF LEFT .LT. RIGHT DEC HL ;TRUE RET ; ; BNE - NOT EQUAL ; BNE: CALL BEQ ;COMPARE ARGS LD A,L ;COMPLEMENT RESULT CPL LD L,A LD A,H ;COMPLEMENT RESULT CPL LD H,A RET ; ; BAND - LOGICAL PRODUCT ; BAND: LD A,E AND L LD L,A LD A,D AND H LD H,A RET ; ; BOR - LOGICAL SUM ; BOR: LD A,E OR L LD L,A LD A,D OR H LD H,A RET ; ; BXOR - LOGICAL DIFFERENCE ; BXOR: LD A,E XOR L LD L,A LD A,D XOR H LD H,A RET ; BSHR: EX DE,HL BSHR1: LD A,D OR E RET Z LD A,H RRA LD H,A LD A,L RRA LD L,A DEC DE JP BSHR1 ; BSHL: EX DE,HL BSHL1: LD A,D OR E RET Z ADD HL,HL DEC DE JP BSHL1 ; ; ULOW - LOW BYTE ; ULOW: LD A,D OR E JP NZ,EXPERR LD H,0 RET ; ; UHIGH - HIGH BYTE ; UHIGH: LD A,D OR E JP NZ,EXPERR LD L,H LD H,0 RET ; OP: DS 1 ;CURRENT OPERATOR FOR EVAL ; ; INT - CONVERT CHARACTERS TO BINARY ; ; ALLOW TRAILING 'H' FOR HEX, 'O' OR 'Q' FOR OCTAL ; AND 'B' FOR BINARY ; DEFAULT TYPE IS DECIMAL (BASE 10) ; INT: LD HL,(PTR1) LD (TEMP),HL ;SAVE POINTER LD HL,10 LD (MULT),HL ;SET DEFAULT BASE TO 10 LD B,0 ;SET UP LENGTH COUNTER CALL GNC ;SKIP BLANKS INT1: CP '+' JP Z,INT1A ;BRANCH IF TERMINATOR CP '-' JP Z,INT1A CP '*' JP Z,INT1A CP '/' JP Z,INT1A CP CR JP Z,INT1A CP ';' JP Z,INT1A CP '(' JP Z,INT1A CP ')' JP Z,INT1A CP ',' JP Z,INT1A CP '.' JP Z,INT1A CP 20H JP Z,INT1A LD HL,(PTR1) LD A,(HL) INC HL LD (PTR1),HL ;GET NEXT CHARACTER INC B JP INT1 ;INCREMENT COUNTER AND CONTINUE LOOP ; INT1A: DEC HL LD (PTR1),HL DEC HL LD A,(HL) CALL CASCVRT CP 'H' JP NZ,INT2A ;BRANCH IF NOT HEX DEC B ;DECREMENT COUNTER LD HL,16 LD (MULT),HL ;SET UP BASE 16 JP INT4 ; INT2A: CP 'Q' JP Z,INT3 ;BRANCH IF OCTAL CP 'O' JP Z,INT3 ;BRANCH IF OCTAL CP 'B' JP NZ,INT4 ;BRANCH IF NOT BINARY DEC B ;DECREMENT COUNTER LD HL,2 LD (MULT),HL ;SET UP BASE 2 JP INT4 ; INT3: DEC B ;DECR COUNTER LD HL,8 ;SET UP BASE 8 LD (MULT),HL INT4: LD HL,(PTR1) EX DE,HL LD HL,(TEMP) LD (PTR1),HL EX DE,HL LD (TEMP),HL ;SAVE PTR LD DE,0 ;SET UP ACCUMULATOR CALL GNC CALL BACKUP INT5: LD HL,(PTR1) LD A,(HL) INC HL LD (PTR1),HL CALL CASCVRT CP 'A' JP C,$+5 ADD 9 ;FOR A-F AND 0FH PUSH AF ;GET BINARY VALUE OF THIS DIGIT PUSH BC ;SAVE COUNTER LD HL,(MULT) CALL BMULT POP BC ;RESTORE COUNTER POP AF LD E,A LD D,0 ADD HL,DE ;ADD IN NEW DIGIT EX DE,HL DEC B JP NZ,INT5 ;GO BACK IF MORE TO DO INT6: LD HL,(TEMP) LD (PTR1),HL EX DE,HL LD (INTBUF),HL ;SAVE VALUE RET ; MULT: DS 2 ;BASE OF NUMBER IN INT CONVERT LOOP ; ; ID - COLLECT ID AND PLACE IN IDBUF ; ID: PUSH HL PUSH DE PUSH BC ;SAVE REGS LD DE,IDBUF+1 LD HL,IDBUF LD BC,15 LD (HL),' ' IF Z80 LDIR ;BLANK ID BUF ELSE CALL LDIR ;BLANK ID BUF ENDIF LD HL,IDBUF CALL GNC CP 'A' JP C,ID5 CP 'Z'+1 JP NC,ID5 ;IF NOT A LETTER THEN BRANCH JP ID3 ; ID2: CALL CASCVRT ;CONVERT L/C TO U/C CP 'A' JP C,ID1 CP 'Z'+1 JP NC,ID1 ;IF NOT A LETTER THEN BRANCH ID3: LD (HL),A INC HL PUSH HL LD HL,(PTR1) LD A,(HL) INC HL LD (PTR1),HL POP HL JP ID2 ; ID1: CP '_' JP Z,ID3 ; ALLOW LABELS WITH '_' IN THEM (E.G. LAB_01) CP '$' JP Z,ID3 ; ALLOW $ FILLERS (E.G. SQR$ROOT) CP '0' JP C,ID4 CP '9'+1 JP C,ID3 ;ALLOW DIGITS ID4: POP BC POP DE POP HL RET ; ID5: LD A,'L' LD (ERRFLG),A JP ID4 ; ; SYMENT - ENTER A SYMBOL INTO THE SYMBOL TABLE ; UPON ENTRY HL POINTS TO NEW ENTRY ; ; ENTRY FORMAT: FLAGS/LENGTH (EACH 4 BITS) ; N A M E ... (UP TO 11 BYTES) ; VALUE LOW (1 BYTE) ; VALUE HIGH (1 BYTE) ; TYPE BYTE (1 BYTE) ; ; ON RETURN HL POINTS TO VALUE LOW BYTE IN NEW ENTRY FOR POSSIBLE ; FURTHER UPDATE (USED BY EQU PSEUDO OPERATOR) ; SYMENT: EX DE,HL ;DE=>SYMBOL TO ENTER PUSH BC LD HL,(MAXMEM) ;HL = MEX MEM ADDRESS LD B,H ;PUT INTO B & C LD C,L LD HL,(SYMPTR) LD A,L ;SUBTRACT BC FROM HL SUB C LD A,H SBC B JP C,SYMEN1 ;JP IF NO OVERFLOW LD A,'W' LD (ERRFLG),A POP BC RET ; SYMEN1: LD A,(DE) AND 0FH LD C,A LD B,0 ;GET LEN EX DE,HL IF Z80 LDIR ;MOVE INTO SYMBOL TABLE ELSE CALL LDIR ;MOVE INTO SYMBOL TABLE ENDIF EX DE,HL LD (SYMPTR),HL LD (HL),0 ; SET UP NEW POINTER AND NEW END MARKER DEC HL DEC HL DEC HL ;BACKUP TO POINT TO VALUE LOW FIELD POP BC LD A,0FFH ;SET THE FLAG TO SAY WE HAD A SYMBOL LD (SYMFLG),A RET ; ; SYMLUK - LOOK UP SYMBOLS IN TABLE ; ; ON RETURN Z=0 MEANS SYMBOL NOT FOUND. ; Z=1 SYMBOL FOUND, HL POINTS TO VALUE LOW BYTE IN TABLE ENTRY ; SYMLUK: EX DE,HL LD HL,(SYMPT) ;GET POINTER TO BEGINING OF TABLE EX DE,HL SYML1: LD A,(DE) OR A JP Z,SYMNF ;BRANCH IF END OF TABLE LD (EVFLGS),A ;SAVE FOR MAIN PROCESSOR AND 0FH SUB 4 LD B,A INC DE ;SET UP FOR COMPARE CALL CMPCHR JP Z,SYML2 ;BRANCH IF MATCH DEC DE ;BACK UP LENGTH BYTE AGAIN SYML3: LD A,(DE) AND 0FH LD C,A LD B,0 EX DE,HL ADD HL,BC EX DE,HL JP SYML1 ;ADVANCE POINTER AND CONTINUE TO LOOP ; SYML2: EQU $ ;NOW SEE IF EXACT MATCH DEC DE LD A,(DE) AND 0FH SUB 4 ;GET LENGTH PUSH HL LD C,A LD B,0 ADD HL,BC LD A,(HL) CP ' ' POP HL JP NZ,SYML3 ;IF NXT CHR NOT BLANK THEN NOT EXACT MATCH LD A,(DE) AND 0FH DEC A DEC A DEC A LD C,A LD B,0 EX DE,HL ADD HL,BC XOR A RET ;POINT TO VALUE LOW AND EXIT ; SYMNF: XOR A INC A RET ;SET Z=0 AND RETURN ; ; GNR - GET NEXT RECORD - FILL REC UNTIL LF OR 1AH (CTRL/Z) IS FOUND ; TRUNCATE SOURCE LINE IF > 128 CHARACTERS ; GNR: LD HL,REC LD (PTR1),HL ;RESET POINTER LD (HL),' ' LD DE,REC+1 LD BC,7FH IF Z80 LDIR ;BLANK BUFFER ELSE CALL LDIR ;BLANK BUFFER ENDIF LD HL,REC ;GET ADDRESS OF RECORD BACK AGAIN LD C,7FH ;LOAD BUFFER COUNT GNR1: CALL GNB CP 1AH JP Z,GNR3 ;RETURN IF EOF MARKER (CTRL/Z) IF FOUND ;WITH THE LINE TERMINATED BY CR/LF CP TAB JP Z,GNR2 ;BRANCH IF TAB CHARACTER LD (HL),A INC HL DEC C ;DECR BUFFER COUNT JP Z,GNR3 ;FULL, TRUNCATE LINE CP LF JP NZ,GNR1 ;BRANCH IF NOT LF RET ; ; PROCESS TAB ; GNR2: LD A,C ;FETCH BUFFER COUNT SUB 8 ;ADJUST FOR TAB LD C,A ;REPLACE COUNT JP M,GNR3 ;BUFFER FULL, EXIT LD DE,8 ADD HL,DE LD A,L AND 0F8H LD L,A ;PROCESS TAB STOP JP GNR1 ; GNR3: LD (HL),CR INC HL LD (HL),LF ;TERMINATE BUFFER INC HL LD (PTR1),HL RET ; ; GNC - GET NEXT CHARACTER ; USE PTR1 TO INDEX INTO REC, SKIP BLANKS ; GNC: PUSH HL LD HL,(PTR1) GNC1: LD A,(HL) CP ' ' INC HL JP Z,GNC1 ;SKIP BLANKS CP TAB JP Z,GNC1 ;BRANCH IF TAB CHARACTER CALL CASCVRT ;CONVERT L/C TO U/C LD (PTR1),HL POP HL RET ; ; BACKUP - BACKUP PTR1 AND RETURN CHARACTER ; BACKUP: PUSH HL PUSH AF LD HL,(PTR1) DEC HL LD A,(HL) LD (PTR1),HL CALL CASCVRT LD C,A POP AF LD A,C POP HL RET TITLE Z80 Assembler - Main Assembler module ;----------------------------------------------- ; ; MAIN ASSEMBLER MODULE ; ;----------------------------------------------- ; ; LOCAL DATA AREA ; SYMAREA:DS 15 ;AREA FOR SYMBOL TABLE BUILD SYMADR: DS 2 ;ADDRESS OF VALUE LOW FIELD FOR LAST SYMENT OLDADR: DW -1 ;LAST ADDR OF WOBJ OBJADR: DS 2 ; MAXLNE: EQU 56 ;LINES PER PAGE ; DS 128 STACK: EQU $ ;Z80 STACK ; ;************************************************************** ; ; MAIN LOOP - READ A SOURCE RECORD ; PROCESS LABEL AND OPCODE ; PRINT LINE (UNLESS OPTION=N) ; OUTPUT HEX (IF NECESSARY) ; BACK TO MAIN LOOP FOR NEXT RECORD ; NEXT: XOR A LD (UFLAG),A ;UDEFINED FLAG LD (LEN),A ;INSTRUCTION LENGTH LD (EFLG),A LD (EQUFLG),A LD (IXFLAG),A LD (IFLIST),A LD (NOLIST),A LD H,A LD L,A LD (LEN2),HL ;INIT LENGTH & FLGS LD A,' ' LD (ERRFLG),A ;FOR THIS RECORD CALL GNR ;GET NEXT RECORD CP 1AH JP Z,S321 ;BRANCH IF EOF LD A,(REC) ;GET COL 1 CP TAB JP Z,S12 ;BRANCH IF TAB CP CR JP Z,S8 ;IGNORE NULL LINE CP ' ' JP Z,S12 ;BRANCH IF NO LABEL CP ';' JP Z,S8 ;IF COMMENT THEN IGNORE CALL ID LD HL,(CONDSP) ;GET CONDITIONAL STATE LD A,(HL) ;GET STATE OR A JP Z,S12 ;DON'T ENTER SYMBOL LD A,(ERRFLG) ;ANY ERROR CP ' ' JP NZ,ENDSTMT ;EXIT ; ; CALCULATE LENGTH OF SYMBOL AND BUILD ENTRY ; S9: LD B,0 ;LENGTH + FLAGS LD HL,IDBUF LD DE,SYMAREA+1 S10: LD A,(HL) CP ' ' JP Z,S10A LD (DE),A INC HL INC DE INC B JP S10 ; S8: CALL GNC ;STEP OVER SEPARATOR JP ENDSTMT ; S10A: EQU $ ;SYMBOL COLLECTED LD A,B CP 0CH JP C,S10B ;BRANCH IF LEN < 12 (I.E. OK) LD A,'L' LD (ERRFLG),A JP ENDSTMT ;ELSE LABEL ERROR ; S10B: ADD 44H ;ADD 4 FOR OVERHEAD & LD (SYMAREA),A ; MARK LABEL FLAG 40H EX DE,HL ;SWITCH REGISTERS LD A,(PC) LD (HL),A INC HL ;MOVE IN PC AS VALUE LD A,(PC+1) LD (HL),A INC HL LD (HL),0 ;AND SET TYPE =0 LD HL,CONDS LD (SYMPT),HL ;NO CONFLICT WITH REG NAMES LD HL,IDBUF CALL SYMLUK PUSH HL ;SAVE TABLE PTR LD (SYMADR),HL ;SAVE ADDRESS FOR EQU LD HL,SYM LD (SYMPT),HL ;PUT IT BACK THE WAY IT WAS POP HL ;RESTORE SYMBOL TABLE PTR JP NZ,S11 ;BRANCH IF NOT ALREADY THERE PUSH HL ;SAVE TABLE PTR S10C: DEC HL ;BACK UP PTR LD A,(HL) ;FETCH LABEL CHAR OR A ;TERMINATOR ? JP NZ,S10C ;MORE IN LABEL INC HL ;INCR PTR TO IDENTITY LD A,(HL) ;FETCH IDENTITY CHAR AND 10H ;EQUATE LABEL POP HL ;RECOVER TABLE PTR JP NZ,S12 ;DONT MARK AS ERROR LD E,(HL) ;FETCH ADDR LSB INC HL ;INCR PTR LD D,(HL) ;FETCH LABEL ADDR MSB LD HL,(PC) ;FETCH CURRENT ADDR CALL CMPHD ;SAME ? JP Z,S12 ;VALUE OK LD A,'M' LD (ERRFLG),A JP ENDSTMT ;ELSE MULTI DEFINED ERROR ; S11: LD HL,SYMAREA CALL SYMENT ;ENTER SYMBOL INTO TABLE LD (SYMADR),HL ;SAVE ADDRESS FOR EQU ; ; NOW PROCESS OPCODE ; S12: CALL GNC ;FETCH CHAR AFTER TAB OR SPACE CP TAB JP Z,S12 ;STEP OVER TABS CP CR JP Z,ENDSTMT CP LF JP Z,ENDSTMT ;IGNORE BLANK LINE CP ';' JP Z,ENDSTMT ;IGNORE COMMENT CALL BACKUP ;RESTORE PTRS CALL ID CP ' ' ;TEST OP-CODE SEPARATOR JP Z,S12C CP TAB JP Z,S12C CP LF JP Z,S12C CP CR JP Z,S12C JP S12B ; S12C: LD A,(IDBUF) CP ' ' JP Z,S12B ;ERROR EXIT S12A: LD HL,IDBUF CALL SYMLUK JP Z,S16 ;BRANCH IF FOUND S12B: LD A,'N' LD (ERRFLG),A JP ENDSTMT ;ERROR IF UNDEFINED OPCODE ; S16: LD A,(EVFLGS) ;FETCH LABEL FLAG AND 0F0H ;TEST IF OP-CODE JP NZ,S12B ;ERROR, NOT OP-CODE LD A,(HL) LD (OPCODE),A INC HL LD A,(HL) LD (OPCODE+1),A ;SAVE OPCODE INC HL LD A,(HL) ;GET TYPE BYTE CP 14 ;IF PSUEDO-OP JP NZ,S17 ;JUST TEST STATE IF NOT LD A,(OPCODE) ;GET OPCODE CP 8 ;IS IT IF? JP Z,S17A ;LET THROUGH ALWAYS CP 9 ;IS IT ELSE? JP Z,S17A ;LET THROUGH ALWAYS CP 10 ;IS IT ENDIF? JP Z,S17A ;LET THROUGH ALWAYS S17: EX DE,HL ;SAVE POINTER IN DE LD HL,(CONDSP) ;GET CONDITIONAL STATE LD A,(HL) ;GET STATE EX DE,HL ;GET POINTER BACK OR A ;NZ=TRU,Z=FALSE JP NZ,S17A ;IF TRUE PROCESS THE OPCODE CALL GNC ;GO PAST SEPARATOR JP ENDSTMT ;JUST FINISH IF FALSE ; S17A: LD A,(HL) ;GET TYPE BYTE DEC A ADD A ;-1 AND DOUBLE FOR TABLE INDEX LD E,A LD D,0 LD HL,TYPTBL ADD HL,DE LD E,(HL) INC HL LD D,(HL) EX DE,HL JP (HL) ;DISPATCH TO PROPER INSTRUCTION TYPE ; ;----------------------------------------------- ; ; INSTRUCTION CLASS DISPATCH TABLE ; TYPTBL: DW S18 ;CLASS 1 - OPCODE ONLY DW S24 ;CLASS 2 - ROTATES DW S42 ;CLASS 3 - JUMPS (NON RELATIVE) CALLS DW S62 ;CLASS 4 - RELATIVE JUMPS (JR AND DJNZ) DW S73 ;CLASS 5 - RST DW S77 ;CLASS 6 - ARITHMETIC INSTRUCTIONS DW S97 ;CLASS 7 - I/O DW S116 ;CLASS 8 - LD INSTRUCTIONS DW S220 ;CLASS 9 - PUSH,POP DW S228 ;CLASS 10- EXCHANGE (EX) DW S243 ;CLASS 11- RETURNS DW S250 ;CLASS 12- BIT,SET,RES DW S268 ;CLASS 13- INC,DEC DW S301 ;CLASS 14- PSEUDO OPERATORS TITLE Z80 Assembler - Instruction Class 1 - Opcode only ; ; CLASS 1 - OPCODE ONLY ; S18: LD A,1 LD (LEN),A LD A,(OPCODE) LD (INST),A LD A,(OPCODE+1) CP 0FFH ;IM SET? JP Z,S19 OR A JP Z,ENDSTMT ;BRANCH IF ONLY 1 BYTE S18A: LD A,2 LD (LEN),A LD A,(OPCODE+1) LD (INST+1),A JP ENDSTMT ; S19: CALL GNC CP '2' LD B,5EH JP Z,S19A LD B,56H CP '1' JP Z,S19A LD B,46H CP '0' JP NZ,OERROR S19A: LD A,B LD (OPCODE+1),A CALL GNC ;BY-PASS NUMBER JP S18A TITLE Z80 Assembler - Instruction Class 2 - Rotates ; ; CLASS 2 - ROTATES ; S24: CALL GNC CP '(' JP Z,S30 ;BRANCH IF () FORM CALL BACKUP CALL ID LD HL,IDBUF CALL SYMLUK JP NZ,OERROR ;BRANCH IF UNDEFINED OPERAND ; S27: LD A,(IDBUF) LD A,0CBH LD (INST),A LD A,(OPCODE) LD B,A LD A,(HL) OR B LD (INST+1),A LD A,2 LD (LEN),A JP ENDSTMT ; S30: CALL GNC CALL GNC CP 'L' CALL Z,S131 ;CHECK ONLY HL JP Z,S40 ;BRANCH IF (HL) CP 'Y' LD (IXFLAG),A ;MARK AS INDEX CALL Z,S131 ;CHECK ONLY IY+ JP Z,S38 ;BRANCH IF (IY) CALL S131 ;CHECK ONLY IX+ JP NZ,OERROR ;ERROR LD A,0DDH ;PROCESS AS (IX) LD (INST),A S33: LD A,0CBH LD (INST+1),A LD A,(OPCODE) ADD 6 LD (INST+3),A XOR A LD (VAL),A LD A,(HL) CP ')' CALL NZ,EVALNEW ;EVALUATE INDEX CALL GNC ;BYPASS ) LD A,(VAL) LD (INST+2),A LD A,4 LD (LEN),A JP ENDSTMT ; S38: LD A,0FDH LD (INST),A JP S33 ;DO IX AND IY SIMILARLY ; S40: CALL GNC CALL GNC ;BYPASS ) LD A,0CBH LD (INST),A LD A,(OPCODE) ADD 6 LD (INST+1),A LD A,2 LD (LEN),A JP ENDSTMT TITLE Z80 Assembler - Instruction Class 3 - Absolute Jumps & Calls ; ; CLASS 3 - JUMPS - CALLS ; S42: CALL GNC CP '(' JP Z,S53 ;BRANCH IF () FORM LD HL,(PTR1) LD A,(HL) CP ',' JP Z,S49 ;BRANCH IF CONDITIONAL TYPE INC HL LD A,(HL) CP ',' JP Z,S49 LD A,(OPCODE) LD (INST),A LD A,3 LD (LEN),A CALL BACKUP S46: CALL EVALNEW ;USE DEFINED SYMBOLS ONLY LD HL,(VAL) LD (INST+1),HL JP ENDSTMT ; S49: EQU $ ;PROCESS CONDITIONAL JUMPS, CALLS LD A,(OPCODE) AND 0C6H LD (INST),A LD A,3 LD (LEN),A CALL BACKUP CALL EVALCND ;SO NO CONFLICT WITH REG NAMES LD A,(UFLAG) ;VALID LABEL ? OR A JP NZ,OERROR ;EARLY ERROR EXIT LD A,(VAL) RLCA RLCA RLCA AND 38H LD B,A LD A,(INST) OR B LD (INST),A LD A,3 LD (LEN),A JP S46 ;NOW GO PROCESS LIKE NON CONDITIONAL ; S53: EQU $ ;PROCESS () FORM CALL GNC CALL GNC ;GET REGISTER TYPE CP 'L' CALL Z,S61 ;CHECK ONLY HL JP Z,S58 ;BRANCH IF (HL) CP 'Y' CALL Z,S61 ;CHECK ONLY IY JP Z,S60 CALL S61 ;CHECK ONLY IX JP NZ,OERROR ;ERROR EXIT LD A,0DDH ;PROCESS AS (IX) LD (INST),A S56: LD A,0E9H LD (INST+1),A LD A,2 LD (LEN),A CALL GNC CALL GNC ;BYPASS ) JP ENDSTMT ; S58: LD A,0E9H LD (INST),A LD A,1 LD (LEN),A CALL GNC CALL GNC ;BYPASS ) JP ENDSTMT ; S60: LD A,0FDH LD (INST),A JP S56 ; S61: CALL GNC CP ')' ;TERMINATOR ? JP BACKUP ;PREV CHAR TITLE Z80 Assembler - Instruction Class 4 - Relative Jumps ; ; CLASS 4 - RELATIVE JUMPS (JR AND DJNZ) ; S62: CALL GNC LD HL,(PTR1) LD A,(HL) CP ',' JP Z,S68 INC HL LD A,(HL) CP ',' JP Z,S68 ;BRANCH IF CONDITIONAL LD A,(OPCODE) LD (INST),A LD A,2 LD (LEN),A CALL BACKUP JP S68A ; S68: CALL BACKUP CALL EVALCND LD A,(UFLAG) ;VALID LABEL ? OR A JP NZ,OERROR ;EARLY ERROR EXIT LD A,(VAL) RLCA RLCA RLCA AND 38H ADD 20H LD (INST),A LD A,2 LD (LEN),A S68A: CALL EVALNEW LD HL,(VAL) EX DE,HL LD HL,0 ;CLEAR LD A,(PCFLAG) ;FETCH PC RELATIVE FLAG OR A ;PC RELATIVE VALUE ? JP Z,S68A1 ;SKIP PC RELATIVE LD HL,(PC) S68A1: LD A,L SUB E CPL LD L,A LD A,H SBC D CPL LD H,A DEC HL LD A,H OR A JP Z,POSTST INC A JP NZ,S68C NEGTST: LD A,L OR A JP P,S68C JP S68B ; POSTST: LD A,L OR A JP M,S68C S68B: LD H,L LD A,(INST) ;ADD OPCODE TO DISPLACEMENT LD L,A S68B1: LD (INST),HL JP ENDSTMT ; S68C: LD A,'D' ;RANGE ERROR LD (ERRFLG),A LD HL,0 JP S68B1 TITLE Z80 Assembler - Instruction Class 5 - Restarts ; ; CLASS 5 - RESTARTS ; S73: CALL EVALNEW LD A,(VAL) ;FETCH OP-CODE VALUE AND 0C7H ;MASK RST BITS OUT JP NZ,OERROR ;OUT OF VALUE ERROR LD A,(VAL) ;FETCH OP-CODE VALUE AND 38H ;MASK TO RST BITS OR 0C7H ;MAKE RST CODE LD (INST),A LD A,1 LD (LEN),A JP ENDSTMT TITLE Z80 Assembler - Instruction Class 6 - Arithmetic & Logical ; ; CLASS 6 - ARITHMETIC OPCODES ; ; ADD,SUB,ADC,SBC,AND,OR,XOR,CP ; S77: CALL GNC LD HL,(PTR1) ;FETCH TEXT PTR CP 27H ;TEST IF SINGLE CHAR JP Z,S77B ;PROCESS NORMALLY LD A,(HL) ;FETCH 2ND CHAR CP ',' ;INDICATES FORMAT ADD A,r INC HL ;STEP PTR TO SOURCE REG LD (PTR1),HL JP Z,S77C ;GO AND PROCESS CALL BACKUP ;ELSE RESTORE PTRS FOR CALL BACKUP ;FORMAT ADD r S77C: CALL GNC ;FETCH SOURCE REG CP '(' JP Z,S85 ;BRANCH IF () FORM S77B: CALL BACKUP CALL EVALREG LD HL,(PTR1) LD A,(EVFLGS) AND RNAME+RPNAME CP RNAME ;SINGLE REG JP Z,S84 CP RPNAME ;DOUBLE REG JP Z,S96B ; ; IMMEDIATE VALUE ; LD A,(OPCODE) ADD 0C6H LD (INST),A LD A,(VAL) LD (INST+1),A LD A,2 LD (LEN),A JP ENDSTMT ; ; PROCESS REGISTER NAME ; S84: LD A,(OPCODE) ADD 80H LD B,A LD A,(VAL) ADD B LD (INST),A LD A,1 LD (LEN),A JP ENDSTMT ; S85: CALL GNC CALL GNC CP 'L' CALL Z,S131 ;CHECK HL ONLY JP Z,S95 ;PROCESS AS (HL) CP 'Y' LD (IXFLAG),A ;MARK AS INDEX CALL Z,S131 ;CHECK IY+ JP NZ,S87 ;PROCESS AS (IY) LD A,0FDH LD (INST),A JP S88 S87: CALL S131 ;CHECK IX+ JP NZ,OERROR ;ERROR EXIT LD A,0DDH ;PROCESS AS (IX) LD (INST),A S88: LD A,(OPCODE) ADD 86H LD (INST+1),A LD A,3 LD (LEN),A XOR A LD (VAL),A LD A,(HL) CP ')' CALL NZ,EVALNEW CALL GNC ;BYPASS ) LD A,(VAL) LD (INST+2),A JP ENDSTMT ; S95: CALL GNC CALL GNC ;BYPASS ) LD A,(OPCODE) ADD 86H LD (INST),A LD A,1 LD (LEN),A JP ENDSTMT ; ; PROCESS REGISTER PAIR ADD,SUB,ADC,SBC ; S96A: CALL BACKUP CALL EVALREG S96B: LD A,(UFLAG) ;VALID LABEL? OR A JP NZ,OERROR ;ERROR EXIT LD A,(VAL) CP 0FFH ;IX JP Z,S96H CP 0FEH ;IY JP Z,S96J LD A,(OPCODE+1) CP 9 JP Z,S96F LD A,0EDH LD (INST),A LD A,2 LD (LEN),A CALL EVAL LD A,(VAL) RLCA RLCA RLCA AND 38H LD B,A LD A,(OPCODE+1) ADD B LD (INST+1),A JP ENDSTMT ; S96F: CALL EVALREG LD A,(VAL) RLCA RLCA RLCA AND 38H LD B,A LD A,(OPCODE+1) ADD B LD (INST),A LD A,1 LD (LEN),A JP ENDSTMT ; S96H: LD A,0DDH LD (INST),A CALL EVALREG LD A,(VAL) CP 0FEH JP Z,OERROR ;ERROR JP S96K ; S96J: LD A,0FDH LD (INST),A CALL EVALREG LD A,(VAL) CP 0FFH JP Z,OERROR ;ERROR S96K: AND 0FEH CP 0FEH JP Z,S96M CP 7 JP NC,OERROR ;ERROR JP S96L ; S96M: LD A,4 LD (VAL),A S96L: LD A,(VAL) RLCA RLCA RLCA AND 38H LD B,A LD A,(OPCODE+1) ADD B LD (INST+1),A LD A,2 LD (LEN),A JP ENDSTMT TITLE Z80 Assembler - Instruction Class 7 - I/O instructions ; ; CLASS 7 - I/O INSTRUCTIONS ; S97: LD A,(OPCODE) CP 0D3H JP Z,S107 ;BRANCH IF OUT OPCODE CALL GNC CP 'A' JP NZ,S104 ;BRANCH IF NOT 8080 TYPE I/O LD HL,(PTR1) INC HL ;SKIP ',' LD A,(HL) CP '(' JP NZ,OERROR INC HL ;SKIP A CHAR INC HL LD A,(HL) CP ')' JP NZ,S98 DEC HL LD A,(HL) CP 'C' JP Z,S104 ;STILL NOT 8080 ; ; 8080 TYPE INPUT INSTRUCTION ; S98: CALL GNC CP ',' JP NZ,OERROR CALL GNC CP '(' JP NZ,OERROR CALL EVALNEW ;GET PORT ADDRESS CALL GNC ;BYPASS ) LD A,0DBH LD (INST),A LD A,(VAL) JP S113 ; ; IN r,(C) ; S104: CALL BACKUP CALL EVALREG ;GET REGISTER NAME S104B: LD A,0EDH LD (INST),A CALL GNC CALL GNC ;GET REGISTER CP 'C' JP NZ,OERROR CALL GNC CALL GNC LD A,(VAL) RLCA RLCA RLCA AND 38H ADD 40H JP S113 ; ; OUT INSTRUCTION ; S107: CALL GNC CP '(' JP NZ,OERROR CALL GNC CP 'C' JP NZ,S109 LD HL,(PTR1) LD A,(HL) CP ')' JP Z,S112 ; ; 8080 TYPE OUTPUT INSTRUCTION ; S109: CALL BACKUP CALL EVALNEW ;GET PORT NUMBER CALL GNC ;BYPASS ) CALL GNC CP 'A' JP NZ,OERROR CALL GNC LD A,0D3H LD (INST),A LD A,(VAL) JP S113 ; ; OUT (C),r ; S112: CALL GNC CALL GNC CP ',' JP NZ,OERROR CALL EVALREG LD A,0EDH LD (INST),A LD A,(VAL) RLCA RLCA RLCA AND 38H ADD 41H S113: LD (INST+1),A LD A,2 LD (LEN),A JP ENDSTMT TITLE Z80 Assembler - Instruction Class 8 - LD Instructions ; ; CLASS 8 - LD INSTRUCTIONS ; ; THE FOLLOWING ARE STRINGS WHICH ARE SPECIAL OPERAND FORMS ; LD1: DB 'A,I' LD2: DB 'A,R' LD3: DB 'I,A' LD4: DB 'R,A' LD5: DB 'SP,HL' LD6: DB 'SP,IX' LD7: DB 'SP,IY' ; S116: CALL GNC CALL BACKUP ;POINT TO OPERAND FIELD LD HL,(PTR1) ;SET UP POINTER TO OPERAND FIELD LD DE,LD1 ;A,I LD B,3 CALL S117 JP Z,S171 LD DE,LD2 ;A,R LD B,3 CALL S117 JP Z,S173 LD DE,LD3 ;I,A LD B,3 CALL S117 JP Z,S175 LD DE,LD4 ;R,A LD B,3 CALL S117 JP Z,S177 LD DE,LD5 ;SP,HL LD B,5 CALL CMPCHR JP Z,S179 LD DE,LD6 ;SP,IX LD B,5 CALL CMPCHR JP Z,S181 LD DE,LD7 ;SP,IY LD B,5 CALL CMPCHR JP Z,S183 ;PROCESS SPECIAL OPRNDS ; CALL GNC CP '(' JP Z,S186 ;BRANCH IF () FORM CALL BACKUP CALL EVALREG LD A,(UFLAG) ;VALID NAME ? OR A JP NZ,OERROR ;ERROR EXIT LD A,(EVFLGS) AND ULBL JP NZ,OERROR ;LABEL ILLEGAL HERE LD A,(EVFLGS) AND RPNAME JP NZ,S147 ;BRANCH IF RPAIR NAME ; ; SINGLE REGISTER OPERAND1 ; CALL GNC CP '(' JP Z,S130 ;BRANCH IF () OPERAND LD HL,(VAL) LD (SAVVAL),HL CP 27H JP Z,S116A CP '-' JP Z,S116A ;LEADING "-" ALLOWED CP '+' JP Z,S116A ;LEADING "+" ALLOWED CP '0' JP C,OERROR S116A: CALL BACKUP CALL EVALREG LD A,(EVFLGS) AND RNAME JP NZ,S128 ;> RNAME LD A,(SAVVAL) RLCA RLCA RLCA AND 38H ADD 6 LD (INST),A LD A,(VAL) LD (INST+1),A LD A,2 LD (LEN),A JP ENDSTMT ;IMMED OPERAND ; S117: CALL CMPCHR ;TEST FOR MATCH RET NZ ;NONE, EXIT PUSH BC ;SAVE REGS PUSH HL LD C,B ;FETCH LENGTH LD B,0H ;16 BITS ADD HL,BC ;MAKE PTR NEXT CHAR LD A,(HL) ;FETCH NEXT CHAR POP HL ;RECOVER REGS POP BC CP ' ' ;BLANK ? RET Z ;VALID MATCH CP CR ;END OF LINE ? RET Z ;VALID MATCH XOR A ;MAKE CERTAIN NON-ZERO .. INC A ;..SIGNALS NO MATCH RET ; S128: EQU $ ;PROCESS RNAME LD A,(SAVVAL) RLCA RLCA RLCA AND 38H LD B,A LD A,(VAL) ADD B ADD 40H ;FOR R-R TYPE INST LD (INST),A LD A,1 LD (LEN),A ;REG-REG OPERAND JP ENDSTMT ; S130: CALL GNC ;LOOK 1ST CHAR AFTER ( CALL GNC ;LOOK 2ND CHAR AFTER ( CP 'L' CALL Z,S131 ;CHECK IS HL ONLY JP Z,S138 CP 'X' LD (IXFLAG),A ;MARK AS INDEX CALL Z,S131 ;CHECK IX+ ONLY JP Z,S140 CALL S131 ;CHECK IY+ ONLY JP Z,S145 JP C,OERROR ;ERROR EXIT OR A JP NZ,S130A ;NOT A LABEL LD (IXFLAG),A ;LABEL, NOT INDEX S130A: CALL BACKUP CALL BACKUP CALL EVALREG LD A,(EVFLGS) AND RPNAME JP NZ,S136 ;BRANCH IF REG PAIR ; ; LD A,(nnnn) ; LD A,3AH LD (INST),A LD HL,(VAL) LD (INST+1),HL LD A,3 LD (LEN),A CALL GNC ;BYPASS ) JP ENDSTMT ; S131: PUSH HL ;SAVE REGS PUSH DE PUSH BC LD HL,(PTR1) ;FETCH TEXT PTR DEC HL ;STEP BACK TO 1ST CHAR AFTER ( DEC HL LD DE,LD01 ;PTR TO 'HL)' LD B,3 ;CHARS TO COMPARE COUNT CALL CMPCHR ;TEST JP Z,S133 ;FOUND LD DE,LD02 ;PTR TO IX+ LD B,3 ;COUNT CALL CMPCHR ;TEST JP Z,S133 ;FOUND LD DE,LD03 ;PTR TO IY+ LD B,3 ;COUNT CALL CMPCHR ;TEST JP Z,S133 ;FOUND LD DE,LD04 ;PTR TO IX- LD B,3 ;COUNT CALL CMPCHR ;TEST JP Z,S133 ;FOUND LD DE,LD05 ;PTR TO IY- LD B,3 ;COUNT CALL CMPCHR ;TEST JP Z,S133 ;FOUND LD DE,LD06 ;PTR TO IX) LD B,3 ;COUNT CALL CMPCHR ;TEST JP Z,S133 ;FOUND LD DE,LD07 ;PTR TO IY) LD B,3 ;COUNT CALL CMPCHR ;TEST JP Z,S133 ;FOUND INC HL ;RESTORE PTR INC HL LD A,(HL) ;FETCH NEXT CHAR CP ')' ;TERMINATOR? LD A,0 ;MARK AS POSSIBLE LABEL JP NZ,S133 ;POSS LABEL DEC HL ;PTR TO CHAR BEFORE ) LD A,(HL) AND 0FEH ;MASK FOR X OR Y CP 58H ;X OR Y ? LD A,0 ;MARK AS POSSIBLE LABEL JP NZ,S133 ;VALID REG PAIR XOR A INC A ;MARK AS NOT FOUND SCF ;MARK AS ERROR JP S132 ;INVALID ; S133: SCF ;CLEAR CARRY = NO ERROR CCF S132: POP BC ;RECOVER REGS POP DE POP HL RET ;DONE, Z=1 IF MATCHED ANY ONE ; ; LD A,(rp) ; S136: LD A,(VAL) RLCA RLCA RLCA AND 38H ADD 0AH LD (INST),A LD A,1 LD (LEN),A CALL GNC JP ENDSTMT ; S138: LD A,(VAL) RLCA RLCA RLCA AND 38H ADD 46H LD (INST),A LD A,1 LD (LEN),A CALL GNC CALL GNC ;BYPASS ) JP ENDSTMT ; ; INDEX REGISTER TYPE ; S140: LD A,0DDH LD (INST),A ; S140A: LD A,(VAL) RLCA RLCA RLCA AND 38H ADD 46H LD (INST+1),A XOR A LD (VAL),A LD A,(HL) CP ')' CALL NZ,EVALNEW LD A,(VAL) LD (INST+2),A LD A,3 LD (LEN),A CALL GNC ;BYPASS ) JP ENDSTMT ; S145: LD A,0FDH LD (INST),A JP S140A ; S147: CALL GNC CP '(' JP Z,S159 LD HL,(VAL) LD (SAVVAL),HL CP 27H JP Z,S147A CP '-' ;LEADING "-" ALLOWED JP Z,S147A CP '+' ;LEADING "+" ALLOWED JP Z,S147A CP '0' JP C,OERROR S147A: CALL BACKUP CALL EVAL LD A,(SAVVAL) CP 0FFH JP Z,S155 CP 0FEH JP Z,S157 LD A,(SAVVAL) RLCA RLCA RLCA AND 38H INC A LD (INST),A LD HL,(VAL) LD (INST+1),HL LD A,3 LD (LEN),A JP ENDSTMT ; S155: LD A,0DDH LD (INST),A S156: LD A,21H LD (INST+1),A LD A,4 LD (LEN),A LD HL,(VAL) LD (INST+2),HL JP ENDSTMT ; S157: LD A,0FDH LD (INST),A JP S156 ; ; LD rp,(label) ; S159: LD HL,(VAL) LD (SAVVAL),HL CALL EVALNEW ;GET LABEL LD A,(SAVVAL) CP 4 JP Z,S165 CP 0FFH JP Z,S167 CP 0FEH JP Z,S169 LD A,0EDH LD (INST),A LD A,(SAVVAL) RLCA RLCA RLCA AND 38H ADD 4BH LD (INST+1),A LD HL,(VAL) LD (INST+2),HL LD A,4 LD (LEN),A CALL GNC ;BYPASS ) JP ENDSTMT ; S165: LD A,2AH LD (INST),A LD HL,(VAL) LD (INST+1),HL LD A,3 LD (LEN),A CALL GNC ;BYPASS ) JP ENDSTMT ; S167: LD A,0DDH LD (INST),A S168: LD A,2AH LD (INST+1),A LD HL,(VAL) LD (INST+2),HL LD A,4 LD (LEN),A CALL GNC JP ENDSTMT ; S169: LD A,0FDH LD (INST),A JP S168 ; S171: LD HL,57EDH S171A: LD (INST),HL LD A,2 LD (LEN),A TSKIP1: LD HL,(PTR1) ADD B LD E,A LD D,0 ADD HL,DE LD (PTR1),HL JP ENDSTMT ; S173: LD HL,5FEDH JP S171A ; S175: LD HL,47EDH JP S171A ; S177: LD HL,4FEDH JP S171A ; S179: LD A,0F9H LD (INST),A LD A,1 LD (LEN),A LD A,2 JP TSKIP1 ; S181: LD HL,0F9DDH JP S171A ; S183: LD HL,0F9FDH JP S171A ; LD01: DB 'HL)' LD02: DB 'IX+' LD03: DB 'IY+' LD04: DB 'IX-' LD05: DB 'IY-' LD06: DB 'IX)' LD07: DB 'IY)' ; S186: CALL GNC ;1ST CHAR AFTER ( CALL GNC ;2ND CHAR AFTER ( CP 'L' CALL Z,S131 ;TEST HL ONLY JP Z,S204 ;PROCESS AS (HL) CP 'X' LD (IXFLAG),A ;MARK AS INDEX CALL Z,S131 ;TEST IX+ JP Z,S210A ;PROCESS AS (IX) CALL S131 ;TEST IY+ JP Z,S210B ;PROCESS AS (IY) JP C,OERROR ;ERROR EXIT OR A JP NZ,S186A ;NOT A LABEL LD (IXFLAG),A ;LABEL, NOT INDEX S186A: CALL BACKUP ;RESTORE TEXT PTR CALL BACKUP CALL EVALREG LD HL,(VAL) LD (SAVVAL),HL LD A,(EVFLGS) AND RPNAME JP NZ,S202 ;PROCESS AS REG PAIR CALL GNC ;BYPASS COMMA CALL EVALREG LD A,(EVFLGS) AND RPNAME JP NZ,S193 S187: LD A,32H LD (INST),A LD HL,(SAVVAL) LD (INST+1),HL LD A,3 LD (LEN),A JP ENDSTMT ; S193: LD A,(VAL) CP 4 JP Z,S196 CP 0FFH JP Z,S198 CP 0FEH JP Z,S200 LD A,0EDH LD (INST),A LD A,(VAL) RLCA RLCA RLCA AND 38H ADD 43H LD (INST+1),A LD HL,(SAVVAL) LD (INST+2),HL LD A,4 LD (LEN),A JP ENDSTMT ; S196: LD A,22H LD (INST),A LD HL,(SAVVAL) LD (INST+1),HL LD A,3 LD (LEN),A JP ENDSTMT ; S198: LD A,0DDH LD (INST),A S199: LD A,22H LD (INST+1),A LD HL,(SAVVAL) LD (INST+2),HL LD A,4 LD (LEN),A JP ENDSTMT ; S200: LD A,0FDH LD (INST),A JP S199 ; S202: CALL GNC ;BY-PASS COMMA CALL GNC ;FETCH REG CP 'A' ;A REG ONLY JP NZ,OERROR ;ERROR EXIT LD A,(VAL) RLCA RLCA RLCA AND 38H ADD 2 LD (INST),A LD A,1 LD (LEN),A CALL GNC JP ENDSTMT ; S204: CALL GNC CALL GNC CALL EVALREG LD A,(EVFLGS) AND RNAME JP NZ,S209 ; ; LD (HL),n ; LD A,36H LD (INST),A LD A,(VAL) LD (INST+1),A LD A,2 LD (LEN),A JP ENDSTMT ; ; LD (HL),r ; S209: LD A,(VAL) ADD 70H LD (INST),A LD A,1 LD (LEN),A JP ENDSTMT ; S210A: LD A,0DDH LD (INST),A JP S211 ; ; LD (IX),x ; S210B: LD A,0FDH LD (INST),A S211: XOR A LD (VAL),A LD (VAL+1),A LD A,(HL) CP ')' CALL NZ,EVALREG LD HL,(VAL) LD (SAVVAL),HL CALL GNC ;BYPASS COMMA CALL EVALREG LD A,(EVFLGS) AND RNAME JP NZ,S218 S212: LD A,36H LD (INST+1),A LD A,(SAVVAL) LD (INST+2),A LD A,(VAL) LD (INST+3),A LD A,4 LD (LEN),A JP ENDSTMT ; S218: LD A,(VAL) ADD 70H LD (INST+1),A LD A,(SAVVAL) LD (INST+2),A LD A,3 LD (LEN),A JP ENDSTMT TITLE Z80 Assembler - Instruction Class 9 - PUSH & POP ; ; CLASS 9 - PUSH POP ; S220: CALL EVALREG LD A,(VAL) CP 0FFH JP Z,S224 CP 0FEH JP Z,S226 LD A,(VAL) RLCA RLCA RLCA AND 38H LD B,A LD A,(OPCODE) ADD B LD (INST),A LD A,1 LD (LEN),A JP ENDSTMT ; S224: LD A,0DDH LD (INST),A S225: LD A,(OPCODE) ADD 20H LD (INST+1),A LD A,2 LD (LEN),A JP ENDSTMT ; S226: LD A,0FDH LD (INST),A JP S225 TITLE Z80 Assembler - Instruction Class 10 - Exchange Instructions ; ; CLASS 10 - EXCHANGE (EX) ; EXD1: DB 'DE,HL' EXD2: DB 'AF,AF''' EXD3: DB 'SP),HL' EXD4: DB 'SP),IY' EXD5: DB 'SP),IX' ; S228: CALL GNC CP '(' JP Z,S235 ;BRANCH IF () FORM LD HL,(PTR1) DEC HL ;PTR TO FIRST CHAR CP 'A' JP Z,S233 ;BRANCH IF EX AF,AF' LD DE,EXD1 LD B,5 CALL TSKIP LD A,0EBH S229: LD (INST),A LD A,1 LD (LEN),A JP ENDSTMT ; S233: LD DE,EXD2 LD B,6 CALL TSKIP LD A,8 JP S229 ; S235: LD HL,(PTR1) PUSH HL LD DE,5 ADD HL,DE LD (PTR1),HL CALL GNC POP HL CP 'L' JP Z,S239 CP 'Y' JP Z,S241 LD DE,EXD5 LD B,6 CALL TSKIP LD A,0DDH LD (INST),A S238: LD A,0E3H LD (INST+1),A LD A,2 LD (LEN),A JP ENDSTMT ; S239: LD DE,EXD3 LD B,6 CALL TSKIP LD A,0E3H JP S229 ; S241: LD DE,EXD4 LD B,6 CALL TSKIP LD A,0FDH LD (INST),A JP S238 ; TSKIP: CALL CMPCHR INC HL LD D,0H LD E,B ADD HL,DE LD (PTR1),HL RET Z ;VALID POP HL ;WASTE STACK JP OERROR ;ERROR TITLE Z80 Assembler - Instruction Class 11 - Returns ; ; CLASS 11 - RETURNS ; S243: CALL GNC CP LF JP Z,S248 ;BRANCH IF NO OPERAND CP CR JP Z,S248 CP ';' JP Z,S248 ;BRANCH IF NO OPERAND CALL BACKUP CALL EVALCND LD A,(VAL) RLCA RLCA RLCA AND 38H ADD 0C0H LD (INST),A LD A,1 LD (LEN),A JP ENDSTMT ; S248: LD A,(OPCODE) LD (INST),A LD A,1 LD (LEN),A JP ENDSTMT TITLE Z80 Assembler - Instruction Class 12 - Bit Manipulation ; ; CLASS 12 - BIT,SET,RES ; S250: CALL EVALNEW ;GET BIT NO LD HL,(VAL) LD (SAVVAL),HL ;SAVE BIT NUMBER CALL GNC CP '(' JP Z,S257 CALL BACKUP CALL EVALREG ;GET REGISTER LD A,0CBH LD (INST),A LD A,(SAVVAL) RLCA RLCA RLCA AND 38H LD B,A LD A,(OPCODE) ADD B LD B,A LD A,(VAL) ADD B LD (INST+1),A LD A,2 LD (LEN),A JP ENDSTMT ; S257: CALL GNC CALL GNC CP 'L' CALL Z,S131 ;CHECK ONLY HL JP Z,S266 ;PROCESS AS (HL) CP 'Y' LD (IXFLAG),A ;MARK AS INDEX CALL Z,S131 ;CHECK IY+ ONLY JP Z,S264 ;PROCESS AS (IY) CALL S131 ;CHECK IX+ ONLY JP NZ,OERROR ;ERROR EXIT LD A,0DDH ;PROCESS AS (IX) LD (INST),A S260: XOR A LD (VAL),A LD A,(HL) CP ')' JP Z,S261 CALL EVALNEW ;GET INDEX S261: LD A,0CBH LD (INST+1),A LD A,(VAL) LD (INST+2),A LD A,(SAVVAL) RLCA RLCA RLCA AND 38H ADD 6 LD B,A LD A,(OPCODE) ADD B LD (INST+3),A LD A,4 LD (LEN),A CALL GNC ;BYPASS ) JP ENDSTMT ; S264: LD A,0FDH LD (INST),A JP S260 ; S266: LD A,0CBH LD (INST),A LD A,(SAVVAL) RLCA RLCA RLCA AND 38H ADD 6 LD B,A LD A,(OPCODE) ADD B LD (INST+1),A LD A,2 LD (LEN),A CALL GNC CALL GNC ;BYPASS ) JP ENDSTMT TITLE Z80 Assembler - Instruction Class 13 - INC & DEC ; ; CLASS 13 - INC,DEC ; S268: CALL GNC CP '(' JP Z,S290 CALL BACKUP CALL EVALREG LD A,(EVFLGS) AND RNAME JP NZ,S280 LD A,(VAL) CP 0FFH JP Z,S275 CP 0FEH JP Z,S278 LD A,(VAL) RLCA RLCA RLCA AND 38H LD B,A LD A,(OPCODE+1) ADD B LD (INST),A LD A,1 LD (LEN),A JP ENDSTMT ; S275: LD A,0DDH LD (INST),A S276: LD A,(OPCODE+1) ADD 20H LD (INST+1),A LD A,2 LD (LEN),A JP ENDSTMT ; S278: LD A,0FDH LD (INST),A JP S276 ; S280: LD A,(VAL) RLCA RLCA RLCA AND 38H LD B,A LD A,(OPCODE) ADD B LD (INST),A LD A,1 LD (LEN),A JP ENDSTMT ; S290: CALL GNC CALL GNC CP 'L' CALL Z,S131 ;CHECK ONLY HL JP Z,S299 ;PROCESS AS (HL) CP 'Y' LD (IXFLAG),A ;MARK AS INDEX CALL Z,S131 ;CHECK IY+ ONLY JP Z,S297 ;PROCESS AS (IY) CALL S131 ;CHECK IX+ ONLY JP NZ,OERROR ;ERROR EXIT LD A,0DDH ;PROCESS AS (IX) LD (INST),A S293: XOR A LD (VAL),A LD A,(HL) CP ')' CALL NZ,EVALNEW ;GET INDEX LD A,(OPCODE) ADD 30H LD (INST+1),A LD A,(VAL) LD (INST+2),A LD A,3 LD (LEN),A CALL GNC ;BYPASS ) JP ENDSTMT ; S297: LD A,0FDH LD (INST),A JP S293 ; S299: LD A,(OPCODE) ADD 30H LD (INST),A LD A,1 LD (LEN),A CALL GNC CALL GNC ;BYPASS ) JP ENDSTMT TITLE Z80 Assembler - Instruction Class 14 - Pseudo-operators ; ; CLASS 14 - PSEUDO OPERATORS ; S301: LD A,(OPCODE) DEC A ADD A LD E,A LD D,0 LD HL,PSDTAB ADD HL,DE LD E,(HL) INC HL LD D,(HL) EX DE,HL JP (HL) ; PSDTAB: DW S302 ;EQU DW S305 ;DEFS,DS DW S308 ;DEFB,DB DW S317 ;DEFW,DW DW S320 ;END DW S323 ;ORG DW S324 ;FORM DW S330 ;IF DW S340 ;ELSE DW S350 ;ENDIF DW S360 ;LIST DW S370 ;TITLE ; ; EQU ; S302: CALL EVALNEW ;GET VALUE LD A,(PASSNO) ;TEST FOR PASS 2 OR A JP NZ,S302D ;DON'T ENTER AGAIN LD HL,(VAL) EX DE,HL LD HL,(SYMADR) LD (HL),E INC HL LD (HL),D DEC HL ;DECR PTR TO LAST IN LABEL S302C: DEC HL ;DECR LABEL PTR LD A,(HL) ;FETCH CHAR OR A ;TERMINATOR ? JP NZ,S302C ;MORE IN LABEL INC HL ;INCR PTR TO IDENTITY LD A,(HL) ;FETCH IDENTITY AND 0FH ;CLEAR LABEL FLAG OR 10H ;MARK AS EQUATE LABEL LD (HL),A ;SAVE BACK S302B: LD HL,(VAL) LD (EQUVAL),HL LD A,1 LD (EQUFLG),A JP ENDSTMT ; ; CHECK VALUE ON SECOND PASS TO SEE IF IT IS THE SAME ; S302D: LD HL,(SYMADR) ;GET ADDRESS OF LABEL VALUE LD E,(HL) ;GET VALUE INC HL LD D,(HL) ;INTO DE LD HL,(VAL) ;GET PASS 2 VALUE CALL CMPHD ;ARE THE THE SAME? JP Z,S302B ;OK IF SO LD HL,(VAL) ;AND PUT THE LATEST VALUE EX DE,HL ;INTO THE SYMBOL TABLE LD HL,(SYMADR) ;GET SYMBOL ADDRESS LD (HL),E ;LOW BYTE INC HL LD (HL),D ;HIGH BYTE JP S302B ;AND EXIT ; ; DS,DEFS ; S305: CALL EVALNEW LD HL,(VAL) LD (LEN2),HL JP ENDSTMT ; ; DB,DEFB ; S308: CALL GNC LD DE,INST LD HL,(PTR1) S308A: CP 27H JP NZ,S308C ;BRANCH IF NO QUOTE INC HL ;INCR PTR LD A,(HL) ;FETCH NEXT CHAR CP 27H ;TEST IF SINGLE CHAR IN QOUTES DEC HL ;STEP PTR BACK JP NZ,S313 ;PROCESS AS STRING IN QUOTES S308C: DEC HL ;PTR TO PREV CHAR LD (PTR1),HL ;RESTORE NEW PTR PUSH DE ;SAVE PTR TO INST STORE CALL EVALNEW LD A,(PCEVAL) ;WAS THE PC INVOLVED? OR A ;NZ=YES JP Z,S308F ;SKIP IF NOT LD A,(LEN) ;GET LENGTH SO FAR LD E,A ;PUT INTO E LD D,0 LD HL,(VAL) ;GET THE VALUE ADD HL,DE ;ADD IN THE EXTRA BYTES LD (VAL),HL ;TO CORRECT THE PC VALUE S308F: POP DE ;RECOVER PTR LD A,(VAL) LD (DE),A ;SAVE VALUE IN STORE INC DE ;INCR PTR LD A,(VAL+1) ;GET HIGH BYTE OR A ;MUST BE 00 OR FF FOR DB JP Z,S308G ;SKIP IF OK INC A JP Z,S308G ;SKIP IF NEGATIVE VALUE LD A,'V' ;ELSE SET VALUE ERROR LD (ERRFLG),A S308G: LD A,(LEN) ;FETCH LENGTH COUNT INC A ;INCR COUNT LD (LEN),A ;SAVE NEW COUNT LD HL,(PTR1) ;RECOVER RECORD PTR DEC HL ;SET PTR BACK S308B: LD A,(HL) ;FETCH NEXT CHAR FROM RECORD CP TAB JP NZ,S308D CALL TABSKP ;SKIP OVER TABS JP S308B ; S308D: CP ' ' JP NZ,S308E CALL SPCSKP ;SKIP SPACES JP S308B ; S308E: LD (PTR1),HL CP CR ;END OF RECORD ? JP Z,S309 ;DONE CP ';' ;COMMENT SEPARATOR ? JP Z,S309 ;IGNORE REST OF LINE CP ',' ;SEPARATOR ? INC HL ;PTR TO NEXT CHAR LD A,(HL) ;FETCH NEXT CHAR LD (PTR1),HL ;SAVE RECORD PTR INC HL ;INCR PTR FOR STRING JP Z,S308A ;PROCESS NEXT JP DEBEND ;EXPRESSION ERROR ; S309: CALL GNC JP ENDSTMT ; S313: LD A,(LEN) INC A LD (LEN),A LD A,(HL) LD (DE),A INC DE INC HL LD A,(HL) CP CR JP Z,S308B ;END WITHOUT ANOTHER QUOTE? CP 27H JP NZ,S313 INC HL ;INCR RECORD PTR LD A,(HL) CP 27H JP Z,S313 ;DOUBLE QUOTES JP S308B ;PROCESS TERMINATOR/SEPARATORS ; DEBEND: LD A,'E' ;LOAD EXPRESSION ERROR LD (ERRFLG),A ;SET FLAG JP ENDSTMT ;DONE ; TABSKP: INC HL ;STEP PTR PAST TAB LD A,(HL) ;FETCH NEXT CHAR CP TAB ;H.TAB ? JP Z,TABSKP ;LOOP LD (PTR1),HL ;SAVE NEW PTR RET ;PROCESS NEXT ; SPCSKP: INC HL ;STEP PTR PAST SPACE LD A,(HL) ;FETCH NEXT CHAR CP 20H ;ASCII SPACE ? JP Z,SPCSKP ;LOOP LD (PTR1),HL ;SAVE NEW PTR RET ;PROCESS NEXT ; ; DW,DEFW ; S317: CALL GNC LD DE,INST LD HL,(PTR1) S317A: CP 27H JP NZ,S317C ;BRANCH IF NO QUOTE INC HL ;INCR PTR LD A,(HL) ;FETCH NEXT CHAR CP 27H ;TEST IF SINGLE CHAR IN QUOTES DEC HL ;STEP PTR BACK JP NZ,S318 ;PROCESS AS STRING IN QUOTES S317C: DEC HL ;PTR TO PREV CHAR LD (PTR1),HL ;RESTORE NEW PTR PUSH DE ;SAVE PTR TO INST STORE CALL EVALNEW LD A,(PCEVAL) ;WAS THE PC INVOLVED? OR A ;NZ=YES JP Z,S317F ;SKIP IF NOT LD A,(LEN) ;GET LENGTH SO FAR LD E,A ;PUT INTO E LD D,0 LD HL,(VAL) ;GET THE VALUE ADD HL,DE ;ADD IN THE EXTRA BYTES LD (VAL),HL ;TO CORRECT THE PC VALUE S317F: POP DE ;RECOVER PTR LD A,(VAL) ;GET LOW BYTE LD (DE),A ;SAVE VALUE IN STORE INC DE ;INCR PTR LD A,(VAL+1) ;GET HIGH BYTE LD (DE),A ;SAVE VALUE IN STORE INC DE ;INCR PTR LD A,(LEN) ;FETCH LENGTH COUNT INC A ;INCR COUNT INC A ; TWICE LD (LEN),A ;SAVE NEW COUNT LD HL,(PTR1) ;RECOVER RECORD PTR DEC HL ;SET PTR BACK S317B: LD A,(HL) ;FETCH NEXT CHAR FROM RECORD CP TAB JP NZ,S317D CALL TABSKP ;SKIP OVER TABS JP S317B ; S317D: CP ' ' JP NZ,S317E CALL SPCSKP ;SKIP SPACES JP S317B ; S317E: LD (PTR1),HL CP CR ;END OF RECORD ? JP Z,S309 ;DONE CP ';' ;COMMENT SEPARATOR ? JP Z,S309 ;IGNORE REST OF LINE CP ',' ;SEPARATOR ? INC HL ;PTR TO NEXT CHAR LD A,(HL) ;FETCH NEXT CHAR LD (PTR1),HL ;SAVE RECORD PTR INC HL ;INCR PTR FOR STRING JP Z,S317A ;PROCESS NEXT JP DEBEND ;EXPRESSION ERROR ; S318: LD C,0 ;CHAR COUNTER S318A: INC C ;CHARS IN THIS STRING LD A,(LEN) INC A LD (LEN),A LD A,(HL) LD (DE),A INC DE INC HL LD A,(HL) CP CR JP Z,S317B ;END WITHOUT ANOTHER QUOTE? CP 27H JP NZ,S318A INC HL ;INCR RECORD PTR LD A,(HL) CP 27H JP Z,S318A ;DOUBLE QUOTES LD A,C ;GET LENGTH OF STRING AND 1 ;IS IT EVEN? JP Z,S317B ;FINISHED IF SO XOR A ;LD UPPER BYTE WITH 0 LD (DE),A ;TO MAKE A WORD LD A,(LEN) INC A LD (LEN),A JP S317B ;PROCESS TERMINATOR/SEPARATORS ; ; END ; S320: CALL EVALNEW LD HL,(VAL) LD A,H ;TEST IF NO VALUE OR L LD HL,(PC) ;FETCH PC IN CASE JP Z,S320A ;NO VALUE LD HL,(VAL) ;FETCH REAL VALUE S320A: LD (ENDADR),HL LD (EQUVAL),HL S321: LD A,1 LD (EQUFLG),A LD (EFLG),A JP ENDSTMT ; ; ORG ; S323: CALL EVALNEW LD HL,(VAL) LD (PC),HL LD (EQUVAL),HL LD A,1 LD (EQUFLG),A JP ENDSTMT ; ; FORM, PAGE, EJECT ; S324: LD A,MAXLNE LD (CURLNE),A ;CAUSE HOF ON RECORD AFTER FORM/PAGE/EJECT LD (NOLIST),A ;SUPPRESS LISTING OF FORM, PAGE, EJECT JP ENDSTMT ; ; IF ; S330: CALL EVALNEW ;EVALUATE THE EXPRESSION LD HL,(VAL) ;GET THE VALUE LD A,L ;TEST FOR ZERO OR H ;ZERO=FALSE JP Z,S331 ;NON ZERO=TRUE LD A,0FFH ;MAKE VALUE 0FFH OR 00H S331: LD HL,(CONDSP) ;GET CONDITIONAL STACK POINTER AND (HL) ;INCLUDE CURRENT STATE LD (SAVVAL),A ;AND SAVE FOR LATER AS NEW STATE LD A,(CLEVEL) ;GET THE STACK DEPTH CP 8 ;MAXIMUM 8 LEVELS JP NC,S332 ;ERROR IF ALREADY 8 DEEP INC A ;INC TO NEXT DEPTH LD (CLEVEL),A ;AND STORE DEC HL ;PUSH STACK LD (CONDSP),HL ;SAVE NEW SP LD A,(SAVVAL) ;GET NEW STATE LD (HL),A ;PUT VALUE IN STACK LD A,0FFH ;SUPPRESS IF,ELSE,ENDIF LD (IFLIST),A ; IF "NOCOND" JP ENDSTMT ;& FINISH OFF ; S332: LD A,'A' ;STACK OV'FLOW S335: LD (ERRFLG),A JP ENDSTMT ;FINISH OFF ; ; ELSE ; S340: LD A,(CLEVEL) ;GET STACK DEPTH OR A ;TEST IF EMPTY LD A,'C' ;NO IF STMT ERROR JP Z,S335 LD HL,(CONDSP) ;GET STACK POINTER LD A,(HL) ;GET CURRENT STATE CPL ;AND FLIP IT INC HL ;POINT TO PREVIOUS STATE AND (HL) ;INCLUDE IN TEST DEC HL ;POINT TO CURRENT STATE AGAIN LD (HL),A ;AND SAVE NEW STATE LD A,(CLEVEL) ;GET STACK DEPTH LD A,0FFH ;SUPPRESS IF,ELSE,ENDIF LD (IFLIST),A ; IF "NOCOND" JP ENDSTMT ; ; ENDIF ; S350: LD A,(CLEVEL) ;GET STACK DEPTH OR A ;TEST IF EMPTY JP Z,S352 DEC A ;REDUCE DEPTH LD (CLEVEL),A ;SAVE NEW DEPTH LD HL,(CONDSP) ;GET CONDITIONAL STACK POINTER INC HL ;POP A STATE LD (CONDSP),HL ;AND PUT BACK LD A,0FFH ;SUPPRESS IF,ELSE,ENDIF LD (IFLIST),A ; IF "NOCOND" JP ENDSTMT ;FINISH OFF ; S352: LD A,'B' ;STACK UNDERFLOW JP S335 ; CONDSP: DW CNDSTK DS 10 CNDSTK: DB 0FFH ;WE ALWAYS START TRUE CLEVEL: DB 0 ;COND STK LEVEL ; ; LIST CONTROL OPERATOR ; ; ON enable listing ; OFF disable listing ; NOCOND don't display FALSE conditional code ; COND display FALSE conditional code ; NOTABS don't use tabs in output ; TABS use tabs in output ; NOSYMBOL don't produce symbols in output ; SYMBOL produce symbols in output ; S360: LD HL,LSTOPS ;LIST OPERANDS LD (SYMPT),HL ;SO NOT CONFLICT WITH REG NAMES CALL BACKUP CALL EVAL LD HL,SYM LD (SYMPT),HL ;PUT IT BACK THE WAY IT WAS LD A,(UFLAG) ;VALID LABEL ? OR A JP NZ,OERROR ;EARLY ERROR EXIT LD HL,(VAL) LD A,H ;HI BYTE MUST BE 0 OR A JP NZ,OERROR OR L ;LOW BYTE MUST NOT! JP Z,OERROR CP LSTNO+1 JP NC,OERROR ;OUT OF RANGE LD DE,LSTTAB DEC HL ;START AT ZERO ADD HL,HL ;*2 FOR WORD OFFSET ADD HL,DE ;INDEX INTO TABLE LD E,(HL) INC HL LD D,(HL) EX DE,HL JP (HL) ; LSTOPS: DB 6,'ON',1,0,0 DB 7,'OFF',2,0,0 DB 8,'COND',3,0,0 DB 10,'NOCOND',4,0,0 DB 8,'TABS',5,0,0 DB 10,'NOTABS',6,0,0 DB 10,'SYMBOL',7,0,0 DB 12,'NOSYMBOL',8,0,0 DB 0 ; LSTTAB: DW LST10 ;ON DW LST20 ;OFF DW LST30 ;COND DW LST40 ;NOCOND DW LST50 ;TABS DW LST60 ;NOTABS DW LST70 ;SYMBOL DW LST80 ;NOSYMBOL LSTNO: EQU $-LSTTAB ; ; ON ; LST10: LD A,0FFH JP LST21 ; ; OFF ; LST20: XOR A LST21: LD (LSTFLG),A JP ENDSTMT ; ; COND ; LST30: LD A,0FFH JP LST41 ; ; NOCOND ; LST40: XOR A LST41: LD (CNDFLG),A JP ENDSTMT ; ; TABS ; LST50: LD A,0FFH JP LST61 ; ; NOTABS ; LST60: XOR A LST61: LD (TABS),A JP ENDSTMT ; ; SYMBOL ; LST70: LD A,0FFH JP LST81 ; ; NOSYMBOL ; LST80: XOR A LST81: LD (SYMBLS),A JP ENDSTMT ; ; TITLE ; S370: CALL GNC ;GET NEXT NON BLANK LD HL,(PTR1) ;GET REC POINTER DEC HL ;BACK UP ONE CHAR EX DE,HL ;DE=REC POINTER LD HL,TITLEB ;POINT TO TITLE BUFFER CP CR ;JUST CR? JP Z,S375 ;FINISH IF SO CP LF ;JUST LF? LD A,CR ;LOAD CR IN CASE JP Z,S375 ;FINISH IF SO LD B,80 ;MAXIMUM COUNT S372: LD A,(DE) ;GET A CHAR CP CR ;END OF LINE JP Z,S374 ;EXIT IF SO LD (HL),A ;STORE CHARACTER INC HL ;UPDATE BUFFER POINTER INC DE ;UPDATE REC POINTER DEC B ;COUNT DOWN CHARS JP NZ,S372 S374: LD (HL),CR ;TERMINATE INC HL ; WITH CR,LF,LF LD A,LF+80H S375: LD (HL),A INC HL LD (HL),LF EX DE,HL ;HL=REC POINTER INC HL LD (PTR1),HL ;UPDATE REC POINTER LD A,MAXLNE ;CAUSE HOF ON RECORD AFTER TITLE LD (CURLNE),A LD (NOLIST),A ;SUPPRESS LISTING OF 'TITLE' JP ENDSTMT ; OERROR: LD A,'O' LD (ERRFLG),A TITLE Z80 Assembler - End of statement processing ; ; END OF STATEMENT PROCESSING ; ENDSTMT:LD HL,(CONDSP) ;POINT TO CONDITIONAL STACK LD A,(HL) ;GET CURRENT STATE OR A ;TRUE/FALSE? JP Z,S401A ;DON'T CHECK TERMINATOR IF FALSE LD HL,(PTR1) ;CHECK VALID TERMINATORS DEC HL ;STEP BACK TO LAST CHAR LD A,(HL) S400A: CP ' ' JP Z,S400 CP TAB JP Z,S400 CP ';' JP Z,S401A CP CR JP Z,S401A CP LF JP Z,S401A JP S401 ;PROBABLE ERROR ; S400: CALL GNC JP S400A ;SKIP TABS/SPACES ; S401: LD A,(ERRFLG) CP ' ' JP NZ,S401A ;ERROR ALREADY, LEAVE IT LD A,'O' LD (ERRFLG),A ; S401A: LD A,(PASSNO) OR A JP Z,S417 ;DONT PRINT DURING PASS 1 ; ; NOW PROCESS THE LISTING OUTPUT IF REQUIRED ; LD A,(UFLAG) ;UNDEFINED SYMBOL OR A JP Z,S401B ;SKIP IF NOT LD A,'U' ;SET UP UNDEFINED ERROR LD (ERRFLG),A XOR A LD (UFLAG),A S401B: LD A,(ERRFLG) ;HAVE WE AN ERROR ON THIS LINE? CP ' ' JP NZ,S402 ;ALWAYS PRINT ERRORS ; ; LFLAG: COPY OF LIST OPTION FROM COMMAND LINE ; LD A,(LFLAG) ;DO WE NEED TO PRINT THIS LINE? CP 'Z' JP Z,S412 ;DON'T PRINT IF USER ASKED US NOT TO ; ; LSTFLG: TRUE IF WE WANT A LISTING ; LD A,(LSTFLG) ;HAS THE LISTING BEEN TURNED OFF? OR A ;Z=YES JP Z,S412 ;SKIP IF LISTING TURNED OFF ; ; NOLIST: TRUE TO AVOID LISTING TITLE, FORM, PAGE, EJECT ; LD A,(NOLIST) ;IS THIS A TITLE, FORM, OR A ; PAGE OR EJECT PSEUDO-OP? JP NZ,S412 ;WE DON'T PRINT THOSE ; LD HL,(CONDSP) ;GET CONDITIONAL STATE LD A,(HL) ;GET STATE OR A ;NZ=TRUE JP Z,S401C ;SKIP IF NOT TRUE ; ; IFLIST: TRUE TO SUPPRESS LISTING ON IF, ELSE, ENDIF ; WHEN "LIST NOCOND" IS CURRENT. ; LD A,(IFLIST) ;IF, ELSE, ENDIF PSEUDO-OP? OR A ;NZ=> IF,ELSE OR ENDIF JP Z,S402 ;DONT PRINT THESE IF "LIST NOCOND" ; ; OK, THE CONDITIONAL STATE IS FALSE, DO WE WANT TO LIST ; THE FALSE INSTRUCTIONS? ; CNDFLG: TRUE IF WE WANT TO LIST FALSE CONDITIONALS ; S401C: LD A,(CNDFLG) ;FF=PRINT CONDS OR A ;00=DON'T PRINT CONDS JP Z,S412 ;DON'T PRINT IF 'LIST NOCOND' ;ALSO MEANS IF,ELSE,ENDIF TOO ; ; PRINT THE LINE ; S402: LD A,(ERRFLG) CP ' ' ;TEST FOR ERROR LD HL,ERMSGB+3 CALL NZ,INCNUM ;INCR COUNT S402A: LD HL,HDRBUF LD DE,HDRBUF+1 LD BC,15 LD (HL),' ' IF Z80 LDIR ELSE CALL LDIR ENDIF LD A,(EQUFLG) OR A LD A,0 LD (EQUFLG),A ;CLEAR EQUATE FLAG JP NZ,S410 ;BRANCH IF TO USE EQUVAL ; LD HL,(PC) EX DE,HL LD HL,HDRBUF LD A,D CALL CNV2HX LD A,E CALL CNV2HX INC HL LD DE,INST LD C,4 LD A,(LEN) LD B,A OR A JP Z,S408 ;BRANCH IF LENGTH IF ZERO S406: LD A,(DE) CALL CNV2HX INC DE DEC B JP Z,S408 DEC C JP NZ,S406 S408: JP S410A ; S410: LD HL,(EQUVAL) EX DE,HL LD HL,HDRBUF LD A,D CALL CNV2HX LD A,E CALL CNV2HX INC HL LD (HL),'=' S410A: CALL PLINE ;WRITE AND PAGE ; LD A,(ERRFLG) ;ANY ERRORS? CP ' ' ;SPACE MEANS NO JP Z,S412 ;SO NOTHING ELSE TO PRINT ; ; PRINT EXTENDED ERROR MESSAGE ; LD HL,ERRH1 ;MOVE SOME STARS TO LD DE,HDRBUF ;THE PRINT BUFFER LD BC,ERRH1L ;TO MAKE ERRORS IF Z80 LDIR ;CONSPICUOUS ELSE CALL LDIR ;CONSPICUOUS ENDIF ; PUSH DE ;SAVE BUFFER POINTER LD A,(ERRFLG) ;GET ERROR CHARACTER SUB 'A' ;REMOVE ASCII OFFSET CP 'Z'-'A'+1 ;IN RANGE? JP C,S410B ;SKIP IF SO LD A,'X'-'A' ;ELSE GENERATE AN ERROR! S410B: ADD A,A ;EACH ENTRY IS 2 BYTES LD E,A ;PUT INDEX INTO DE LD D,0 LD HL,ERRTAB ;START OF ERROR TABLE ADD HL,DE ;POINT TO MSG ADDRESS LD E,(HL) ;GET ADDRESS OF ERROR MSG INC HL LD D,(HL) POP HL ;GET REC PTR BACK ERRMLP: LD A,(DE) ;GET A CHAR FROM THE MSG OR A JP Z,ERRMDN LD (HL),A ;STORE IN PRINT BUFFER INC HL INC DE JP ERRMLP ; ERRMDN: EX DE,HL ;DE POINTS TO OUTPUT BUFFER LD HL,ERRH2 ;SOME MORE STARS LD BC,ERRH2L IF Z80 LDIR ELSE CALL LDIR ENDIF CALL PLINE ; S412: LD A,(LEN) OR A JP Z,S417 LD B,A LD DE,INST LD HL,(PC) LD (TEMP),HL S414: PUSH BC LD A,(DE) LD HL,(TEMP) CALL WOBJ POP BC INC DE LD HL,(TEMP) INC HL LD (TEMP),HL DEC B JP NZ,S414 ; S417: LD HL,(PC) LD A,(LEN) LD C,A LD B,0 ADD HL,BC EX DE,HL LD HL,(LEN2) ADD HL,DE LD (PC),HL LD A,(EFLG) OR A JP Z,NEXT ;GO PROCESS NEXT RECORD LD A,(PASSNO) CPL LD (PASSNO),A OR A JP Z,ENDIT ;EXIT IF FINISHED LD HL,FCB1+12 LD (HL),0 LD DE,FCB1 CALL OPNFIL LD HL,FCB1+32 LD (HL),0 LD HL,INBUF+1024 LD (IBP),HL XOR A LD (EFLG),A ;RESET END OF RECORD FLAG LD (OBJCNT),A ;CLEAR OBJECT COUNT LD HL,0 ;HL=0 LD (PC),HL ;RESET PC LD (OBJADR),HL LD HL,TITLEB ;CLEAN OUT TITLE BUFFER LD (HL),CR INC HL LD (HL),LF LD A,MAXLNE ;FORCE HEADER AT BEGINNING OF LISTING LD (CURLNE),A S417A: LD DE,0FFFFH JP NEXT ;PROCESS ANOTHER RECORD ; ENDIT: LD A,(HFLAG) CP 'Z' JP Z,ENDITX ;BRANCH IF NO HEX FILE LD A,(OBJCNT) OR A JP Z,ENDIT1 ;BRANCH IF NO OBJ TO WRITE CALL WREC ;ELSE WRITE FINAL RECORD ENDIT1: LD HL,EOFREC LD B,13 ENDIT2: LD A,(HL) CALL WNB2 INC HL DEC B JP NZ,ENDIT2 LD B,255 ;FLUSH BUFFER ENDIT3: LD A,1AH CALL WNB2 DEC B JP NZ,ENDIT3 LD DE,FCB2 CALL CLSFIL ; ENDITX: LD DE,HDRBUF ;PTR TO OUTPUT BUFFER LD HL,ERMSG ;PTR TO MESSAGE LD BC,ERMSGL ;LOAD ERROR MESSAGE COUNT LD A,C ;SET ERRORS FLAG TO FORCE PRINTING LD (ERRFLG),A IF Z80 LDIR ;SHIFT TO OP BUFFER ELSE CALL LDIR ;SHIFT TO OP BUFFER ENDIF CALL PLINE ;PRINT ERRORS LD A,' ' ;CLEAR ERROR FLAG LD (ERRFLG),A ; LD HL,(PC) EX DE,HL LD HL,PCMSGA LD A,D CALL CNV2HX LD A,E CALL CNV2HX LD HL,PCMSG LD DE,HDRBUF LD BC,PCMSGL ;CHAR COUNT IF Z80 LDIR ;TO OP BUFFER ELSE CALL LDIR ;TO OP BUFFER ENDIF CALL PLINE ; LD A,(LFLAG) ;DO WE NEED TO PRINT ANY SYMBOLS CP 'Z' JP Z,ENDIT4 ;DON'T PRINT IF USER ASKED US NOT TO ; LD A,(SYMBLS) ;ARE SYMBOLS TO BE GENERATED? OR A JP Z,ENDIT4 ;SKIP IF NOT ; LD A,(SYMFLG) ;WERE ANY SYMBOLS IN THE PROGRAM OR A CALL NZ,SYMBOL ;DO IT IF SO ; ENDIT4: LD HL,ENDMSG ;SIGN OFF CALL CLINE ; ENDITP: LD A,(LFLAG) CP 'O' ;FILE ASKED FOR ? JP NC,ENDALL ;BRANCH, NO FILE TO CLOSE LD B,255 ENDIT5: LD A,1AH CALL WNB DEC B JP NZ,ENDIT5 LD DE,FCB3 CALL CLSFIL ENDALL: JP BOOT ; ERMSG: DB CR,LF+80H ERMSGA: DB 'Errors ' ERMSGB: DB ' 0',CR,LF ERMSGL: EQU $-ERMSG ; PCMSG: DB CR,LF+80H,'Next address: ' PCMSGA: DB 'XXXXH',CR,LF PCMSGL: EQU $-PCMSG ; ENDMSG: DB CR,LF+80H,'Finished',CR,LF+80H,LF ; EOFREC: DB ':0000000000',CR,LF ; ; WOBJ - WRITE OBJECT BYTE ROUTINE ; ; INPUT BYTE IN A REGISTER ; OBJSIZ: EQU 28 ; WOBJ: LD B,A ;SAVE INPUT LD A,(HFLAG) CP 'Z' RET Z ;RETURN IF NO HEX FILE LD A,B ;GET INPUT CHARACTER BACK PUSH DE PUSH AF PUSH HL LD HL,(OBJADR) LD DE,0FFFFH CALL CMPHD POP HL JP Z,WOBJ0 ;BRANCH IF FIRST TIME THROUGH EX DE,HL LD HL,(OLDADR) INC HL CALL CMPHD JP Z,WOBJ1 ;BRA IF NEXT SEQ BYT CALL WREC WOBJ0: EX DE,HL LD (OBJADR),HL EX DE,HL XOR A LD (OBJCNT),A WOBJ1: EX DE,HL LD (OLDADR),HL LD A,(OBJCNT) CP OBJSIZ JP C,WOBJ2 ;BRANCH IF NOT TIME TO WRITE CALL WREC LD HL,(OBJADR) LD DE,OBJSIZ ADD HL,DE LD (OBJADR),HL ;ADVANCE ADDR XOR A LD (OBJCNT),A WOBJ2: INC A LD (OBJCNT),A ;UPDATE COUNT DEC A LD E,A LD D,0 LD HL,OBJ ADD HL,DE WOBJ3: POP AF LD (HL),A POP DE RET ; ; WREC - FORMAT AND WRITE HEX RECORD ; WREC: PUSH HL PUSH DE LD HL,(OLDADR) LD DE,0FFFFH CALL CMPHD JP Z,WREC3 ;BRANCH IF FIRST TIME LD A,(OBJCNT) LD C,A LD B,A ;SET CHKSUM AND COUNT LD HL,REC LD (HL),':' INC HL LD A,B CALL CNV2HX LD A,(OBJADR+1) ADD C LD C,A LD A,(OBJADR+1) CALL CNV2HX LD A,(OBJADR) ADD C LD C,A LD A,(OBJADR) CALL CNV2HX XOR A CALL CNV2HX LD DE,OBJ WREC1: LD A,(DE) ADD C LD C,A LD A,(DE) CALL CNV2HX INC DE DEC B JP NZ,WREC1 LD A,C CPL INC A CALL CNV2HX LD (HL),CR INC HL LD (HL),LF ;PUT CRLF ON END LD HL,REC ;WRITE OUT RECORD WREC2: LD A,(HL) CALL WNB2 LD A,(HL) CP LF INC HL JP NZ,WREC2 ;GO UNTIL LF WREC3: POP DE POP HL RET ; ; PLINE ROUTINE - WRITE AND PAGE ; PLINE: LD A,(CURLNE) CP MAXLNE JP C,PLINE0 ;BRANCH IF LESS ; ; HEADERS ARE REQUIRED IF WE GENERATE A LISTING ; TO ANY DESTINATION. SUPPRESSED IF 'Z' OPTION. ; LD A,(LFLAG) ;GET LIST FLAG CP 'Z' ;Z=NO PRINT JP Z,PLINE0 ;SKIP HEADING ; LD HL,HOFPG+3 ;INCREMENT PAGE NUMBER CALL INCNUM LD HL,HOFMSG ;PRINT IT CALL PLINE1 ;(DON'T INC LINE NO, NO TABS) LD HL,TITLEB ;PRINT TITLE CALL PLINE1 XOR A ; PLINE0: INC A LD (CURLNE),A LD HL,HDRBUF ;POINT TO BUFFER LD A,(TABS) ;DO WE WANT TO USE TABS? OR A CALL NZ,SETTABS ;DO SO IF NON ZERO ; PLINE1: LD A,(LFLAG) ;GET LIST FLAG CP 'Z' ;Z=NO PRINT, ERRORS TO CONSOLE JP Z,CLINE ;SKIP PRINT, BUT SEND ERRORS ; CP 'X' ;LIST TO CONSOLE ONLY JP Z,CLINE ; CP 'Y' ;ERRORS TO PRINTER ? JP NZ,PLINE2 PUSH AF ;SAVE LIST FLAG PUSH HL ;SAVE PTR LINE BUFFER CALL CLINE ;LISTING TO CONSOLE POP HL ;RECOVER LINE BUFF PTR POP AF ;GET LIST FLAG LD A,(ERRFLG) ;FETCH ERROR FLAG CP ' ' ;BLANK ? JP NZ,WLINE ;ERRORS TO PRINTER RET ;ELSE DONE ; PLINE2: LD A,(ERRFLG) ;FETCH ERROR FLAG CP ' ' ;BLANK ? PUSH HL ;SAVE PTR LINE BUFFER CALL NZ,CLINE ;LISTING TO CONSOLE POP HL ;RECOVER LINE BUFF PTR ; PLINE3: LD A,(LFLAG) ;GET LIST FLAG CP 'P' ;LISTING TO PRINTER ? JP Z,WLINE ;LISTING TO PRINTER ;ELSE LISTING TO DISC .PRN FILE ; PRNOPT: PUSH HL ;SAVE LINE BUFFER PTR PRNOP: EX DE,HL ;LINE BUFFER TO DE LD A,(DE) ;FETCH CHAR FROM LINE BUFFER CALL WNB ;WRITE CHAR TO PRINT BUFFER CP LF ;WAS CHAR TERMINATOR ? EX DE,HL ;REPLACE PTRS INC HL ;INCR LINE PTR JP NZ,PRNOP ;LOOP, MORE ON LINE POP HL ;RECOVER BUFFER PTR RET ;DONE ; ; SETTAB CONVERTS SPACES IN A LINE TO TABS. ; CALLED PRIOR TO WRITING A LINE TO DISK, TO SAVE ; DISK SPACE. ; ; INPUT HL = LINE TO CONVERT. ; OUTPUT HL = CONVERTED LINE (CONVERTED IN PLACE) ; SETTABS:PUSH HL ;SAVE BUFF ADDR ; ; SETTABS PASS 1: STORES TABS EVERY 8 COLUMNS, PROVIDING ; THERE IS A SPACE IN THE COLUMN. ; LD B,1 ;SET COLUMN STTBL: LD A,(HL) ;GET CHAR CP CR ;END? JP Z,STTB2 ;YES, TO PASS 2 LD A,B ;AT A.. AND 7 ;..TAB STOP? JP NZ,STTNT ;NOT TIME FOR TAB LD A,(HL) ;STORE TAB IF.. CP ' ' ;..IT'S A SPACE JP NZ,STTNT ;..NOT A SPACE LD (HL),TAB ;OVERLAY SPACE STTNT: INC HL ;TO NEXT CHAR INC B ;BUMP COLUMN JP STTBL ;LOOP ; ; SETTABS PASS 2: BACKS UP THRU THE LINE STORING 00H ; IN EVERY BYTE PRECEDING A TAB. ; STTB2: DEC B ;BACK UP COL # JP Z,STTB3 ;DONE? DEC HL ;BACK UP COL ADDR LD A,(HL) ;GET CHAR CP TAB JP Z,STTB2T ;GOT A TAB OR A ;GET A NULL? JP NZ,STTB2 ;NO, LOOP STTB2T: DEC B ;BACK UP COL JP Z,STTB3 ;TO PASS 3 IF DONE DEC HL ;BACK UP COL LD A,(HL) ;GET CHAR CP TAB ;IF TAB SKIP.. JP Z,STTB2T ;..BACK OVER IT CP ' ' ;IF NOT SPACE,.. JP NZ,STTB2 ;..DON'T.. LD (HL),0 ;OVERLAY ' ' WITH OOH JP STTB2T ;LOOP ; ; SETTABS PASS 3: DELETE NON-ESSENTIAL TABS, ; I.E. ONES WHICH FUNCTIONALLY CAN BE REPLACED ; BY A SINGLE SPACE. ; STTB3: INC HL ;TO NEXT CHAR LD A,(HL) ;GET CHAR CP LF ;END? JP Z,STTB4 ;TO PASS 4 CP TAB ;LOOP IF.. JP NZ,STTB3 ;..NOT A TAB DEC HL ;PREVIOUS LD A,(HL) INC HL ;A NULL? OR A JP Z,STTB3 ;YES, OK LD (HL),' ' ;CHANGE TAB TO SPACE JP STTB3 ; ; SETTABS PASS 4: DELETES THE NULLS (00H) ; PACKING THE LINE IN PLACE. ; STTB4: POP HL ;GET BUFF ADDR PUSH HL ;SAVE BACK LD D,H ;HL = INPUT POINTER LD E,L ;DE = OUTPUT POINTER STTB4L: LD A,(HL) ;GET CHAR OR A ;NULL? JP Z,STTB4N ;YES, SKIP STORE LD (DE),A ;SAVE OUTPUT INC DE STTB4N: INC HL CP LF ;END OF BUFF? JP NZ,STTB4L ;NO, LOOP POP HL ;GET BUFF RET ; HOFMSG: DB 0CH,'ZSM-',VER1,'.',VER2,' Source file name: ' HOFNAM: DB 'XXXXXXXX Page No: ' HOFPG: DB ' ',CR,LF ; INCNUM: LD A,(HL) CP ' ' JP NZ,INCNU1 LD A,'0' INCNU1: INC A LD (HL),A CP '9'+1 RET NZ LD (HL),'0' DEC HL JP INCNUM TITLE Z80 Assembler - Symbol Table sort and list ; ; SORT SYMBOL TABLE AND LIST IT ; ; BUILD SYMBOL POINTER TABLE, A LIST OF ADDRESSES ; POINTING TO THE START OF EACH SYMBOL TABLE ENTRY ; SYMBOL: LD HL,TITLEB ;CLEAN OUT TITLE BUFFER LD (HL),CR INC HL LD (HL),LF LD HL,(MAXMEM) ;HL = MAX MEM ADDRESS LD B,H ;PUT INTO B & C LD C,L LD HL,(SYMPTR) ;GET ADDRESS OF NEXT FREE MEMORY SPACE INC HL ;POINT TO BYTE AFTER LD (SYMREF),HL ;START OF SYMBOL REF TABLE LD (REFPTR),HL ;REF TABLE POINTER ; LD DE,SYMNXT ;START OF SYMBOLS SYMB5: LD A,(DE) ;GET LENGTH OF THIS SYMBOL AND 0FH ;IN BOTTOM 4 BITS JP Z,SYMB6 ;FINISH IF DONE LD HL,(REFPTR) ;POINTER INTO REFERENCE TABLE LD (HL),E ;STORE SYMBOL START ADDRESS INC HL ;BUMP POINTER LD (HL),D ;HIGH BYTE OF ADDRESS INC HL ;BUMP POINTER LD (REFPTR),HL ;AND SAVE FOR LATER LD A,L ;SUBTRACT MAXMEM SUB C ;FROM POINTER LD A,H ;TO CHECK RANGE SBC B JP NC,SYMB90 ;JP IF OVERFLOW LD A,(DE) ;GET LENGTH OF THIS SYMBOL AND 0FH ;IN BOTTOM 4 BITS LD L,A ;ADD TO START ADDRESS LD H,0 ;TO FIND START OF NEXT SYMBOL ADD HL,DE ;HL=START OF NEXT SYMBOL EX DE,HL ;DE=START OF NEXT SYMBOL JP SYMB5 ;ROUND AGAIN ; SYMB6: LD HL,(REFPTR) ;POINTER INTO REFERENCE TABLE LD (HL),0 ;END OF REF TABLE MARKER INC HL ;BUMP POINTER LD (HL),0 LD HL,(SYMREF) ;GET START OF REFERENCE TABLE LD (REFPTR),HL ;REINITIALISE POINTER ; ; THE SYMBOL POINTER TABLE IS COMPLETE. NOW START SORTING ; THE POINTERS USING A SIMPLE BUBBLE SORT COMPARING THE ; SYMBOLS. ; SYMB8: XOR A ;CHEAP ZERO LD (SWPFLG),A ;INITIALISE THE SWAP FLAG LD HL,(SYMREF) ;START OF SYMBOL TABLE POINTERS SYMB9: LD E,(HL) ;GET FIRST POINTER INC HL LD D,(HL) ;HI BYTE INC HL PUSH HL ;SAVE POINTER LD A,(HL) ;GET 2ND POINTER INC HL LD H,(HL) ;HI BYTE LD L,A ;HL=2ND POINTER OR H ;SEE IF END OF LIST JP Z,SYMB60 ;END OF THIS PASS ; SYMB10: LD A,(DE) ;GET LENGTH OF FIRST SYMBOL AND 0FH ;4 BITS ONLY JP Z,SYMB60 ;0=END OF TABLE SUB 4 ;JUST LENGTH OF SYMBOL LD B,A ;SAVE IN B LD A,(HL) ;GET LENGTH OF 2ND SYMBOL AND 0FH ;4 BITS ONLY JP Z,SYMB60 ;0=END OF TABLE SUB 4 ;JUST THE SYMBOL LENGTH LD C,A ;SAVE IN C FOR LATER PUSH HL ;SAVE ALL POINTERS PUSH DE ;AND LENGTHS PUSH BC CP B ;COMPARE TO LENGTH OF FIRST JP NC,SYMB20 ;SKIP IF 1ST SHORTER LD B,A ;2ND IS SHORTER SYMB20: INC DE ;POINT INTO INC HL ;ACTUAL LABEL LD A,(DE) ;GET CHAR FROM 1ST CP (HL) ;COMPARE TO 2ND JP Z,SYMB25 ;KEEP TRYING IF EQUAL JP C,SYMB50 ;2ND > 1ST SO NO SWAP JP SYMB40 ;1ST > 2ND SO SWAP IMMEDIATE ; SYMB25: DEC B ;DEC CHARACTER COUNT JP NZ,SYMB20 ;LOOP FOR NEXT CHAR ; ; BY NOW THE SYMBOLS ARE THE SAME (AS FAR AS ; WE HAVE CHECKED.) NOW SWAP POINTERS ONLY IF ; 2ND IS LONGER THAN 1ST. ; POP BC ;GET LENGTHS BACK LD A,C ;GET 2ND LENGTH CP B ;IS 1ST LONGER? JP NC,SYMB55 ;DON'T SWAP IF NOT DEC SP ;CLEAN UP STACK DEC SP ;TO JUST DROP THROUGH ; ; 1ST > 2ND, SWAP POINTERS ; SYMB40: POP BC ;GET COUNTS BACK & IGNORE POP DE ;POP FIRST POINTER INTO DE POP BC ;POP SECOND POINTER INTO BC POP HL ;GET REFERENCE POINTER DEC HL ;DROP BACK TO FIRST POINTER DEC HL LD (HL),C ;AND STORE 2ND POINTER THERE INC HL LD (HL),B INC HL LD (HL),E ;STORE FIRST POINTER INC HL LD (HL),D DEC HL ;RESTORE REF POINTER LD A,0FFH ;SET THE SWAP FLAG LD (SWPFLG),A ; TO SAY WE HAVE SWAPPED JP SYMB9 ;AND ROUND AGAIN ; ; 2ND > 1ST, NO SWAP REQUIRED ; SYMB50: POP BC ;GET COUNTS BACK SYMB55: POP DE ;POP FIRST POINTER POP HL ;POP SECOND POINTER POP HL ;GET REF POINTER JP SYMB9 ;AND ROUND AGAIN ; SYMB60: POP HL LD A,(SWPFLG) OR A JP NZ,SYMB8 ;ROUND AGAIN ; ; LIST OF POINTERS IS NOW SORTED ; LD A,MAXLNE+1 ;FORCE NEW PAGE LD (CURLNE),A LD HL,(SYMREF) ;START OF SYMBOL TABLE POINTERS LD (REFPTR),HL ;SAVE POINTER SYMB65: LD A,' ' LD HL,HDRBUF LD DE,HDRBUF+1 LD (HL),A LD BC,80 IF Z80 LDIR ELSE CALL LDIR ENDIF XOR A ;SET UP SYMBOL COUNTER LD (SYMCNT),A ; SYMB70: LD HL,(REFPTR) ;GET SYMBOL POINTER BACK LD E,(HL) ;GET POINTER INC HL LD D,(HL) ;HI BYTE INC HL LD (REFPTR),HL ;SAVE REFERENCE POINTER PUSH DE ;SAVE SYMBOL POINTER ; LD HL,HDRBUF ;POINT TO OUTPUT BUFFER LD A,(SYMCNT) ;WHICH SYMBOL? LD E,A ;MULTIPLY BY 20 ADD A ;*2 ADD A ;*4 ADD E ;*5 ADD A ;*10 ADD A ;*20 LD E,A LD D,0 ADD HL,DE ;INDEX INTO LINE EX DE,HL ;PUT OUTPUT RECORD POINTER IN DE POP HL ;GET SYMBOL POINTER BACK LD A,L ;TEST FOR END OF TABLE OR H JP Z,SYMXIT PUSH DE ;SAVE RECORD POINTER LD A,(HL) ;GET LENGTH AND 0FH SUB 4 ;CALCULATE LENGTH OF SYMBOL LD C,A ;LENGTH IN BC LD B,0 INC HL ;POINT TO START OF SYMBOL POP DE ;GET OUTPUT RECORD POINTER PUSH DE ;AND SAVE AGAIN FOR LATER ; IF Z80 LDIR ELSE CALL LDIR ENDIF LD C,(HL) ;BC = VALUE INC HL LD B,(HL) POP HL ;GET RECORD POINTER LD DE,12 ;MOVE OVER TO VALUE ADD HL,DE LD A,B CALL CNV2HX LD A,C CALL CNV2HX LD A,(SYMCNT) INC A LD (SYMCNT),A CP 4 JP C,SYMB70 ; LD (HL),CR INC HL LD (HL),LF CALL PLINE JP SYMB65 ; SYMXIT: EX DE,HL ;HL=BUFFER POINTER LD (HL),CR ;TERMINATE THE LINE INC HL LD (HL),LF JP PLINE ;PRINT IT AND RETURN ; SYMB90: LD A,'W' LD (ERRFLG),A RET ; SYMREF: DW 0 ;SYMBOL REF ADDRESS REFPTR: DW 0 ;POINTER INTO REFERENCE TABLE SWPFLG: DB 0 ;FLAG TO INDICATE A SWAP IN THE SORT SYMCNT: DB 0 ;SYMBOL COUNTER SYMFLG: DB 0 ;FLAG TO SAY IF ANY SYMBOLS WERE FOUND TITLE Z80 Assembler - Error Messages ; ERRH1: DB '***** ' ERRH1L: EQU $-ERRH1 ERRH2: DB ' *****',CR,LF,0 ERRH2L: EQU $-ERRH2 ; ; ERROR MESSAGE STORAGE ; ; INDEX TABLE ; ERRTAB: DW ERRMA DW ERRMB DW ERRMC DW ERRMD DW ERRME DW ERRMF DW ERRMG DW ERRMH DW ERRMI DW ERRMJ DW ERRMK DW ERRML DW ERRMM DW ERRMN DW ERRMO DW ERRMP DW ERRMQ DW ERRMR DW ERRMS DW ERRMT DW ERRMU DW ERRMV DW ERRMW DW ERRMX DW ERRMY DW ERRMZ ; ; ACTUAL ERROR MESSAGES ; ERRMA: DB 'Too many IF statements',0 ERRMB: DB 'ENDIF without matching IF statement',0 ERRMC: DB 'ELSE without matching IF statement',0 ERRMD: DB 'Relative jump range error',0 ERRME: DB 'Expression error',0 ERRMF: DB 0 ERRMG: DB 0 ERRMH: DB 0 ERRMI: DB 0 ERRMJ: DB 0 ERRMK: DB 0 ERRML: DB 0 ERRMM: DB 'Multiple definition',0 ERRMN: DB 'Illegal opcode',0 ERRMO: DB 'Syntax error',0 ERRMP: DB 0 ERRMQ: DB 0 ERRMR: DB 0 ERRMS: DB 0 ERRMT: DB 0 ERRMU: DB 'Undefined symbol',0 ERRMV: DB 'Value error',0 ERRMW: DB 'Symbol Table Overflow',0 ERRMX: DB 'Illegal error code!',0 ERRMY: DB 0 ERRMZ: DB 'Divide by Zero',0 TITLE Z80 Assembler - Opcode Table ;----------------------------------------------- ; ; OPCODE TABLE ; ;----------------------------------------------- ; ; EACH SYMBOL TABLE ENTRY IS OF VARYING LENGTH ; ; THE FIRST BYTE CONTAINS THE LENGTH IN THE LOWER 4 BITS ; AND FLAGS IN THE UPPER 4 BITS. ; ; THIS LIMITS THE MAX LENGTH OF AN ENTRY TO 15 BYTES ; ; FOLLOWING THE FLAG/LENGTH BYTE IS THE NAME WHICH MAY BE FROM ; 1 TO 11 BYTES IN LENGTH ; ; FOLLOWING THE NAME ARE 2 BYTES OF VALUE (LO,HI) ; AND 1 BYTE OF TYPE (USED IN OPCODES) ; ; THE TABLE IS SCANNED SEQUENTIALLY AND IS ENDED BY A 00 BYTE ; ;::::::::::::::::::::::::::::::::::::::::::::::: ; ; SYMBOL TABLE ADDRESS EQUATES ; ;::::::::::::::::::::::::::::::::::::::::::::::: ; EQUNAME:EQU 10H ;FLAG BIT - EQUATE LABEL RNAME: EQU 20H ;FLAG BIT - REGISTER NAME ULBL: EQU 40H ;FLAG BIT - LABEL NAME RPNAME: EQU 80H ;FLAG BIT - REGISTER PAIR NAME ; ;SYMBEG POINTER TO FIRST USEABLE ENTRY ;SYM ACTUAL BEGINNING ADDRESS FOR SEARCHES ;SYMNXT START OF NEW SYMBOLS ; ; S Y M B O L T A B L E ; ; THE OPCODE TABLE IS ARRANGED IN ORDER OF ; POPULARITY IN A SAMPLE OF MY Z80 PROGRAMS ; CNH JAN 83 ; SYMBEG: DW SYMNXT ;SYMBEG ; SYM: DB 6,'LD',0,0,8 DB 6,'DB',3,0,14 DB 7,'EQU',1,0,14 DB 6,'DW',4,0,14 DB 8,'CALL',0CDH,0,3 DB 6,'JR',18H,0,4 DB 7,'RET',0C9H,0,11 DB 6,'DS',2,0,14 DB 6,'JP',0C3H,0,3 DB 6,'CP',38H,0,6 DB 6,'OR',30H,0,6 DB 7,'AND',20H,0,6 DB 7,'ADD',0,9,6 DB 7,'INC',04H,03H,13 DB 7,'XOR',28H,0,6 DB 6,'IN',0DBH,0,7 DB 7,'OUT',0D3H,0,7 DB 8,'PUSH',0C5H,0,9 DB 7,'POP',0C1H,0,9 DB 7,'DEC',05H,0BH,13 DB 6,'EX',0EBH,0,10 DB 8,'DJNZ',10H,0,4 DB 7,'ADC',8,4AH,6 DB 7,'SBC',18H,42H,6 DB 7,'SUB',10H,0,6 DB 8,'LDIR',0EDH,0B0H,1 ; DB 8,'RLCA',07H,0,1 DB 7,'RLA',17H,0,1 DB 8,'RRCA',0FH,0,1 DB 7,'RRA',1FH,0,1 ; DB 7,'CPL',2FH,0,1 DB 7,'DAA',27H,0,1 DB 7,'NEG',0EDH,044H,1 DB 7,'CCF',03FH,0,1 DB 7,'SCF',037H,0,1 DB 7,'NOP',0,0,1 ; DB 6,'IF',8,0,14 DB 8,'ELSE',9,0,14 DB 9,'ENDIF',10,0,14 ; DB 7,'RLC',0,0,2 DB 6,'RL',10H,0,2 DB 7,'RRC',8,0,2 DB 6,'RR',18H,0,2 DB 7,'SLA',20H,0,2 DB 7,'SRA',28H,0,2 DB 7,'SRL',38H,0,2 DB 7,'BIT',040H,0,12 DB 7,'SET',0C0H,0,12 DB 7,'RES',80H,0,12 DB 7,'EXX',0D9H,0,1 DB 8,'DEFS',2,0,14 DB 8,'DEFB',3,0,14 DB 8,'DEFW',4,0,14 DB 7,'ORG',6,0,14 DB 7,'END',5,0,14 DB 8,'FORM',7,0,14 DB 8,'PAGE',7,0,14 DB 9,'EJECT',7,0,14 DB 8,'LIST',11,0,14 DB 9,'TITLE',12,0,14 ; DB 7,'RLD',0EDH,06FH,1 DB 7,'RRD',0EDH,067H,1 DB 7,'RST',0C7H,0,5 ; DB 7,'LDI',0EDH,0A0H,1 DB 7,'LDD',0EDH,0A8H,1 DB 8,'LDDR',0EDH,0B8H,1 ; DB 7,'CPI',0EDH,0A1H,1 DB 8,'CPIR',0EDH,0B1H,1 DB 7,'CPD',0EDH,0A9H,1 DB 8,'CPDR',0EDH,0B9H,1 ; DB 7,'INI',0EDH,0A2H,1 DB 8,'INIR',0EDH,0B2H,1 DB 7,'IND',0EDH,0AAH,1 DB 8,'INDR',0EDH,0BAH,1 ; DB 8,'OUTI',0EDH,0A3H,1 DB 8,'OTIR',0EDH,0B3H,1 DB 8,'OUTD',0EDH,0ABH,1 DB 8,'OTDR',0EDH,0BBH,1 ; DB 6,'DI',0F3H,0,1 DB 6,'EI',0FBH,0,1 DB 6,'IM',0EDH,0FFH,1 DB 8,'RETI',0EDH,4DH,1 ; DB 8,'HALT',76H,0,1 DB 8,'RETN',0EDH,45H,1 ; ; REGISTERS ; REGS: DB 5+RNAME,'A',7,0,0 DB 5+RNAME,'B',0,0,0 DB 5+RNAME,'C',1,0,0 DB 5+RNAME,'D',2,0,0 DB 5+RNAME,'E',3,0,0 DB 5+RNAME,'H',4,0,0 DB 5+RNAME,'L',5,0,0 DB 6+RPNAME,'HL',4,0,0 DB 6+RPNAME,'BC',0,0,0 DB 6+RPNAME,'DE',2,0,0 DB 6+RPNAME,'SP',6,0,0 DB 6+RPNAME,'IX',0FFH,0,0 DB 6+RPNAME,'IY',0FEH,0,0 DB 6,'AF',6,0,0 ;FOR PUSH/POP ; CONDS: DB 5,'Z',1,0,0 DB 6,'NZ',0,0,0 DB 5,'C',3,0,0 DB 6,'NC',2,0,0 DB 6,'PO',4,0,0 DB 6,'PE',5,0,0 DB 5,'P',6,0,0 DB 5,'M',7,0,0 LIST ON SYMNXT: DB 0 ;FIRST AVAILABLE SLOT END