Close

Assembly Assembler v1.0

A project log for Model 100 Assembler

Development of an assembler for the Tandy Model 100 portable computer.

clintrclintr 10/08/2014 at 03:400 Comments

The first assembly assembler is ready. Below I will paste the code and explain how to assemble it with the BASIC assembler. First some comments, though.

I have been using the following book as a reference:

"8080/8085 ASSEMBLY LANGUAGE PROGRAMMING MANUAL", 1977, 1978 Intel Corporation, 3065 Bowers Avenue, Santa Clara, California 95051

The assembler I'm posting here behaves like the one described in the book, except:

  1. no macros
  2. no EQU or SET directives (yet?)
  3. binary numbers are not understood; i.e. "101110B" is not valid
  4. multiple labels may start a line
  5. negative numbers are not understood (yet)
  6. the data list of a DB or DW directive may end with a comma; i.e. "db 0, 1, 'hello', 0dfH," (as a line) is not an error, and is equivalent to "db 0, 1, 'hello', 0dfH"
  7. no expression evaluation (yet)
  8. lines can be up to 255 characters long, I think, but don't do that
  9. labels can be up to 254 characters long
  10. some other stuff I don't remember right now?

I'm still using the "Virtual T" Model 100 emulator for development, and the instructions below are meant for that emulator, with 32kbyte of RAM. If you are using an actual Model 100, you will need to have 32kbyte of RAM, and some way of moving files into and out of its RAM file system.

The Code

Cut and paste the following into file "asmrun.ba":

1 'Copyright (C) 2014 Clinton Reddekop.
2 'You can redistribute and/or modify
3 'this program under the terms of the
4 'GNU General Public License as
5 ' published by the Free Software
6 'Foundation, either version 3 of the
7 'License, or (at your option) any
8 'later version.
9 'This program is distributed in the
10 'hope that it will be useful, but
11 'WITHOUT ANY WARRANTY; without even
12 'the implied warranty of
13 'MERCHANTABILITY or FITNESS FOR A
14 'PARTICULAR PURPOSE. See the GNU
15 'General Public License
16 '(available at
17 '<http://www.gnu.org/licenses/gpl-3.0.html>)
18 'for details.
19 '
20 ON ERROR GOTO6000
21 LOADM"ASM.CO"
22 CLEAR512:LN$="":HL!=0
25 CO!=58253+2:' ADX IN ASM.CO TO CALL
30 CALLCO!,0,HL!
40 HL!=0
50 SW!=PEEK(CO!+3)
60 ONSW!GOTO100,200,300,400,500,600,700,800,900
70 IFSW!<>0THENGOTO5000
80 CLEAR
90 GOTO7000
100 LINEINPUT#1,LN$
110 HL!=VARPTR(LN$)
120 GOTO30
200 OPENFN$FORINPUTAS1
210 GOTO30
300 CLOSE1
310 GOTO30
400 NM!=PEEK(CO!+8)+256*PEEK(CO!+9)
410 ?"error near input file line:";NM!
420 GOTO30
500 INPUT"input file";FN$
510 GOTO30
600 L!=PEEK(CO!+4)+256*PEEK(CO!+5)
605 H!=PEEK(CO!+6)+256*PEEK(CO!+7)
610 AL!=CO!-2
615 AH!=PEEK(CO!+10)+256*PEEK(CO!+11)
620 IFHIMEM>L!THENGOTO680
625 IFL!>H!THENGOTO680
630 IFH!>=MAXRAMTHENGOTO680
635 IFL!<AL!ANDH!>=AL!THENGOTO680
640 IFL!>=AL!ANDL!<AH!THENGOTO680
670 GOTO30
680 HL!=1:?"bounds error"
690 GOTO30
700 L!=PEEK(CO!+4)+256*PEEK(CO!+5)
710 H!=PEEK(CO!+6)+256*PEEK(CO!+7)
720 ?"LO =";L!,"HI =";H!
730 GOTO30
800 ?"success"
810 GOTO30
900 STOP
910 GOTO30
5000 ?"error bad instruction"
5010 CALLCO!,1,0
5020 GOTO7000
6000 CALLCO!,1,0
6010 ERROR ERR
7000 ?"assembler exit"
7010 END

Cut and paste the following into file "asm.do":

; asmasm v1.0
;An assembler program for the TRS-80
;Model 100 computer
; Copyright (C) 2014 Clinton Reddekop.
; You can redistribute and/or modify
; this program under the terms of the
; GNU General Public License as
; published by the Free Software
; Foundation, either version 3 of the
; License, or (at your option) any
; later version.
; This program is distributed in the
; hope that it will be useful, but
; WITHOUT ANY WARRANTY; without even
; the implied warranty of
; MERCHANTABILITY or FITNESS FOR A
; PARTICULAR PURPOSE. See the GNU
; General Public License (available at
; <http://www.gnu.org/licenses/gpl-3.0.html>)
; for details.
;Reference:
; "8080/8085 ASSEMBLY LANGUAGE
; PROGRAMMING MANUAL",
; 1977, 1978 Intel Corporation
; 3065 Bowers Avenue, Santa Clara,
; California 95051
;NOTE for any function:
; I always allow and assume that the
; a register and flags may be modified,
; unless noted otherwise.
; Any registers listed as outpars
; are modified by the function.
; Otherwise registers are preserved,
; unless noted otherwise.
;NOTE below, "ASM" means this program,
; as machine language LOADM'ed to the
; correct location in RAM.
;This program has been designed to be
; run from a companion BASIC program.
;The BASIC program, in a loop, keeps
; calling calladdr in ASM. Every time
; ASM returns to the BASIC program,
; BASIC checks the byte at inst (below)
; and performs the job corresponding to
; the inst value.
;BASIC normally calls ASM with
; a=0. BASIC may call ASM
; with a=1 instead, to tell ASM to put
; itself back in its initial state.
org 0e38dh
ret ;prevent running w/o companion
;BASIC prog
db 0h ;this byte wasted
; address 0e002h - call to this address
; from the companion BASIC program
calladdr:
jmp entry
; GLOBALS
;instruction byte back to BASIC
;instructions are:
; 0: end
; 1: get next line from input file
; into BASIC variable LN$
; (on next call, in hl, BASIC passes
; back a ptr to LN$ in its var tbl)
; 2: open input file
; 3: close input file
; 4: print error msg w/ line number
; taken from linenum (below)
; 5: get input filename
; 6: check for bounds error
; 7: print bounds (loaddr and hiaddr)
; 8: print "success"
;
; (DO 5 before 2, 2 before 1 before 3)
;
; NOTE inst 6: the bounds in loaddr,
; hiaddr are checked to make sure the
; program we are assembling will fit
; in [HIMEM, MAXRAM), and also not
; overwrite ASM - if so, the
; next call into ASM will have
; hl=0, and if not, BASIC will print
; an error message and call this prog
; with hl=1.
;
;calladdr+3 (BASIC PEEKS THIS)
inst:
db 0h
;calladdr+4 (BASIC PEEKS THIS)
loaddr: dw 0h ;lowest address of prog
;calladdr+6 (BASIC PEEKS THIS)
hiaddr: dw 0h ;highest address of prog
;calladdr+8 (BASIC PEEKS THIS)
linenum: dw 0h ;line number in input
;file of curline
;calladdr+10 (BASIC PEEKS THIS)
dw asmend
hlin: dw 0h
spbasic: dw 0h ;for saving BASIC's sp
splocal: dw endstack ;initial sp
loccntr: dw 0h ;location counter
;;; for testing
; dw 0ffh
;svaf: dw 0fafh
;svbc: dw 0cbch
;svde: dw 0edeh
;svhl: dw 1414h
;from: dw 0h
;local stack
;;;stack: ds 30h
;;;endstack: dw main ;go here on 1st entry
stack:
db 0ach,0ach,0ach,0ach,0ach,0ach
db 0ach,0ach,0ach,0ach,0ach,0ach
db 0ach,0ach,0ach,0ach,0ach,0ach
db 0ach,0ach,0ach,0ach,0ach,0ach
db 0ach,0ach,0ach,0ach,0ach,0ach
db 0ach,0ach,0ach,0ach,0ach,0ach
db 0ach,0ach,0ach,0ach,0ach,0ach
db 0ach,0ach,0ach,0ach
endstack: dw main ;go here on 1st entry
db 0fh,0fh,0fh,0fh,0fh,0fh,0fh,0fh
;workspace
curtoken: ds 100h ;current token
curline: ds 100h ; current line
; from input file
labelmap: ds 800h ;label map storage
endlabelmap:
clpos: dw 0h ;position in curline
mapused: dw 0h ;lbl map amount used
; BASIC INTERFACE CODE
; BASIC calls always go through here ;
entry:
;if a is nonzero, restore to
; uncalled state
ana a
jnz restore
; save BASIC sp, use local stack ;
shld hlin ; save hl param
lxi h,0h
dad sp
shld spbasic
lhld splocal
sphl
lhld hlin ; restore hl param
; tos has address to return to ;
; (on first entry this is main) ;
ret
; final return to BASIC when ending ;
exit:
xra a
sta inst ; end instruction
lxi h,main
shld endstack
lxi sp,endstack
; fall through... ;
; we return to BASIC through here ;
backtobasic:
; save local sp, use BASIC stack ;
lxi h,0h
dad sp
shld splocal
lhld spbasic
sphl
ret
; reinitialize stack when BASIC tells ;
; us an error occurred ;
restore:
lxi h,main
shld endstack
lxi h,endstack
shld splocal
xra a
sta inst
ret
; MORE GLOBALS
;strings
strsp: db 73h,70h,0h
strpsw: db 70h,73h,77h,0h
;when this is set, looking up a label
; that doesn't exist doesn't cause an
; error:
inel: db 1h
;FUNCTIONS
;main
;does a first pass of the input file to
; make sure that:
; - there are no syntax errors,
; - the file is formatted correctly,
; - the memory to be written is
; within [HIMEM,MAXRAM-1]
; and to determine values of all labels
;if no problems with first pass, does
; a second pass to write the program
; into memory.
;NOTE:
; You probably want to read this one
; last; it won't make sense unless you
; know what the other functions do.
;stack usage 0 words ** return address
; and regs are saved on BASIC stack,
; we are not counting words on it
main:
; *** first pass *** ;
; get input file name ;
call getinfilename
; open input file ;
call openinputfile
; init globals for first pass ;
lxi h,0ffffh
shld loaddr
lxi h,0h
shld hiaddr
shld loccntr
shld linenum
shld labelmap ;null marks end
inx h
shld mapused
; set up functions for first pass ;
lxi h,pokefirst
shld pokeaddr
mvi a,1h
sta inel
; run first pass ;
call singlepass
; close input file ;
call closeinputfile
; check hiaddr,loaddr ;
call checkbounds
; *** second pass *** ;
; open input file ;
call openinputfile
; init globals for second pass ;
lxi h,0h
shld loccntr
shld linenum
; set up funcs for second pass ;
lxi h,labeldef
mvi m,0c9h ; opcode for ret
lxi h,pokesecond
shld pokeaddr
xra a
sta inel
; run second pass ;
call singlepass
; close input file ;
call closeinputfile
; report success ;
call reportsuccess
jmp exit
;handle an error in currently-open
; input file - prints line number as
; part of error message
;modifies all regs
;stack usage 2 words
errorinfile:
mvi a,4h ; instruction to
sta inst ; print error w/line num
lxi h,reportederror
push h
jmp backtobasic
reportederror:
jmp exit
;func to get the input file name
;(BASIC keeps it for use later)
;modifies all regs
;stack usage 1 words
getinfilename:
mvi a,5h ; instruction to
sta inst ; get input file name
jmp backtobasic
;func to open the input file
;modifies all regs
;stack usage 1 words
openinputfile:
mvi a,2h ; instruction to
sta inst ; open input file
jmp backtobasic
;func to close the input file
;modifies all regs
;stack usage 1 words
closeinputfile:
mvi a,3h ; instruction to
sta inst ; open input file
jmp backtobasic
;func to print bounds (loaddr, hiaddr)
;modifies all regs
;stack usage 1 words
printbounds:
mvi a,7h ; instruction to
sta inst ; print bounds
jmp backtobasic
;;; FOR TESTING
;basicpause:
; shld svhl
; pop h
; push h
; shld from
; xchg
; shld svde
; push psw
; pop h
; shld svaf
; push b
; pop h
; shld svbc
; mvi a,9h
; sta inst
; lxi h,paused
; push h
; jmp backtobasic
;paused:
; lhld svbc
; push h
; pop b
; lhld svaf
; push h
; pop psw
; lhld svde
; xchg
; lhld svhl
; ret
;func to check bounds (loaddr, hiaddr)
; to make sure the code will fit in
; [HIMEM, MAXRAM)
; and print "bounds error" if not
;modifies all regs
;stack usage 2 words
checkbounds:
mvi a,6h ; instruction to
sta inst ; check bounds
lxi h,checkedbounds
push h
jmp backtobasic
checkedbounds:
;BASIC passes nonzero in hl if
; bounds error found
mov a,h
ora l
rz
call printbounds
jmp exit
;func to print "success"
;modifies all regs
;stack usage 2 words
reportsuccess:
mvi a,8h ; instruction to
sta inst ; print "success"
lxi h,printedsuccess
push h
jmp backtobasic
printedsuccess:
call printbounds
ret
;func to get the next line of the input
; file and put it in curline, 0-term'd
;also sets clpos to point at the first
; char of the line
;if got a line, cy=1 on return;
;else some error and cy=0 on return.
;; CORRECTION might get a BASIC
;; error and never return
;stack usage 5 words
getnextline:
push b
push d
push h
; get line into BASIC var LN$ ;
mvi a,1h ; instruction to
sta inst ; get line from file
lxi h,gotline
push h
jmp backtobasic
gotline:
;now hl points to the variable
;entry in the variable table
; copy LN$ to curline ;
mov b,m ; length
inx h
mov e,m ; string start lsB
inx h
mov d,m ; string start msB
lxi h,curline
call memcopy
mvi m,0h ; 0-terminate
; set clpos to start of line ;
lxi h,curline
shld clpos
; update line number ;
lhld linenum
inx h
shld linenum
pop h
pop d
pop b
stc
ret
;func to copy a block of mem to another
; inpars: src block ptr in de,
; dst block ptr in hl, length in b
; modifies b, advances both hl and de
; past their respective blocks
memcopy:
xra a
cmp b
rz
memcopyloop:
ldax d
mov m,a
inx d
inx h
dcr b
jnz memcopyloop
ret
;func to compare 2 strings upto first
; 0-terminator or max 255 characters
;inpars: str1 in de, str2 in hl,
;result is in z, cy flags on return
;stack usage 2 words
strcmp:
push b
mvi b,0ffh
call strncmp
pop b
ret
;func to compare 2 strings upto first
; 0-terminator or a max length
;*** NOTE that msb of first byte of
; str1 is ignored in comparison - see
; comment in code
;inpars: str1 in de, str2 in hl,
; max length in b
;result is in z, cy flags on return
;stack usage 3 words
strncmp:
push b
push d
push h
inr b
; clear msb of first byte in str1 ;
; we use this to flag assembler ;
; directives in opcode table ;
ldax d ; a = *str1
ani 7fh
strncmploop:
; check for max length
dcr b
jz retstrncmp
; compare next char
cmp m ; none = a - *str2
jnz retstrncmp
inx d ; str1++
inx h ; str2++
; check for 0-terminator
ana a
ldax d ; a = *str1
jnz strncmploop
retstrncmp:
pop h
pop d
pop b
ret
;func to find length of 0-term'd string
;inpar: str in hl
;outpar: length in bc
;stack usage 2 words
strlen:
push h
lxi b,0h
xra a
strlenloop:
cmp m
jz retstrlen
inx h
inx b
jmp strlenloop
retstrlen:
pop h
ret
;func to search for given instruct'n in
; the opcode table - uses binary search
;the string must have been converted
; to lowercase
;inpar: str in hl - mnemonic to find
;outpars:
; if str found, cy clear, else cy set
; if str found, bc gets last 2 bytes
; of entry
; if str found and the entry in the
; opcode table is marked as an
; assembly directive, then z clear
; else z set
;stack usage 5 words
getopc:
push d
push h
;; if (strlen(str)>4) { return(fail); }
call strlen
mov a,b
ana a
jnz retgetopcfail
mvi a,4h
cmp c
jc retgetopcfail
;; bottom = 0; top = 0x55;
mvi b,0h ;idx of first table entry
mvi c,55h ;idx one past last entry
;; while ((top-bottom) != 1) {
getopcloop:
mov a,c
sub b
dcr a
mov a,b
jz checkbottom
; if str is in the table, index is
; in [b,c)
;;mid = (bottom + top) / 2;
;;switch(strncmp(&table[mid],str,4){
add c ; note cy is clear here
push b ; save bottom and top
rar ; now a = (b+c)/2
mov c,a ; save mid for later in c
ana a ; clear cy
ral ; now a = ((b+c)/2)*2
push h ; save str
mov l,a
mvi h,0h
mov d,h
mov e,l
dad d
dad d ; now hl has offset of entry
; to check = 6*((b+c)/2)
lxi d,opctblstart
dad d
xchg ; de is adx of entry to check
; = &table[mid]
pop h ; hl is str
mvi b,4h
call strncmp
mov a,c ; a = mid
pop b ; b = bottom, c = top
;; case 0: return (success); break;
;; case -1: bottom = mid; break;
;; case +1: top = mid; break;}
;; } // while
;; return (fail);
jz retgetopcpass
jc movebottomup
mov c,a
jmp getopcloop
movebottomup:
mov b,a
jmp getopcloop
checkbottom:
;if bottom still zero, haven't
; checked table[0] yet
;(bottom is still in a)
;; if (bottom == 0) {
;; ** can use strcmp b/c btm table **
;; ** entry has a null-term'd mnem. **
;; if (strcmp(&table[0],str)==0){
;; return (success);
;; }
;; }
ana a
jnz retgetopcfail
lxi d,opctblstart
call strcmp
jz retgetopcpass
retgetopcfail:
stc
jmp retgetopc
retgetopcpass:
; check if entry is an assembler
; directive
ldax d
ani 80h
mov h,a ; save directive flag
; put last 2 bytes of entry in bc
inx d
inx d
inx d
inx d
ldax d
mov b,a
inx d
ldax d
mov c,a
mov a,h ; ret directive flag in a
ana a ; cy=0, set/reset z
retgetopc:
pop h
pop d
ret
;func to skip past whitespace in string
;whitespace: chars in [1h,20h]
;inpar: str in hl
;outpar: ptr to first non-ws char in hl
;stack usage 2 words
skipws:
push b
mvi c,21h
skipwsloop:
mov a,m
ana a
jz retskipws
cmp c
jnc retskipws
inx h
jmp skipwsloop
retskipws:
pop b
ret
;func to convert a string to lowercase
; in-place
;inpar: str in hl
;stack usage 4 words
tolower:
push b
push d
push h
mvi c,20h ; 'a'-'A'
mvi d,41h ; 'A'
mvi e,5bh ; 'Z'+1
tolowerloop:
mov a,m
ana a
jz rettolower ; end of str
cmp d
jc contlowerloop ; a < 'A'
cmp e
jnc contlowerloop ; a > 'Z'
add c ; convert to lowercase
mov m,a
contlowerloop:
inx h
jmp tolowerloop
rettolower:
pop h
pop d
pop b
ret
;func to check if the char in a is
; alphanumeric
;iff so, cy=1 on return
;preserves all registers
;stack usage 1 words
isalphanum:
cpi 30h ; '0'
jc failalphanum
cpi 3ah ; '9'+1
rc ; a is ['0','9']
cpi 41h ; 'A'
jc failalphanum
cpi 5bh ; 'Z'+1
rc ; a is ['A','Z']
cpi 61h ; 'a'
jc failalphanum
cpi 7bh ; 'z'+1
rc ; a is ['a','z']
failalphanum:
ana a ; clear cy
ret
;func to get next token from curline
; (starting at clpos) and put it in
; curtoken, advancing clpos past the
; token in the line
;comments (starting with ';') are
; skipped
;the token put in curtoken is always
; 0-terminated
;valid tokens are:
; - "" (indicates end of line reached)
; - ","
; - "'"
; - an alphanumeric string
; - an alphanumeric string followed
; immediately by ':'
; if the token ends with ':' the ':'
; is not copied into curtoken
; on return, z=0 if the token ended
; with ':', else z=1
;stack usage 4 words
getnexttoken:
push b
push d
push h
mvi b,0h
lxi d,curtoken ; de points to token
lhld clpos ; hl points to line
call skipws
; get first char of token
mov a,m
ana a ; end of line?
jz retgetnexttoken
cpi 2ch ; ','
jz onechartoken
cpi 27h ; '\''
jz onechartoken
cpi 3bh ; ';'
jz retgetnexttoken ; ignore comment
; if we get here token must start
; with an alphanumeric char
call isalphanum
jnc errorinfile
getnexttokenloop:
stax d
inx d
inx h
mov a,m
call isalphanum
jc getnexttokenloop
; token may end with ':'
cpi 3ah ; ':'
jnz retgetnexttoken
mov b,a ; make b nonzero
inx h
jmp retgetnexttoken
onechartoken:
stax d
inx d
inx h
retgetnexttoken:
shld clpos
xra a
stax d ; 0-terminate the token
lxi h,curtoken
call tolower
mov a,b
ana a ; return z=0/1
pop h
pop d
pop b
ret
;same as above, but error if token
; is label
getnexttokennolabel:
call getnexttoken
jnz errorinfile
ret
;fnc to do a single pass of the input
; file
;loops reading and processing lines
; from the input file
;no inpars or return value
;stack usage 4 words
singlepass:
push b
push d
push h
jmp mainloopnoemptycheck
mainloop:
; make sure rest of line empty
call getnexttoken
lda curtoken
ana a
jnz errorinfile
mainloopnoemptycheck:
; handle next line
call getnextline
jnc errorinfile ;no end directive
mainloopuserestofline:
call getnexttoken
jz notlabel
; make sure label not a keyword ;
call getopc
jnc errorinfile
; store the label/value ;
lhld loccntr
call labeldef
jmp mainloopuserestofline
notlabel:
lxi h,curtoken
xra a
cmp m
jz mainloopnoemptycheck ;empty line
call getopc
jc errorinfile
jnz handledirective
; now b=opcode, c=fmt
; handle operands as given by fmt
mov a,c
ral
mov c,a
cc dorm543
mov a,c
ral
mov c,a
cc dorp
mov a,c
ral
mov c,a
cc dorbd
mov a,c
ral
mov c,a
cc dorw
mov a,c
ral
mov c,a
cc docomma
mov a,c
ral
mov c,a
cc dorm210
mov a,b
call poke ; write opcode to mem
mov a,c
ral
mov c,a
cc do1byte
mov a,c
ral
mov c,a
cc do2byte
jmp mainloop
handledirective:
; jump to cb
mov h,c
mov l,b
pchl
dorst:
call getnexttokennolabel
call getconstant
mov a,h
ana a
jnz errorinfile ; too big
mov a,l
cpi 8h
jnc errorinfile ; too big
ana a ; clear cy
ral ; shift code up
ral
ral
adi 0c7h ; add opcode into code
call poke ; write opcode to mem
jmp mainloop
dodb:
; get next byte(s), write to mem ;
call getnexttokennolabel
lda curtoken
; allow line to end before any ;
; data, or immediately after ',' ;
ana a
jz mainloop
; not line end ;
cpi 27h ; '\''
jz strconstant
call getconstant
mov a,h
ana a
jnz errorinfile ; const too big
mov a,l
call poke
jmp endstrconstant
strconstant:
mvi b,27h ; '\''
lhld clpos
strconstantloop:
mov a,m
inx h
cmp b
jz endstrconstantloop
call poke
jmp strconstantloop
endstrconstantloop:
shld clpos
endstrconstant:
; get comma or line end ;
call getnexttoken
lda curtoken
cpi 2ch ; ','
jz dodb
ana a
jz mainloop
jmp errorinfile
dodw:
; get next word, write to mem ;
call getnexttokennolabel
; allow line to end before any ;
; data, or immediately after ',' ;
lda curtoken
ana a
jz mainloop
; must be a constant ;
call getconstant
mov a,l
call poke
mov a,h
call poke
; get comma or line end ;
call getnexttoken
lda curtoken
cpi 2ch ; ','
jz dodw
ana a
jz mainloop
jmp errorinfile
dods:
call getnexttokennolabel
lda inel ;we need to know the val
mov b,a ; to set loccntr to in
xra a ; BOTH passes, so set
sta inel ; inel to 0 here
call getconstant
mov a,b
sta inel ;restore inel
xchg
; poke 0 de times ;
dsloop:
mov a,d
ora e
jz mainloop
xra a
call poke
dcx d
jmp dsloop
doorg:
call getnexttokennolabel
lda inel ;we need to know the val
mov b,a ; to set loccntr to in
xra a ; BOTH passes, so set
sta inel ; inel to 0 here
call getconstant
mov a,b
sta inel ;restore inel
shld loccntr
jmp mainloop
doend:
;TODO def of start address?
;fall through;
retsinglepass:
pop h
pop d
pop b
ret
;func to read the next token from
; curline, expecting the name of an
; 8-bit reg or "m".
;puts the corresponding 3-bit
;code into opcode[2,1,0]
;inpars: opcode in b
;outpars: modified opcode in b
;stack usage 2 words
dorm210:
push d
call helprm
mov a,d
add b
mov b,a
pop d
ret
;func to read the next token from
; curline, expecting the name of an
; 8-bit reg or "m".
;outpar: the corresponding 3-bit code
;in d
;stack usage 2 words
helprm:
push h
call getnexttoken
lxi h,curtoken
mov a,m
cpi 61h ; 'a'
mvi d,7h
jz rethelprm
cpi 62h ; 'b'
mvi d,0h
jz rethelprm
cpi 63h ; 'c'
mvi d,1h
jz rethelprm
cpi 64h ; 'd'
mvi d,2h
jz rethelprm
cpi 65h ; 'e'
mvi d,3h
jz rethelprm
cpi 68h ; 'h'
mvi d,4h
jz rethelprm
cpi 6ch ; 'l'
mvi d,5h
jz rethelprm
cpi 6dh ; 'm'
mvi d,6h
jnz errorinfile
rethelprm:
;check that token length is 1
inx h
xra a
cmp m
jnz errorinfile
pop h
ret
;func to read the next token from
; curline, expecting name of an rp:
; "b", "d", "h" or "sp".
;puts the corresponding 2-bit
;code into opcode[5,4]
;inpars: opcode in b
;outpars: modified opcode in b
;stack usage 2 words
dorp:
push d
call helprbdh
mov a,e ; don't allow "psw"
cpi 70h
jz errorinfile
mov a,d
add b
mov b,a
pop d
ret
;func to read the next token from
; curline, expecting "b", "d", "h",
; "sp" or "psw".
;outpars:
; puts the corresponding code for bits
; [5,4] into d, and the first char of
; the token into e
;stack usage 2 words
helprbdh:
push h
call getnexttoken
lxi h,curtoken
mov a,m
mov e,a
cpi 62h ; 'b'
mvi d,0h
jz rethelprbdh
cpi 64h ; 'd'
mvi d,10h
jz rethelprbdh
cpi 68h ; 'h'
mvi d,20h
jz rethelprbdh
lxi d,strsp
call strcmp
mvi d,30h
mvi e,73h
jz rethelprbdhnolenchk
lxi d,strpsw
call strcmp
mvi d,30h
mvi e,70h
jz rethelprbdhnolenchk
jmp errorinfile
rethelprbdh:
;check that token length is 1
inx h
xra a
cmp m
jnz errorinfile
rethelprbdhnolenchk:
pop h
ret
;func to read the next token from
; curline, expecting name of an rbd:
; "b" or "d".
;puts the corresponding 1-bit
;code into opcode[4]
;inpars: opcode in b
;outpars: modified opcode in b
;stack usage 2 words
dorbd:
push d
call helprbdh
mov a,d
cpi 11h ; only allow "b" or "d"
jnc errorinfile
add b
mov b,a
pop d
ret
;func to read the next token from
; curline, expecting name of an rw:
; "b", "d", "h" or "psw".
;puts the corresponding 2-bit
;code into opcode[5,4]
;inpars: opcode in b
;outpars: modified opcode in b
;stack usage 2 words
dorw:
push d
call helprbdh
mov a,e ; don't allow "sp"
cpi 73h
jz errorinfile
mov a,d
add b
mov b,a
pop d
ret
;func to read the next token from
; curline, expecting ","
;stack usage 1 words
docomma:
;note no need to check length -
; getnexttoken never returns any
; token starting w/',' other than
; ","
call getnexttoken
lda curtoken
cpi 2ch
jnz errorinfile
ret
;func to read the next token from
; curline, expecting the name of an
; 8-bit reg or "m".
;puts the corresponding 3-bit
;code into opcode[5,4,3]
;inpars: opcode in b
;outpars: modified opcode in b
;stack usage 2 words
dorm543:
push d
call helprm
mov a,d
ana a ; clear cy
ral
ral
ral
add b
mov b,a
pop d
ret
;func to read the next token from
; curline, expecting a constant (either
; literal or named) in range [0h,ffh]
; which it writes in a single byte to
; mem at the current location counter,
; advancing the location counter past
; the write
;modifies h, l
;stack usage 1 words
do1byte:
call getnexttokennolabel
call getconstant
mov a,h
ana a
jnz errorinfile ; > ffh
mov a,l
call poke
ret
;func to read the next token from
; curline, expecting a constant (either
; literal or named) in range [0h,ffffh]
; which it writes in a 2-byte word to
; mem at the current location counter,
; advancing the location counter past
; the write
;modifies h, l
;stack usage 1 words
do2byte:
call getnexttokennolabel
call getconstant
mov a,l
call poke
mov a,h
call poke
ret
;func to write the byte in a to mem at
; the current location counter, and
; advance the location counter past the
; write
;stack usage 3 words
poke:
db 0c3h ; opcode for jmp
;addr of pokefirst or pokesecond
; will be put here, depending on
; which pass:
pokeaddr:
dw 0h
pokefirst: ;does this in first pass
;keep track of lowest and highest
; addresses of program, but don't
; write memory yet
push h
push d
lhld loaddr
xchg
lhld loccntr
mov a,l
sub e
mov a,h
sbb d
jnc checkhiaddr
shld loaddr
checkhiaddr:
xchg ;loccntr in de
lhld hiaddr
mov a,l
sub e
mov a,h
sbb d
xchg ;loccntr in hl
jnc retpokefirst
shld hiaddr
retpokefirst:
;advance loccntr
inx h
shld loccntr
pop d
pop h
ret
pokesecond: ;does this in second pass
;do the byte write
push h
lhld loccntr
mov m,a
inx h
shld loccntr
pop h
ret
;func to read a constant (literal or
; named) from curtoken
;no inpars
;outpar: the number in hl
;stack usage 3 words
getconstant:
push b
push d
lda curtoken
ana a
jz errorinfile
checkforcharliteral:
cpi 27h ; '\''
jnz checkfordigit
; token was '\'' - get rest of
; char lit. from clpos in curline
lhld clpos
mov a,m
ana a
jz errorinfile
cpi 27h ; '\''
jz errorinfile
mov b,a ; save value in b
; check for closing '\''
inx h
mov a,m
cpi 27h ; '\''
jnz errorinfile
inx h
shld clpos
; put value in hl
mvi h,0h
mov l,b
jmp retgetconstant
checkfordigit:
cpi 30h ; '0'
jc errorinfile
cpi 3ah ; '9'+1
jnc checkforlabel
call getnumfromtoken
jmp retgetconstant
checkforlabel:
cpi 61h ; 'a'
jc errorinfile
cpi 7bh ; 'z'+1
jnc errorinfile
call labellookup
retgetconstant:
pop d
pop b
ret
;func to read a literal number from
; curtoken
;curtoken MUST start w/ decimal digit -
; this is not checked
;number must fit in 16 bits or you'll
; get (number mod 65536)
;no inpars
;outpar: the number in hl
;stack usage 4 words
getnumfromtoken:
push b
push d
lxi h,curtoken
call strlen
dcx b
dad b ;now hl points to last char
mov a,m
mvi m,0h
; hex numbers end in 'h' ;
cpi 68h ; 'h'
jz gethexnum
; decimal numbers may end in 'd' ;
cpi 64h ; 'd'
jz getdecnum
; no end letter is also decimal ;
mov m,a
getdecnum:
lxi h,curtoken
lxi b,0h ;bc will accumulate number
decnumloop:
; get value of next digit ;
mov a,m
inx h
ana a
jz retgetnumfromtoken
cpi 3ah ; '9'+1
jnc errorinfile
cpi 30h ; '0'
jc errorinfile
sbi 30h ; now next digit val is in a
; multiply bc by 10 and add a ;
push h
mov h,b
mov l,c ;hl = bc
dad h ;hl = 2bc
dad h ;hl = 4bc
dad h ;hl = 8bc
dad b ;hl = 9bc
dad b ;hl = 10bc
add l ;a += l
mov c,a
mov b,h
pop h
jnc decnumloop
inr b
jmp decnumloop
gethexnum:
lxi h,curtoken
lxi b,0h ;bc will accumulate number
hexnumloop:
; get value of next digit ;
mov a,m
inx h
ana a
jz retgetnumfromtoken
cpi 3ah ; '9'+1
jnc chkatof
cpi 30h ; '0'
jc errorinfile
sbi 30h ; now next digit val is in a
jmp hexnummult
chkatof:
cpi 67h ; 'f'+1
jnc errorinfile
cpi 61h ; 'a'
jc errorinfile
sbi 57h ; now next digit val is in a
hexnummult:
; multiply bc by 16 and add a ;
push h
mov h,b
mov l,c ;hl = bc
dad h ;hl = 2bc
dad h ;hl = 4bc
dad h ;hl = 8bc
dad h ;hl = 16bc
add l ;a += l
mov c,a
mov b,h
pop h
jnc hexnumloop
inr b
jmp hexnumloop
retgetnumfromtoken:
mov h,b
mov l,c
pop d
pop b
ret
;next function might be too SLOW
; consider better implementation later
; if necessary
;label map entry is just label string
; (0-terminated)followed immediately
; by 2-byte value of label
;the label map is a running list of
; these entries i.e.
; [key1][val1][key2][val2]...
; in memory, not sorted in any way
;end of list is indicated by empty str
;helper for labellookup and labeldef
;finds curtoken in labelmap if there
;on return:
; if label found, cy=1 and hl points
; to corresponding value
; else cy=0 and hl points to null that
; marks end of labelmap
;modifies d,e,h,l
;stack usage 1 words
labelfind:
lxi h,labelmap
labelsearch:
xra a
cmp m
rz ;with cy=0
lxi d,curtoken
labelcmp:
ldax d
cmp m
jnz nextlabel
inx d
inx h
ana a
jnz labelcmp
; label matches key
stc
ret
nextlabel:
mov a,m
ana a
inx h
jnz nextlabel
inx h
inx h
jmp labelsearch
;func which:
; (inel==0): gets the value associated
; with the key in curtoken, reporting
; error if not found
; (inel==1): gets 0
;outpar: the value in hl
;stack usage 2 words
labellookup:
push d
lda inel
ana a
jz gofindlabel
lxi h,0h
jmp retlabellookup
gofindlabel:
call labelfind
jnc errorinfile
mov e,m
inx h
mov h,m
mov l,e
retlabellookup:
pop d
ret
;func to associate a value with a key
; in the constmap
;if key already there, it is an error
;inpars: value in hl, key in curtoken
;modifies h, l
;stack usage 4 words
labeldef:
push d
push h ;save value
; check if curtoken already there ;
call labelfind
jc errorinfile
push h ;save ptr to labelmap end
; check if there is room for new ;
; label in labelmap ;
lxi h,curtoken
call strlen
lhld mapused
dad b
inx h
inx h
inx h
;hl now contains space needed
;compare to max labelmap size
ana a ;clear cy
;;mov a,l
;;sbi 0h
mov a,h
sbi 8h
jnc errorinfile
shld mapused ;update used space
; put the new label in the map ;
pop h ;ptr to labelmap end
lxi d,curtoken
writelabelloop:
ldax d
mov m,a
inx h
inx d
ana a
jnz writelabelloop
pop d ;value
mov m,e
inx h
mov m,d
inx h
xra a
mov m,a ;null marks labelmap end
pop d
ret
;table to look up opcodes by mnemonic
; and assembly directives
;** EXCEPTION rst is treated as a
; directive
;entries are 6 bytes each
;table is in alph. order of mnem./dir'v
;entry format - opcode:
; 4-byte mnemonic, rt-pad w/ 0
; 1-byte opcode
; 1 format byte:
; bit:meaning if set
; 7:opcode[2,1,0] gets an rm operand
; 6:opcode[5,4] gets an rp operand
; 5:opcode[4] gets an rbd operand
; 4:opcode[5,4] gets an rw operand
; 3:instruction has 2 operands
; 2:opcode[5,4,3] gets an rm operand
; 1:instruction has 2 bytes
; 0:instruction has 3 bytes
;entry format - directive:
; 4-byte directive, rt-pad w/ 0
; 2-byte address of part of getopc that
; handles this directive
opctblstart:
db 61h,63h,69h,00h,0ceh,02h
db 61h,64h,63h,00h,088h,04h
db 61h,64h,64h,00h,080h,04h
db 61h,64h,69h,00h,0c6h,02h
db 61h,6eh,61h,00h,0a0h,04h
db 61h,6eh,69h,00h,0e6h,02h
db 63h,61h,6ch,6ch,0cdh,01h
db 63h,63h,00h,00h,0dch,01h
db 63h,6dh,00h,00h,0fch,01h
db 63h,6dh,61h,00h,02fh,00h
db 63h,6dh,63h,00h,03fh,00h
db 63h,6dh,70h,00h,0b8h,04h
db 63h,6eh,63h,00h,0d4h,01h
db 63h,6eh,7ah,00h,0c4h,01h
db 63h,70h,00h,00h,0f4h,01h
db 63h,70h,65h,00h,0ech,01h
db 63h,70h,69h,00h,0feh,02h
db 63h,70h,6fh,00h,0e4h,01h
db 63h,7ah,00h,00h,0cch,01h
db 64h,61h,61h,00h,027h,00h
db 64h,61h,64h,00h,009h,40h
db 0e4h,62h,0h,0h
dw dodb
db 64h,63h,72h,00h,005h,80h
db 64h,63h,78h,00h,00bh,40h
db 64h,69h,00h,00h,0f3h,00h
db 0e4h,73h,0h,0h
dw dods
db 0e4h,77h,0h,0h
dw dodw
db 65h,69h,00h,00h,0fbh,00h
db 0e5h,6eh,64h,0h
dw doend
db 68h,6ch,74h,00h,076h,00h
db 69h,6eh,00h,00h,0dbh,02h
db 69h,6eh,72h,00h,004h,80h
db 69h,6eh,78h,00h,003h,40h
db 6ah,63h,00h,00h,0dah,01h
db 6ah,6dh,00h,00h,0fah,01h
db 6ah,6dh,70h,00h,0c3h,01h
db 6ah,6eh,63h,00h,0d2h,01h
db 6ah,6eh,7ah,00h,0c2h,01h
db 6ah,70h,00h,00h,0f2h,01h
db 6ah,70h,65h,00h,0eah,01h
db 6ah,70h,6fh,00h,0e2h,01h
db 6ah,7ah,00h,00h,0cah,01h
db 6ch,64h,61h,00h,03ah,01h
db 6ch,64h,61h,78h,00ah,20h
db 6ch,68h,6ch,64h,02ah,01h
db 6ch,78h,69h,00h,001h,49h
db 6dh,6fh,76h,00h,040h,8ch
db 6dh,76h,69h,00h,006h,8ah
db 6eh,6fh,70h,00h,000h,00h
db 6fh,72h,61h,00h,0b0h,04h
db 0efh,72h,67h,0h
dw doorg
db 6fh,72h,69h,00h,0f6h,02h
db 6fh,75h,74h,00h,0d3h,02h
db 70h,63h,68h,6ch,0e9h,00h
db 70h,6fh,70h,00h,0c1h,10h
db 70h,75h,73h,68h,0c5h,10h
db 72h,61h,6ch,00h,017h,00h
db 72h,61h,72h,00h,01fh,00h
db 72h,63h,00h,00h,0d8h,00h
db 72h,65h,74h,00h,0c9h,00h
db 72h,69h,6dh,00h,020h,00h
db 72h,6ch,63h,00h,007h,00h
db 72h,6dh,00h,00h,0f8h,00h
db 72h,6eh,63h,00h,0d0h,00h
db 72h,6eh,7ah,00h,0c0h,00h
db 72h,70h,00h,00h,0f0h,00h
db 72h,70h,65h,00h,0e8h,00h
db 72h,70h,6fh,00h,0e0h,00h
db 72h,72h,63h,00h,00fh,00h
db 0f2h,73h,74h,0h
dw dorst
db 72h,7ah,00h,00h,0c8h,00h
db 73h,62h,62h,00h,098h,04h
db 73h,62h,69h,00h,0deh,02h
db 73h,68h,6ch,64h,022h,01h
db 73h,69h,6dh,00h,030h,00h
db 73h,70h,68h,6ch,0f9h,00h
db 73h,74h,61h,00h,032h,01h
db 73h,74h,61h,78h,002h,20h
db 73h,74h,63h,00h,037h,00h
db 73h,75h,62h,00h,090h,04h
db 73h,75h,69h,00h,0d6h,02h
db 78h,63h,68h,67h,0ebh,00h
db 78h,72h,61h,00h,0a8h,04h
db 78h,72h,69h,00h,0eeh,02h
db 78h,74h,68h,6ch,0e3h,00h
asmend:
end

Cut and paste the following into file "asmsml.do":

; asmasm v1.0
;An assembler program for the TRS-80
;Model 100 computer
; Copyright (C) 2014 Clinton Reddekop.
; You can redistribute and/or modify
; this program under the terms of the
; GNU General Public License as
; published by the Free Software
; Foundation, either version 3 of the
; License, or (at your option) any
; later version.
; This program is distributed in the
; hope that it will be useful, but
; WITHOUT ANY WARRANTY; without even
; the implied warranty of
; MERCHANTABILITY or FITNESS FOR A
; PARTICULAR PURPOSE. See the GNU
; General Public License (available at
; <http://www.gnu.org/licenses/gpl-3.0.html>)
; for details.
org 0e38dh
ret
db 0h
calladdr:
jmp entry
inst:
db 0h
loaddr: dw 0h
hiaddr: dw 0h
linenum: dw 0h
dw asmend
hlin: dw 0h
spbasic: dw 0h
splocal: dw endstack
loccntr: dw 0h
stack:
db 0ach,0ach,0ach,0ach,0ach,0ach
db 0ach,0ach,0ach,0ach,0ach,0ach
db 0ach,0ach,0ach,0ach,0ach,0ach
db 0ach,0ach,0ach,0ach,0ach,0ach
db 0ach,0ach,0ach,0ach,0ach,0ach
db 0ach,0ach,0ach,0ach,0ach,0ach
db 0ach,0ach,0ach,0ach,0ach,0ach
db 0ach,0ach,0ach,0ach
endstack: dw main
db 0fh,0fh,0fh,0fh,0fh,0fh,0fh,0fh
curtoken: ds 100h
curline: ds 100h
labelmap: ds 800h
endlabelmap:
clpos: dw 0h
mapused: dw 0h
entry:
ana a
jnz restore
shld hlin
lxi h,0h
dad sp
shld spbasic
lhld splocal
sphl
lhld hlin
ret
exit:
xra a
sta inst
lxi h,main
shld endstack
lxi sp,endstack
backtobasic:
lxi h,0h
dad sp
shld splocal
lhld spbasic
sphl
ret
restore:
lxi h,main
shld endstack
lxi h,endstack
shld splocal
xra a
sta inst
ret
strsp: db 73h,70h,0h
strpsw: db 70h,73h,77h,0h
inel: db 1h
main:
call getinfilename
call openinputfile
lxi h,0ffffh
shld loaddr
lxi h,0h
shld hiaddr
shld loccntr
shld linenum
shld labelmap
inx h
shld mapused
lxi h,pokefirst
shld pokeaddr
mvi a,1h
sta inel
call singlepass
call closeinputfile
call checkbounds
call openinputfile
lxi h,0h
shld loccntr
shld linenum
lxi h,labeldef
mvi m,0c9h
lxi h,pokesecond
shld pokeaddr
xra a
sta inel
call singlepass
call closeinputfile
call reportsuccess
jmp exit
errorinfile:
mvi a,4h
sta inst
lxi h,reportederror
push h
jmp backtobasic
reportederror:
jmp exit
getinfilename:
mvi a,5h
sta inst
jmp backtobasic
openinputfile:
mvi a,2h
sta inst
jmp backtobasic
closeinputfile:
mvi a,3h
sta inst
jmp backtobasic
printbounds:
mvi a,7h
sta inst
jmp backtobasic
checkbounds:
mvi a,6h
sta inst
lxi h,checkedbounds
push h
jmp backtobasic
checkedbounds:
mov a,h
ora l
rz
call printbounds
jmp exit
reportsuccess:
mvi a,8h
sta inst
lxi h,printedsuccess
push h
jmp backtobasic
printedsuccess:
call printbounds
ret
getnextline:
push b
push d
push h
mvi a,1h
sta inst
lxi h,gotline
push h
jmp backtobasic
gotline:
mov b,m
inx h
mov e,m
inx h
mov d,m
lxi h,curline
call memcopy
mvi m,0h
lxi h,curline
shld clpos
lhld linenum
inx h
shld linenum
pop h
pop d
pop b
stc
ret
memcopy:
xra a
cmp b
rz
memcopyloop:
ldax d
mov m,a
inx d
inx h
dcr b
jnz memcopyloop
ret
strcmp:
push b
mvi b,0ffh
call strncmp
pop b
ret
strncmp:
push b
push d
push h
inr b
ldax d
ani 7fh
strncmploop:
dcr b
jz retstrncmp
cmp m
jnz retstrncmp
inx d
inx h
ana a
ldax d
jnz strncmploop
retstrncmp:
pop h
pop d
pop b
ret
strlen:
push h
lxi b,0h
xra a
strlenloop:
cmp m
jz retstrlen
inx h
inx b
jmp strlenloop
retstrlen:
pop h
ret
getopc:
push d
push h
call strlen
mov a,b
ana a
jnz retgetopcfail
mvi a,4h
cmp c
jc retgetopcfail
mvi b,0h
mvi c,55h
getopcloop:
mov a,c
sub b
dcr a
mov a,b
jz checkbottom
add c
push b
rar
mov c,a
ana a
ral
push h
mov l,a
mvi h,0h
mov d,h
mov e,l
dad d
dad d
lxi d,opctblstart
dad d
xchg
pop h
mvi b,4h
call strncmp
mov a,c
pop b
jz retgetopcpass
jc movebottomup
mov c,a
jmp getopcloop
movebottomup:
mov b,a
jmp getopcloop
checkbottom:
ana a
jnz retgetopcfail
lxi d,opctblstart
call strcmp
jz retgetopcpass
retgetopcfail:
stc
jmp retgetopc
retgetopcpass:
ldax d
ani 80h
mov h,a
inx d
inx d
inx d
inx d
ldax d
mov b,a
inx d
ldax d
mov c,a
mov a,h
ana a
retgetopc:
pop h
pop d
ret
skipws:
push b
mvi c,21h
skipwsloop:
mov a,m
ana a
jz retskipws
cmp c
jnc retskipws
inx h
jmp skipwsloop
retskipws:
pop b
ret
tolower:
push b
push d
push h
mvi c,20h
mvi d,41h
mvi e,5bh
tolowerloop:
mov a,m
ana a
jz rettolower
cmp d
jc contlowerloop
cmp e
jnc contlowerloop
add c
mov m,a
contlowerloop:
inx h
jmp tolowerloop
rettolower:
pop h
pop d
pop b
ret
isalphanum:
cpi 30h
jc failalphanum
cpi 3ah
rc
cpi 41h
jc failalphanum
cpi 5bh
rc
cpi 61h
jc failalphanum
cpi 7bh
rc
failalphanum:
ana a
ret
getnexttoken:
push b
push d
push h
mvi b,0h
lxi d,curtoken
lhld clpos
call skipws
mov a,m
ana a
jz retgetnexttoken
cpi 2ch
jz onechartoken
cpi 27h
jz onechartoken
cpi 3bh
jz retgetnexttoken
call isalphanum
jnc errorinfile
getnexttokenloop:
stax d
inx d
inx h
mov a,m
call isalphanum
jc getnexttokenloop
cpi 3ah
jnz retgetnexttoken
mov b,a
inx h
jmp retgetnexttoken
onechartoken:
stax d
inx d
inx h
retgetnexttoken:
shld clpos
xra a
stax d
lxi h,curtoken
call tolower
mov a,b
ana a
pop h
pop d
pop b
ret
getnexttokennolabel:
call getnexttoken
jnz errorinfile
ret
singlepass:
push b
push d
push h
jmp mainloopnoemptycheck
mainloop:
call getnexttoken
lda curtoken
ana a
jnz errorinfile
mainloopnoemptycheck:
call getnextline
jnc errorinfile
mainloopuserestofline:
call getnexttoken
jz notlabel
call getopc
jnc errorinfile
lhld loccntr
call labeldef
jmp mainloopuserestofline
notlabel:
lxi h,curtoken
xra a
cmp m
jz mainloopnoemptycheck
call getopc
jc errorinfile
jnz handledirective
mov a,c
ral
mov c,a
cc dorm543
mov a,c
ral
mov c,a
cc dorp
mov a,c
ral
mov c,a
cc dorbd
mov a,c
ral
mov c,a
cc dorw
mov a,c
ral
mov c,a
cc docomma
mov a,c
ral
mov c,a
cc dorm210
mov a,b
call poke
mov a,c
ral
mov c,a
cc do1byte
mov a,c
ral
mov c,a
cc do2byte
jmp mainloop
handledirective:
mov h,c
mov l,b
pchl
dorst:
call getnexttokennolabel
call getconstant
mov a,h
ana a
jnz errorinfile
mov a,l
cpi 8h
jnc errorinfile
ana a
ral
ral
ral
adi 0c7h
call poke
jmp mainloop
dodb:
call getnexttokennolabel
lda curtoken
ana a
jz mainloop
cpi 27h
jz strconstant
call getconstant
mov a,h
ana a
jnz errorinfile
mov a,l
call poke
jmp endstrconstant
strconstant:
mvi b,27h
lhld clpos
strconstantloop:
mov a,m
inx h
cmp b
jz endstrconstantloop
call poke
jmp strconstantloop
endstrconstantloop:
shld clpos
endstrconstant:
call getnexttoken
lda curtoken
cpi 2ch
jz dodb
ana a
jz mainloop
jmp errorinfile
dodw:
call getnexttokennolabel
lda curtoken
ana a
jz mainloop
call getconstant
mov a,l
call poke
mov a,h
call poke
call getnexttoken
lda curtoken
cpi 2ch
jz dodw
ana a
jz mainloop
jmp errorinfile
dods:
call getnexttokennolabel
lda inel
mov b,a
xra a
sta inel
call getconstant
mov a,b
sta inel
xchg
dsloop:
mov a,d
ora e
jz mainloop
xra a
call poke
dcx d
jmp dsloop
doorg:
call getnexttokennolabel
lda inel
mov b,a
xra a
sta inel
call getconstant
mov a,b
sta inel
shld loccntr
jmp mainloop
doend:
retsinglepass:
pop h
pop d
pop b
ret
dorm210:
push d
call helprm
mov a,d
add b
mov b,a
pop d
ret
helprm:
push h
call getnexttoken
lxi h,curtoken
mov a,m
cpi 61h
mvi d,7h
jz rethelprm
cpi 62h
mvi d,0h
jz rethelprm
cpi 63h
mvi d,1h
jz rethelprm
cpi 64h
mvi d,2h
jz rethelprm
cpi 65h
mvi d,3h
jz rethelprm
cpi 68h
mvi d,4h
jz rethelprm
cpi 6ch
mvi d,5h
jz rethelprm
cpi 6dh
mvi d,6h
jnz errorinfile
rethelprm:
inx h
xra a
cmp m
jnz errorinfile
pop h
ret
dorp:
push d
call helprbdh
mov a,e
cpi 70h
jz errorinfile
mov a,d
add b
mov b,a
pop d
ret
helprbdh:
push h
call getnexttoken
lxi h,curtoken
mov a,m
mov e,a
cpi 62h
mvi d,0h
jz rethelprbdh
cpi 64h
mvi d,10h
jz rethelprbdh
cpi 68h
mvi d,20h
jz rethelprbdh
lxi d,strsp
call strcmp
mvi d,30h
mvi e,73h
jz rethelprbdhnolenchk
lxi d,strpsw
call strcmp
mvi d,30h
mvi e,70h
jz rethelprbdhnolenchk
jmp errorinfile
rethelprbdh:
inx h
xra a
cmp m
jnz errorinfile
rethelprbdhnolenchk:
pop h
ret
dorbd:
push d
call helprbdh
mov a,d
cpi 11h
jnc errorinfile
add b
mov b,a
pop d
ret
dorw:
push d
call helprbdh
mov a,e
cpi 73h
jz errorinfile
mov a,d
add b
mov b,a
pop d
ret
docomma:
call getnexttoken
lda curtoken
cpi 2ch
jnz errorinfile
ret
dorm543:
push d
call helprm
mov a,d
ana a
ral
ral
ral
add b
mov b,a
pop d
ret
do1byte:
call getnexttokennolabel
call getconstant
mov a,h
ana a
jnz errorinfile
mov a,l
call poke
ret
do2byte:
call getnexttokennolabel
call getconstant
mov a,l
call poke
mov a,h
call poke
ret
poke:
db 0c3h
pokeaddr:
dw 0h
pokefirst:
push h
push d
lhld loaddr
xchg
lhld loccntr
mov a,l
sub e
mov a,h
sbb d
jnc checkhiaddr
shld loaddr
checkhiaddr:
xchg
lhld hiaddr
mov a,l
sub e
mov a,h
sbb d
xchg
jnc retpokefirst
shld hiaddr
retpokefirst:
inx h
shld loccntr
pop d
pop h
ret
pokesecond:
push h
lhld loccntr
mov m,a
inx h
shld loccntr
pop h
ret
getconstant:
push b
push d
lda curtoken
ana a
jz errorinfile
checkforcharliteral:
cpi 27h
jnz checkfordigit
lhld clpos
mov a,m
ana a
jz errorinfile
cpi 27h
jz errorinfile
mov b,a
inx h
mov a,m
cpi 27h
jnz errorinfile
inx h
shld clpos
mvi h,0h
mov l,b
jmp retgetconstant
checkfordigit:
cpi 30h
jc errorinfile
cpi 3ah
jnc checkforlabel
call getnumfromtoken
jmp retgetconstant
checkforlabel:
cpi 61h
jc errorinfile
cpi 7bh
jnc errorinfile
call labellookup
retgetconstant:
pop d
pop b
ret
getnumfromtoken:
push b
push d
lxi h,curtoken
call strlen
dcx b
dad b
mov a,m
mvi m,0h
cpi 68h
jz gethexnum
cpi 64h
jz getdecnum
mov m,a
getdecnum:
lxi h,curtoken
lxi b,0h
decnumloop:
mov a,m
inx h
ana a
jz retgetnumfromtoken
cpi 3ah
jnc errorinfile
cpi 30h
jc errorinfile
sbi 30h
push h
mov h,b
mov l,c
dad h
dad h
dad h
dad b
dad b
add l
mov c,a
mov b,h
pop h
jnc decnumloop
inr b
jmp decnumloop
gethexnum:
lxi h,curtoken
lxi b,0h
hexnumloop:
mov a,m
inx h
ana a
jz retgetnumfromtoken
cpi 3ah
jnc chkatof
cpi 30h
jc errorinfile
sbi 30h
jmp hexnummult
chkatof:
cpi 67h
jnc errorinfile
cpi 61h
jc errorinfile
sbi 57h
hexnummult:
push h
mov h,b
mov l,c
dad h
dad h
dad h
dad h
add l
mov c,a
mov b,h
pop h
jnc hexnumloop
inr b
jmp hexnumloop
retgetnumfromtoken:
mov h,b
mov l,c
pop d
pop b
ret
labelfind:
lxi h,labelmap
labelsearch:
xra a
cmp m
rz
lxi d,curtoken
labelcmp:
ldax d
cmp m
jnz nextlabel
inx d
inx h
ana a
jnz labelcmp
stc
ret
nextlabel:
mov a,m
ana a
inx h
jnz nextlabel
inx h
inx h
jmp labelsearch
labellookup:
push d
lda inel
ana a
jz gofindlabel
lxi h,0h
jmp retlabellookup
gofindlabel:
call labelfind
jnc errorinfile
mov e,m
inx h
mov h,m
mov l,e
retlabellookup:
pop d
ret
labeldef:
push d
push h
call labelfind
jc errorinfile
push h
lxi h,curtoken
call strlen
lhld mapused
dad b
inx h
inx h
inx h
ana a
mov a,h
sbi 8h
jnc errorinfile
shld mapused
pop h
lxi d,curtoken
writelabelloop:
ldax d
mov m,a
inx h
inx d
ana a
jnz writelabelloop
pop d
mov m,e
inx h
mov m,d
inx h
xra a
mov m,a
pop d
ret
opctblstart:
db 61h,63h,69h,00h,0ceh,02h
db 61h,64h,63h,00h,088h,04h
db 61h,64h,64h,00h,080h,04h
db 61h,64h,69h,00h,0c6h,02h
db 61h,6eh,61h,00h,0a0h,04h
db 61h,6eh,69h,00h,0e6h,02h
db 63h,61h,6ch,6ch,0cdh,01h
db 63h,63h,00h,00h,0dch,01h
db 63h,6dh,00h,00h,0fch,01h
db 63h,6dh,61h,00h,02fh,00h
db 63h,6dh,63h,00h,03fh,00h
db 63h,6dh,70h,00h,0b8h,04h
db 63h,6eh,63h,00h,0d4h,01h
db 63h,6eh,7ah,00h,0c4h,01h
db 63h,70h,00h,00h,0f4h,01h
db 63h,70h,65h,00h,0ech,01h
db 63h,70h,69h,00h,0feh,02h
db 63h,70h,6fh,00h,0e4h,01h
db 63h,7ah,00h,00h,0cch,01h
db 64h,61h,61h,00h,027h,00h
db 64h,61h,64h,00h,009h,40h
db 0e4h,62h,0h,0h
dw dodb
db 64h,63h,72h,00h,005h,80h
db 64h,63h,78h,00h,00bh,40h
db 64h,69h,00h,00h,0f3h,00h
db 0e4h,73h,0h,0h
dw dods
db 0e4h,77h,0h,0h
dw dodw
db 65h,69h,00h,00h,0fbh,00h
db 0e5h,6eh,64h,0h
dw doend
db 68h,6ch,74h,00h,076h,00h
db 69h,6eh,00h,00h,0dbh,02h
db 69h,6eh,72h,00h,004h,80h
db 69h,6eh,78h,00h,003h,40h
db 6ah,63h,00h,00h,0dah,01h
db 6ah,6dh,00h,00h,0fah,01h
db 6ah,6dh,70h,00h,0c3h,01h
db 6ah,6eh,63h,00h,0d2h,01h
db 6ah,6eh,7ah,00h,0c2h,01h
db 6ah,70h,00h,00h,0f2h,01h
db 6ah,70h,65h,00h,0eah,01h
db 6ah,70h,6fh,00h,0e2h,01h
db 6ah,7ah,00h,00h,0cah,01h
db 6ch,64h,61h,00h,03ah,01h
db 6ch,64h,61h,78h,00ah,20h
db 6ch,68h,6ch,64h,02ah,01h
db 6ch,78h,69h,00h,001h,49h
db 6dh,6fh,76h,00h,040h,8ch
db 6dh,76h,69h,00h,006h,8ah
db 6eh,6fh,70h,00h,000h,00h
db 6fh,72h,61h,00h,0b0h,04h
db 0efh,72h,67h,0h
dw doorg
db 6fh,72h,69h,00h,0f6h,02h
db 6fh,75h,74h,00h,0d3h,02h
db 70h,63h,68h,6ch,0e9h,00h
db 70h,6fh,70h,00h,0c1h,10h
db 70h,75h,73h,68h,0c5h,10h
db 72h,61h,6ch,00h,017h,00h
db 72h,61h,72h,00h,01fh,00h
db 72h,63h,00h,00h,0d8h,00h
db 72h,65h,74h,00h,0c9h,00h
db 72h,69h,6dh,00h,020h,00h
db 72h,6ch,63h,00h,007h,00h
db 72h,6dh,00h,00h,0f8h,00h
db 72h,6eh,63h,00h,0d0h,00h
db 72h,6eh,7ah,00h,0c0h,00h
db 72h,70h,00h,00h,0f0h,00h
db 72h,70h,65h,00h,0e8h,00h
db 72h,70h,6fh,00h,0e0h,00h
db 72h,72h,63h,00h,00fh,00h
db 0f2h,73h,74h,0h
dw dorst
db 72h,7ah,00h,00h,0c8h,00h
db 73h,62h,62h,00h,098h,04h
db 73h,62h,69h,00h,0deh,02h
db 73h,68h,6ch,64h,022h,01h
db 73h,69h,6dh,00h,030h,00h
db 73h,70h,68h,6ch,0f9h,00h
db 73h,74h,61h,00h,032h,01h
db 73h,74h,61h,78h,002h,20h
db 73h,74h,63h,00h,037h,00h
db 73h,75h,62h,00h,090h,04h
db 73h,75h,69h,00h,0d6h,02h
db 78h,63h,68h,67h,0ebh,00h
db 78h,72h,61h,00h,0a8h,04h
db 78h,72h,69h,00h,0eeh,02h
db 78h,74h,68h,6ch,0e3h,00h
asmend:
end

The file "asmsml.do" above is a cleaned up and comment-free version of "asm.do". It has the advantage of fitting in the RAM of the emulator.

Instructions for Assembling the Assembly Assembler

asm.do and asmsml.do are both written to be assembled into memory addresses 58253 to 62959 which I am assuming can be CLEARed on your machine. If you need to change this, change the value given to the ORG directive, and adjust the value "58253" on line 25 of asmrun.ba to match.

Below, asm2.ba refers to the BASIC assembler from the previous Project Log titled "BASIC Assembler Fix".

Below, "<enter>" means press the enter key.

  1. start with no files in the emulator
  2. load asmsml.do and asm2.ba into emulator
  3. select the BASIC menu item, <enter>
  4. type: CLEAR0,58253 <enter>
  5. type: LOAD"ASM2.BA <enter>
  6. type: RUN <enter>
  7. when prompted for 'FILENAME?', type: ASMSML.DO
  8. wait for a really long time...
  9. if it works, you will see a success message; if not, an error message
  10. type: KILL"ASMSML.DO <enter>
  11. type: SAVEM"ASM",58253,62959 <enter>
  12. type: NEW <enter>
  13. type: KILL"ASM2.BA <enter>
  14. type: MENU <enter>
  15. you should probably back up "ASM.CO" somewhere
  16. load asmrun.ba into the emulator

You now have asmrun.ba and asm.co in the filesystem; these 2 files work together as an assembler that I will call "asmasm".

Using ASMASM

Now suppose you have an assembly file called "prog.do" that you want assembled into machine code in RAM, for example between addresses 58000 and 58252. To do this:

  1. make sure prog.do is written to assemble into those locations (use the ORG directive, keep the program short enough)
  2. load prog.do into emulator
  3. select the BASIC menu item, <enter>
  4. type: CLEAR0,58000 <enter>
  5. type: MENU <enter>
  6. select ASMRUN.BA in the menu, <enter>
  7. when prompted for 'input file?', type: PROG.DO <enter>
  8. wait for not very long
  9. success or error will be reported on the screen

I hope someone will find this useful or at least interesting. A few more features are hopefully coming later.

Discussions