; UCASM Micro-controller Assembler ; copyright (c) 1987, 1988 by J. L. Post ; This file MUST be accompanied by UCASM.DOC - Please read it ; ; Version 1.2 - Dec/21/87 ; added line number in error reports ; Version 1.1 - Dec/18/87 ; added DB, DW, and DS as equivalents of DEFB, DEFW, and DEFS ; added operators <, >, and ^ for shift left, shift right, and ; control character definition. Also check for divide by zero. ; Version 1.0 - first release Dec/13/87 ; ; Assembles code for the following micro-controllers: ; ; ROM (E = eprom) ; 1K 2K 4K External ;----------------------------------------------- ; RAM 64 8021 * 8035 ; (bytes) 8041 * ; 8048 ; 8741 (E) * ; 8748 (E) ;----------------------------------------------- ; 128 8041 * 8049 8039 ; 8741 (E) * ;----------------------------------------------- ; 256 8042 8050 8040 ; 8742 (E) ;----------------------------------------------- ; ; * 8021 is a 28 pin chip that supports a subset of 8035 code. ; 8041/8741 are listed twice because the NEC chip has only 64 ; bytes of internal ram, while the Intel chip has 128 bytes ; (at least that's what the spec sheets say.) ; VER EQU 1 ; Version number REV EQU 2 ; Revision number ; TAB EQU 9 CR EQU 13 LF EQU 10 CTRLZ EQU 1AH ; Control Z ESC EQU 1BH ; Escape QUOTE EQU 27H ; ASCII single quote (') CARAT EQU 5EH ; Control char definition (^) ; BOOT EQU 0 BDOS EQU 5 FCB EQU 5CH ; ; ORG 100H ; JR START ; ; HEXOPT: DEFB 0 ; 0 if no FILE.HEX output LSTOPT: DEFB 0 ; 0 if no FILE.LST output LSTTYP: DEFB 'LST' ; Default list file type HEXTYP: DEFB 'HEX' ; Default object file type MAXERR: DEFB 10 ; Maximum error count before abort PAGLEN: DEFB 0 ; List page length (0 if no paging, usually 66) DEFB 0 ; Lines printed per page (suggest 60) SRCTYP: DEFB 'Z80' ; Default source file type ; ; HEXOPT and LSTOPT can be modified at run time by the H or L options ; START: LD SP,STACK LD HL,IMSG ; Say hello CALL PRINT CALL INIT ; Init fcb's and options CALL GETSRC ; Read in source file JR C,RDERR ; Abort if read error CALL ASM ; Do the assembly CALL DCRLF LD BC,(BYTCNT) ; Show how many bytes in object code CALL PDEC LD HL,DONMSG ; Points to 'byte' text CALL DUALP ; Print to console and/or list file LD BC,(BYTCNT) LD A,B OR A JR NZ,END1 ; Go if object byte count > 256 LD A,C DEC A JR Z,END2 ; Go if byte count = 1 ; END1: LD A,'s' ; Else append 's' CALL DCHR ; Output to console and/or list file ; END2: LD A,',' CALL DCHR LD A,' ' CALL DCHR LD A,(ERRCNT) ; Show error count LD C,A LD B,0 CALL PDEC CALL DUALP LD A,(ERRCNT) DEC A JR Z,EXIT LD A,'s' CALL DCHR ; EXIT: LD A,(FILFLG) ; Test if any files open OR A JR Z,EXIT1 ; Go if none opened AND 1 CALL NZ,CLSHEX ; Close open hex file LD A,(FILFLG) AND 4 CALL NZ,CLSLST ; Close open list file ; EXIT1: JP BOOT ; Exit to system ; RDERR: LD HL,RDERMS ; Source file read error CALL PRINT JR EXIT ; CLSHEX: LD A,(HEXFLG) ; Close .HEX object file AND 80H CALL NZ,HEXDMP ; Clear record buffer LD HL,LASTHR ; Set last hex record ; CLSHX1: LD A,(HL) INC HL OR A JR Z,CLSHX2 CALL HEXOUT JR CLSHX1 ; CLSHX2: LD A,(FILFLG) AND 2 CALL NZ,FLSHEX ; Flush hex buffer LD DE,FCB1 JR CLOSE ; Close file ; CLSLST: LD A,(FILFLG) ; Close open list file AND 8 CALL NZ,FLSLST ; Flush list buffer LD DE,FCB2 ; And close file ; CLOSE: PUSH HL ; Close open files LD C,16 CALL BDOS POP HL RET ; ; Flush data out of open buffers ; FLSHEX: PUSH HL LD DE,(HEXDMA) ; Set transfer address CALL SETDMA LD HL,(HEXDMA) LD DE,FCB1 LD BC,(HEXOFF) ; Get byte count JR FLSLS0 ; FLSLST: PUSH HL LD DE,(LSTDMA) CALL SETDMA LD HL,(LSTDMA) LD DE,FCB2 LD BC,(LSTOFF) FLSLS0: ADD HL,BC ; FLSLS1: BIT 7,C JR NZ,WRITE LD (HL),CTRLZ ; Fill extra space with control Z INC HL INC C JR FLSLS1 ; WRITE: LD C,21 ; Write record to file CALL BDOS POP HL RET ; LASTHR: DEFB ':00000001FF',0 ; ; Initialize FCBs and options ; INIT: LD A,(FCB+9) ; See if file type specified CP ' ' JR NZ,INIT1 LD HL,SRCTYP ; Set default file type LD DE,FCB+9 LD BC,3 LDIR INIT1: LD HL,FCB1 LD DE,FCB1+1 LD BC,72 LD (HL),0 ; Clear output fcbs LDIR LD HL,FCB LD DE,FCB1 LD BC,9 LDIR LD HL,HEXTYP ; Init object file fcb LD BC,3 LDIR LD HL,FCB LD DE,FCB2 LD BC,9 LDIR LD HL,LSTTYP ; Init list file FCB LD BC,3 LDIR LD HL,FCB+17 ; Point to auxiliary FCB for options ; INIT2: LD A,(HL) INC HL CP 21H RET C CP 'H' JR Z,INIT3 CP 'L' JR NZ,INIT2 LD (LSTOPT),A ; Set list file option JR INIT2 ; INIT3: LD (HEXOPT),A ; Set .HEX file option JR INIT2 ; ; Read in source file ; GETSRC: LD DE,IBFR ; Set DMA address LD (IBFRP),DE ; Set input buffer pointer LD (IBFEND),DE ; Flag end of input buffer CALL SETDMA LD DE,FCB ; Attempt to open file CALL OPNFIL RET C ; Abort if error ; GTSRC1: LD DE,FCB LD C,20 CALL BDOS ; Read record OR A JR NZ,GTSRC2 ; Go if done LD HL,(IBFEND) ; Else updata DMA address LD BC,128 ADD HL,BC LD (IBFEND),HL ; Update buffer end address LD D,H LD E,L CALL SETDMA ; Update transfer address JR GTSRC1 ; GTSRC2: LD HL,(IBFEND) ; Test for empty file LD DE,IBFR INC DE SBC HL,DE RET C ; Abort with error if file empty LD A,(IBFR) ; Get first character CP CTRLZ JR Z,GTSRC3 ; Error if end of file char LD HL,(IBFEND) INC HL INC HL LD (SYMTAB),HL ; Initialize symbol table pointers LD (SYMEND),HL LD (SYMPTR),HL LD (HL),0 ; Disable garbage in symbol table OR A ; By setting terminator byte RET ; GTSRC3: SCF ; Flag error and return RET ; HEXOPN: LD A,(FILFLG) ; Open .HEX file, if optioned OR 1 LD (FILFLG),A LD DE,FCB1 LD (FCBTMP),DE JR OPEN ; LSTOPN: LD A,(FILFLG) ; Open list file, if optioned OR 4 LD (FILFLG),A LD DE,FCB2 LD (FCBTMP),DE ; OPEN: LD DE,(FCBTMP) CALL OPNFIL ; Attempt to open file JR C,OPEN1 ; Go if doesn't exist LD DE,(FCBTMP) LD C,19 CALL BDOS ; Else kill it (ouch!) ; OPEN1: LD DE,(FCBTMP) LD C,22 CALL BDOS ; Make new file INC A ; Z set if error RET ; OPNFIL: LD C,15 CALL BDOS INC A JR Z,OPNFL1 ; Go if can't open file XOR A RET ; OPNFL1: SCF ; Open file error return RET ; SETDMA: PUSH HL ; Set dma transfer address in DE PUSH BC LD C,26 CALL BDOS POP BC POP HL RET ; ; End of Assembly Pass Processing ; ASMDON: LD HL,IBFR LD (IBFRP),HL ; Reset input buffer pointer LD HL,0 LD (PC),HL ; Reset program counter LD (LINUM),HL ; Clear line number LD A,(PASS) INC A LD (PASS),A ; Update pass number CP 2 RET NC DEC A JR NZ,ASM ; Go if pass 1 LD A,(HEXOPT) OR A JR Z,ASMDN1 ; Go if no object file option LD HL,(SYMEND) INC H LD (HEXDMA),HL ; Set address of hex file buffer CALL HEXOPN ; Open hex file LD HL,HEXTYP JR Z,OPNERR ; Go if can't open object file ; ASMDN1: LD A,(LSTOPT) OR A JR Z,ASM ; Go if no list file option LD HL,(SYMEND) INC H INC H LD (LSTDMA),HL ; Set address of list file buffer CALL LSTOPN ; Open list file LD HL,LSTTYP JR NZ,ASM ; Go if open ok ; OPNERR: EX DE,HL ; Can't open file LD HL,OPNEMS ; Report error CALL PRINT LD B,3 ; OPNER1: LD A,(DE) ; Show type of file CALL CHROUT INC DE DJNZ OPNER1 CALL PRINT JP EXIT ; Then abort ; ; Assemble source code ; ASM: XOR A LD (ERRFLG),A ; Clear error flag LD (OCLEN),A ; And opcode length LD HL,(LINUM) INC HL LD (LINUM),HL CALL GETLIN ; Get next line into buffer CP CTRLZ JR Z,ASMDON ; Go if end of file LD HL,LINBFR LD A,(HL) CP CTRLZ JR Z,ASMDON ; Go if end of file CP CR JP Z,ASMRET ; Ignore empty line CP TAB JR Z,ASM1 ; Go if no label CP ' ' JR Z,ASM1 ; Ditto CP ';' JP Z,ASMRET ; Go if comment line LD A,(PASS) OR A JR NZ,ASM01 ; Go if pass two PUSH HL ; Else enter label in symbol table CALL FNDSYM ; See if already exists EX DE,HL ; Put pointer in DE POP HL JR NZ,ASM0 ; Go if entry not found LD A,3 LD (ERRFLG),A ; Else flag multi def label error EX DE,HL ; ASM00: DEC HL LD A,(HL) CP ' ' JR NC,ASM00 ; Find byte count SET 7,(HL) ; Flag as multiple for pass 2 JP ASMERR ; ASM0: PUSH HL CALL ENTSYM ; Enter into symbol table DEC HL ; Point to value DEC HL LD BC,(PC) ; Get program counter LD (HL),C INC HL LD (HL),B POP HL ; ASM01: CALL SKPLBL ; Skip over label ; ASM1: CALL NEXTF ; Point to next field LD A,1 LD (ERRFLG),A CALL LOOKUP ; Find entry in opcode table JR C,ASMER ; Go if not found XOR A LD (ERRFLG),A ; ; Opcode entry found in table, DE => opcode in table ; CALL SKPLBL ; Skip past mnemonic CALL NEXTF ; Point HL to next field LD A,(DE) LD (OPCODE),A ; Set base opcode LD A,(MAXTYP) LD B,A INC DE LD A,(DE) ; Get group type BIT 7,A JR NZ,PSUDO ; Go if pseudo op DEC A CP B JR NC,ASMERR ; Go if error LD C,A LD B,0 LD IX,GRPTBL ; Point to routine entry table ADD IX,BC ADD IX,BC LD C,(IX+0) ; Get address of routine LD B,(IX+1) PUSH BC POP IX ; ASM2: LD IY,ASMRET ; Set return address on stack PUSH IY JP (IX) ; Execute routine ; PSUDO: AND 7 PUSH HL LD HL,PSUDTB ; Get pseudo op table entry ADD A,A LD C,A LD B,0 ADD HL,BC LD A,(HL) INC HL LD H,(HL) LD L,A PUSH HL POP IX POP HL JR ASM2 ; ASMER: ASMERR: LD A,(ERRFLG) ADD A,A LD C,A LD B,0 XOR A LD (ERRFLG),A ; Clear error LD A,C CP 6 JR Z,ASMER1 ; Go if multiple label def LD A,(PASS) OR A JR Z,ASMRET ; No error msg or update in pass 1 ; ASMER1: LD HL,ERRMSG CALL DUALP LD HL,ERRTBL ADD HL,BC LD A,(HL) INC HL LD H,(HL) LD L,A CALL DUALP LD HL,LINMSG CALL DUALP LD BC,(LINUM) CALL PDEC CALL DCRLF LD HL,LINBFR CALL PRINT CALL UPERCT ; Update error counter XOR A ; ASMRET: JR C,ASMERR ; Go if subroutine error LD A,(ERRFLG) OR A JR NZ,ASMERR LD A,(PASS) OR A JR Z,ASMR1 ; Go if first pass LD A,(HEXOPT) OR A CALL NZ,HEXWR ; Go if hex output required LD A,(LSTOPT) OR A CALL NZ,LSTWR ; Go if list output required ; ASMR1: LD A,(OCLEN) ; Update program counter LD HL,(PC) LD C,A LD B,0 ADD HL,BC LD (PC),HL LD A,(PASS) OR A JP Z,ASM ; No update byte count on pass 1 LD HL,(BYTCNT) ADD HL,BC LD (BYTCNT),HL JP ASM ; ; Find mnemonic in opcode table ; On entry HL => start of linebfr opcode ; On exit DE => opcode in opcode table, C set if not found ; LOOKUP: LD DE,OPTBL ; LOOK1: LD A,(DE) AND 7 JR NZ,LOOK2 SCF RET ; LOOK2: LD B,A PUSH HL INC DE CALL CPSTRG ; Compare strings LD A,(HL) ; Check if source end POP HL JR Z,LOOK5 ; Go if match ; LOOK3: INC DE ; Skip to end of mnemonic DJNZ LOOK3 ; LOOK4: INC DE ; Skip over code and group INC DE JR LOOK1 ; LOOK5: CP 21H JR NC,LOOK4 ; Go if not full match XOR A RET ; CPSTRG: LD A,(DE) CP (HL) RET NZ INC HL INC DE DJNZ CPSTRG RET ; GETLIN: LD HL,(IBFRP) ; Get input buffer pointer LD DE,LINBFR LD (LINBFP),DE ; Init line buffer pointer LD B,80 ; Max char count per line ; GTLIN1: LD A,(HL) CP CTRLZ RET Z ; Done if end of file LD (DE),A ; Store in line buffer INC DE INC HL CP LF JR Z,GTLIN2 ; Do for entire line DJNZ GTLIN1 ; GTLIN2: XOR A LD (DE),A ; Flag end of line LD (IBFRP),HL ; Update input buffer pointer RET ; ; Print decimal value in BC, suppress leading zeros ; PDEC: PUSH HL PUSH DE LD H,B LD L,C LD C,0 LD DE,1000 CALL PDECS LD DE,100 CALL PDECS LD DE,10 CALL PDECS LD A,L OR 30H CALL DCHR POP DE POP HL RET ; PDECS: LD B,0 ; PDECS1: INC B SBC HL,DE JR NC,PDECS1 DEC B ADD HL,DE LD A,B OR A JR NZ,PDECS2 BIT 0,C RET Z ; PDECS2 SET 0,C OR 30H JR DCHR ; DUALP: LD A,(PASS) OR A JR Z,PRINT LD A,(LSTOPT) OR A JR Z,PRINT PUSH HL PUSH BC ; DUALP1: LD A,(HL) INC HL OR A JR Z,DUALP2 CALL PUTLST JR DUALP1 ; DUALP2: POP BC POP HL ; PRINT: LD A,(HL) INC HL OR A RET Z CALL CHROUT JR PRINT ; DCRLF: LD A,(PASS) OR A JR Z,CRLF LD A,(LSTOPT) OR A JR Z,CRLF PUSH HL PUSH BC LD A,CR CALL PUTLST LD A,LF CALL PUTLST POP BC POP HL ; CRLF: LD A,CR CALL CHROUT LD A,LF ; CHROUT: PUSH HL PUSH DE PUSH BC PUSH AF LD E,A LD C,6 CALL BDOS POP AF ; SYSRET: POP BC POP DE POP HL RET ; DCHR: CALL CHROUT PUSH AF LD A,(PASS) OR A JR Z,DCHRX LD A,(LSTOPT) OR A JR Z,DCHRX POP AF PUSH AF PUSH HL PUSH BC CALL PUTLST POP BC POP HL ; DCHRX: POP AF RET ; ; Multiply BC * DE, product in BC ; MULT: PUSH HL PUSH DE LD HL,0 LD A,16 ; Loop count ; MULT1: ADD HL,HL ; Shift partial product EX DE,HL ADC HL,HL ; Shift partial prod with carry EX DE,HL JR NC,MULT2 ADD HL,BC JR NC,MULT2 INC DE ; MULT2: DEC A JR NZ,MULT1 LD C,L LD B,H POP DE POP HL RET ; ; Divide DE / BC, quotient in DE remainder in BC ; DIV: LD A,B OR C JP Z,INVOPD ; Can't divide by 0 PUSH HL LD HL,0 LD A,16 ; DIV1: EX DE,HL ADD HL,HL EX DE,HL ADC HL,HL INC DE OR A SBC HL,BC JR NC,DIV2 DEC DE ADD HL,BC ; DIV2: DEC A JR NZ,DIV1 LD C,L LD B,H POP HL RET ; KBSCAN: PUSH HL PUSH DE PUSH BC LD E,0FFH LD C,6 CALL BDOS AND 7FH JR SYSRET ; ROTATE: RLCA RLCA RLCA RLCA RET ; ; Skip to next field in line buffer ; NEXTF: LD A,(HL) CP CTRLZ RET Z CP ' ' JR Z,NEXTF1 CP TAB RET NZ ; NEXTF1: INC HL JR NEXTF ; ; Skip to next operand in line buffer ; NEXTOP: LD A,(HL) CP CTRLZ RET Z CP ' ' JR Z,NEXTO1 CP TAB JR Z,NEXTO1 CP ',' RET NZ ; NEXTO1: INC HL JR NEXTOP ; UPERCT: LD A,(MAXERR) PUSH BC LD B,A LD A,(ERRCNT) INC A CP B POP BC JR NC,UPERC1 LD (ERRCNT),A RET ; UPERC1: LD HL,AWCRAP CALL PRINT JP EXIT ; PSUDTB: DEFW PSEQU DEFW PSORG DEFW PSDFB DEFW PSDFW DEFW PSDFS DEFW PSEND DEFW PSINV DEFW PSINV ; PSINV: SCF ; Invalid pseudo-op ; PSEND: RET ; Ignore end pseudo-op ; PSDFB: LD IY,OCLEN LD IX,OPCODE ; PSDB0: LD A,(HL) CP QUOTE JR Z,PSDB3 ; Go if ASCII literal ; PSDB1: CALL EXP INC (IY+0) LD A,(NUMBER) LD (IX+0),A INC IX ; PSDB2: LD A,(HL) INC HL CP ',' JR Z,PSDB0 OR A RET ; PSDB3: INC HL INC HL LD A,(HL) DEC HL DEC HL CP QUOTE JR Z,PSDB1 ; Go if single ascii literal INC HL ; Else assume it's a string ; PSDB4: LD A,(HL) INC HL CP CR RET Z CP QUOTE JR Z,PSDB2 LD (IX+0),A INC IX INC (IY+0) JR PSDB4 ; PSDFS: CALL EXP PUSH HL LD HL,(PC) LD BC,(NUMBER) ADD HL,BC LD (PC),HL POP HL JR PSORGX ; PSORG: CALL EXP LD BC,(NUMBER) LD (PC),BC ; PSORGX: LD A,(HEXFLG) OR 40H ; Set bit 6 LD (HEXFLG),A ; Flag skip in hex record XOR A LD (OCLEN),A RET ; PSDFW: CALL EXP LD BC,(NUMBER) LD A,C LD C,B ; Reverse order of bytes LD B,A LD (OPCODE),BC LD A,(OCLEN) ADD A,2 LD (OCLEN),A OR A RET ; ; EQU - enter label in symbol table ; PSEQU: LD A,(PASS) ; On first pass only OR A RET NZ CALL EXP PUSH HL LD HL,(SYMEND) ; Better be last symbol entered DEC HL DEC HL LD BC,(NUMBER) LD (HL),C INC HL LD (HL),B POP HL OR A RET ; MAXTYP: DEFB 15 ; GRPTBL: DEFW GROUP1 ; Single byte, no options DEFW GROUP2 ; Jumps DEFW GROUP3 ; Call DEFW GROUP4 ; Clr cpl DEFW GROUP5 ; En dis DEFW GROUP6 ; Sel DEFW GROUP7 ; Strt DEFW GROUP8 ; Mov DEFW GROUP9 ; Dec inc DEFW GROUPA ; Movd DEFW GROUPB ; Add addc anl anld orl orld xrl DEFW GROUPC ; In DEFW GROUPD ; Outl DEFW GROUPE ; Movx DEFW GROUPF ; Xch xchd ; ; Group 1 - single byte opcodes, no options ; GROUP1: LD A,1 LD (OCLEN),A OR A RET ; ; Group 2 - double byte opcodes, second byte = address (jumps) ; GROUP2: LD A,2 LD (OCLEN),A LD A,(OPCODE) ; Get base opcode AND 0FH CP 2 JR Z,GRP2A ; Go if jbx CP 6 JR Z,GRP2A ; Go if conditional jumps CP 4 JR Z,GRP2B ; Go if jmp or call LD A,(HL) ; Else djnz, get register CP 'R' JP NZ,INVOPD ; Go if not valid INC HL LD A,(HL) INC HL SUB 30H JP C,INVOPD CP 8 JP NC,INVOPD LD B,A LD A,(OPCODE) OR B LD (OPCODE),A ; Set register in opcode LD A,(HL) CP ',' JP NZ,INVOPD INC HL ; GRP2A: CALL EXP ; Conditional jumps & jBX LD A,(NUMBER+1) LD B,A LD A,(PC+1) CP B JR Z,GRP2X LD A,4 LD (ERRFLG),A SCF RET ; GRP2X: LD A,(NUMBER) LD (OPCODE+1),A OR A RET ; GRP2B: CALL EXP ; Jump LD A,(NUMBER+1) AND 7 RRCA RRCA RRCA OR 4 LD (OPCODE),A JR GRP2X ; ; Group 3 - calls, second byte = A0 - A7 ; GROUP3: LD A,2 LD (OCLEN),A CALL EXP LD A,(NUMBER) LD (OPCODE+1),A LD A,(NUMBER+1) AND 7 RRCA RRCA RRCA OR 14H LD (OPCODE),A RET ; ; Group 4 - CLR, CPL ; GROUP4: LD A,1 LD (OCLEN),A LD A,(HL) ; Get operand CP 'A' ; Done if CLR/CPL A RET Z LD B,70H CP 'C' JR Z,GRP4A ; Go if carry flag CP 'F' JP NZ,INVOPD ; Go if invalid operand LD B,5EH INC HL LD A,(HL) ; Better be '0' or '1' AND 1 JR Z,GRP4A LD B,7EH GRP4A: LD A,(OPCODE) ; Modify opcode for carry flag ADD A,B LD (OPCODE),A RET ; ; EN DIS ; GROUP5: LD A,1 LD (OCLEN),A LD A,(HL) ; Get option CP 'I' RET Z CP 'T' JP NZ,INVOPD LD B,20H JR GRP4A ; ; SEL ; GROUP6: LD A,1 LD (OCLEN),A LD B,0 LD A,(HL) ; Get operand CP 'R' JR Z,GRP6A CP 'M' JP NZ,INVOPD INC B INC B ; GRP6A: INC HL LD A,(HL) CP 'B' JP NZ,INVOPD INC HL LD A,(HL) AND 1 JR Z,GRP6B INC B ; GRP6B: LD A,B CALL ROTATE LD B,A LD A,(OPCODE) ADD A,B LD (OPCODE),A RET ; ; STRT ; GROUP7: LD A,1 LD (OCLEN),A LD A,(HL) CP 'C' RET Z CP 'T' JP NZ,INVOPD LD A,(OPCODE) OR 10H LD (OPCODE),A RET ; ; MOV ; GROUP8: LD A,1 LD (OCLEN),A LD A,(HL) INC HL CP 'A' JR NZ,GRP8B ; Go if not text ; EXP: LD BC,0 LD (EXPOP),BC ; Clear operation and flags LD (NUMBER),BC ; Clear temp result LD (TMPVAL),BC DEC HL ; EXPNXT: INC HL LD A,(HL) CP ' ' JR Z,EXPNXT ; Skip spaces CP ';' ; Done if comment RET Z CP '$' JP Z,EXPC ; Go if PC reference CP '<' JR Z,EXPO CP '>' JR Z,EXPO CP CARAT JR Z,EXPO CP '0' JR C,EXPO ; Go if operator CP '9'+1 JP C,EXPN ; Go if decimal PUSH HL CALL FNDSYM ; Else is a symbol JR Z,EXPNX1 ; Go if found LD A,(PASS) OR A JR Z,EXPNX1 ; Go if pass 1 LD A,5 LD (ERRFLG),A ; Set undefined symbol error ; EXPNX1: LD C,(HL) ; Get it's value INC HL LD B,(HL) LD (NUMBER),BC DEC HL ; Point back to byte count ; EXPNX2: DEC HL LD A,(HL) AND 7FH CP 7 JR NC,EXPNX2 BIT 7,(HL) ; Test for multiple def POP HL JR Z,EXPNX3 LD A,3 LD (ERRFLG),A ; EXPNX3: CALL SKPLBL DEC HL JR EXPDO ; EXPO: CP ',' RET Z INC HL CP '+' JR Z,EXAD ; Go if addition CP '-' JR Z,EXSB ; Go if subtraction CP '*' JR Z,EXML ; Go if multiplication CP '/' JR Z,EXDV ; Go if division CP '&' JR Z,EXAND ; Go if logical and CP '%' JR Z,EXOR ; Go if logical or CP '!' JR Z,EXEOR ; Go if exclusive or CP '<' JR Z,EXSHL ; Go if shift left CP '>' JR Z,EXSHR ; Go if shift right CP CARAT JR Z,EXCRT ; Go if control char def CP QUOTE RET NZ ; Go if not ascii literal LD A,(HL) LD (NUMBER),A ; Set ascii literal INC HL LD A,(HL) CP QUOTE JR Z,EXPDO ; Go if ascii terminator DEC HL ; Else point back JR EXPDO ; EXCRT: LD A,(HL) SUB 40H LD (NUMBER),A JP EXPNXT ; EXAD: XOR A JR EXOPN ; EXSB: LD A,1 JR EXOPN ; EXML: LD A,2 JR EXOPN ; EXDV: LD A,3 JR EXOPN ; EXAND: LD A,4 JR EXOPN ; EXOR: LD A,5 JR EXOPN ; EXEOR: LD A,6 JR EXOPN ; EXSHL: LD A,7 JR EXOPN ; EXSHR: LD A,8 ; EXOPN: LD (EXPOP),A ; Set operator flag LD BC,(NUMBER) LD (TMPVAL),BC DEC HL JP EXPNXT ; And get next ; EXPC: LD BC,(PC) LD (NUMBER),BC ; Set pc value and do operation ; EXPDO: LD A,(EXPOP) OR A JR Z,EXPDOA ; Do addition DEC A JR Z,EXPDOS ; Do subtraction DEC A JR Z,EXPDOM ; Do multiplication DEC A JR Z,EXPDOD ; Do division DEC A JR Z,EXPDAN ; Do logical and DEC A JR Z,EXPDOR ; Do logical or DEC A JR Z,EXPDOX ; Do exclusive or DEC A JR Z,EXPDSL ; Do shift left JP EXPDSR ; Else do shift right ; EXPDOX: LD DE,(TMPVAL) LD BC,(NUMBER) LD A,D XOR B LD B,A LD A,E XOR C LD C,A LD (NUMBER),BC JP EXPNXT ; EXPDOD: LD DE,(TMPVAL) LD BC,(NUMBER) CALL DIV LD (NUMBER),DE JP EXPNXT ; EXPDOA: PUSH HL LD HL,(TMPVAL) LD BC,(NUMBER) ADD HL,BC JR EXPDX1 ; EXPDOS: PUSH HL LD HL,(TMPVAL) LD BC,(NUMBER) OR A SBC HL,BC ; EXPDX1: LD (NUMBER),HL POP HL JP EXPNXT ; EXPDOM: LD DE,(TMPVAL) LD BC,(NUMBER) CALL MULT ; EXPDX2: LD (NUMBER),BC JP EXPNXT ; EXPDAN: LD BC,(TMPVAL) LD DE,(NUMBER) LD A,B AND D LD B,A LD A,C AND E LD C,A JR EXPDX2 ; EXPDOR: LD BC,(TMPVAL) LD DE,(NUMBER) LD A,B OR D LD B,A LD A,C OR E LD C,A JR EXPDX2 ; EXPDSL: LD DE,(TMPVAL) LD A,(NUMBER) AND 0FH ; No more than 15 counts JR Z,EXPSXX ; Done if no shift LD B,A ; (else why shift it?) EXPSL1: SLA E ; Shift 16 bits left by count RL D DJNZ EXPSL1 ; EXPSSX: LD (NUMBER),DE ; EXPSXX: JP EXPNXT ; EXPDSR: LD DE,(TMPVAL) LD A,(NUMBER) AND 0FH JR Z,EXPSXX LD B,A ; EXPSR1: SRL D ; Shift 16 bits right RR E DJNZ EXPSR1 JR EXPSSX ; EXPN: CALL GETINT ; Get integer value BIT 0,C JR NZ,EXPN1 DEC HL ; EXPN1: DEC HL JP EXPDO ; GETINT: LD BC,0 LD (NUMBER),BC PUSH HL ; GTNT1: LD A,(HL) INC HL CP '0' JR C,GTNT2 CP '9'+1 JR C,GTNT1 CP 'A' JR C,GTNT2 CP 'F'+1 JR C,GTNT1 CP 'H' JR NZ,GTNT2 INC C ; GTNT2: POP HL LD A,(HL) INC HL SUB 30H RET C CP 10 JR C,GTNT3 BIT 0,C RET Z SUB 7 RET C CP 16 RET NC ; GTNT3: PUSH HL LD D,0 LD E,A LD HL,(NUMBER) BIT 0,C JR Z,GTNT5 LD B,4 ; GTNT4: SLA L RL H ; * 16 DJNZ GTNT4 ; GTNT4A: ADD HL,DE LD (NUMBER),HL JR GTNT2 ; GTNT5: ADD HL,HL PUSH BC LD B,H LD C,L ADD HL,HL ADD HL,HL ADD HL,BC POP BC JR GTNT4A ; ; Opcode Table ; Each entry contains a byte count for the mnemonic, the mnemonic ; itself, the default object code, and the group number. ; OPTBL: DEFB 3,'ADD',3,11 DEFB 4,'ADDC',13H,11 DEFB 3,'ANL',53H,11 DEFB 4,'ANLD',9CH,11 DEFB 4,'CALL',14H,3 DEFB 3,'CLR',27H,4 DEFB 3,'CPL',37H,4 DEFB 2,'DA',57H,1 DEFB 3,'DEC',7,9 DEFB 3,'DIS',15H,5 DEFB 4,'DJNZ',0E8H,2 DEFB 2,'EN',5,5 DEFB 2,'IN',8,12 DEFB 3,'INC',17H,9 DEFB 3,'INS',8,1 DEFB 3,'JB0',12H,2 DEFB 3,'JB1',32H,2 DEFB 3,'JB2',52H,2 DEFB 3,'JB3',72H,2 DEFB 3,'JB4',92H,2 DEFB 3,'JB5',0B2H,2 DEFB 3,'JB6',0D2H,2 DEFB 3,'JB7',0F2H,2 DEFB 2,'JC',0F6H,2 DEFB 3,'JF0',0B6H,2 DEFB 3,'JF1',76H,2 DEFB 3,'JMP',4,2 DEFB 4,'JMPP',0B3H,1 DEFB 3,'JNC',0E6H,2 DEFB 3,'JNI',86H,2 DEFB 4,'JNT0',26H,2 DEFB 4,'JNT1',46H,2 DEFB 3,'JNZ',96H,2 DEFB 3,'JTF',16H,2 DEFB 3,'JT0',36H,2 DEFB 3,'JT1',56H,2 DEFB 2,'JZ'0C6H,2 DEFB 3,'MOV',23H,8 DEFB 4,'MOVD',0CH,10 DEFB 4,'MOVP',0A3H,1 DEFB 5,'MOVP3',0E3H,1 DEFB 4,'MOVX',80H,14 DEFB 3,'NOP',0,1 DEFB 3,'ORL',43H,11 DEFB 4,'ORLD',8CH,11 DEFB 4,'OUTL',2,13 DEFB 3,'RET',83H,1 DEFB 4,'RETR',93H,1 DEFB 2,'RL',0E7H,1 DEFB 3,'RLC',0F7H,1 DEFB 2,'RR',77H,1 DEFB 3,'RRC',67H,1 DEFB 3,'SEL',0C5H,6 DEFB 4,'STOP',65H,1 DEFB 4,'STRT',45H,7 DEFB 4,'SWAP',47H,1 DEFB 3,'XCH',20H,15 DEFB 4,'XCHD',30H,15 DEFB 3,'XRL',0D3H,11 ; ; Pseudo ops ; DEFB 3,'EQU',0,80H DEFB 3,'ORG',0,81H DEFB 4,'DEFB',0,82H DEFB 2,'DB',0,82H DEFB 4,'DEFW',0,83H DEFB 2,'DW',0,83H DEFB 4,'DEFS',0,84H DEFB 2,'DS',0,84H DEFB 3,'END',0,85H ; Just in case some dork puts it in ; DEFB 0 ; End of table ; ; ; Write byte to hex file (at this point PC has not been updated) ; HEXFLG: ; Bits 0 - 4 = number of bytes in this record. Write record when ; full (32 bytes), and append checksum (in HEXCKS). ; Bit 5 = ; Bit 6 = 1 if skip has occured (ie: org or defs) ; Bit 7 = 1 if hex record has been opened, if = 0 then create header. ; HEXWR: LD A,(OCLEN) OR A RET Z ; Done if nothing to output LD B,A ; Set loop count PUSH HL PUSH DE LD DE,(PC) LD (HEXPC),DE ; Update .HEX file PC LD HL,OPCODE ; HEXWR1: LD A,(HL) ; Get data INC HL CALL HEXPUT ; Put in hex buffer LD DE,(HEXPC) INC DE LD (HEXPC),DE ; Update file PC DJNZ HEXWR1 POP DE POP HL RET ; HEXPUT: PUSH HL PUSH DE PUSH BC LD C,A ; Save .HEX data LD A,(HEXFLG) ; Get flags AND 0C0H ; Get bits 6 & 7 CP 80H JR C,HEXPTC ; Go if new record JR Z,HEXPTB ; Go if no skip & not new record PUSH BC CALL HEXDMP ; Else flush hexbfr & start new rcrd POP BC ; HEXPTC: LD A,80H ; Create new header LD (HEXFLG),A ; Flag new header LD DE,(HEXPC) LD (RECPC),DE ; Set start adrs of record LD A,D ADD A,E LD (HEXCKS),A ; Initialize checksum LD HL,HEXBFR LD (HL),':' INC HL LD (HL),'0' ; For now default byte count to zero INC HL LD (HL),'0' INC HL LD A,D ; Set record pc in hex buffer CALL PHEX LD A,E CALL PHEX LD (HL),'0' ; Set record type INC HL LD (HL),'0' ; HEXPTB: LD A,(HEXFLG) ; Get offset into hexbfr AND 1FH RLCA ADD A,9 ; Skip over record header LD E,A LD D,0 LD HL,HEXBFR ADD HL,DE LD A,(HEXCKS) ; Update checksum ADD A,C LD (HEXCKS),A LD A,C ; Get data CALL PHEX LD A,(HEXFLG) INC A LD (HEXFLG),A AND 1FH CALL Z,HEXDMP ; Write record if full POP BC POP DE POP HL RET ; PHEX: PUSH AF CALL ROTATE CALL PHEX1 POP AF ; PHEX1: AND 0FH CP 10 JR C,PHEX2 ADD A,7 ; PHEX2: ADD A,30H LD (HL),A INC HL RET ; ; Dump current record to hex output buffer ; HEXDMP: LD A,(HEXFLG) AND 1FH LD B,A ; Set loop count JR NZ,HXDMP1 ; Go if not full record LD B,32 ; HXDMP1: LD A,(HEXCKS) ; Update checksum ADD A,B LD (HEXCKS),A LD A,B LD HL,HEXBFR+1 CALL PHEX ; Set byte count LD A,B RLCA ADD A,9 ; Add overhead LD B,A LD HL,HEXBFR ; HXDMP2: LD A,(HL) INC HL PUSH BC CALL HEXOUT ; Put in file buffer POP BC DJNZ HXDMP2 LD A,(HEXCKS) ; Get checksum CPL INC A CALL PHEX DEC HL DEC HL LD A,(HL) ; Send checksum CALL HEXOUT INC HL LD A,(HL) CALL HEXOUT LD A,CR CALL HEXOUT LD A,LF CALL HEXOUT XOR A LD (HEXFLG),A RET ; HEXOUT: PUSH HL LD HL,(HEXDMA) LD BC,(HEXOFF) ADD HL,BC LD (HL),A POP HL LD A,(FILFLG) OR 2 LD (FILFLG),A INC BC LD A,C AND 80H LD (HEXOFF),BC RET Z LD A,(FILFLG) AND 0FDH LD (FILFLG),A CALL FLSHEX LD BC,0 LD (HEXOFF),BC RET ; LSTWR: LD A,(PASS) ; No output during pass 1 OR A RET Z LD HL,LSTBFR LD A,(OCLEN) OR A JR Z,LSTWR2 ; Go if no code generated LD DE,(PC) LD A,D CALL HEXBUF ; Put pc in list buffer LD A,E CALL HEXBUF LD (HL),':' INC HL LD (HL),' ' INC HL LD A,(OCLEN) CP 3 JR C,LSTWR0 ; Max of 2 hex in list file LD A,2 ; LSTWR0: LD B,A LD DE,OPCODE ; LSTWR1: LD A,(DE) INC DE CALL HEXBUF ; Put hex in buffer <= HL LD (HL),' ' INC HL DJNZ LSTWR1 LD A,(OCLEN) ; LSTWR2: LD B,13 OR A JR Z,LSTWR3 LD B,4 DEC A JR Z,LSTWR3 LD B,1 ; LSTWR3: LD (HL),' ' INC HL DJNZ LSTWR3 EX DE,HL ; List buffer -> DE LD HL,LINBFR LD B,64 ; LSTWR4: LD A,(HL) CP TAB JR NZ,LSTW4X INC HL ; LSTW4A: LD A,' ' LD (DE),A INC DE DEC B JR Z,LSTWR5 LD A,B AND 7 JR NZ,LSTW4A JR LSTWR4 ; LSTW4X: LD (DE),A INC HL INC DE CP LF JR Z,LSTWR5 CP CTRLZ JR Z,LSTWR5 DJNZ LSTWR4 ; LSTWR5: EX DE,HL LD (HL),CR INC HL LD (HL),LF LD HL,LSTBFR LD A,(PAGLEN) OR A JR Z,LSTWR6 LD A,(CURLIN) INC A LD (CURLIN),A ; LSTWR6: LD A,(HL) OR A RET Z INC HL PUSH AF CALL PUTLST POP AF CP LF JR Z,LSTWR7 CP CTRLZ JR NZ,LSTWR6 RET ; LSTWR7: LD A,(CURLIN) OR A RET Z LD B,A LD A,(PAGLEN+1) ; Get max printed line CP B RET NZ XOR A LD (CURLIN),A ; Clear line counter LD A,(PAGLEN) SUB B RET Z LD B,A ; LSTWR8: LD A,LF PUSH BC CALL PUTLST POP BC DJNZ LSTWR8 RET ; ; Write byte to list file ; PUTLST: PUSH HL LD HL,(LSTDMA) LD BC,(LSTOFF) ADD HL,BC LD (HL),A POP HL LD A,(FILFLG) OR 8 LD (FILFLG),A INC BC LD A,C AND 80H LD (LSTOFF),BC RET Z LD A,(FILFLG) AND 0F7H LD (FILFLG),A CALL FLSLST LD BC,0 LD (LSTOFF),BC RET ; HEXBUF: PUSH AF CALL ROTATE CALL HEXBF1 POP AF ; HEXBF1: AND 0FH CP 10 JR C,HEXBF2 ADD A,7 ; HEXBF2: ADD A,30H LD (HL),A INC HL RET ; SKPLBL: LD A,(HL) CP ':' JR Z,SKPLB1 CP ' ' JR Z,SKPLB1 CP 'A' JR NC,SKPLB1 CP '9'+1 RET NC CP '0' RET C ; SKPLB1: INC HL JR SKPLBL ; ; Find entry in symbol table. On entry HL => label name. ; On exit: NZ if not found, else Z and HL => value entry. ; FNDSYM: PUSH HL ; Get length of new entry LD C,0 ; FNDS1: LD A,(HL) CP '0' JR C,FNDS3 ; Go if end of label CP '9'+1 JR C,FNDS2 ; Go if numeric CP 'A' JR C,FNDS3 ; Go if end CP 'Z'+1 JR NC,FNDS3 ; Go if end ; FNDS2: INC HL INC C LD A,C CP 6 JR NZ,FNDS1 ; ; C = length of label name (6 maximum) ; FNDS3: POP HL ; Recover pointer to label LD DE,(SYMTAB) ; Get pointer to start of table ; FNDS4: LD A,(DE) ; Get label char count AND 7 JR NZ,FNDS5 ; Go if not end of table OR 0FFH ; Else entry not found LD HL,(SYMTAB) RET ; FNDS5: CP C JR NZ,FNDS8 ; Go if not same length PUSH HL ; Save text pointer PUSH DE ; And entry pointer PUSH BC ; And length LD B,A ; FNDS6: INC DE LD A,(DE) CP (HL) JR NZ,FNDS7 ; Go if no match INC HL DEC C DJNZ FNDS6 ; Do for all EX DE,HL ; Else point to value entry in HL INC HL POP BC POP DE ; Clean stack POP DE RET ; FNDS7: POP BC POP DE ; Restore registers POP HL LD A,(DE) ; Get length of this entry AND 7 ; FNDS8: ADD A,3 LD B,A ; FNDS9: INC DE ; Skip this entry DJNZ FNDS9 JR FNDS4 ; Go check next entry ; ; Enter label into symbol table. On entry HL => label name, ; (tmpval) = value of label. Only chars '0'-'9' and 'A'-'F' allowed. ; On exit HL => end of table (value entry + 2). ; ENTSYM: LD DE,(SYMEND) ; Get pointer to end of table PUSH HL ; Compute length of entry LD C,0 ; ENTSM1: LD A,(HL) CP '0' JR C,ENTSM3 ; Go if end of label CP '9'+1 JR C,ENTSM2 ; Go if numeric CP 'A' JR C,ENTSM3 ; Go if end CP 'Z'+1 JR NC,ENTSM3 ; Go if end ; ENTSM2: INC HL INC C LD A,C CP 6 JR NZ,ENTSM1 ; 6 char maximum ; ENTSM3: POP HL LD A,C ; No entry if count = 0 OR A RET Z LD (DE),A ; Set byte count in table INC DE LD B,0 ; Transfer label to table LDIR EX DE,HL LD BC,(TMPVAL) ; Get value LD (HL),C INC HL LD (HL),B INC HL LD (HL),0 ; Flag end of table LD (SYMEND),HL ; Update end of table pointer RET ; ; Data area ; LINUM: DEFW 0 ; Input line number PASS: DEFB 0 ; Pass number FILFLG: DEFB 0 ; File open flag IBFRP: DEFW IBFR ; Source buffer pointer FCBTMP: DEFW 0 ; Temp fcb pointer FCB1: DEFS 36,0 ; Object file fcb HEXDMA: DEFW 0 ; Dma pointer for hex file HEXOFF: DEFW 0 ; Offset into hex buffer HEXFLG: DEFB 0 ; Hex output flags HEXCKS: DEFB 0 ; Hex checksum HEXPC: DEFW 0 ; Hex file pc RECPC: DEFW 0 ; Hex record pc FCB2: DEFS 36,0 ; List file fcb CURLIN: DEFW 0 ; Current line in list buffer LSTDMA: DEFW 0 ; Dma pointer for list file LSTOFF: DEFW 0 ; Offset into list buffer BYTCNT: DEFW 0 ; Object code byte count ERRCNT: DEFW 0 ; Error count DMA1: DEFW 0 ; Dma buffer pointer for object code DMA2: DEFW 0 ; Dma bfr ptr for list code IBFEND: DEFW 0 ; End of input buffer pointer TMPVAL: DEFW 0 ; Temp value storage PC: DEFW 0 ; Program counter value OPCODE: DEFS 60 ; Current binary opcode OCLEN: DEFB 0 ; Opcode length (0 - 2) ERRFLG: DEFB 0 ; Error flag DEFW 0 SYMTAB: DEFW 0 ; Pointer to start of symbol table SYMEND: DEFW 0 ; Ptr to end of sym tbl SYMPTR: DEFW 0 ; Current ptr to sym tbl SYMVAL: DEFW 0 ; Pointer to value in sym tbl NUMBER: DEFW 0 ; Return value from expression evaluator EXPOP: DEFW 0 ; Expression evaluator operator and flags ; ERRTBL: DEFW ERRM0 ; Pointers to error messages DEFW ERRM1 DEFW ERRM2 DEFW ERRM3 DEFW ERRM4 DEFW ERRM5 DEFW ERRM6 ; IMSG: DEFB CR,LF,'UCASM Micro-controller Assembler v' DEFB VER+30H,'.',REV+30H,CR,LF DEFB 'Copyright (c) 1987, 1988 by J. L. Post',CR,LF,0 ; RDERMS: DEFB 'Can''t open source file',0 AWCRAP: DEFB CR,LF,'Too many errors!',0 ERRMSG: DEFB CR,LF,'** Error, ',0 LINMSG: DEFB ' in line number ',0 ; ERRM0: DEFB 'system failure',0 ; Should never happen ERRM1: DEFB 'unknown opcode',0 ; Pass 1 ERRM2: DEFB 'invalid operand',0 ; Pass 1 ERRM3: DEFB 'multiply defined symbol',0 ; Pass 1 ERRM4: DEFB 'out of range',0 ; Pass 2 ERRM5: DEFB 'undefined symbol',0 ; Pass 2 ERRM6: DEFB 'invalid expression',0 ; Pass 2 ; OPNEMS: DEFB 'Can''t open .',0 DEFB ' file',0 ; DONMSG: DEFB ' byte',0 DEFB ' error',0 ; LINBFP: DEFW LINBFR ; Line buffer pointer LINBFR EQU $ ; Input line buffer LSTBFR EQU LINBFR+100 ; List output buffer HEXBFR EQU LSTBFR+100 ; Hex output buffer ; STACK EQU HEXBFR+200 ; System stack space IBFR EQU STACK ; Source code input buffer ; ; ; Symbol table starts at end of input buffer. Entries consist of one ; byte for length of symbol name, ascii text of symbol, two bytes for ; symbol value (low byte first). ; END