From 9c61845db3912d77f9402a974e2d9c2670fec382 Mon Sep 17 00:00:00 2001 From: Ketmar Dark Date: Fri, 14 Aug 2020 03:22:56 +0300 Subject: [PATCH] more comments; assembler is isolated in its own module now; moved listing part out of the assembler --- bzasm80.zas | 217 +++++++++++++++++++++++------------------------------------- labman.zas | 121 +++++++++++++++++---------------- main.zas | 183 ++++++++++++++++++++++++++++++++++++++++++++++++-- output.zas | 44 +----------- parser.zas | 111 +++++++++++++++++-------------- 5 files changed, 388 insertions(+), 288 deletions(-) rewrite labman.zas (67%) diff --git a/bzasm80.zas b/bzasm80.zas index 1195e6d..5a0f809 100644 --- a/bzasm80.zas +++ b/bzasm80.zas @@ -1,24 +1,43 @@ -;WARNING! THIS MUST BE PAGE-ALIGNED! -ACCS: defs 256 ;STRING ACCUMULATOR - IF low(ACCS) - $ERROR "ACCS must be page-aligned!" - ENDIF +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Z80 assembler, based on the code from BBC Basic for Z80 +;; original code was written by R.T.Russell +;; the original license: +;; +;; Copyright (c) 1984-2000 R.T. Russell +;; +;; This software is provided 'as-is', without any express or implied +;; warranty. In no event will the authors be held liable for any damages +;; arising from the use of this software. +;; +;; Permission is granted to anyone to use this software for any purpose, +;; including commercial applications, and to alter it and redistribute it +;; freely, subject to the following restrictions: +;; +;; 1. The origin of this software must not be misrepresented; you must not +;; claim that you wrote the original software. If you use this software +;; in a product, an acknowledgment in the product documentation would be +;; appreciated but is not required. +;; 2. Altered source versions must be plainly marked as such, and must not be +;; misrepresented as being the original software. +;; 3. This notice may not be removed or altered from any source distribution. +;; +;; modifications, cleanups, etc. by Ketmar Dark // Invisible Vector +;; + MODULE BZ80ASM -LF equ 0AH -CR equ 0DH +CR equ #0D PC: defw #C000 ;PROGRAM COUNTER OC: defw #C000 ;ORIGIN of CODE -;LISTON: defb #ff ;LISTO & OPT FLAG -LISTON: defb #10 ;LISTO & OPT FLAG +ASMOPTS: defb #10 ;LISTO & OPT FLAG -;Bit 0 - LSB -;Bit 0 controls the listing. If it is set, a listing is displayed. -;Bit 1 -;Bit 1 controls the error reporting. If it is set, errors are reported. -;Bit 2 +; bits 0-3: seems to be unused, and never modified with OPT +; bit 4: show listing +; bit 5: seems to be unused +; bit 6: put code at OC instead of PC (but PC is still used in address calculations) +; bit 7: seems to be unused -COUNT: defb 0 ;PRINT POSITION +; bit 5 or bit 7 can be used to mark current pass (to avoid failing on unknown labels on pass 0) ;TCALL equ 0D6H @@ -29,133 +48,53 @@ TOR equ 84H csizestart = $ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; this puts 0 integer number into the corresponding registers -;; called exactly once to clear result of VAR -;; -ZERO: - ld a,0 - exx - ld h,a - ld l,a - exx - ld h,a - ld l,a - ld c,a - ret - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ASSEMBLER: ;; LANGUAGE-INDEPENDENT CONTROL SECTION: -;; Outputs: A=delimiter, carry set if syntax error. +;; Inputs: +;; IY: text buffer +;; IX: destination to put generated code at +;; Outputs: +;; A: delimiter +;; IY: delimiter position in text buffer +;; IX: points right after the generated code +;; carry set if syntax error (and A is undefined in this case) +;; others are dead (including extra register set and extra accum/flags) ;; ASSEM: call SKIP inc iy cp ':' jr z,ASSEM - cp ']' - ret z + ; this was used to terminate assembler section in BBC Basic + ;cp ']' + ;ret z cp CR ret z dec iy - ld ix,(PC) ;PROGRAM COUNTER - ld hl,LISTON - bit 6,(hl) - jr z,ASSEM0 - ld ix,(OC) ;ORIGIN of CODE -ASSEM0: push ix - push iy + ;push iy call ASMB - pop bc + ;pop bc pop de + ; exit if syntax error ret c + ; skip delimiters (but not terminators) call SKIP + ; exit with error flag if we're not at a terminator scf ret nz - dec iy -ASSEM3: - inc iy - ld a,(iy) - call isCmdSep - jr nz,ASSEM3 - ld a,(LISTON) + ; advance PC push ix pop hl or a sbc hl,de ex de,hl ;DE= NO. OF BYTES - push hl ld hl,(PC) - push hl add hl,de ld (PC),hl ;UPDATE PC - bit 6,a - jr z,ASSEM5 - ld hl,(OC) - add hl,de - ld (OC),hl ;UPDATE OC -ASSEM5: - pop hl ;OLD PC - pop ix ;CODE HERE - bit 4,a - jr z,ASSEM - ld a,h - call HEX - ld a,l - call HEXSP + ; reset carry and Z xor a - cp e - jr z,ASSEM2 -ASSEM1: - ld a,(COUNT) - cp 17 - ld a,5 - call nc,TABIT ;NEXT LINE - ld a,(ix) - call HEXSP - inc ix - dec e - jr nz,ASSEM1 -ASSEM2: - ld a,18 - call TABIT - push iy - pop hl - sbc hl,bc -ASSEM4: - ld a,(bc) - call EMIT - inc bc - dec l - jr nz,ASSEM4 - call CRLF - jp ASSEM - -; -HEXSP: - call HEX - ld a,' ' - jr OUTCH1 -HEX: - push af - rrca - rrca - rrca - rrca - call HEXOUT - pop af -HEXOUT: - and 0FH - add a,90H - daa - adc a,40H - daa -OUTCH1: - jp EMIT + ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -175,17 +114,11 @@ OUTCH1: ASMB: cp '.' jr nz,ASMB1 + ; skip dot inc iy - push ix - call VAR - push af - call ZERO - exx ld hl,(PC) - exx - pop af - call STORE - pop ix + call @DEFLABEL + ; done with the label ASMB1: call SKIP ret z @@ -362,7 +295,7 @@ GRPA: sbc a,a cp h TOOFAR: - jp nz,error_out_of_range + jp nz,@error_out_of_range VAL8: ld a,l jr BYTE2 @@ -494,7 +427,7 @@ OPT: jp z,DBX djnz ADDR call NUMBER - ld hl,LISTON + ld hl,ASMOPTS ld c,a rld ld a,c @@ -517,9 +450,10 @@ BYTE1: MISC: djnz OPT push ix - call EXPRS + call @PARSE_STR_EXPR pop ix - ld hl,ACCS + ; HL: buffer address + ; E: buffer length DEFM1: xor a cp e @@ -622,14 +556,17 @@ OFFSET: cp ')' ld hl,0 ret z + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; parse numeric expression NUMBER: call SKIP push bc push de push ix - call EXPRI + call @PARSE_INT_EXPR + ; HL: expression value pop ix - exx pop de pop bc ld a,l @@ -777,20 +714,28 @@ SIGN: ;; this entry point is used by FIND SKIP0: inc hl -; this entry point is used to skip blanks -; note that comma and right paren are considered blanks too -; as a consequence, operands may be delimited by spaces, or -; right parens, lol +;; this entry point is used to skip blanks and delimiters +;; note that comma and right paren are considered blanks too +;; as a consequence, operands may be delimited by spaces, or +;; right parens, lol +;; returns current char in A (and IY pointing to it) +;; zero flag set if we hit a terminator +;; zero flag reset if we hit a delimiter SKIP: + ; delimiter or terminator? call DELIM ret nz + ; if this is terminator, still stop call TERM ret z + ; this is delimiter, skip it inc iy jr SKIP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; used by FIND and SKIP +;; check if the current char is a delimiter or a terminator +;; zero flag set on delimiter/terminator DELIM: ld a,(iy) ;ASSEMBLER DELIMITER cp ' ' @@ -811,7 +756,7 @@ TERM: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; also used by assembler to check for command separator ;; the assembler itself does nothing with separators -isCmdSep: +ASM_IS_SEP: cp ':' ;ASSEMBLER SEPARATOR ret nc cp CR @@ -1121,3 +1066,5 @@ LDOPS: csizeend = $ $printf "assembler tables size: %d", csizeend-csizestart + + ENDMODULE BZ80ASM diff --git a/labman.zas b/labman.zas dissimilarity index 67% index b814e0e..9a3d6f8 100644 --- a/labman.zas +++ b/labman.zas @@ -1,58 +1,63 @@ -; label manager - -; this stores 4-byte integer at IX -; used inly once, to store label value -; number is H'L'HL (HL is high word) -; this is called from assembler after -; calling VAR to store PC value into -; newly created label (see ASMB) -; actually, only H'L' matters here, and -; it contains the value to store -STORE: - ret - - -; create new label -; this is called from assembler to -; create a new label (or replace an existing one) -; the code is in (see ASMB) -; the assembler doesn't try to check for duplicate labels -; IY points to the first char of label name -; after parsing, IY should point right after the parsed label -; labels always starts with dot, and the dot is already consumed -; it doesn't matter what this subroutine returns, it only -; has to parse and create a label -; STORE will be called immediately after calling this -; you can use IX register to pass address of label data block -; other registers can be considered dead -VAR: - ld hl,msg_getvar - call printstr - ld a,(iy) - call isAlpha - jp c,error_identifier_expected - ld b,0 - push iy -.idloop: - ld a,(iy) - call isIdChar - jr c,.idend - inc b - inc iy - jr .idloop -.idend: - ; print id - pop iy -.iddumploop: - ld a,(iy) - call EMIT - inc iy - djnz .iddumploop - ld a,'>' - call EMIT - ld a,13 - call EMIT - - ret - -msg_getvar: defx "NEWLABEL:<" +; label manager + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; create new label +;; this is called from assembler to create a new label (or replace an existing one) +;; the assembler doesn't try to check for duplicate labels +;; IY points to the first char of label name +;; after parsing, IY should point right after the parsed label +;; it doesn't matter what this subroutine returns, it only +;; has to parse and create a label +;; all registers expect IY can be trashed +;; +;; IN: +;; IY: text input buffer +;; HL: label value +;; OUT: +;; IY: text input buffer after the label +;; others (including alternate sets) are dead +;; +DEFLABEL: + push hl + ld hl,msg_getvar + call printstr + + pop hl + push hl + ld a,h + call HEX + pop hl + ld a,l + call HEX + ld a,':' + call EMIT + + ld a,(iy) + call isAlpha + jp c,error_identifier_expected + ld b,0 + push iy +.idloop: + ld a,(iy) + call isIdChar + jr c,.idend + inc b + inc iy + jr .idloop +.idend: + ; print id + pop iy +.iddumploop: + ld a,(iy) + call EMIT + inc iy + djnz .iddumploop + ld a,'>' + call EMIT + ld a,13 + call EMIT + + ret + +msg_getvar: defx "NEWLABEL:<" diff --git a/main.zas b/main.zas index fb4ed97..f13fff9 100644 --- a/main.zas +++ b/main.zas @@ -33,7 +33,8 @@ main: di ;ld ix,dest ld iy,strbuf -.goon: +asmmore: + ; print line to assemble $IF 0 push iy .dumploop: @@ -51,17 +52,59 @@ main: call EMIT $ENDIF - call ASSEM - jp c,error_syntax +doasm: + push ix ; save code destination + push iy ; we'll need it for listing + call BZ80ASM.ASSEM + jp c,list_and_error + pop bc + pop de + call ASM_LISTING +.skipnl: ld a,(iy) + inc iy + cp 13 + jr z,.skipnl + cp ':' + jr z,.skipnl + cp 32 + jr z,.skipnl + dec iy or a - jr nz,.goon + jr nz,asmmore ld a,6 out (#fe),a jr $ +list_and_error: + ld hl,errline + call printstrnl + push iy + pop bc ; current position in text buffer + pop de ; initial position + ; calc length + or a + sbc hl,de + jr z,.nothing + ; de: print from here + ; hl: length +.prloop: + ld a,(de) + call EMIT + inc de + dec hl + ld a,h + or l + jr nz,.prloop +.nothing: + ld hl,errline + call printstrnl + jp error_syntax +errline: defx "---------" + + strbuf: defm ".label:",13 ;defm "jr label",13 @@ -74,3 +117,135 @@ strbuf: defb 0 dest: defs 64,0 + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; show listing +;; extracted from assembler code, because we don't need it there +;; +;; IN: +;; IY: current position in text buffer +;; IX: current position in code buffer +;; BC: starting position in text buffer +;; DE: starting position in code buffer +;; OUT: +;; IX, IY: unchanged +;; others are dead +;; +;COUNT: defb 0 ;PRINT POSITION +ASM_LISTING: + ; calculate code length + push ix + pop hl + or a + sbc hl,de + ld (.codelen),hl + ld (.codestart),de + + ; calculate original PC + ex de,hl + ld hl,(BZ80ASM.PC) + or a + sbc hl,de + ; print original PC + push hl + ld a,h + call HEX + pop hl + ld a,l + call HEXSP + + ; print code bytes (if there are any) + ld de,(.codelen) + ld a,d + or a + jr nz,.nocode + ; do not print code if more than 4 bytes (long strings and dbs) + or e + jr z,.nocode + ; do not print code if more than 4 bytes (long strings and dbs) + cp 5 + jr nc,.nocode + + ; print hex bytes + ld hl,(.codestart) +.cdumploop: + ld a,(hl) + call HEXSP + inc hl + dec e + jr nz,.cdumploop + ; align printed code + ld a,(.codelen) +.alignloop: + cp a,4 + jr nc,.aligndone + ld e,a + ld a,32 + call EMIT + ld a,32 + call EMIT + ld a,32 + call EMIT + ld a,e + inc a + jr .alignloop +.aligndone: + +.nocode: + ; print source line, from BC to IY (exclusive) + ; calculate string length + push iy + pop hl + or a + sbc hl,bc + jr z,.notextline +.textloop: + ld a,(bc) + call EMIT + inc bc + dec hl + ld a,h + or l + jr nz,.textloop +.notextline: + ; print final CR + ld a,13 + call EMIT + ret + +.codelen: defw 0 +.codestart: defw 0 + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; print byte in hex with the trailing space +;; +HEXSP: + call HEX + ld a,' ' + jr OUTCH1 + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; print byte in hex +;; +HEX: + push af + rrca + rrca + rrca + rrca + call HEXOUT + pop af +HEXOUT: + and 0FH + add a,90H + daa + adc a,40H + daa +OUTCH1: + jp EMIT diff --git a/output.zas b/output.zas index 8571ebf..a110a1f 100644 --- a/output.zas +++ b/output.zas @@ -12,49 +12,7 @@ WIDTH: defb 30 ;PRINT WIDTH ; Destroys: A,F ; EMIT: - cp 10 - ret z - push af - call OSWRCH - pop af - cp 13 - ld a,0 - jr z,.cr - ld a,(COUNT) - inc a -.cr: - ld (COUNT),a - ret - -CRLF: - ld a,13 - call EMIT - ret - - -; A: position to move to -; if current output is further than that, go to the new line -; should preserve registers (except A and F) -TABIT: - ld hl,COUNT - cp (hl) - ret z - push af - call c,CRLF - pop af - sub (hl) -FILL: - or a - ret z - push bc - ld b,a -FILL1: - ld a,' ' - call EMIT - djnz FILL1 - pop bc - xor a - ret + jp OSWRCH printstr: diff --git a/parser.zas b/parser.zas index 8c9589e..1e89a29 100644 --- a/parser.zas +++ b/parser.zas @@ -1,20 +1,33 @@ -; returns current char in A -; sets zero flag on EOL +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; skip blanks +;; returns current char in A +;; sets zero flag on EOL +;; IN: +;; IY: text buffer +;; OUT: +;; IY: text buffer at non-blank or EOL +;; A: non-blank or EOL char +;; zero flag is set on EOL +;; skipBlanks: ld a,(iy) or a ret z cp 13 ret z - cp 10 - ret z inc iy cp 33 jr c,skipBlanks dec iy + ; reset zero flag + or a ret -; carry set: not alpha char + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; check if A is an alpha char +;; carry set: not alpha char +; isAlpha: cp 'a' jr c,.notlower @@ -29,7 +42,11 @@ isAlpha: ccf ret -; carry set: not digit char + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; check if A is a decimal digit +;; carry set: not digit char +;; isDigit: cp '0' ret c @@ -37,7 +54,11 @@ isDigit: ccf ret -; carry set: not id char + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; check if A is a valid identifier char (including digits) +;; carry set: not id char +;; isIdChar: call isAlpha ret nc @@ -53,39 +74,19 @@ isIdChar: or a ret -;EXPR - VARIABLE-TYPE EXPRESSION EVALUATION -; Expression type is returned in A'F': -; Numeric - A' bit 7=0, F' sign bit cleared. -; String - A' bit 7=1, F' sign bit set. -;Floating-point or integer result returned in HLH'L'C -; Integer result denoted by C=0 and HLH'L' non-zero. -;String result returned in string accumulator, DE set. -; DE addresses byte after last (E=length). -; -;Hierarchy is: (1) Variables, functions, -; constants, bracketed expressions. -; (2) ^ -; (3) * / MOD DIV -; (4) + - -; (5) = <> <= >= > < -; (6) AND -; (7) EOR OR -; -;ITEM - VARIABLE TYPE NUMERIC OR STRING ITEM. -;Item type is returned in A: Bit 7=0 numeric. -; Bit 7=1 string. -;Numeric item returned in HLH'L'C. -;String item returned in string accumulator, -; DE addresses byte after last (E=length). -; -;ITEM: CALL CHECK - - -; parse integer expression -; Output: Integer in HLH'L', C=0 -; Destroys: A,C,H,L,A',B',C',H',L',F,F' -EXPRI: +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; parse an integer expression +;; +;; IN: +;; IY: text buffer +;; OUT: +;; IY: text buffer after the expression +;; HL: expression value +;; everything other (including all alternate registers) is dead +;; +PARSE_INT_EXPR: call skipBlanks jp z,error_integer_expected ld c,0 @@ -132,15 +133,21 @@ EXPRI: sbc hl,de .noneg: call skipBlanks - exx - ld c,0 ret -; parse string expression -;String result returned in string accumulator, DE set. -; DE addresses byte after last (E=length). -EXPRS: +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; parse a string expression +;; +;; IN: +;; IY: text buffer +;; OUT: +;; HL: string buffer start +;; E: parsed string length +;; everything other (including all alternate registers) is dead +;; +PARSE_STR_EXPR: call skipBlanks jp z,error_string_expected cp 34 @@ -150,7 +157,8 @@ EXPRS: .strok: ld c,a ; terminator inc iy - ld de,ACCS + ld hl,ACCS + push hl .strloop: ld a,(iy) or a @@ -158,8 +166,15 @@ EXPRS: inc iy cp c jr z,.strdone - ld (de),a - inc de + ld (hl),a + inc hl jr .strloop .strdone: + pop de + or a + sbc hl,de + ex de,hl ret + +;; string accumulator +ACCS: defs 256,0 -- 2.11.4.GIT