Files
Digital-Research-Source-Code/CPM OPERATING SYSTEMS/CPM 3.X/CPM 3.0/SOURCE/prs2mon.asm
Sepp J Morris 31738079c4 Upload
Digital Research
2020-11-06 18:50:37 +01:00

3860 lines
84 KiB
NASM

; cp/m symbolic debugger main module
title 'Symbolic Interactive Debugger (demon) 7/12/82'
;
; copyright (c) 1976,1977,1982
; Digital Research
; box 579 Pacific Grove
; California 93950
;
false equ 0
true equ not false
isis2 equ false ;true if running under is interface
debug equ false ;true if debugging in cp/m environment
reloc equ false ;true if relocation image
rmac equ true ;[JCE] true if RMAC is used for relocation
rst6 equ true ;[JCE] true if using RST6 (RST7 is used by Z80 CPU)
if debug
org 8000h ;base if debugging
else
if isis2
org 0e500h
else
if reloc ;building relocation image
org 0000h ;base for relocation
else
if rmac
cseg ;[JCE] no ORG for RMAC
else
org 0d000h ;testing in 64 k
endif
endif
endif
endif
;
;
if rmac
extrn modbas, disin, disen, dispc, assem, dispm, dispg
public demon
else
modbas equ $ ;base of assem/disassem/debug
disin equ modbas+3
disen equ disin+3 ;disassembler entry point
assem equ disen+3 ;assembler entry point
dispc equ assem+3 ;disassembler pc value
dispm equ dispc+2 ;disassembler pc max value
dispg equ dispm+2 ;disassembler page mode if non zero
ds 680h ;space for disassem/assem module
endif
demon equ $ ;base of debugging monitor
bdose equ 0005h ;primary bdos entry point
;
if isis2
bdos equ 103h ;real bdos entry
pcbase equ 3180h
spbase equ 3180h
dstart equ 107h ;start of debugger code
dbase equ dstart+2;start of loaded program
dnext equ dbase+2 ;next free address
bdbase equ 100h ;low bdos location
bdtop equ 3180h ;high bdos location
else
;bdos equ modbas+1806h
bdos equ demon+1186h
bdbase equ bdos ;base of bdos
bdtop equ bdbase+0d00h ;top of bdos
pcbase equ 100h ;default pc
spbase equ 100h ;default sp
endif
;
psize equ 12 ;number of assembly lines to list with 'l'
csize equ 64 ;command buffer size
ssize equ 50 ;local stack size
pbsize equ 8 ;number of permanent breaks
pbelt equ 4 ;size of each perm break element
;
; basic disk operating system constants
cif equ 1
cof equ 2
rif equ 3
pof equ 4
lof equ 5
;
ids equ 7
getf equ 10 ;fill buffer from console
chkio equ 11 ;check io status
lift equ 12 ;lift head on disk
opf equ 15 ;disk file open
DELF equ 19 ;delete file func
rdf equ 20 ;read disk file
WRITF equ 21 ;sequential write func
dmaf equ 26 ;set dma address
;
dbp equ 5bh ;disk buffer pointer
dbf equ 80h ;disk buffer address
dfcb equ 5ch ;disk file control block
fcb equ dfcb
fcbl equ 32 ;length of file control block
fcb2 equ fcb+16 ;second file control block
fdn equ 0 ;disk name
ffn equ 1 ;file name
ffnl equ 8 ;length of file name
fft equ 9 ;file type
fftl equ 3 ;length of file type
frl equ 12 ;reel number
frc equ 15 ;record count
fcr equ 32 ;current record
fln equ fcbl+1 ;fcb length including current rec
;
deof equ 1ah ;control-z (eof)
eof equ deof ;eof=deof
tab equ 09h ;horizontal tab
cr equ 0dh
lf equ 0ah
;
if debug
rstnum equ 6 ;use restart 6 for debug mode
else
if rst6
rstnum equ 6
else
rstnum equ 7 ;restart number
endif
endif
rstloc equ rstnum*8 ;restart location
rstin equ 0c7h or (rstnum shl 3) ;restart instruction
;
; template for programmed breakpoints
; ---------
; pch : pcl
; hlh : hll
; sph : spl
; ra : flg
; b : c
; d : e
; ---------
; flg field: mz0i0e1c (minus,zero,idc,even,carry)
;
aval equ 5 ;a register count in header
bval equ 6
dval equ 7
hval equ 8
sval equ 9
pval equ 10
;
;
; demon entry points
TPATOP:
jmp trapad ;trap address for return in case interrupt
jmp begin
breaka:
jmp breakp
; useful entry points for programs running with ddt
jmp getbuff ;get another buffer full
jmp gnc ;get next character
jmp pchar ;print a character from a
jmp pbyte ;print byte in register a
jmp paddsy ;print address/symbol reference
jmp scanexp ;scan 0,1,2, or 3 expressions
jmp getval ;get value to h,l
jmp break ;check for console ready
jmp prlabel ;print label given by hl, if it exists
;
;
trapad: ;get the return address for this jump to bdos in case of
; a soft interrupt during bdos processing.
xthl ;pc to hl
shld retloc ;may not need it
xthl
trapjmp:
; address field of the following jump is set at "begin"
jmp 0000h
;
begin:
; set the bdos entry address to reflect the reduced memory
; size, as well as to trap the calls on the bdos. upon
; entry to "begin" the memory addresses are set as follows-
; bdose: jmp bdos
; modbas: jmp begin
; demon: jmp trapad
; trapad: ...
; trapjmp:jmp xxxx
; begin: ...
; bdose: bdos (or next module)
;
; change the memory map to appear as follows-
; bdose: jmp modbas
; modbas: jmp trapad
; demon: jmp trapad
; trapad: ...
; trapjmp:jmp bdos
; ...
; bdos: bdos (or next module)
;
; note that we do not assume that the next module above
; the debugger is the bdos. in fact, the next module up may
; be another copy of the debugger itself.
;
lhld bdose+1 ;address of next module in memory
shld trapjmp+1;change jump instruction address in trap code
lxi h,trapad;address of trap code
shld modbas+1 ;change address field of jump at beginning
lxi h,modbas ;base of dis/assembler code
shld bdose+1 ;change primary bdos entry address
shld sytop ;mark symbol table empty
;
; note that -a will change the bdose jump address to
; the base of the debugger module only, which removes the
; dis/assembler from the memory image.
; "a-" is implied if the load address exceeds modbas.
;
if isis2
pop h ;recall return address to is.com
shld dbase ;set up as base of program
lxi h,beginr;read beginning of ddt
shld dstart;mark as debug mode
beginr:
endif
xra a ;zero acc
sta breaks ;clears break point count
sta dasm ;00 in dasm marks dis/assembler present
sta pbtrace ;perm break trace set false
sta tmode ;trace mode cleared
;
if isis2
lhld dbase ;base address of program
else
lxi h,pcbase
endif
shld dispc ;initial value for disassembler pc
shld disloc ;initial value for display
shld ploc ;pc in restart template
if isis2
lxi h,pcbase ;primary entry to ddt, no high addr
endif
shld mload ;max load local
shld DEFLOAD
lxi h,spbase
lxi sp,stack-4
push h ;initial sp
lxi h,10b ;initial psw
push h
dcx h
dcx h ;cleared
shld hloc ;h,l cleared
push h ;b,c cleared
push h ;d,e cleared
shld userbrk ;clear user break during trace/untrace
;
mvi a,jmp ;(jmp restart)
sta rstloc
lxi h,breaka ;break point subroutine
shld rstloc+1 ;restart location address field
;
; check for file name passed to demon, and load if present
lda fcb+ffn ;blank if no name passed
cpi ' '
jz start
;
; use a zero bias and read
lda FCB+9 ;is COM specified?
cpi ' ' ;blank if not
jnz DEFREAD ;read it in
;
call COMDEF
;
lda FCB+010h ;sym file location
cpi ' ' ;is it there?
jz DEFREAD ;jump over if no sym file
;
lda FCB+019h
cpi ' ' ;is the type specified?
jnz DEFREAD ;bypass if present
;
call SYMDEF ;insert .SYM file type
;
DEFREAD:
lxi h,0
jmp readn
;
;
; *********************************
; * *
; * main command loop *
; * *
; *********************************
;
start:
lxi sp,stack-12 ;initialize sp in case of error
call break ;any active characters?
mvi c,cif ;console input function
cnz trapad ;to clear the character
call crlf ;initial crlf
if debug
mvi a,'@'
else
mvi a,'#'
endif
call pchar ;output prompt
;
; get input buffer
call getbuff ;fill command buffer
;
call gnc ;get character
cpi cr
jz start
; check for negated command
lxi h,negcom
mvi m,0
cpi '-' ;preceding "-"?
jnz poscom ;skip to positive command if not
; negated command, mark by negcom=true
dcr m ;00 becomes ff
call gnc ;to read the command
poscom:
sui 'A' ;legal character?
jc cerror ;command error
cpi 'Z'-'A'+1
jnc cerror
; character in register a is command, must be in the range a-z
mov e,a ;index to e
mvi d,0 ;double precision index
lxi h,jmptab;base of table
dad d
dad d ;indexed
mov e,m ;lo byte
inx h
mov d,m ;ho byte
xchg ;to h,l
pchl ;gone...
;
jmptab: ;jump table to subroutines
dw assm ;a enter assembler language
dw cerror ;b
dw callpr ;c call program
dw display ;d display ram memory
dw EXECUTE ;e
dw fill ;f fill memory
dw goto ;g go to memory address
dw hexari ;h hexadecimal sum and difference
dw infcb ;i fill input file control block
dw cerror ;j
dw cerror ;k
dw lassm ;l list assembly language
dw move ;m move memory
dw cerror ;n
dw cerror ;o
dw permbrk ;p
dw 0 ;q [JCE] q=quit, as in SID-86
dw read ;r read hexadecimal file
dw setmem ;s set memory command
dw trace ;t
dw untrace ;u
dw VALUE ;v
dw WRITE ;w
dw examine ;x examine and modify registers
dw cerror ;y
dw cerror ;z
;
;
; *********************************
; * *
; * a - assemble *
; * *
; *********************************
;
assm: ;assembler language input
; check for assm present
call chkdis ;generate "no carry" if not there
jnc cerror ;not there
call scanexp ;read the expressions
ora a ;none given?
jnz assm0 ;skip to check for single parameter
;
; no parms, must be -a or a command
lda negcom ;must be set
ora a ;ff?
jz assm1 ;use old dispc for base
call nodis ;remove disassembler
jmp start ;for another command
;
assm0:
dcr a ;one expression expected
jnz cerror
call getval ;get expression to h,l
shld dispc
assm1: call assem
jmp start
;
; *********************************
; * *
; * c - call *
; * *
; *********************************
callpr:
; call user program from ddt
call scanexp
jc cerror ;cannot be ,xxx
jz cerror ;cannot be c alone
call getval ;address to call in h,l
push h ;ready for call
; get remaining parameters
; reg-a contains 1,2,or 3 corresponding to number of values
lxi b,0
dcr a
jnz call0
; no parameters, stack two zeroes
push b
push b
jmp call2
call0: ;at least one parameter
call getval
push h
dcr a
jnz call1
; only one parameter, stack a zero
push b
jmp call2
call1: ;must be two parameters for the call
call getval
push h
call2: ;set up parameters in b,c and d,e
pop d ;recall second parameter
pop b ;recall first parameter
; ready for the user program call
lxi h,start ;return address
xthl ;call address in h,l return in stack
pchl ;call user
;
; *********************************
; * *
; * d - display RAM *
; * *
; *********************************
;
; display memory, forms are
; d display from current display line
; dnnn set display line and assume d
; dnnn,mmm display nnn to mmm
; new display line is set to next to display
display:
call scanword
jz disp1 ;assume current disloc
call getval ;get value to h,l
jc disp0 ;carry set if ,b form
shld disloc ;otherwise dispc already set
disp0: ;get next value
ani 7fh ;in case ,b
dcr a
jz disp1 ;set half page mode
call getval
dcr a ;a,b,c not allowed
jnz cerror
jmp DISP2 ;store it
;
;
disp1:
;0 or 1 expn, display half screen
lhld disloc
lxi d,psize*16-1
dad d
jnc DISP2 ;this is O.K.
;
lxi h,0FFFFh ;end of RAM in this case
disp2:
shld dismax
;
; display memory from disloc to dismax
disp3:
;
call break ;break key?
jnz start ;stop current expansion
;
;
lhld DISMAX ;check for the end
xchg ;DE=DISMAX
lhld disloc ;HL=current location
shld tdisp
xchg ;get set for check
call HLDE ;are we done?
; jz START ;yes
jc START ;yes
;no we have more
call CRLF ;next line
lhld DISLOC ;
call paddr ;print line address
mvi a,':'
call pchar ;to delimit address
lda wdisp ;word display?
ora a
jz disp4 ;skip to byte display if not
;
mvi c,8 ;display 8 items per line (double bytes)
; full word display, get next value to de
word0: call blank ;blank delimiter
mov e,m ;low byte
inx h
mov d,m ;high byte
inx h ;ready for next address
xchg ;hl is address
call paddr ;print the address value
call blank
xchg ;back to DE with the address value
dcr c ;
push a ;save flags
call DISCOM
jc WORD1
pop a ;restore flags
jnz word0 ;for another item
jmp disch ;to display characters
;
WORD1:
pop a
WORD2:
mov a,c
ora c ;are we at the end of the line?
jz DISCH ;yes, branc to char print
;no, continue
call BLANK ! call BLANK ! call BLANK
call BLANK ! call BLANK ! call BLANK
dcr c ;finished this char
jnz WORD2 ;were not done yet
jmp DISCH
disp4:
mvi c,16 ;counter
disp5:
call blank ;blank byte delimiter
mov a,m ;get next data byte
call pbyte ;print byte
dcr c ;decrement counter
push a ;save it
inx h
xchg ;DE = current address
lhld DISMAX ;HL = top of ram
call HLDE
xchg
; jz DISP6 ;end of the line print blanks
jc DISP6 ;go print the ending characters
pop a ;restore status
jnz DISP5 ;print next byte
jmp DISCH
;
DISP6:
pop a
DISP7:
mov a,c
ora c ;are we at the end of the line?
jz DISCH ;yes, branc to char print
;no, continue
call BLANK ;
call BLANK ;
call BLANK ;
dcr c ;finished this char
jnz DISP7 ;were not done yet
;
;
;DISP7:
; dcr c ;to adjust the printer count
; mov a,c
; ora c
; jz DISP7
; call blank ;print the blank
; mov a,m ;print the last character
; call PBYTE ;
; inx h ;adjust the RAM pointer
; dcr c ;decrement counter
; mvi a,TRUE
; sta DISEND ;end flag
;
;
;
DISCH: ;display area in character form
shld disloc ;update for next write
lda negcom ;negated command?
ora a ;ff if negated
jnz DISP3 ;to skip the character display
lhld tdisp
xchg
call blank
mvi c,16 ;set up loop counter
;
disch0: ldax d ;get byte
call pgraph ;print if graphic character
inx d
lhld DISMAX ;compare for end of line
call HLDE ;HL=disloc
jz DISP8 ;we have reached the end
jc DISP3
dcr c ;16 characters?
jnz DISCH0 ;no, do it again
jmp DISP3
;
DISP8:
ldax d ;get last character
call PGRAPH ;print it
;
lda DISEND
cpi TRUE ;
jnz DISP3 ;we have finished
mvi a,FALSE
sta DISEND
jmp START
;
;
;
;
; *********************************
; * *
; * e - execute *
; * *
; *********************************
;
execute:
lda CURLEN
ora a
jz CERROR ;
;
EX1:
call FCBIN ;read in the FCBs
; Check for default
lda FCB+9
cpi ' '
jnz EX2
call COMDEF
EX2:
lda FCB+019h
cpi ' '
jnz EX3
call SYMDEF
EX3:
lxi h,0 ;HL = BIAS for load into program
jmp readn ;now read it in
;
;
; *********************************
; * *
; * f - fill *
; * *
; *********************************
;
fill:
call scan3 ;expressions scanned bc , de , hl
mov a,h ;must be zero
ora a
jnz cerror
fill0:
call WRPCHK ;check for wrap
;
jc START ;back to start
call bcde ;end of fill?
jc start
mov a,l ;data
stax b ;to memory
inx b ;next to fill
jmp fill0
;
; *********************************
; * *
; * g - goto *
; * *
; *********************************
;
goto:
xra a ;clear autou flag to indicate goto
sta autou ;autou=00 if goto, ff if tr/untr or perm brk
call crlf ;ready for go.
call scanexp ;0,1, or 2 exps
sta gobrks ;save go count
call getval
push h ;start address
call getval
shld gobrk1 ;primary break point
push h ;bkpt1
call getval
shld gobrk2 ;secondary break point
mov b,h ;bkpt2
mov c,l
pop d ;bkpt1
pop h ;goto address
jmp gopr1 ;to skip autou=ff
;
gopr:
; mark autou with ff to indicate trace/untrace or perm break
push h ;save go address
lxi h,autou ;00 if "go" ff if tr/untr/perm brk
mvi m,0ffh ;mark as tr/untr/perm brk
pop h ;recall go address
;
gopr1: ;arrive here from "goto" above with autou=00
di
jz gop1 ;no break points
jc gop0
; set pc
shld ploc ;into machine state
gop0: ;set breaks
ani 7fh ;clear , bit
dcr a ;if 1 then skip (2,3 if breakpoints)
jz gop1
call setbk ;break point from d,e
dcr a
jz gop1
; second break point
mov e,c
mov d,b ;to d,e
call setbk ;second break point set
;
gop1: ;now check the permanent break points
; scan the permanent break point table, forms are
; count low(addr) high(addr) data
lxi h,pbtable
mvi c,pbsize ;number of elements
setper0:
push h ;save next table elt address
mov a,m ;low(count)
ora a ;00 if not in use
jz setper2 ;skip if not
inx h ;to low(addr)
mov e,m
inx h ;to high(addr)
mov d,m ;de is the break address
push h ;save data address-1
; may be continue from current perm break address
; or a trace/untrace mode operation
lda autou ;00 if not
ora a ;set flags
jz setper1 ;set the break point
; this is a continuation from a perm break/or a trace/untrace
lhld ploc ;auto "u" necessary?
mov a,e ;low(addr)
cmp l ;=low(ploc)?
jnz setper1 ;skip if not
mov a,d ;high(addr)
cmp h ;=high(ploc)?
jnz setper1 ;skip if addr <> ploc
;
; address match, set auto "u" command
pop h ;recall data address-1
pop h ;recall table address
shld pbloc ;table location for "u"
push h ;save for next iteration
mov a,m ;count
mvi m,0 ;cleared in memory
sta pbcnt ;marks as auto u command necessary
jmp setper2 ;to iterate
;
setper1:
;break is not at current address
pop h ;recall data address-1
inx h ;.data
ldax d ;memory data
mov m,a ;saved in the table
xchg ;memory addr to hl
mvi m,rstin ;set to restart instruction
setper2:
pop h ;recall table base
lxi d,pbelt ;element size
dad d ;incremented to next element
dcr c ;end of table?
jnz setper0 ;for another element
;
gop2: ;permanent break points set, now start the program
lxi sp,stack-12
pop d
pop b
pop psw
pop h ;sp in hl
sphl
lhld ploc ;pc in hl
push h ;into user's stack
lhld hloc ;hl restored
ei
ret
;
setbk: ;set break point at location d,e
push psw
push b
lxi h,breaks ;number of breaks set so far
mov a,m
inr m ;count breaks up
ora a ;one set already?
jz setbk0
; already set, move past addr,data fields
inx h
mov a,m ;check = addresses
inx h
mov b,m ;check ho address
inx h
; don't set two breakpoints if equal
cmp e ;low =?
jnz setbk0
mov a,b
cmp d ;high =?
jnz setbk0
; equal addresses, replace real data
mov a,m ;get data byte
stax d ;put back into code
setbk0: inx h ;address field
mov m,e ;lsb
inx h
mov m,d ;msb
inx h ;data field
ldax d ;get byte from program
mov m,a ;to breaks vector
mvi a,rstin ;restart instruction
stax d ;to code
pop b
pop psw
ret
;
; *********************************
; * *
; * h - hex arithmetic *
; * *
; *********************************
;
hexari:
call scanexp
jz hexlist ;to list the symbol table
call getval ;ready the first value
dcr a ;1 becomes 0, 2 becomes 1
jz hexsym ;print the symbol only
dcr a ;2 became 1, now becomes 0
jnz cerror
; first value is in hl
push h
call getval ;second value to h,l
pop d ;first value to d,e
push h ;save a copy of second vaalue
call crlf ;new line
dad d ;sum in h,l
call paddr
call blank
pop h ;restore second value
xra a ;clear accum for subtraction
sub l
mov l,a ;back to l
mvi a,0 ;clear it again
sbb h
mov h,a
dad d ;difference in hl
call paddr
jmp start
;
hexsym: ;print symbol name
xchg
call crlf ;new line for symbol
push d ;save de (address value) for ascii printout
push d ;save de for the decimal printout
call paddsy
; print the value in decimal
call blank
mvi a,'#'
call pchar
;
mvi b,1 shl 7 or 5 ;five digits, zero suppress on
lxi h,dtable ;decimal value table
; initial/partial dividend is stacked at this point
nxtdig: ;convert first/next digit in dvalue table
mov e,m ;low order divisor
inx h ;to next value
mov d,m ;high order divisor
inx h ;ready for next digit
xthl ;dividend to hl, dtable addr to stack
mvi c,'0' ;count c up while subtracting
hdig0: mov a,l ;low order dividend
sub e ;low order dividend
mov l,a ;partial difference
mov a,h ;high order dividend
sbb d ;high order divisor
mov h,a ;hl = hl - decade
jc hdig1 ;carry gen'ed if too many subtracts
inr c ;to next ascii digit
jmp hdig0 ;for another subtract
;
hdig1: ;counted down too many times
dad d ;add decade back
mov a,b ;check for zero suppress
ora a ;sign bit set?
jp hdig2 ;skip if 0 bit set
push psw ;save the zero suppress / count
; high order bit set, must be zero suppression
mov a,c ;check for ascii zero
cpi '0'
jz hdig3 ;skip print if zero
; digit is not zero, clear the zero suppress flag
call pchar
pop psw
ani 7fh ;remove suppress flag
mov b,a ;back to b register
jmp hdig4 ;to decrement the b register
;
hdig2: ;zero suppression not set, print the digit
mov a,c ;ready to print
call pchar ;printed to console
jmp hdig4 ;to decrement the b register
;
hdig3: ;character is zero, suppression set
; may be the last digit
pop psw ;recall digit count
ani 7fh ;mask low bits
cpi 1 ;last digit?
jnz hdig4 ;to decrement the b register
mov b,a ;clear zero suppression
jmp hdig2 ;to print the character
;
hdig4: ;digit suppressed or printed, decrement count
xthl ;dtable address to hl, partial to stack
dcr b ;count b down
jnz nxtdig ;for another digit
;
; operation complete, remove partial result
pop d ;removed
pop d ;original value to de
; print the character in ascii if graphic
mov a,d ;must be zero
ora a
jnz start ;skip the test
mov a,e ;character graphic?
ani 7fh ;strip parity
cpi ' ' ;below space?
jc start ;skip if so
inr a ;7fh (rubout) becomes 00
jz start ;skip if so
call blank ;blank before quotes
mvi a,'''' ;first quote
call pchar
mov a,e
ani 7fh ;remove parity (again)
call pchar ;character
mvi a,''''
call pchar
jmp start
;
hexlist:
;dump the symbol table to the console
lhld sytop ;topmost element
inx h ;to low address
inx h ;to high address
hexlis0:
mov d,m ;high address to d
dcx h ;move down to low
mov e,m ;low address to e
dcx h ;move down to length
mov c,m ;length to c
dcx h ;to the first character
mov a,c ;to accumulator for compare
cpi 16 ;stop if length > 16
jnc start ;for the next instruction
; otherwise, print the symbol
call crlf ;newline for symbol
xchg ;symbol address to hl
call paddr ;address is printed
xchg ;hl is the first symbol
call blank ;to print a blank after address
inr c ;in case c = 00
hexlis1:
dcr c ;count = count - 1
jz hexlis2 ;skip to end of symbol if so
mov a,m ;character in a
dcx h ;to next symbol to get
call pchar ;to print the character
jmp hexlis1 ;for another character
hexlis2:
;end of symbol, carriage return line feed
call break
jnz start ;to skip the remainder
jmp hexlis0 ;for another symbol
;
; *********************************
; * *
; * i - input fcb *
; * *
; *********************************
infcb:
lda negcom ;negated?
ora a
jnz cerror ;command error if so
;
call FCBIN
;
jmp start ;for another command
;
; *********************************
; * *
; * l - list mnemonics *
; * *
; *********************************
;
lassm:
; assembler language output listing
; l<cr> lists from current disassm pc for several lines
; l<number><cr> lists from <number> for several lines
; l<number>,<number> lists between locations
call chkdis ;disassm present?
jnc cerror
;
call scanexp ;scan expressions which follow
jz spage ;branch if no expressions
call getval ;exp1 to h,l
shld dispc ;sets base pc for list
dcr a ;only expression?
jz spage ;sets single page mode
;
; another expression follows
call getval
shld dispm ;sets max value
dcr a
jnz cerror ;error if more expn's
xra a ;clear page mode
jmp spag0
;
spage: mvi a,psize ;screen size for list
spag0: sta dispg
call disen ;call disassembler
jmp start ;for another command
;
;
; *********************************
; * *
; * m - move memory *
; * *
; *********************************
;
move:
call scan3 ;bc,de,hl
move0: ;has b,c passed d,e?
call bcde
jc start ;end of move
; Check for wrap around
push b ;save state
push d
push h
lxi h,0FFFFh
mov a,h ;get high order
cmp b ;are they the same?
jnz MOVE1 ;B < H so keep movin....
;
mov a,l ;B = H so check low order
cmp c ;set flags
jnz MOVE1
;
jmp START ;they are equal,BC = FFFFh do not wrap
MOVE1:
pop h
pop d
pop b ;restore registers
; Else continue
ldax b ;char to accum
inx b ;next to get
mov m,a ;move it to memory
inx h
jmp move0 ;for another
;
;
; *********************************
; * *
; * p - permanent break *
; * *
; *********************************
permbrk:
call scanexp ;0,1, or 2 values
jc cerror ;p, not allowed
jz permzer ;no expressions
; 1 or 2 expressions found
call getval ;first value to hl (bp name)
push h ;saved to stack
lxi h,1 ;set to one break if not there
dcr a ;item count
lda negcom ;ready negated command flag
jz setpval ;skip if 1 expression
ora a ;negated if ff
jnz cerror ;command error if form is -px,y
call getval ;may be zero, usually pass count
jmp setpval0
setpval:
;only one expression, may be negated
lxi h,0
ora a ;negated if ff
jnz setpval0;to store the 00
lxi h,1 ;otherwise the pass count is 1
setpval0:
mov a,h ;high byte must be zero
ora a ;00?
jnz cerror ;command error if not
;
shld bias ;held in bias
lxi h,pbtable;search for the stacked address
mvi c,pbsize
perm0: push h ;save current element
mov a,m ;is count=00?
ora a ;set flags
jz perm2
; count is non-zero, may be current address
inx h ;low(addr)
mov a,m
inx h
mov d,m ;da is table address to compare
pop h ;table element base to hl
xthl ;stacked search address to hl
cmp l ;low(addr) = low(search)?
jnz perm1 ;skip if not
mov a,d
cmp h ;high(addr) = high(search)?
jnz perm1 ;skip if addr <> search
;
; found the address to operate upon
lda bias ;new count
pop h ;table element base to hl
mov m,a ;set to memory, may be zero
ora a
jmp start ;get next command
;
perm1: xthl ;search address back to stack
push h ;table address back to stack
perm2: pop h ;table address revived
lxi d,pbelt ;element size
dad d ;hl is next to scan
dcr c ;count down table length
jnz perm0 ;for another try
;
; arrive here if item cannot be found, must be setting break
lda bias ;=00?
ora a ;set flags
jz cerror ;error if not found
; search address is still stacked
;
; setting non zero permanent pass count, find free entry
lxi h,pbtable
mvi c,pbsize
lperm0: push h ;save current table base
mov a,m ;get low(count)
ora a ;count=00?
jnz lperm1 ;skip if in use
; free location, use it
lda bias ;count in reg-a
pop h ;table base to hl
mov m,a ;non zero count set
pop d ;search address
inx h
mov m,e ;set low search
inx h
mov m,d ;set high search address
jmp start ;for another command
;
lperm1: pop h ;recall table base
lxi d,pbelt
dad d ;hl is next to scan
dcr c ;count table size down
jnz lperm0
;
; no table space available
jmp cerror
;
;
permzer:
;no expressions encountered, must be display or clear
lxi h,pbtable ;search for display or reset
mvi c,pbsize
permz0: push h ;save next table element addr
mov a,m ;count to a
ora a ;skip if zero count
jz permz2 ;skip if inactive
; display or clear
lda negcom ;-p?
ora a
jz permz1
;
; this is a clear, so count = 00
mvi m,0 ;clear count
jmp permz2 ;to go to next item
;
permz1: ;this is a display
push b ;save pbtable count (c)
call crlf ;new line
mov a,m ;recall count to register a
call pbyte ;print byte
call blank ;blank delimiter
inx h ;low of address
mov e,m
inx h
mov d,m ;de is address of break point
call paddsy ;print symbol reference
pop b ;recall pbtable count in c
permz2: pop h ;recall table base
lxi d,pbelt ;element size
dad d ;to hl
dcr c ;count table down
jnz permz0 ;for another
jmp start ;for a command
;
; *********************************
; * *
; * r - read *
; * *
; *********************************
read:
lda CURLEN
ora a
jz CERROR ;no file after read command
;
lxi h,DFCB ;HL = default fcb
call GETFILE ;get filename
mvi m,00
inx h ;bump FCB pointer
mvi a,020h ;Blank in Acc
mvi c,11 ;counter for file blank
r1:
mov m,a ;blank at mem
inx h
dcr c ;
jnz r1 ;back if more
mvi a,00
mvi c,4 ;
r2: mov m,a ;zero out rest of FCB
inx h
dcr c
jnz r2
mvi m,0
;
call scanexp ;check for offset expression
lxi h,0 ;HL = initial BIAS offset
jz readn ;if none to readn
dcr a ;one expression?
jnz cerror
lhld EXPLIST+1 ;HL = new BIAS value
;
readn:
;hl holds bias value for load operation
shld bias
; copy the second half of the file control block to temp
lxi h,fcb2
lxi d,tfcb
mvi c,fcbl/2 ;half of the fcb size
read0: mov a,m
stax d ;store to temp position
inx h
inx d
dcr c ;count to end of fcb
jnz read0
; second half now saved, look at first name
lda fcb+1 ;* specified?
cpi '?'
jz checksy ;skip load if so
rinit: call opn ;open input file
cpi 255
jz cerror
; continue if file open went ok
; disk file opened and initialized
; check for 'hex' file and load til eof
;
lxi h,PCBASE
shld DEFLOAD
mvi a,'H' ;hex file?
lxi b,'XE' ;remainder of name to bc
call qtype ;look for 'hex'
lhld bias ;recall bias value
push h ;save to mem for loader
jz hread
;
; com/utl file, load with offset given by "bias"
pop h ;recall bias
lxi d,pcbase ;base of transient area
dad d
; reg h holds load address
lcom0: ;load com file
push h ;save dma address
lxi d,dfcb
mvi c,rdf ;read sector
call trapad
pop h
ora a ;set flags to check return code
jnz checksy
; move from 80h to load address in h,l
lxi d,dbf
mvi c,80h ;buffer size
lcom1: ldax d ;load next byte
inx d
mov m,a ;store next byte
inx h
dcr c
jnz lcom1
; loaded, check address against mload
call ckmload
call CKDFLD
xchg ;HL & DE correct
lhld BDOSE+1 ;HL = top of memory
call HLDE ;is DMA address > base of SID?
xchg
jnc LCOM0 ;if so then error.
lxi h,PCBASE
shld DEFLOAD
shld MLOAD
jmp CERROR
;
;
; otherwise assume hex file is being loaded
hread: call diskr ;next char to accum
cpi deof ;past end of tape?
jz cerror ;for another command
sbi ':'
jnz hread ;looking for start of record
;
; start found, clear checksum
mov d,a
pop h
push h
call rbyte
mov e,a ;save length
call rbyte ;high order addr
push psw
call rbyte ;low order addr
pop b
mov c,a
dad b ;biased addr in h
mov a,e ;check for last record
ora a
jnz rdtype
; end of tape, set load address
mov a,b
ora c ;load address = 00?
lxi h,pcbase;default = pcbase if 0000
jz setpc
; otherwise, pc at end of tape non zero
mov l,c ;low byte
mov h,b ;high byte
setpc: shld ploc ;set pc value
jmp checksy ;for symbol command
;
rdtype:
call rbyte ;record type = 0
;
; load record
red1: call rbyte
mov m,a
inx h
dcr e
jnz red1 ;for another byte
; otherwise at end of record - checksum
call rbyte
push psw ;for checksum check
call ckmload ;check against mload
call CKDFLD
pop psw
jnz cerror ;checksum error
jmp hread ;for another record
;
rdhex: ;read one hex byte without accumulating checksum
call diskr ;get one character
rdhex0: call hexcon ;convert to hex
rlc
rlc
rlc
rlc ;moved to high order nibble
ani 0f0h ;masked low order to 0000
push psw ;and stacked
call diskr ;get second character
call hexcon ;converted to hex in accum
pop b ;old accum to register b
ora b ;and'ed into result
ret
;
rbyte: ;read one byte from buff at wbp to reg-a
; compute checksum in reg-d
push b
push h
push d
;
call rdhex ;read one hex value
mov b,a ;value is now in b temporarily
pop d ;checksum
add d ;accumulating
mov d,a ;back to cs
; zero flag remains set
mov a,b ;bring byte back to accumulator
pop h
pop b ;back to initial state with accum set
ret
;
checksy:
; check for dis/assem overload
lxi h,modbas
call comload ;hl > mload? carry if so
jc chksym ;no dis/assem overlay
lda dasm ;00 if present
ora a
cz nodis ;remove if not already
;
chksym: ;check for symbol table file
; first save utl condition, if present
mvi a,'U' ;first character of utl
lxi b,'LT' ;remainder of name
call qtype ;find the file type - may be utl
push psw ;save condition for below
lxi h,tfcb ;name held here
lxi d,fcb ;source file control block
mvi c,fcbl/2
chksy0: mov a,m ;get character
stax d ;save into fcb
inx h
inx d ;pointers to next chars
dcr c
jnz chksy0
;
; fcb filled with second file name, clear cr field
xra a
sta fcb+fcr
lda fcb+1
cpi ' '
jz prstat ;skip if no file name
;
; symbol load follows
lxi h,symsg ;write ''symbols'
call prmsg ;print the message
; bias value is stored in "bias"
call opn ;open the symbol file
inr a ;255 becomes 00
jz cerror ;cannot open?
; file opened, load symbol table from file
;
; symbol table load routine - load elements of the
; form -
; (cr/lf/tab)hhhh(space)aaaaa(tab/cr)
; where hhhh is the hex address, aaaaa is a list of
; characters of length <16. add bias address to each loc'n
;
loadsy: call diskr ;get next starting character
loadsy0:
cpi eof
jz prstat ;completes the load
cpi ' '+1 ;graphic?
jc loadsy ;until graphic found
;
; get the symbol address to hl
call rdhex0 ;pre-read first character
push psw ;high order byte saved
call rdhex ;second half
pop d ;high order byte goes to d
mov e,a ;low order byte to e
lhld bias ;bias value in r command
dad d ;hl is offset address
push h ;save the address for later
call diskr ;get the blank char
cpi ' '
jz okload ;ok to load symbol if blank
;
; clear to the next non graphic character
pop h ;throw out the load address
skload:
;skip to non graphic character
call diskr ;read the next character
cpi ' ' ;below space if non graphic
jc loadsy0 ;for the next character test
jmp skload ;to bypass another character
;
okload:
lhld bdose+1 ;pointer to topmost jmp xxx around table
mvi e,0 ;counts the symbol length
loadch: ;load characters
dcx h ;next to fill
call diskr ;next char to a
cpi tab ;end of symbol?
jz syend
cpi cr ;may be end of line
jz syend
cpi ' '+1 ;graphic?
jc cerror ;it must be
mov m,a ;save it in memory
inr e ;count the length up
mov a,e ;past 16?
cpi 16
jnc cerror ;error if longer than 16 chars
jmp loadch ;for another character
;
syend: ;end of current symbol, set pointers for this one
; structure is:
; high bdos
; low bdos
; bjump: jmp
; ...
; high bjump
; low bjump
; bdose: jmp
;
; constructing symbol below bjump of the form
; high addr
; low addr
; bjump: length
; char1
; ...
; char length
;
; then move jmp bdos down below the symbol
;
push d ;save the length
push h ;save the next to fill
xchg ;de contains the next to fill
lhld bdose+1 ;address of the jmp xxx above symbol
inx h ;low jump address
mov e,m ;to e for now
inx h ;high jump address
mov d,m ;de is the xxx for the jmp xxx to install
pop h ;next to fill address
mov m,d ;high order address
dcx h ;.low address
mov m,e ;xxx filled below symbol
dcx h ;.jmp
mvi m,jmp ;jump instruction filled
; hl address the base of the table, ensure not below mload
call comload ;hl > mload ?
jnc cerror ;cy if so
xchg ;jmp xxx address to de
lhld bdose+1 ;previous jmp xxx address
xchg ;to de, hl is new jmp xxx address
shld bdose+1 ;changed jump address in low mem
xchg ;old jump address back to hl
pop d ;length is in e
mov m,e ;stored to memory
inx h ;low address location
pop d ;low address in de
mov m,e
inx h ;high address location
mov m,d
; now ready for another symbol
jmp loadsy
;
; end of the symbol load subroutine
prstat: ;print the statistics for the load or start utility
pop psw ;zero flag set if this is a utility
jnz prstat0 ;skip if not utility
;
; this is a ddt utility, start it
lxi h,retutl ;return address from utility
push h ;to stack
lhld ploc ;probably = pcbase
pchl ;gone to the utility ...
;
retutl:
;return here to reset the symbol table base
lhld bdose+1 ;new base of modules
dad d ;de is length of symbols inserted by utility
shld sytop ;new symbol top
jmp start ;for another command
;
;
prstat0:
; not a ddt utility, print statistics
lxi h,lmsg ;'next pc end'
call prmsg ;printed to console
lhld DEFLOAD ;default load address
call PADDR
call BLANK
lhld mload ;next address
call paddr
call blank ;following blank
lhld ploc ;pc value
call paddr
call blank ;next and pc printed
lhld bdose+1 ;end of memory+1
dcx h ;real end of memory
call paddr
jmp start ;for the crlf
;
;
;
; *********************************
; * *
; * s - set memory *
; * *
; *********************************
;
setmem: ;one expression expected
call scanword ;sets flags
dcr a ;one expression only
jnz cerror
call getval ;start address is in h,l
setm0: call crlf ;new line
push h ;save current address
call paddr ;address printed
call blank ;separator
pop h ;get data
push h ;save address to fill
; check for display mode
lda wdisp
ora a ;word mode?
jz setbyte
; set words of memory
mov e,m ;low order byte
inx h
mov d,m ;high order byte
xchg
call paddr ;address value printed
jmp setget ;get value from input
;
setbyte:
; byte mode set
mov a,m
call pbyte ;print byte
setget: call blank ;another separator
call getbuff ;fill input buffer
call gnc ;may be empty (no change)
pop h ;restore address to fill
cpi cr
jz setm1
cpi '.'
jnz chkasc ;skip to check ascii
; must be length zero (otherwise .symbol)
lda curlen
ora a
jz start ;for next command
mvi a,'.' ;otherwise restore
chkasc:
cpi '"' ;ascii input?
; filling ascii/ byte/ address data
push h ;save address to fill
jnz sethex ;hex single or double precision
; set ascii data to memory
setasc: call gnlc ;next byte to fill
pop h ;next address to fill
cpi cr ;end of line
jz setm0 ;for next input
mov m,a ;otherwise store it
inx h ;to next address to fill
push h ;save the address
jmp setasc
;
; byte or address data is being changed
sethex:
call scanex ;first character already scanned
dcr a ;one item?
jnz cerror ;more than one
call getval ;value to h,l
lda wdisp ;word mode?
ora a ;word mode=ff
jz setbyt0
; filling double precision value
xchg ;value to de
pop h ;recall fill address
mov m,e ;low order
inx h ;addressing high order position
mov m,d ;filled
inx h ;move to next address
jmp setm0 ;for the next address
;
; filling byte value
setbyt0:
ora a ;high order must be zero
jnz cerror ;data is in l
mov a,l
pop h ;restore data value
mov m,a
setm1: inx h ;next address ready
lda wdisp
ora a ;word mode?
jz setm0 ;skip inx if so
inx h ;to next double word
jmp setm0
;
; *********************************
; * *
; * u - untrace mode *
; * *
; *********************************
;
untrace:
mvi a,1 ;untrace mode = 1
jmp etrace
;
; *********************************
; * *
; * t - start trace *
; * *
; *********************************
;
trace: mvi a,2 ;set trace mode flag=2
etrace:
sta tmode
; allow tw/uw to suppress out-of-line trace
call scanword
lxi h,0
shld userbrk ;clear userbrk
inx h ;default to one trace
jz trac0
; expressions were given, forms are
; tx trace for x steps acc = 1
; tx,brk trace for x steps, call "brk" at each stop acc=2
; t,brk call "brk" acc = 1, cy = 1
;
jc settr0
call getval ;to h,l
push psw
mov a,l ;check for zero
ora h
jz cerror
pop psw ;recall number of parameters
settr0: ;h,l contains trace count, save it for later
push h
; look for break address
dcr a ;if only one specified, then skip userbrk
jz settr1
dcr a ;must be two values
jnz cerror ;more than two specified
call getval ;value to h,l
shld userbrk
settr1: ;recall trace count
pop h
trac0: shld tracer
xra a ;00 to accum
sta gobrks ;mark as no user breaks
call dstate ;starting state is displayed
jmp gopr ;sets breakpoints and starts execution
;
; *********************************
; * *
; * v - value *
; * *
; *********************************
;
VALUE:
jmp PRSTAT0
;
;
; *********************************
; * *
; * w - write *
; * *
; *********************************
;
WRITE:
lda CURLEN
ora a
jz CERROR ;exit if no file present
;
;
lxi h,FCB ;load HL with fcb address
call GETFILE ;obtain file from command string
mvi a,00h
sta FCB+32 ;zero out the record count
lxi h,0100h
shld WBEGIN ;store begining address
lhld DEFLOAD ;get default end address
shld WEND ;store in Write END
;
call SCANEXP ;check for specified address
lda EXPLIST ;get number of experessions
ora a ;
jz NOWRPRM
;
cpi 2
jnz CERROR ;error if not two expr
lhld EXPLIST+1 ;HL = start address
shld WBEGIN ;store in begin
lhld EXPLIST+3 ;HL = finish address
shld WEND ;store in end
;
; Continue with WRITE
NOWRPRM:
;
lhld WBEGIN ;HL = beginning address
call CHKEND ;is end > begin ?
jc CERROR ; if so error
;
lxi h,00h ;get ready to zero out
shld WRTREC ;# of records written
; Now that FCB is set up get ready to write out
; to the specified file.
;
lxi d,DFCB
call DELETE
;
call MAKE
inr a
jz CERROR
lhld WBEGIN ;get beginning address
;
WLOOP0:
call WFLAG
lxi d,DBF ;DE = default DMA address
mvi c,80h ;counter for loop
;
WLOOP1:
mov a,m ;get byte
inx h ;bump pointer
stax d ;store in buffer
inx d ;bump pointer
dcr c ;decrement counter
jnz WLOOP1 ;again if not finished
;
lxi d,DFCB
call DWRITE ;write it out
ora a ;set flags for write check
jnz CERROR ;error if not 0
push h ;save source address
lhld WRTREC ;get # of records written
inx h ;bump it by one
shld WRTREC ;put it back
pop h ;get source address back
;
call CHKEND
;
lda ONEFLG ;set for flag check
cpi TRUE ;last record?
jnz WLOOP0 ;next record if not finished
WCLOSE:
lxi d,DFCB
call CLOSE
;
lxi h,WRTMSG
call PRMSG
lhld WRTREC ;# of records
call PADDR
lxi h,WRTMSG1
call PRMSG ;print out end of string
;
jmp START ;exit
;
CHKEND:
lda WEND ;get high order end byte
sub l ;get low order
sta rslt ;low order in rslt
lda WEND+1 ;high order equal check
sbb h ;sub high order
sta rslt+1 ;high order answer
ret
;
WFLAG:
mvi a,FALSE ;zero out flag
sta ONEFLG ;store
lda RSLT+1
cpi 00h
rnz
lda RSLT
cpi 080h ;record length
jc WFLAG1
jz WFLAG1
ret
WFLAG1:
mvi a,TRUE
sta ONEFLG
ret
;
ONEFLG: db 0
RSLT: dw 0
;
; *********************************
; * *
; * x - examine *
; * *
; *********************************
;
examine:
call gnc ;cr?
cpi cr
jnz exam0
call dstate ;display cpu state
jmp start
;
exam0: ;register change operation
lxi b,pval+1 ;b=0,c=pval (max register number)
; look for register match in rvect
lxi h,rvect
exam1: cmp m ;match in rvect?
jz exam2
inx h ;next rvect
inr b ;increment count
dcr c ;end of rvect?
jnz exam1
; no match
jmp cerror
;
exam2: ;match in rvect, b has register number
call gnc
cpi cr ;only character?
jnz cerror
;
; write contents, and get another buffer
push b ;save count
call crlf ;new line for element
call delt ;element written
call blank
call getbuff ;fill command buffer
call scanexp ;get input expression
ora a ;none?
jz start
dcr a ;must be only one
jnz cerror
call getval ;value is in h,l
pop b ;recall register number
; check cases for flags, reg-a, or double register
mov a,b
cpi aval
jnc exam4
; setting flags, must be zero or one
mov a,h
ora a
jnz cerror
mov a,l
cpi 2
jnc cerror
; 0 or 1 in h,l registers - get current flags and mask position
call flgshf
; shift count in c, d,e address flag position
mov h,a ;flags to h
mov b,c ;shift count to b
mvi a,0feh ;111111110 in accum to rotate
call lrotate ;rotate reg-a left
ana h ;mask all but altered bit
mov b,c ;restore shift count to b
mov h,a ;save masked flags
mov a,l ;0/1 to lsb of accum
call lrotate ;rotated to changed position
ora h ;restore all other flags
stax d ;back to machine state
jmp start ;for another command
;
lrotate: ;left rotate for flag setting
; pattern is in register a, count in register b
dcr b
rz ;rotate complete
rlc ;end-around rotate
jmp lrotate
;
exam4: ;may be accumulator change
jnz exam5
; must be byte value
mov a,h
ora a
jnz cerror
mov a,l ;get byte to store
lxi h,aloc ;a reg location in machine state
mov m,a ;store it away
jmp start
;
exam5: ;must be double register pair
push h ;save value
call getdba ;double address to hl
pop d ;value to d,e
mov m,e
inx h
mov m,d ;altered machine state
jmp start
;
diskr: ;disk read
push h
push d
push b
;
rdi: ;read disk input
lda dbp
ani 7fh
jz ndi ;get next disk input record
;
; read character
rdc:
mvi d,0
mov e,a
lxi h,dbf
dad d
mov a,m
cpi deof
jz RRET ;end of file
lxi h,dbp
inr m
ora a
jmp rret
;
ndi: ;next buffer in
mvi c,rdf
lxi d,dfcb
call trapad
ora a
jnz def
;
; buffer read ok
sta dbp ;store 00h
jmp rdc
;
def: ;store EOF and return (end file)
mvi a,DEOF
rret:
pop b
pop d
pop h
ret
;
; *********************************
; * *
; * ERROR ROUTINES *
; * *
; *********************************
;
cerror:
;error in command
call crlf
mvi a,'?'
call pchar
jmp start
;
; *********************************
; * *
; * general purpose subroutines *
; * *
; *********************************
;
COMDEF:
lxi h,FCB+9 ;set up address
mvi a,'C'
mov m,a ;store it
inx h
mvi a,'O'
mov m,a ;store it
inx h
mvi a,'M'
mov m,a
ret
;
;
SYMDEF:
lxi h,FCB+019h ;set up address
mvi a,'S'
mov m,a ;store it
inx h
mvi a,'Y'
mov m,a ;store it
inx h
mvi a,'M'
mov m,a
ret
;
;
fildel:
;file character delimiter in a?
cpi '.'
rz
fildel0:
cpi ',' ;comma?
rz
cpi cr
rz
cpi '*'
rz ;series of ?'s
cpi ' '
ret ;zero for cr, ., or blank
;
filfield:
;fill the current fcb field to max c characters
call fildel ;delimiter?
jz filf1 ;skip if so
mov m,a
inx h ;character filled
call gnfcb ;get next character
dcr c ;field length exhausted?
jnz filfield;for another character
; clear to delimiter
filf0: call fildel
rz ;return with delimiter in a
call gnfcb ;get another char
jmp filf0 ;to remove it
;
filf1: ;delimiter found before field exhausted
mvi d,' ' ;fill with blanks?
cpi '*'
jnz filf2 ;yes, if not *
call gnfcb ;read past the *
mvi d,'?' ;otherwise fill with ?'s
filf2: mov m,d ;fill remainder with blanks/questions
inx h ;to next character
dcr c ;count field length down
jnz filf2 ;for another blank
ret ;with delimiter in reg-a
;
;
bcde: ;compare bc > de (carry gen'd if true)
mov a,e
sub c
mov a,d
sbb b
ret
;
WRPCHK:
push h
push d
push b
mov d,b
mov e,c
lxi h,0FFFFh
call HLDE
pop b
pop d
pop h
ret
;
HLDE:
mov a,h ;Acc = H
cmp d ;is H <= D
rc ;return if H < D with carry
rnz ;return if H > D
mov a,l ;low order check H = D
cmp e ;what is the relationship
; H = D so test lower byte
rc ;return if L < E with carry
rnz ;return if L > E
xra a ;set zero for equality
ret
;
nodis: ;remove dis/assembler from memory image
mvi a,1
sta dasm ;marks dis/assem as missing
lxi h,demon
shld bdose+1 ;exclude dis/assembler
shld sytop ;mark top of symbol table
ret
;
; Scanners for various needs
;
; move the command buffer to the default area at dbf
FCBIN: lxi d,curlen ;current length dec'ed at gnc
lxi h,dbf ;default buffer
ldax d ;dec'ed length (exclude i)
mov c,a ;ready for loop
mov m,a ;store dec'ed length
inr c ;length ready for looping
inx d ;past 'i'
dbfill: inx d ;to first/next char
inx h ;to first/next to fill
ldax d ;get next char
ani 07Fh ;zero out lower case bit
mov m,a ;to buffer
dcr c ;end of buffer?
jnz dbfill ;loop if not
mov m,c ;00 at end of buffer
;
; now fill the file control blocks at fcb and fcb2
mvi e,2 ;fill fcb/fcb2
lxi h,fcb ;start of default fcb
call GETFILE
;
;
; now check for both fcb's complete
dcr e
cnz GETFILE ;to scan the second half
mvi m,0 ;fill current record field
ret
;
;
;
getbuff: ;fill command buffer and set pointers
mvi c,getf ;get buffer function
lxi d,comlen;start of command buffer
call trapad ;fill buffer
lxi h,combuf;next to get
shld nextcom
ret
;
;
scan3: ;scan three expn's for fill and move
call scanexp
cpi 3
jnz cerror
call getval
push h
call getval
push h
call getval
pop d
pop b ;bc,de,hl
ret
;
;
scanword:
;perform scan, with possible word mode
call gnc ;check for w
lxi h,wdisp
mvi m,0 ;clear it now, check for w
cpi 'W'
jnz scanex ;skip if not w and continue
; w encountered, set word mode
mvi m,0ffh
; and drop through for remainder of scan
;
scanexp: ;scan expressions - carry set if ,b
; zero set if no expressions, a set to number of expressions
; hi order bit set if ,b also
call gnc
;
scanex: ;enter here if character already scanned
lxi h,explist
mvi m,0 ;zero expressions
inx h ;ready to fill expression list
cpi cr ;end of line?
jz scanret
;
; not cr, must be digit or comma
cpi ','
jnz scane0
; mark as comma
mvi a,80h
sta explist
lxi d,0
jmp scane1
;
scane0: ;not cr or comma
call getexp ;expression to d,e
scane1: call scstore ;store the expression and increment h,l
cpi cr
jz scanret
call gnc
call getexp
call scstore
; second digit scanned
cpi cr
jz scanret
call gnc
call getexp
call scstore
cpi cr
jnz cerror
scanret:
lxi d,explist ;look at count
ldax d ;load count to acc
cpi 81h ;, without b?
jz cerror
inx d ;ready to extract expn's
ora a ;zero flag may be set
rlc
rrc ;set carry if ho bit set (,b)
ret ;with flags set
;
;
GETFILE:
; Get filename for FCB routine
fildisk:
call gnfcb0 ;read and clear lookahead character
cpi ' '
jz fildisk ;deblank input line
;
push psw ;save first character
call gnfcb ;get second character
cpi ':'
jnz nodisk ;skip if not disk drive
;
; disk specified, fill with drive name
pop psw
sui 'A'-1 ;normalized to 1,2,...
mov m,a
inx h ;filled to memory
call gnfcb0 ;scan another character
jmp filnam
;
nodisk: ;use default drive (00 in fcb/fcb2)
mov b,a ;save second char
mvi m,0
inx h ;character filled
pop psw ;recall original character
;
filnam:
;fill the file name field, first character in a
mvi c,ffnl ;file name length
call filfield;filed filled, padded with blanks
cpi '.' ;delimiter period filename.filetype
cz gnfcb ;clear the period
;
mvi c,fftl ;file type length in c
call filfield;fill the type field
;
filext: ;now cleared to next blank or cr
mvi c,fcbl/2-ffnl-fftl-1 ;number of bytes remaining
filex0:
mvi m,0
inx h ;fill a zero
dcr c
jnz filex0
ret
;
;
; set input file control block (at 5ch) to simulate console command
; useful subroutines for infcb:
gnfcb0: ;zero the lookahead character and read
mvi b,0
gnfcb: ;get next fcb character from lookahead or input
mov a,b ;lookahead active?
mvi b,0 ;clear if so
ora a ;set flags
rnz
jmp gnc ;otherwise get real character
;
gnc: ;get next console character with translation
call gnlc ;get next lower case char
;drop through to translate
trans:
; translate to upper case
cpi 7fh ;rubout?
rz
cpi ('A' or 0100000b) ;upper case a
rc
ani 1011111b ;clear upper case bit
ret
;
gnlc:
; get next buffer character from console w/o translation
push h ;save for reuse locally
lxi h,curlen
mov a,m
ora a ;zero?
mvi a,cr
jz gncret ;return with cr if exhausted
dcr m ;curlen=curlen-1
lhld nextcom
mov a,m ;get next character
inx h ;nextcom=nextcom+1
shld nextcom ;updated
gncret: pop h ;restore environment
ret;
;
; *********************************
; * *
; * Disk I/O routines *
; * *
; *********************************
;
opn:
;file open routine. this subroutine opens the disk input
push h
push d
push b
xra a
sta dbp ;clear buffer pointer
mvi c,opf
lxi d,dfcb
call trapad ;to bds
pop b
pop d
pop h
ret
CLOSE:
push b
push d
push h
mvi c,16
call TRAPAD
pop h
pop d
pop b
ret
;
DWRITE:
; Disk write routine
push b
push d
push h
mvi c,WRITF ;write func
call TRAPAD
pop h
pop d
pop b
ret
;
;
SETDMA:
; DMA address set routine
push b
push d
push h
mvi c,DMAF ;DMA func #
call TRAPAD
pop h
pop d
pop b
ret
;
MAKE:
;make a file
push b
push d
push h
mvi c,22
call TRAPAD
pop h
pop d
pop b
ret
;
DELETE:
; File delete routine
push b
push d
push h
mvi c,DELF
call TRAPAD
pop h
pop d
pop b
ret
;
; read files (hex or com)
;
;
qtype: ;check for command file type (com, hex, utl)
; regs a,b,c contain characters to match
lxi h,fcb+fft
cmp m
rnz ;return with no match?
mov a,b ;matched, check next
inx h ;next fcb char
cmp m
rnz ;matched?
mov a,c ;yes, get next char
inx h
cmp m ;compare, and
ret ;return with nz flag if no match
;
;
comload: ;compare hl > mload
xchg ;h,l to d,e
lhld mload ;mload to h,l
mov a,l ;mload lsb
sub e
mov a,h
sbb d ;mload-oldhl gens carry if hl>mload
xchg
ret
;
ckmload: ;check for hl > mload and set mload if so
call comload ;carry if hl>mload
rnc
shld mload ;change it
ret
;
;
CKDFLD:
xchg
lhld DEFLOAD
mov a,l ;lsb
sub e ;
mov a,h ;msb
sbb d ;is it smaller?
xchg
rnc ;no change
shld DEFLOAD ;return new value
ret
;
;
chkdis: ;check for disassm present
lda dasm ;=00 if present
cpi 1 ;00-1 generates carry
rnc ;01-1 generates "no carry"
; otherwise, check high load address
push h
lxi h,modbas ;base address
call comload
pop h
ret
;
; Print routines for sscreen display
;
blank:
mvi a,' '
;
pchar: ;print character to console
push h
push d
push b
mov e,a
mvi c,cof
call trapad
pop b
pop d
pop h
ret
;
prmsg: ;print message at hl until 00 encountered
mov a,m
ora a
rz ;end if 00 found
call pchar ;print the current char
inx h ;move to next char
jmp prmsg ;for another char
;
pnib: ;print nibble in lo accum
cpi 10
jnc pnibh ;jump if a-f
adi '0'
jmp pchar ;ret thru pchar
pnibh: adi 'A'-10
jmp pchar
;
pbyte: push psw ;save a copy for lo nibble
rar
rar
rar
rar
ani 0fh ;mask ho nibble to lo nibble
call pnib
pop psw ;recall byte
ani 0fh
jmp pnib
;
crlf: ;carriage return line feed
mvi a,cr
call pchar
mvi a,lf
jmp pchar
;
break: ;check for break key
push b
push d
push h
mvi c,chkio
call trapad
ani 1b
pop h
pop d
pop b
ret
;
paddsh: ;print address reference given by hl
xchg
;
paddsy: ;print address reference given by de, along
; with symbol at that address (if it exists)
push d ;save the address for symbol lookup
xchg ;ready for the address dump
call paddr ;hex value printed
pop d ;recall search address
lda negcom ;negated command?
ora a ;ff?
rnz ;return if true
call alookup ;address lookup
rz ;skip symbol if not found
; symbol found, print it
prdotsy:
;print symbol preceded by .
call blank
mvi a,'.'
call pchar
;
; drop through to print symbol
prsym:
mov e,m ;get length of symbol
prsy0: dcx h ;to first/next character
mov a,m ;next to print
call pchar ;character out
dcr e ;count length down
jnz prsy0
ret ;return to caller
;
; enter here to print optional label at hl
prlabel:
push h ;save address
lda negcom ;negated?
ora a
pop d ;recalled in case return
rnz ;continue if not negated
call alookup ;does the label exist?
rz ;return if not present
call crlf ;go to newline
call prsym ;print the symbol
mvi a,':'
call pchar ;label:
ret
;
;
paddr: ;print the address value in h,l
mov a,h
call pbyte
mov a,l
jmp pbyte
;
pgraph: ;print graphic character in reg-a or '.' if not
cpi 7fh
jnc pperiod
cpi ' '
jnc pchar
pperiod:
mvi a,'.'
jmp pchar
;
discom: ;compare h,l against dismax. carry set if hl > dismax and
xchg
lhld dismax
mov a,l
sub e
mov l,a ;replace for zero tests later
mov a,h
sbb d
xchg
ret
;
;
; sydelim checks for / + - cr , or blank
; sysep checks for + - cr , or blank
; delim checks for cr , or blank
;
;
sydelim:;check for symbol delimiter
cpi '/' ;separator
rz
sysep: ;separator?
cpi '+'
rz
cpi '-'
rz
;
delim: ;check for delimiter character
cpi cr
rz
cpi ','
rz
cpi ' '
ret
;
hexcon: ;convert accumulator to pure binary from external hex
sui '0'
cpi 10
rc ;must be 0-9
adi ('0'-'A'+10) and 0ffh
cpi 16
rc ;must be 0-15
jmp cerror ;bad hex digit
;
getval: ;get next expression value to h,l (pointer in d,e assumed)
xchg
mov e,m
inx h
mov d,m
inx h
xchg
ret
;
getsymv:
;lookup symbol preceded by =, @, or . operator
push d ;save next to fill in address vector
call gnc ;read the next character
lhld sytop ;hl is beginning of search
getsy0: push psw ;save first character
mov c,m ;length of current symbol
mov a,c ;to a for end of search check
cpi 16 ;length 16 or more ends search
jnc cerror ;? error if not there
pop psw ;recall first character
xchg ;symbol address to de
push d ;save search address
push psw ;save character
lhld nextcom ;next buffer position
push h ;saved to memory
lhld comlen ;comlen and curlen
push h ;save to memory
; stacked: curlen/nextcom/char/symaddr
xchg ;de is next to match+1
inr c ;count+1
sychar: ;check next character
call sydelim ;/, comma, cr, or space?
jz sydel ;stop scan if so
; not a delimiter in the input, end of symbol?
dcr c ;count=count-1
jz synxt ;skip to next symbol if so
; not end of symbol, check for match
dcx h ;next symbol address
cmp m ;same?
jnz synxt ;skip if not
call gnc ;otherwise, get next input character
jmp sychar ;for another match attempt
;
sydel: ;delimiter found, count should go to zero
dcr c
jnz synxt ;skip symbol if not
;
; symbol matched, return symbol's value
pop h ;discard comlen
pop h ;discard nextcom
pop h ;discard first character
call sysep ;+ - cr, comma, or space? (not / test)
jz syloc ;return if not a / at end
call gnc ;remove the / and continue the scan
jmp synxt0 ;for another symbol
;
; end of input, get value to de
syloc: pop h ;recall symbol address
inx h ;to low address
mov e,m ;low address to de
inx h ;to high address
mov d,m ;to d
pop h ;re-instate hl
ret ;with de=value, hl=next to fill
;
;
synxt: ;move to the next symbol
pop h ;comlen
shld comlen ;restored
pop h ;nextcom
shld nextcom ;restored
pop psw ;first character to a
synxt0: pop h ;symbol address
push psw ;save first character
mov a,m ;symbol length
cma ;1's complement of length
add l ;hl=hl-length-1
mov l,a
mvi a,0ffh ;extend sign of length
adc h ;high order bits
mov h,a ;now move past address field
dcx h ;-1
dcx h ;total is: hl=hl-length-3
pop psw ;recall first character
jmp getsy0 ;for another search
;
;
; otherwise, numeric operand expected
getoper: ;get hex value to d,e (possible symbol reference)
xchg ;next to fill in de
lxi h,0 ;ready to accumulate value
cpi '.' ;address reference?
jz getsymv ;return through getsymv
cpi '@' ;value reference?
jnz getoper0 ;skip if not
call getsymv ;address to de
push h ;save next to fill
xchg ;address of double prec value to hl
mov e,m
inx h
mov d,m ;double value to de
pop h ;restore next to fill
ret ;with de=value, hl=next to fill
getoper0:
cpi '=' ;byte reference?
jnz getoper1 ;skip if not
; found a byte reference, look up symbol
call getsymv ;de = address, hl = next to fill
push h ;save hl
xchg ;operand address to hl
mov e,m ;get byte value
mvi d,0 ;high byte is zero
pop h ;restore next to fill
ret ;with de=value, hl=next to fill
;
getoper1:
; not ., @, or .
cpi '''' ;start of string?
jnz getoper2
; start of string, scan until matching quote
xchg ;return 0000 to de, next to fill to hl
getstr0:
call gnlc ;inside quoted string
cpi ' ' ;must be grapic
jc cerror ;otherwise report error
; character is graphic, check for embedded quotes
cpi ''''
jnz getstr1 ;skip if not
; must be embedded quote or end of string
call gnlc ;character following quote
call sysep ;symbol separator?
rz ;return with value in de
; otherwise the symbol is not a separator, must be quote
cpi ''''
jnz cerror ;report error if not
getstr1:
;store the ascii character into low order de
mov d,e ;low character to high character
mov e,a ;low character from accumulator
jmp getstr0 ;for another character scan
;
getoper2:
;check for decimal input
cpi '#'
jnz getoper3 ;must be hex
; decimal input, convert
getdec0:
call gnc ;get next digit
call sysep ;separator?
jz getdec1 ;skip to end if so
sui '0' ;decimal digit?
cpi 10
jnc cerror ;error if above 9
dad h ;hl=hl*2
mov b,h ;save high order
mov c,l ;save low order
dad h ;*4
dad h ;*8
dad b ;*10
mov c,a ;ready to add digit
mvi b,0
dad b ;digit added to hl
jmp getdec0 ;for another digit
;
getdec1:
xchg
ret ;with de=value
;
getoper3:
cpi '^' ;stacked value?
jnz getoper4;skip if not
;
; get stacked value
push d ;save next to fill
lhld sloc ;stack pointer
getstk: mov e,m
inx h
mov d,m ;de is stacked value
inx h ;in case another ^
call gnc ;get another char
cpi '^' ;^ ... ^
jz getstk
pop h ;de=value, hl=next to fill
ret ;with value in de
;
getoper4:
; not ., @, =, or ', must be numeric
call hexcon
dad h ;*2
dad h ;*4
dad h ;*8
dad h ;*16
ora l ;hl=hl+hex
mov l,a
call gnc
call sysep ;delimiter?
jnz getoper3
xchg
ret
;
scstore: ;store d,e to h,l and increment address
xchg
shld lastexp ;save as "last expression"
xchg
mov m,e
inx h
mov m,d
inx h
push h
lxi h,explist
inr m ;count number of expn's
pop h
ret
;
getexp:
;scan the next expression with embedded +,- symbols
cpi '-' ;leading minus?
jnz getexpp ;skip to next if not
lxi d,0 ;assume a starting 0, with following minus
jmp getexp2 ;to continue with the scan
;
getexpp:
;check for leading + operator
cpi '+'
jnz getexp0 ;to continue the scan
; leading + found, use last expression
xchg ;de=hl
lhld lastexp ;last expression to hl
xchg ;then to de
jmp getplus ;handle the plus operator
getexp0:
;scan next item
call getoper ;value to de
getexpo:
;get expression operator
cpi '+' ;stopped on +?
jnz getexp1 ;skip to next test if not
; + delimiter found, scan following operand
getplus:
push d ;save current value
call gnc ;scan past the +
call getoper ;next value to de
pop b ;recall previous value
xchg ;next value to hl
dad b ;sum in hl
xchg ;back to position
jmp getexpo ;to test for following operand
;
getexp1:
;not a +, check for - operator
cpi '-'
rnz ;return with delimiter in a if not
; - delimiter found
getexp2:
call gnc ;to clear the operator
push d ;save current value
call getoper ;to get the next value
pop b ;recall original value to bc
push psw ;save character
mov a,c ;low byte to a
sub e ;diff in low bytes
mov e,a ;back to e
mov a,b ;high byte to a
sbb d ;diff in high bytes
mov d,a ;back to de
pop psw ;restore next character
jmp getexpo ;for the remainder of the expression
;
;
; subroutines for cpu state display
flgshf: ;shift computation for flag given by reg-b
; reg a contains flag upon exit (unshifted)
; reg c contains number of shifts required+1
; regs d,e contain address of flags in template
push h
lxi h,flgtab ;shift table
mov e,b
mvi d,0
dad d
mov c,m ;shift count to c
lxi h,floc ;address of flags
mov a,m ;to reg a
xchg ;save address
pop h
ret
;
getflg: ;get flag given by reg-b to reg-a and mask
call flgshf ;bits to shift in reg-a
getfl0: dcr c
jz getfl1
rar
jmp getfl0
getfl1: ani 1b
ret
;
getdba: ;get double byte address corresponding to reg-a to hl
sui bval ;normalize to 0,1,...
lxi h,rinx ;index to stacked values
mov e,a ;index to e
mvi d,0 ;double precision
dad d ;indexed into vector
mov e,m ;offset to e
mvi d,0ffh ;-1
lxi h,stack
dad d ;hl has base address
ret
;
getdbl: ;get double byte corresponding to reg-a to hl
call getdba ;address of elt in hl
mov e,m ;lsb
inx h
mov d,m ;msb
xchg ;back to hl
ret
;
delt: ;display cpu element given by count in reg-b, address in h,l
mov a,b ;get count
cpi aval ;past a?
jnc delt0 ;jmp if not flag
;
; display flag
call getflg ;flag to reg-a
ora a ;flag=0?
mvi a,'-' ;for false display
jz pchar ;return through pchar
mov a,m ;otherwise get the character
jmp pchar ;print the flag name if true
;
delt0: ;not flag, display x= and data
push psw
mov a,m
call pchar ;register name
mvi a,'='
call pchar
pop psw
jnz delt1 ;jump if not reg-a
;
; register a, display byte value
lxi h,aloc
mov a,m
call pbyte
ret
;
delt1: ;double byte display
call getdbl ;to h,l
call paddr ;printed
ret
;
dstate: ;display cpu state
call crlf ;new line
call blank ;single blank
lxi h,rvect ;register vector
mvi b,0 ;register count
dsta0: push b
push h
call delt ;element displayed
pop h ;rvect address restored
pop b ;count restored
inr b ;next count
inx h ;next register
mov a,b ;last count?
cpi pval+1
jnc dsta1 ;jmp if past end
cpi aval ;blank after?
jc dsta0
; yes, blank and go again
call blank
jmp dsta0
;
; ready to send decoded instruction
dsta1:
call blank
call nbrk ;compute breakpoints in case of trace
push psw ;save expression count - b,c and d,e have bpts
push d ;save bp address
push b ;save aux breakpoint
call chkdis ;check to see if disassember is here
jnc dchex ;display hex if not
; disassemble code
lhld ploc ;get current pc
shld dispc ;set disassm pc
lxi h,dispg;page mode = 0ffh to trace
mvi m,0ffh
call disen
jmp dstret
;
dchex: ;display hex
dcx h ;point to last to write
shld dismax ;save for compare below
lhld ploc ;start address of trace
mov a,m ;get opcode
call pbyte
inx h ;ready for next byte
call discom ;zero set if one byte to print, carry if no more
jc dstret
push psw ;save result of zero test
call blank ;separator
pop psw ;recall zero test
ora e ;zero test
jz dsta2
; display double byte
mov e,m
inx h
mov d,m
call paddsy ;print address
jmp dstret
;
dsta2: ;print byte value
mov a,m
call pbyte
dstret:
; now print symbol for this instruction if implied memory op
lhld ploc ;instruction location
mov a,m ;instruction to a register
mov b,a ;copy to b register
; check for adc, add, ana, cmp, ora, sbb, sub, xra m
ani 1100$0000b ;high order bits 11?
cpi 1000$0000b ;check
jnz notacc
; found acc-reg operation, involving memory?
mov a,b ;restore op code
ani 0000$0111b
cpi 6 ;memory = 6
jnz disrest ;skip to restore registers if not
jmp dismem ;to display symbol
;
notacc: ;not an accumulator operation, check for mov x,m or m,x
cpi 0100$0000b ;mov operation?
jnz notmov
mov a,b ;mov operation or halt
cpi hlt ;skip halt test
jz disrest ;to skip tests
ani 111b ;move from memory?
cpi 6
jz dishl ;skip to print hl if so
; not move from memory, move to memory?
mov a,b ;restore operation code
ani 111000b ;select high order register
cpi 6 shl 3 ;check for memory op
jnz disrest ;skip to restore if not
jmp dishl ;to display hl register
;
notmov: ;not a move operation, check for mvi m
mov a,b ;restore operation code
cpi 0011$0110b ;mvi m,xx?
jz dishl ;display hl address if so
; now look for inr m, dcr m
cpi 0011$0100b ;inr m?
jz dismem ;skip to print hl if so
cpi 0011$0101b ;dcr m?
jnz notidcr ;skip if not inr / dcr m
dismem: ;display memory value first
mvi a,'='
call pchar
lhld hloc
mov a,m
call pbyte
;
dishl: ;display the hl symbol, if it exists
lhld hloc
jmp dissym ;to retrieve the symbol
;
notidcr:
;check for ldax/stax b/d
ani 1110$0111b ;ldax = 000 x1 010
cpi 0000$0010b ;stax = 000 x0 010
jnz disrest ;skip if not
mov a,b ;ldax/stax, get register
ani 0001$0000b ;get the b register bit
lhld dloc
jnz dissym ;skip to display
lhld bloc ;display b instead
dissym: ;enter here with the hl register set to symbol location
lda negcom ;negated?
ora a
jnz disrest ;forget it.
xchg ;search address to de
call alookup ;zero set if not found
jz disrest ;restore if not found
call prdotsy ;.symbol printed
; drop through to restore the registers
disrest:
pop b ;aux breakpoint
pop d ;restore breakpoint
pop psw ;restore count
ret
;
; data vectors for cpu display
rvect: db 'CZMEIABDHSP'
rinx: db (bloc-stack) and 0ffh ;location of bc
db (dloc-stack) and 0ffh ;location of de
db (hloc-stack) and 0ffh ;location of hl
db (sloc-stack) and 0ffh ;location of sp
db (ploc-stack) and 0ffh ;location of pc
; flgtab elements determine shift count to set/extract flags
flgtab: db 1,7,8,3,5 ;cy, zer, sign, par, idcy
;
clrtrace: ;clear the trace flag
lxi h,0
shld tracer
xra a ;clear accumulator
sta tmode ;clear trace mode
ret
;
breakp: ;arrive here when programmed break occurs
di
shld hloc ;hl saved
pop h ;recall return address
dcx h ;decrement for restart
shld ploc
; dad sp below destroys cy, so save and recall
push psw ;into user's stack
lxi h,2 ;bias sp by 2 because of push
dad sp ;sp in hl
pop psw ;restore cy and flags
lxi sp,stack-4;local stack
push h ;sp saved
push psw
push b
push d
; machine state saved, clear break points
ei ;in case interrupt driven io
lhld ploc ;check for rst instruction
mov a,m ;opcode to a
cpi rstin
; save condition codes for later test
push psw
; save ploc for later increment or decrement
push h
;
; clear any permanent break points
;
; check for auto "u" command from perm break pass
lda pbcnt ;=00 if no auto u in effect
sta autou ;hold this condition in auto u
;
; permanent breaks may be active, clear them
;
lxi h,pbtable+(pbsize-1)*pbelt ;set to last elt
mvi c,pbsize ;number of elements
resper0:
push h ;save element address
mov a,m ;(count)
ora a ;set flags
jz resper1 ;skip if not in use
inx h ;to next address
mov e,m ;low(addr)
inx h
mov d,m ;high(addr)
inx h
mov a,m ;data to set at addr
stax d ;data back to memory
resper1:
pop h ;base of element
lxi d,-pbelt ;element size
dad d ;addressing previous element
dcr c ;count table douwn
jnz resper0 ;for another element
;
; drop through when we have replaced all elements,
; now check for an "auto u" command from the last
; permanent break point bypass
call respbc ;restore pbcnt
;
clergo:
; clear "go" breakpoints which are pending
lxi h,breaks
mov a,m
mvi m,0 ;set to zero breaks
cler0: ora a ;any more?
jz cler1
dcr a
mov b,a ;save count
inx h ;address of break
mov e,m ;low addr
inx h
mov d,m ;high addr
inx h
mov a,m ;instruction
stax d ;back to program
mov a,b ;restore count
jmp cler0
;
cler1:
; all breakpoints have been cleared, check type of interrupt
pop h ;restore ploc
pop psw ;restore condition rstin=instruction
jz softbrk ;skip to softbreak if rst instruction
inx h ;front panel interrupt, don't dec ploc
shld ploc ;incremented
xchg ;ploc to de
if isis2 ;check for below bdtop
lxi b,bdtop
call bcde
jnc softbrk
else
lxi h,trapjmp+1 ;address ifeld of jmp bdos
mov c,m ;low address
inx h ;.high address
mov b,m ;bc is bdos address
call bcde ;to compare
jc softbrk
endif
;
; in the bdos, don't break until the return occurs
call clrtrace
lhld retloc ;trapped upon entry to bdos
xchg
mvi a,82h ;looks like g,bbbb
ora a ;sets flags
stc ;"," after g
jmp gopr ;to set break points
;
softbrk:
;now check for a matching address for a permanent break
; a matching address for a permanent break
lda pbtrace ;ff if trace from last perm break
ora a ;ff if traced
jnz stopcrx ;stop if so
;
; may be active permanent breaks, are we at one now?
lxi h,pbtable
mvi c,pbsize
chkpb0: ;check next element for permanent break address
push h ;save current pbtable address
mov a,m ;(count)
ora a ;set flags
jz chkpb3 ;skip if zero
inx h ;.low(addr)
mov a,m ;low(addr) in a
inx h
mov d,m ;high(addr) in d
lhld ploc ;program location
cmp l ;low(addr) = low(ploc)?
jnz chkpb3 ;skip if not
mov a,d ;check high bytes
cmp h
jnz chkpb3 ;skip if addr <> ploc
;
; addresses match, print trace or stop
pop h ;recall element address
mov a,m ;pass count
dcr a ;1 becomes 0
jnz chkpb1 ;skip if not last count
;
; stop execution at this point
push psw ;for "pass" report below
dcr a ;00 becomes ff
sta pbtrace ;perm break trace on
; trace is cleared on next iteration through code
; zero in accumulator printed in trace heading
jmp chktra0 ;to trace and stop
;
chkpb1: ;not the last count, decrement and set autou mode
mov m,a ;count=count-1
push psw ;save count
call dectra ;decrement trace counters
cpi 2 ;trace mode = 2?
jz chktra0 ;skip to print trace if so
;
; must be u/-u or g/-g, check negative command
lda negcom
ora a ;set to ff if -u or -g
jz chktra0 ;00 if u or g, so trace it
;
; must be -u or -g, so suppress the trace through
; ploc will match perm break address in gopr, so compute breaks
call nbrk ;setup break addresses
jmp gopr ;to move past break address
;
chktra0:
;print the header and go around again (may be one more time)
; (decremeted count is currently stacked)
call crlf
pop psw
inr a ;restore count
call pbyte ;print the byte value
lxi h,passmsg ;hh pass
call prmsg ;pass message printed
lhld ploc ;location counter
xchg ;readied for paddsy
call paddsy ;print address and symbol
call dstate ;display the current cpu state
jmp gopr ;to iterate one last time
;
chkpb3: ;move to next element
pop h ;recall element address
lxi d,pbelt ;element size
dad d ;to next element
dcr c ;count table down
jnz chkpb0
;
cler2: ;end of permanent breakpoint scan
; arrive here following simple break from a g command, or
; following an autou past a permanent break point
; may also be trace/untrace mode
;
call break ;break at the console?
jnz stopcrx ;stop execution if so
call dectra ;decrement trace flags
jz stopcr ;end if auto u not set (tmode=0)
dcr a ;1=untrace becomes 0
jnz break1 ;skip to print trace if not
;
; untrace mode, with or without autou set
; current ploc is not a permanent break address
call nbrk ;next break computed
jmp gopr ;go to the program untraced
;
break1: ;must be trace mode, not a permanent break address
; with or without the autou flag set
lhld ploc ;label trace
call prlabel
call dstate ;display cpu state
jmp gopr ;to next machine instruction
;
stopcr: ;not untrace/trace mode, if autou set then continue
; since this must be a step through a break point
lda autou
ora a ;zero set?
jz stopcrx ;skip if autou not set
; auto u set, must be step through a break point, next address
; is not a permanent break point, so go to user breaks
lhld gobrk2 ;auxiliary break point
mov c,l ;to bc
mov b,h ;in case set
lhld gobrk1 ;primary break point
xchg ;to de
lda gobrks ;number of breaks set by user
ora a ;may set the zero flag
stc ;carry indicates use current ploc
jmp gopr ;to continue
;
stopcrx:
call crlf
;
stopex:
call respbc ;restore pbcnt/pbloc, if necessary
lxi h,0
shld userbrk ;clear user break address
call clrtrace ;trace flags go to zero
sta pbtrace ;clear perm trace flag
mvi a,'*'
call pchar
lhld ploc
; check to ensure disassembler is present
call chkdis
jnc stop0
shld dispc
stop0: call paddsh ;print address with symbol location
lhld hloc
shld disloc
jmp start
;
passmsg:
db ' PASS ',0 ;printed in pass trace
;
dectra: ;decrement trace flags if trace mode
lxi h,tmode ;trace mode 0 if off, 1 un, 2 tr
mov a,m ;to accum
ora a ;set condition flags
rz ;no action if off
push h ;save tmode address
lhld tracer ;get count
dcx h ;count=count-1
shld tracer ;back to memory
mov a,h ;now zero?
ora l ;hl=0000?
pop h ;restore tmode address
jnz dectr0 ;skip if not
mov m,a ;tmode = 0
dcr a ;accum = ff
sta pbtrace ;to stop on next iteration
dectr0: mov a,m ;recall tmode
ora a ;set flags
ret
;
cat: ;determine opcode category - code in register b
; d,e contain double precision category number on return
lxi d,opmax ;d=0,e=opmax
lxi h,oplist
cat0: mov a,m ;mask to a
ana b ;mask opcode from b
inx h ;ready for compare
cmp m ;same after mask?
inx h ;ready for next compare
jz cat1 ;exit if compared ok
inr d ;up count if not matched
dcr e ;finished?
jnz cat0
cat1: mov e,d ;e is category number
mvi d,0 ;double precision
ret
;
respbc: ;restore pbcnt to pbloc, if req'd
lda pbcnt ;00 if no auto u
ora a ;set flags
rz ;no further actions if so
lhld pbloc ;pbtable element to restore
mov m,a ;(count)
xra a ;clear accumulator
sta pbcnt ;clear auto u mode
ret
;
nbrk: ;find next break point address
; upon return, register a is setup as if user typed g,b1,b2 or
; g,b1 depending upon operator category. b,c contains second bp,
; d,e contains primary bp. hl address next opcode byte
lhld ploc
mov b,m ;get operator
inx h ;hl address byte following opcode
push h ;save it for later
call cat ;determine operator category
lxi h,catno ;save category number
mov m,e
lxi h,cattab;category table base
dad d ;inxed
dad d ;inxed*2
mov e,m ;low byte to e
inx h
mov d,m ;high byte to d
xchg
pchl ;jump into table
;
; opcode category table
callop equ 2 ;position of call operator
callcon equ 3 ;position of call conditional
cattab: dw jmpop ;jump operator
dw ccop ;jump conditional
dw jmpop ;call operator (treated as jmp)
dw ccop ;call conditional
dw retop ;return from subroutine
dw rstop ;restart
dw pcop ;pchl
dw imop ;single precision immediate (2 byte)
dw imop ;adi ... cpi
dw dimop ;double precision immediate (3 bytes)
dw dimop ;lhld ... sta
dw rcond ;return conditional
dw imop ;in/out
; next dw must be the last in the sequence
dw simop ;simple operator (1 byte)
;
jmpop: ;get operand field, check for bdos
call getopa ;get operand address to d,e and compare with bdos
jnz endop ;treat as simple operator if not bdos
; otherwise, treat as a return instruction
retop: call getsp ;address at stacktop to d,e
jmp endop ;treat as simple operator
;
cbdos: ;de addresses a possible break point - check to ensure
; it is not a jump to the bdos
;
lda trapjmp+1 ;low bdos address
cmp e
rnz
lda trapjmp+2 ;high bdos address
cmp d
ret
;
getopa: ;get operand address and compare with bdos
pop b ;get return address
pop h ;get operand address
mov e,m
inx h
mov d,m
inx h
push h ;updated pc into stack
push b ;return address to stack
jmp cbdos ;return through cbdos with zero flag set
;
getsp: ;get return address from user's stack to d,e
lhld sloc
mov e,m
inx h
mov d,m
ret
;
ccop: ;call conditional operator
call getopa ;get operand address to d,e / compare with bdos
jz ccop1
; not the bdos, break at operand address and next address
pop b ;next address to b,c
push b ;back to stack
mvi a,2 ;two breakpoints
jmp retcat ;return from nbrk
;
ccop1: ;break address at next location only, wait for return from bdos
pop d
push d ;back to stack
jmp endop ;one breakpoint address
;
rstop: ;restart instruction - check for rst 7
mov a,b
cpi rstin ;restart instruction used for soft int
jnz rst0
;
; soft rst, no break point since it will occur immediately
xra a
jmp retcat1 ;zero accumulator
rst0: ani 111000b ;get restart number
mov e,a
mvi d,0 ;double precision breakpoint to d,e
jmp endop
;
pcop: ;pchl
lhld hloc
xchg ;hl value to d,e for breakpoint
call cbdos ;bdos value?
jnz endop
; pchl to bdos, use return address
jmp retop
;
chkcall:
;check for call or call conditional operator,
;if found, use the return address (pc+3) as break
;return "no carry" if call or call conditional
lda catno ;category number
cpi callop ;category number for call operator
rc ;carry if below callop
; must be call operator or above
cpi callcon+1
; carry set if below callcon+1, so complement
cmc ;carry if callcon+1 or above
rc ;carry implies not between callop and callcon
; must be between callop and callcon (inclusive)
; use pc+3 as the break for tw/uw or rom entry
lhld ploc
inx h
inx h
inx h ;ploc+3
xchg ;to de
ret ;with the no-carry bit set
;
;
simop: ;simple operator, use stacked pc
pop d
push d
jmp endop
;
rcond: ;return conditional
call getsp ;get return address from stack
pop b ;b,c alternate location
push b ;replace it
mvi a,2
jmp retcat ;to set flags and return
;
dimop: ;double precision immediate operator
pop d
inx d ;incremented once, drop thru for another
push d ;copy back
;
imop: ;single precision immediate
pop d
inx d
push d
;
endop: ;end operator scan
mvi a,1 ;single breakpoint
retcat: ;return from nbrk
inr a ;count up for g,...
stc
retcat1:
push psw ;save register state in case userbrk
lhld userbrk
mov a,h
ora l
jz retcat2 ;no userbrk if zero
;
push d ;save break point
push b ;save aux break point
push h ;save userbrk address for pchl below
; user break occurs here, call user routine and check return
lxi h,catno
mov c,m ;opcode category is in c
lhld ploc
xchg ;location of instruction in d,e
lxi h,retuser
xthl ;return address to stack, userbrk to h,l
pchl
retuser: ;return from user break, check register a
ora a
pop b ;restore breakpoints
pop d
jz retcat2
; abort the operation with a condition
push psw
mvi a,'#'
call pchar
pop psw
call pbyte
mvi a,' '
call pchar
jmp stopex ;stop execution
retcat2:
;check for call operator with tw or uw mode set
lda tmode
lxi h,wdisp ;wdisp=ff if w encountered
ana m ;non zero if tmode>0, wmode set
jz notcall ;skip if not a call
;
; this may be a call or call condition in tw/uw mode
call chkcall ;check for call, nc set if found
jc notcall ;skip if not a call
;
; this is a call in tw/uw mode, de is pc+3, use it for break
pop psw ;previous break count in a
mvi a,2 ;use only one break
jmp retcat4 ;to return from nbrk
;
notcall:
pop psw ;recall g, state
push psw ;save for final return below
;
; now check to ensure that break is not in rom
ora a ;zero break points set?
jz retcat3 ;skip to end if so
;
; must be 2/3 in accumulator
dcr a ;resulting in 1/2 breakpoints
; bc = aux breakpoint, de = primary breakpoint
romram: xchg ;first/aux breakpoint to hl
mov e,a ;breakpoint count to e (1/2)
mov a,m ;get code byte
cma ;complement for rom test
mov m,a ;store to rom/ram
cmp m ;did it change?
cma ;complement back to orginal
mov m,a ;restore in case ram
mov a,e ;restore breakpoint count
; arrive here with zero flag set if ram break
xchg ;break address back to de
push psw ;save count
jz ramloc ;skip if ram location
;
; break address is in rom. if conditional call, let
; it go, the return break is already set. if a simple
; call, set break at the ploc+3. otherwise, assume that
; the stack contains the return address
call chkcall ;check for call or call conditional
jnc ramloc ;nc if found, de is return address
;not a call operation, must be pchl or jmp
call getsp ;get the return address from stack
;
ramloc: pop psw ;restore break count
dcr a ;1/2 breaks becomes 0/1
jz retcat3 ;stop analysis if breaks exhausted
; otherwise, exchange bc/de and retry
push d ;de saved for exchange
mov e,c ;low bc to low de
mov d,b ;high bc to high de
pop b ;old de to bc
jmp romram ;to analze next break
;
retcat3:
;analysis of rom/ram complete, restore counts
pop psw ;break count and carry
retcat4:
pop h ;next address recalled
ret
;
;
;
; opcode category tables
oplist: db 1111$1111b, 1100$0011b ;0 jmp
db 1100$0111b, 1100$0010b ;1 jcond
db 1111$1111b, 1100$1101b ;2 call
db 1100$0111b, 1100$0100b ;3 ccond
db 1111$1111b, 1100$1001b ;4 ret
db 1100$0111b, 1100$0111b ;5 rst 0..7
db 1111$1111b, 1110$1001b ;6 pchl
db 1100$0111b, 0000$0110b ;7 mvi
db 1100$0111b, 1100$0110b ;8 adi...cpi
db 1100$1111b, 0000$0001b ;9 lxi
db 1110$0111b, 0010$0010b ;10 lhld shld lda sta
db 1100$0111b, 1100$0000b ;11 rcond
db 1111$0111b, 1101$0011b ;in out
opmax equ ($-oplist)/2
;
; symbol access algorithms
alookup:
;look for the symbol with address given by de
;return with non zero flag if found, zero if not found
;when found, base address is returned in hl:
; : high addr :
; : low addr:
; hl: : length :
; : char 1 :
; . . .
; : char len:
; (list terminated by length > 15)
lhld sytop ;top symbol in table
inx h ;to low address
inx h ;to high address field
alook0: mov b,m ;high address
dcx h
mov c,m ;low address
dcx h ;.length
mov a,m ;get length
cpi 16 ;max length is 15
jnc alook2 ;to stop the search
push h ;save current location in case matched
cma ;1's complement of low(length)
add l ;add to hl
mov l,a
mvi a,0ffh ;1's complement of high(length)
adc h ;propagate carry for subtract
mov h,a ;hl is hl-length-1
; now compare symbol address
mov a,e ;low of search address
cmp c ;-low of symbol address
jnz alook1 ;skip if unequal
mov a,d
sub b ;skip if unequal
jnz alook1
; symbol matched, return hl as symbol address
pop h
inr a ;difference was zero
ret ;with non zero flag set
;
alook1: ;symbol not matched, look for next
inx sp
inx sp ;remove stacked address
jmp alook0 ;for another search
;
; symbol address not found
alook2: xra a
ret ;with zero flag set
;
;
; *********************************
; * *
; * Data Structures *
; * *
; *********************************
;
; D - structures
disloc: ds 2 ;display location
DISEND: db FALSE ;storage for end of display
dismax: ds 2 ;max value for current display
tdisp: ds 2 ;temp 16 bit location
DISTMP: ds 2 ;temp storage for 16bit add
;
; G - structures
autou: ds 1 ;ff if auto "u" command in effect
gobrks: ds 1 ;number of breaks in go command
gobrk1: ds 2 ;primary break in go command
gobrk2: ds 2 ;secondary break in go command
pbloc: ds 2 ;pbtable location for auto u
pbcnt: db 00 ;permanent break temp counter
;
; H - structures
dtable: ;decimal division table
dw 10000
dw 1000
dw 100
dw 10
dw 1
;
; R - structures
bias: ds 2 ;holds r bias value for load
sytop: ds 2 ;high symbol table address
mload: ds 2 ;max load address
dasm: ds 1 ;00 if dis/assem present, 01 if not
symsg: db cr,lf,'SYMBOLS',0
lmsg: db cr,lf,'NEXT MSZE PC END',cr,lf,0
DEFLOAD: ds 2 ;holds the default read address
;
; T - structures
tmode: ds 1 ;trace mode
userbrk:ds 2 ;user break address if non-zero
tracer: ds 2 ;trace count
;
; W - structures
WRTREC: ds 2 ;# of written records
WBEGIN: ds 2 ;Beginning address of write
WEND: ds 2 ;ending address of write
WRTMSG: db CR,LF,0
WRTMSG1: db 'h record(s) written.',0
;
; Common to all routines
;
lastexp:dw 0000 ;last expression encountered
;
pbtrace:
ds 1 ;trace on for perm break
pbtable:
rept pbsize ;one for each element
db 0 ;counter
ds 2 ;address
ds 1 ;data
endm
; each perm table element takes the form:
; low(count) high(count) low(addr) high(addr) data
;
;
negcom: ds 1 ;00 if normal command, ff if "-x"
wdisp: ds 1 ;00 if byte display, ff if word display
catno: ds 1 ;category number saved in nbrk
retloc: ds 2 ;return address to user from bdos
breaks: ds 7 ;#breaks/bkpt1/dat1/bkpt2/dat2
explist:ds 7 ;count+(exp1)(exp2)(exp3)
nextcom:ds 2 ;next location from command buffer
comlen: db csize ;max command length
curlen: ds 1 ;current command length
combuf: ds csize ;command buffer
; temporary values used in "r" command share end of buffer
tfcb equ $-fcbl/2;holds name of symbol file during code load
;
ds ssize ;stack area
stack:
ploc equ stack-2 ;pc in template
hloc equ stack-4 ;hl
sloc equ stack-6 ;sp
aloc equ stack-7 ;a
floc equ stack-8 ;flags
bloc equ stack-10 ;bc
dloc equ stack-12;d,e
;
nop ;for relocation boundary
end