0001 ;===============================================================
0002 ;
0003 ; MACREF input-ref output-ref
0004 ;
0005 ; Processes a MAC-compatible file of assembly source to produce
0006 ; a new file that is still MAC-compatible, but which has
0007 ; appended to it a cross-reference of symbol use and a census
0008 ; of opcode use. The expected use is in a sequence of commands
0009 ; like this (which might well be in a submit file):
0010 ;
0011 ; unpack b:somefile.pak a:.asi
0012 ; include somefile.asi .asm /b
0013 ; mac somefile $lb pz
0014 ; load somefile
0015 ; MACREF somefile.asi somefile.out
0016 ; pip prn:=somefile.out
0017 ;
0018 ; input-ref :: [x:] inputname [.typ]
0019 ;
0020 ; If the drivecode x: is omitted the default drive is used. An
0021 ; input filename is required. A filetype is optional (or, to
0022 ; be precise, a filetype of 3 blanks is allowed).
0023 ;
0024 ; The input file is assumed to be assembly source. Sequence
0025 ; numbers may be present. If they are, they will be stripped.
0026 ; A previous cross-reference from this program will be dropped.
0027 ;
0028 ; A file named "[x:]inputname.SYM" must exist.
0029 ;
0030 ; output-ref :: [y:] [outputname] [.typ]
0031 ;
0032 ; Each omitted part of the output fileref is supplied from the
0033 ; input-ref. Thus the default drive is the input drive, the
0034 ; default name is the input name, and the default type is the
0035 ; input type. If all are omitted, the output file replaces the
0036 ; input file.
0037 ; *****************************************************
0038 ; * This program was originally published in *
0039 ; * A PROGRAMMER'S NOTEBOOK *
0040 ; * Utilities for CP/M-80 *
0041 ; * by David E. Cortesi *
0042 ; * Copyright (C) Reston Publishing Company Inc. 1983 *
0043 ; *****************************************************
0044 ;===============================================================
0045 MACLIB CPMEQU
0046 MACLIB PROG
0047 PROLOG
0048 ;---------------------------------------------------------------
0049 ; Pointers to allocated storage:
0050 LineBuffer dw 0 ; space for one input line
0051 SymbolTable dw 0 ; start of array of SymRecs
0052 NSymbols dw 0 ; count of SymRecs
0053 NextPool dw 0 ; next free byte to allocate
0054 PoolEnd dw 0 ; byte after end of pool
0055 OpRoot dw 0 ; root of OpRec binary tree
0056 ;---------------------------------------------------------------
0057 ; Scratch space used by various modules:
0058 WP dw 0 ; used in PeruseFile
0059 WO dw 0 ; ditto
0060 WF dw 0 ; used in GetWord
0061 WI dw 0 ; used in Symbol
0062 WM dw 0 ; ditto, and in FirstSymbol
0063 String ds 32 ; used for comparison strings
0064 ;---------------------------------------------------------------
0065 ; Define the SymRec:
0066 LengthSymRec equ 24 ; SymRec is 8+16 for ez addressing
0067 SymLabel equ 0 ; 17 bytes for name-string
0068 SymLine equ 17 ; 2 bytes for line number
0069 SymVal equ 19 ; 2 bytes for hex value
0070 SymUse equ 21 ; 2 bytes for UseRec chain
0071 ; 1 byte unused
0072 ;---------------------------------------------------------------
0073 ; Define the UseRec:
0074 LengthUseRec equ 6 ; i.e.,
0075 UseNext equ 0 ; pointer to next in chain
0076 UseLine equ 2 ; line number where use occurred
0077 UseOp equ 4 ; ->OpRec of opcode in that line
0078 ;---------------------------------------------------------------
0079 ; Define the OpRec:
0080 LengthOpRec equ 15 ; made up of:
0081 OpName equ 0 ; 9 bytes for the name-string
0082 OpCount equ 9 ; 2 bytes for use-count
0083 OpLeft equ 11 ; 2 bytes for left subtree
0084 OpRight equ 13 ; 2 bytes for right subtree
0085 ;---------------------------------------------------------------
0086 ; Define various maxima
0087 MaxSym equ 8192 ; limit of SymRec space
0088 MaxPool equ 8192 ; limit of Op, Use Rec space
0089 LengthLine equ 1024 ; generous assembly linesize
0090 MaxCol equ 72 ; longest line in displays
0091 SeqStart equ 0001h ; first sequence number
0092 SeqIncr equ 0001h ; sequence number increment
0093 ; #include utilio.inc,InVars
0094 ; #include utilio.inc,OutVars
0095 ;---------------------------------------------------------------
0096 ; Constant message and display strings:
0097 TypeSym db 'SYM'
0098 MsgNoSym db 'Input .SYM file not found.$'
0099 MsgNoPool db 'Ran out of free storage -- file too large.$'
0100 ; #include utilio.inc,FileMessages
0101 XrefHead db '* CROSS-REFERENCE',AsciiCR,AsciiLF,00h
0102 XrefLegend db '* def. val. symbol and uses',AsciiCR,AsciiLF,00h
0103 Dashes db '----',00h
0104 XrefOverFlow db AsciiCR,AsciiLF,'*',AsciiTAB,AsciiTAB,00h
0105 OpUseHead db AsciiCR,AsciiLF,'* CENSUS OF OPCODE USAGE'
0106 OpOverFlow equ $ ; note this is part of "OpUseHead"
0107 db AsciiCR,AsciiLF,'*',AsciiTAB,00h
0108 ;===============================================================
0109 ; Main program:
0110 Main:
0111 push h ; save address of top storage
0112 ;
0113 lhld CpmFcb+9
0114 lda CpmFcb+11 ; ".typ" of input-ref...
0115 push h
0116 push psw ; ...saved
0117 lhld TypeSym
0118 lda TypeSym+2
0119 shld CpmFcb+9
0120 sta CpmFcb+11 ; set type to "SYM"
0121 lxi h,MsgNoSym ; abort msg if open fails
0122 call SetUpInput ; set to read inputname.SYM
0123 ;
0124 lxi h,ProgEnd
0125 shld Linebuffer ; set address of Line buffer
0126 ;
0127 ; initialize GetMain words to regulate the allocation of SymRecs
0128 ;
0129 lxi b,LengthLine
0130 dad b
0131 shld NextPool ; next byte starts pool space
0132 lxi b,MaxSym
0133 dad b
0134 shld PoolEnd ; set limit of SymRec array
0135 ;
0136 call LoadSymbols ; read .SYM, make SymRecs
0137 ;
0138 ; now reset GetMain words for allocation of OpRecs, UseRecs
0139 ;
0140 lhld NextPool ; now set max pool space
0141 lxi b,MaxPool ; ..beyond what went to symbols
0142 dad b
0143 shld PoolEnd
0144 ;
0145 pop psw ; recover the original
0146 pop h ; ..".typ" of input-ref and
0147 shld CpmFCB+9 ; ..restore it to the FCB
0148 sta CpmFCB+11
0149 lxi h,MsgNoFile ; abort msg if open fails
0150 call SetUpInput ; prepare to read main file
0151 ;
0152 lxi b,InFCB ; default fileref for outfile
0153 lhld PoolEnd
0154 xchg ; DE->start of output buffer
0155 pop h ; ..HL->its end
0156 call SetUpOutput ; prepare output mechanism
0157 ;
0158 call PeruseFile ; read and copy input file
0159 call AddXref ; append cross-ref display
0160 call AddOpUse ; append opcode census
0161 call FinishOutput ; complete the output file
0162 ret
0163 ;===============================================================
0164 ;
0165 ; Phase 1 : Load the symbol table
0166 ;
0167 ; The current input file is a MAC ".SYM" file (or equivalent).
0168 ; Read it to create a SymRec for each symbol in the SymbolTable.
0169 ; Set NSymbols to the count of symbols.
0170 ;
0171 ; This code assumes that the usual GetChar mechanism is set to
0172 ; read from a MAC .SYM file. It views that file as a sequence:
0173 ;
0174 ; fill XXXX fill LABEL fill XXXX fill ... CpmEof
0175 ;
0176 ; where "fill" is any amount of blanks and control characters.
0177 ; This view lets us ignore line boundaries. EOF should only
0178 ; happen before a hex value XXXX, but if it happens early no
0179 ; harm will be done.
0180 ;
0181 ; MAC, but not RMAC, has a bug in that if two symbols are the
0182 ; same for length of the shorter, and the longer appears first
0183 ; in the source file, the longer comes first in the .SYM file.
0184 ; Both assemblers treat an initial "?" in a name as if it
0185 ; collated after "Z". Thus the .SYM file is not in perfect
0186 ; order. The SymRec list must be ordered, or we will fail to
0187 ; find names. So we have to check the order of the symbols as
0188 ; we enter them, and swap some names up to their correct
0189 ; position. This amounts to a sort, but it would be very slow
0190 ; if the .SYM file were in any but near-perfect order.
0191 ;
0192 ; preserves -- all
0193 ;===============================================================
0194 LoadSymbols:
0195 push psw
0196 push b
0197 push d
0198 push h
0199 ;
0200 lxi b,LengthSymRec ; create a phony 1st SymRec,
0201 call GetMain ; to stop the sort at the
0202 shld WP ; head of the list
0203 ;
0204 lhld NextPool
0205 shld SymbolTable ; set table start-address
0206 ;
0207 lxi h,0
0208 shld NSymbols ; NSymbols := 0
0209 mov d,h ; D := 00h
0210 ;
0211 LoadSloop: ; loop:
0212 call ReadFill ; ReadFill(D)
0213 mov a,d
0214 cpi CpmEof ; while (D<>CpmEof)
0215 JRZ LoadSend
0216 ;
0217 lhld NSymbols
0218 inx h
0219 shld NSymbols ; NSymbols := NSymbols + 1
0220 lxi b,LengthSymRec
0221 call GetMain ; GetMain(Len=BC,Sn=HL)
0222 call ReadHex ; ReadHex(D,Sn=HL)
0223 call ReadFill ; ReadFill(D)
0224 call ReadLabel ; ReadLabel(D,Sn=HL)
0225 call ReOrder ; Reorder(Pn=WP,Sn=HL)
0226 shld WP ; Pn := Sn
0227 JMPR LoadSloop ; end loop
0228 ;
0229 LoadSend:
0230 pop h
0231 pop d
0232 pop b
0233 pop psw
0234 ret
0235 ;---------------------------------------------------------------
0236 ; ReadFill(D): consume insignificant bytes from the .SYM file
0237 ; preserves -- PSW, BC, E, HL
0238 ; returns -- D = next significant byte from file.
0239 ;---------------------------------------------------------------
0240 ReadFill:
0241 push psw
0242 ;
0243 ReadFloop:
0244 mov a,d
0245 cpi CpmEof ; end of file is significant
0246 JRZ ReadFend
0247 cpi AsciiBlank+1 ; as is anything > blank
0248 JRNC ReadFend
0249 call GetChar
0250 mov d,a
0251 JMPR ReadFloop
0252 ;
0253 ReadFend:
0254 pop psw
0255 ret
0256 ;---------------------------------------------------------------
0257 ; ReadHex(D,Sn): stow the hex value of a symbol in HL->SymRec.
0258 ; The first digit is in D and we assume that there are 3 more.
0259 ; preserves -- PSW, BC, E, HL
0260 ; returns -- D = byte after the fourth hex digit.
0261 ;---------------------------------------------------------------
0262 ReadHex:
0263 push psw
0264 push b
0265 push h
0266 push h ; save extra copy of ->SymRec
0267 ;
0268 lxi h,0 ; Q := 0
0269 ;
0270 mov a,d
0271 cpi CpmEof
0272 JRZ ReadHend ; if (D <> CpmEof) then
0273 ;
0274
0275 mvi b,4 ; do 4 times:
0276 ReadHoop:
0277 dad h
0278 dad h
0279 dad h
0280 dad h ; Q := (Q*16) ...
0281 mov a,d
0282 cpi 'A'
0283 JRC ReadHoop2
0284 sui 'A'-('9'+1)
0285 ReadHoop2:
0286 ani 0Fh
0287 ora l
0288 mov l,a ;...+ hex value of D
0289 call GetChar
0290 mov d,a ; D := GetChar
0291 DJNZ ReadHoop
0292 ;
0293 ReadHend:
0294 xthl ; HL->SymRec, value on stack
0295 lxi b,SymVal
0296 dad b ; HL->SymRec.Val
0297 pop b
0298 mov m,c
0299 inx h
0300 mov m,b ; Sn.Value := Q
0301 ;
0302 pop h
0303 pop b
0304 pop psw
0305 ret
0306 ;---------------------------------------------------------------
0307 ; ReadLabel(D,Sn): stow the (uppercase) label of a symbol in
0308 ; HL->SymRec. D contains the first character. The symbol
0309 ; extends to the next blank or control character and is assumed
0310 ; to be 16 or fewer bytes. The SymRec has already been zeroed
0311 ; so there is no need to terminate the string with 00h.
0312 ; preserves -- PSW, BC, E, HL
0313 ; returns -- D contains byte after label
0314 ;---------------------------------------------------------------
0315 ReadLabel:
0316 push psw
0317 push b
0318 push h
0319 ;
0320 ReadLloop:
0321 mvi a,AsciiBlank
0322 cmp d ; while D>AsciiBlank
0323 JRNC ReadLend
0324 mov m,d
0325 inx h ; append D to Sn.Label
0326 call GetChar
0327 mov d,a ; D := GetChar
0328 JMPR ReadLloop ; end while
0329 ;
0330 ReadLend:
0331 pop h
0332 pop b
0333 pop psw
0334 ret
0335 ;---------------------------------------------------------------
0336 ; ReOrder(Pn,Sn): ensure that HL->current SymRec is correctly
0337 ; ordered, i.e. greater than WP->prior SymRec.
0338 ; preserves -- all
0339 ;---------------------------------------------------------------
0340 ReOrder:
0341 push psw
0342 push d
0343 push h
0344 xchg
0345 lhld WP
0346 ReOrder1:
0347 xchg ; HL->current SymRec, DE->prior one
0348 push d
0349 push h
0350 call CmpString
0351 pop h
0352 pop d
0353 JRC ReOrderDone
0354 call SwapSyms
0355 lxi h,-LengthSymRec
0356 dad d ; HL->SymRec before DE->SymRec
0357 JMPR ReOrder1
0358 ;
0359 ReOrderDone:
0360 pop h
0361 pop d
0362 pop psw
0363 ret
0364 ;
0365 ; Swap the positions of DE->SymRec and HL->SymRec, using
0366 ; scratch space String as a holding area.
0367 ;
0368 SwapSyms:
0369 push b
0370 lxi b,LengthSymRec ; length for all moves
0371 push d
0372 lxi d,String
0373 push h
0374 call MoveHtoD ; String := Sn->SymRec
0375 pop h
0376 pop d
0377 xchg
0378 push d
0379 push h
0380 call MoveHtoD ; Sn->SymRec := Pn->SymRec
0381 pop h
0382 pop d
0383 xchg
0384 push h
0385 lxi h,String
0386 push d
0387 call MoveHtoD ; Pn->SymRec := String
0388 pop d
0389 pop h
0390 pop b
0391 ret
0392 ;===============================================================
0393 ;
0394 ; Phase 2 : Process the file, note symbol usage
0395 ;
0396 ; The input file is a MAC-compatible assembler source file. It
0397 ; may have sequence numbers on all (or some) lines, and it may
0398 ; have an old cross-reference at the end.
0399 ; Copy it to the output with new sequence numbers. Note all
0400 ; symbol usage, recording defining-points in the symbol table
0401 ; and usage-points in UseRecs. Note all opcodes in OpRecs.
0402 ;
0403 ; preserves -- all
0404 ; uses scratch words "WP" and "WO"
0405 ;
0406 ; Note -- it is at this level that we eliminate lines that
0407 ; begin with "*", which are comment lines to ASM, MAC.
0408 ;---------------------------------------------------------------
0409 PeruseFile:
0410 push psw
0411 push b
0412 push d
0413 push h
0414 ;
0415 lxi b,SeqStart ; initial line number
0416 ;
0417 Peruse1:
0418 call CopyLine ; While (CopyLine(Ln=BC))
0419 JRZ PeruseZ
0420 ;
0421 lhld LineBuffer ; P := 1
0422 mov a,m
0423 cpi '*' ; old-style comment line?
0424 JRZ Peruse9 ; ..yes, skip it all
0425 lxi d,String ; (address our string-area)
0426 call GetWord ; GetWord(S=DE,P=HL)
0427 JRZ Peruse9 ; ..if no word, loop
0428 shld WP ; (save text pointer P)
0429 ;
0430 call Symbol ; if Symbol(S=DE,Sn=HL) then
0431 JRZ Peruse2 ; (not a known symbol)
0432 call NoteDef ; NoteDef(Sn=HL,Ln=BC)
0433 lhld WP
0434 call GetWord ; GetWord(S=DE,P=HL)
0435 JRZ Peruse9
0436 shld WP
0437 ;
0438 Peruse2: ; DE->string is an opcode -- line's first non-label.
0439 call NoteOpCode ; NoteOpCode(S=DE,On=HL)
0440 shld WO ; (save On, the OpRec address)
0441 Peruse3: ; scan the rest of the line for uses of symbols
0442 lxi d,String
0443 lhld WP
0444 call GetWord ; get next word
0445 JRZ Peruse9 ; ..if there is one
0446 shld WP
0447 call Symbol ; if Symbol(S=DE,Sn=HL) then
0448 JRZ Peruse3 ; (not known. register?)
0449 xchg
0450 lhld WO
0451 xchg
0452 call NoteUse ; NoteUse(Ln=BC,On=DE,Sn=HL)
0453 JMPR Peruse3
0454 ;
0455 Peruse9: ; end of one line, try next
0456 lxi h,SeqIncr ; increment the line number
0457 dad b
0458 mov b,h
0459 mov c,l ; BC has next line number
0460 JMPR Peruse1
0461 ;
0462 PeruseZ: ; end of file, exit
0463 pop h
0464 pop d
0465 pop b
0466 pop psw
0467 ret
0468 ;---------------------------------------------------------------
0469 ; CopyLine(Ln): read the next line from the input file to the
0470 ; line buffer. If end-of-file occurs, or if the line turns out
0471 ; to be the start of an old cross-reference, then return Z true.
0472 ; Otherwise write the line (with a new line number Ln) to the
0473 ; output file and return Z false.
0474 ; input -- BC contains line number Ln
0475 ; preserves -- BC, DE, HL
0476 ;---------------------------------------------------------------
0477 CopyLine:
0478 call ReadLine ; read line, strip number
0479 rz ; exit on end of file
0480 ;
0481 push d
0482 push h
0483 lhld LineBuffer
0484 lxi d,XrefHead
0485 call CmpStrText ; start of old Xref?
0486 pop h
0487 pop d
0488 rz ; ..treat that as end of file
0489 ;
0490 call WriteLine ; no. write the line,
0491 ret ; ..return z false
0492 ;---------------------------------------------------------------
0493 ; ReadLine: read a complete line into LineBuffer. If the line
0494 ; has a sequence number on it, strip it off (WriteLine will add
0495 ; a new one). Return Z true on end of file.
0496 ; preserves -- BC, DE, HL
0497 ; Notes: this code assumes that all lines end in CR, LF. LF is
0498 ; taken as the end-of-line signal. The next buffer byte, HL->m,
0499 ; is used for the pseudo-code variable "C". CheckDigit and
0500 ; WhiteSpace test HL->m.
0501 ;---------------------------------------------------------------
0502 ReadLine:
0503 push h
0504 ;
0505 lhld LineBuffer
0506 call GetChar
0507 ;
0508 cpi CpmEof
0509 JRZ ReadLineZ ; EOF expected only on 1st call
0510 mov m,a ; C := GetChar
0511 call CheckDigit ; if C is a digit
0512 JRNZ ReadLine3 ; (isn't)
0513 ReadLine1:
0514 call GetChar ; repeat
0515 mov m,a ; C:=GetChar
0516 call CheckDigit ; until C is not a digit
0517 JRZ ReadLine1
0518 call WhiteSpace ; if C is blank or tab
0519 cz GetChar ; swallow that, too
0520 mov m,a
0521 ;
0522 ReadLine3:
0523 mvi a,AsciiLF ; while C is not a LF
0524 cmp m
0525 JRZ ReadLineNZ
0526 inx h ; increment P
0527 call GetChar
0528 mov m,a ; C := GetChar
0529 JMPR ReadLine3 ; end while
0530 ;
0531 ReadLineNZ: ; line completed
0532 ori -1 ; ..return Z false
0533 ReadLineZ: ; eof seen, Z true
0534 pop h
0535 ret
0536 ;---------------------------------------------------------------
0537 ; WriteLine(Ln): write the line in the line buffer to the
0538 ; output file, prefixing it with a new line number (in BC).
0539 ; preserves -- all
0540 ;---------------------------------------------------------------
0541 WriteLine:
0542 push psw
0543 push b
0544 push h
0545 mov h,b
0546 mov l,c ; (pass Ln in HL)
0547 call Put9999 ; write 4-digit line number
0548 call PutTab ; ..and a tab
0549 ;
0550 lhld LineBuffer
0551 WriteLine2:
0552 mov a,m
0553 call PutChar
0554 inx h
0555 cpi AsciiLF
0556 JRNZ WriteLine2
0557 ;
0558 pop h
0559 pop b
0560 pop psw
0561 ret
0562 ;---------------------------------------------------------------
0563 ; GetWord(S,P): Advance pointer P through an assembly statement,
0564 ; collecting a word -- a MAC or ASM label. Return the word in
0565 ; string S. Return Z true if no word is found by line end.
0566 ; preserves -- BC, DE
0567 ; returns -- HL advanced, Z flag set
0568 ; uses scratch word "WF".
0569 ;
0570 ; The syntax allowed by MAC is fairly complicated, so a fairly
0571 ; sophisticated scanner is needed to deal with it rapidly. This
0572 ; code uses a finite-state automaton, an algorithm developed
0573 ; for compilers. A simplified version of the rules of MAC syntax
0574 ; is encoded as a state-transition matrix. A matrix row stands
0575 ; for the type of token being scanned, while the (class of the)
0576 ; current input byte selects a matrix column. A cell holds two
0577 ; things: the state (row) to be used next, and an action to be
0578 ; performed on the current character. See the text for a picture
0579 ; of the matrix in human terms.
0580 ;
0581 ; As implemented, each matrix cell is a pair of bytes. The first
0582 ; is the offset from the start of the table to the desired row,
0583 ; the second is the offset from "Action" to the desired action.
0584 ;
0585 ;---------------------------------------------------------------
0586 FSA:
0587 FSA0 equ $-FSA ; state 0 value -- skipping
0588 db FSA0,ACT0, FSA1,ACT1A, FSA1,ACT1, FSA2,ACT0
0589 db FSA0,ACT0, FSA3,ACT0, FSA0,ACT3, FSA0,ACT3
0590 FSA1 equ $-FSA ; state 1 value -- building word
0591 db FSA0,ACT2, FSA1,ACT1, FSA1,ACT1, FSA1,ACT1
0592 db FSA1,ACT0, FSA0,ACT2, FSA0,ACT2, FSA0,ACT2
0593 FSA2 equ $-FSA ; state 2 value -- skip number
0594 db FSA0,ACT0, FSA1,ACT1, FSA2,ACT0, FSA2,ACT0
0595 db FSA2,ACT0, FSA3,ACT0, FSA0,ACT3, FSA0,ACT3
0596 FSA3 equ $-FSA ; state 3 value -- skip literal
0597 db FSA3,ACT0, FSA3,ACT0, FSA3,ACT0, FSA3,ACT0
0598 db FSA3,ACT0, FSA0,ACT0, FSA3,ACT0, FSA0,ACT3
0599 ;---------------------------------------------------------------
0600 ; This table is used to classify input bytes into one of:
0601 ; Class1 alphabetics not used in numeric constants
0602 ; Class2 alphabetics used in numeric constants
0603 ; Class3 digits 0..9
0604 ; Class4 dollar sign
0605 ; Class5 single quote
0606 ; Class6 semicolon (logical statement end)
0607 ; Class7 LineFeed (physical statement end)
0608 ; Class0 all others
0609 ;---------------------------------------------------------------
0610 Class0 equ 2*0 ; class values multiples of 2 because
0611 Class1 equ 2*1 ; each FSA cell is two bytes.
0612 Class2 equ 2*2
0613 Class3 equ 2*3
0614 Class4 equ 2*4
0615 Class5 equ 2*5
0616 Class6 equ 2*6
0617 Class7 equ 2*7
0618 ;
0619 Class macro ?a,?z,?c
0620 ORG CLASSTABLE+?A
0621 IF NOT NUL ?Z
0622 REPT ?Z-?A+1
0623 DB ?C
0624 ENDM
0625 ELSE
0626 DB ?C
0627 ENDIF
0628 ENDM
0629 $-PRINT
0630 ClassTable:
0631 rept 128
0632 db Class0
0633 endm
0634 CLASS '?','Z',Class1
0635 CLASS 'a','z',Class1
0636 CLASS 'A','F',Class2
0637 CLASS 'a','f',Class2
0638 CLASS 'H',,Class2
0639 CLASS 'h',,Class2
0640 CLASS 'O',,Class2
0641 CLASS 'o',,Class2
0642 CLASS 'Q',,Class2
0643 CLASS 'q',,Class2
0644 CLASS '0','9',Class3
0645 CLASS '$',,Class4
0646 CLASS '''''',,Class5
0647 CLASS AsciiLF,,Class7
0648 CLASS ';',,Class6
0649 org ClassTable+128
0650 $+PRINT
0651 ;---------------------------------------------------------------
0652 ; On entry to GetWord, DE->string area S, and HL->line text.
0653 ; In the code, HL and BC are used for work registers, the string
0654 ; address is kept on the stack, and DE->line text.
0655 ;---------------------------------------------------------------
0656 GetWord:
0657 push b ; preserve BC,
0658 push d ; ..and DE as input
0659 mvi b,0 ; B will contain 00h throughout
0660 push d ; put ->string on the stack
0661 xchg ; ..and ->text in DE
0662 lxi h,FSA
0663 shld WF ; set initial FSA state of "other"
0664 ;
0665 GetFSA:
0666 ldax d ; current input byte..
0667 ani AsciiDEL; ..less high bit for safety
0668 mov c,a ; BC gets 00xx
0669 lxi h,ClassTable
0670 dad b ; HL->class of DE->byte
0671 mov c,m ; BC gets 00cc
0672 lhld WF
0673 dad b ; HL->FSA[state,class]
0674 mov c,m ; BC gets 00ss
0675 inx h
0676 mov a,m ; A holds the action
0677 lxi h,FSA
0678 dad b
0679 shld WF ; set next FSA state
0680 mov c,a ; BC gets 00aa
0681 lxi h,ACT
0682 dad b
0683 pchl ; go do the action
0684 ;
0685 ACT:
0686 ; action 1A: store the initial byte of a word. No different
0687 ; than any other byte, but it could be.
0688 ;
0689 ACT1A equ $-ACT
0690 ; action 1: store the current byte, as uppercase, in the string.
0691 ;
0692 ACT1 equ $-ACT
0693 pop h ; HL->developing string
0694 ldax d
0695 call UpperCase
0696 mov m,a
0697 inx h
0698 push h
0699 ;
0700 ; action 0 (the nil action): advance the input pointer.
0701 ;
0702 ACT0 equ $-ACT
0703 inx d
0704 jmp GetFSA
0705 ;
0706 ; action 3: end of line with no word found, exit.
0707 ;
0708 ACT3: equ $-ACT
0709 xra a ; return Z true
0710 ;
0711 ; action 2: exit at the end of a word. A is nonzero.
0712 ;
0713 ACT2: equ $-ACT
0714 ora a ; set Z according to A
0715 ;
0716 GetWordEnd:
0717 pop h ; HL->byte after word (if any)
0718 mvi m,00h ; terminate the word-string
0719 xchg ; HL is advanced text pointer
0720 pop d ; restore DE->string
0721 pop b
0722 ret
0723 ;---------------------------------------------------------------
0724 ; Symbol(S,Sn): look up DE->uppercase symbol-word in the symbol
0725 ; table. If found, return Sn (in HL), the address of the
0726 ; matching SymRec. Otherwise return HL=0000 and Z true.
0727 ; preserves -- BC, DE
0728 ; returns -- HL->SymRec and Z false, or HL=00 and Z true
0729 ; uses scratch words "WI" and "WM"
0730 ;---------------------------------------------------------------
0731 Symbol:
0732 push b
0733 ;
0734 lhld NSymbols
0735 shld WM
0736 call Mdiv2 ; HL:= ceiling(M/2) (initial I)
0737 ; M := floor(M/2)
0738 SymboLoop:
0739 shld WI ; save current (first) I
0740 ;
0741 ; convert HL=I into a SymRec address: (I*24)+SymbolTable.
0742 ; Note that I is an origin-1 index while the array is really
0743 ; addressed origin-0.
0744 ;
0745 dcx h ; i=1 becomes i=0, etc.
0746 dad h ; i * 2
0747 dad h ; i * 4
0748 dad h ; i * 8
0749 mov b,h
0750 mov c,l ; (save i*8)
0751 dad h ; i * 16
0752 dad b ; i * 24
0753 mov b,h
0754 mov c,l ; (save i*24)
0755 lhld SymbolTable
0756 dad b ; HL->SymbolTable[I]
0757 ;
0758 ; compare (DE->string) vs. (HL->SymRec.Label).
0759 ;
0760 push d
0761 push h
0762 call CmpString
0763 pop h
0764 pop d
0765 ;
0766 ; if S=Table[I], the search succeeded and HL->SymRec
0767 ;
0768 JRZ SymbolHit
0769 ;
0770 ; No? Proceed according to S::Table[I]. If M=0 in either case
0771 ; then the search has failed. But M can't be tested until the
0772 ; present flag-settings have been used to jump with.
0773 ;
0774 JRC SymboLess
0775 ;
0776 ; S>Table[I], so let I := I+ceiling(M/2)
0777 ;
0778 call Mdiv2
0779 JRZ SymbolFail
0780 mov b,h
0781 mov c,l ; BC := ceiling(M/2)
0782 lhld WI
0783 dad b
0784 jmp SymboLoop
0785 ;
0786 ; S
SymRec
0808 ;
0809 SymbolHit:
0810 pop b
0811 mov a,h
0812 ora l ; set Z for success/fail
0813 ret
0814 ;
0815 ; This subroutine sets word M to floor(M/2), and returns
0816 ; ceiling(M/2) in HL. If M=0, returns Z flag set and HL=0000
0817 ;
0818 Mdiv2:
0819 lhld WM
0820 mov a,l
0821 ora h ; test M, clear carry
0822 rz
0823 ; Shift HL right one bit into Carry. The Z80 "RR H" and "RR L"
0824 ; instructions can't be used here, because they affect the Z
0825 ; flag while "RAR" leaves it alone.
0826 mov a,h ! rar ! mov h,a
0827 mov a,l ! rar ! mov l,a
0828 shld WM ; M := floor(M/2)
0829 rnc ; exit if floor(M/2)=ceiling(M/2)
0830 inx h ; form ceiling
0831 ret
0832 ;---------------------------------------------------------------
0833 ; NoteDef(Ln,Sn): The defining-point of HL->SymRec has been
0834 ; found at line BC=Ln. Plop the line number into SymRec.Line
0835 ; preserves -- all
0836 ; Note: a set-symbol may be defined more than once. This code
0837 ; just replaces any previous definition line number.
0838 ;---------------------------------------------------------------
0839 NoteDef:
0840 push h
0841 push d
0842 lxi d,SymLine
0843 dad d
0844 mov m,c
0845 inx h
0846 mov m,b
0847 pop d
0848 pop h
0849 ret
0850 ;---------------------------------------------------------------
0851 ; NoteOpCode(S,On): Find/Insert the opcode whose name is in
0852 ; DE->string, in the binary tree of OpRecs. Increment its
0853 ; count of uses. Return HL->the OpRec.
0854 ; preserves -- PSW, BC, DE
0855 ; returns -- HL contains address of OpRec
0856 ;---------------------------------------------------------------
0857 NoteOpCode:
0858 push psw
0859 push b
0860 push d
0861 lxi h,OpRoot ; FindAdd(T->OpRoot->Node)
0862 ;
0863 FindAdd:; (T->pointer->node)
0864 mov c,m
0865 inx h
0866 mov b,m ; BC -> Node
0867 mov a,b
0868 ora c ; if (T->pointer is nil) then
0869 JRNZ FindAdd2
0870 ;
0871 dcx h
0872 call MakeNode ; T->pointer := MakeNode(S,T)
0873 JMPR NoteOpZ1 ; return
0874 ;
0875 FindAdd2: ; else
0876 mov h,b
0877 mov l,c ; Q:=T->pointer (HL is Q, too)
0878 push d
0879 push h ; if (S=Q->Node.Value) then
0880 call CmpString
0881 pop h
0882 pop d
0883 JRZ NoteOpZ2 ; return
0884 ; else point to Node.Left or Node.Right, as appropriate
0885 lxi b,OpLeft ; offset to Node.Left
0886 JRC FindAdd3
0887 inx b
0888 inx b ; ..or offset to Node.Right
0889 FindAdd3:
0890 dad b ; T->Node.Left or Node.Right
0891 JMPR FindAdd
0892 ;
0893 ; A new node has been created. MakeNode puts its address where
0894 ; HL points. Move that to HL, so HL->new Node.
0895 ;
0896 NoteOpZ1:
0897 mov a,m
0898 inx h
0899 mov h,m
0900 mov l,a
0901 ;
0902 ; A matching node has been found, and Q (HL) points to it.
0903 ;
0904 NoteOpZ2:
0905 ;
0906 ; increment the use count in HL->Node
0907 ;
0908 xchg
0909 lxi h,OpCount
0910 dad d
0911 mov c,m
0912 inx h
0913 mov b,m
0914 inx b
0915 mov m,b
0916 dcx h
0917 mov m,c
0918 xchg
0919 ;
0920 ; exit with HL->Node
0921 ;
0922 pop d
0923 pop b
0924 pop psw
0925 ret
0926 ;---------------------------------------------------------------
0927 ; MakeNode(S,T): create a new OpRec, initialize it with a name
0928 ; from DE->string and zero Count, Left, and Right, and plant its
0929 ; address where HL points.
0930 ; preserves -- all
0931 ;---------------------------------------------------------------
0932 MakeNode:
0933 push psw
0934 push b
0935 push d
0936 push h
0937 ;
0938 lxi b,LengthOpRec
0939 call GetMain ; HL->allocated space
0940 xchg ; CopyString moves H to D
0941 call CopyString ; OpName := S
0942 ; DE->new OpRec
0943 pop h ; HL->word to point to it
0944 mov m,e
0945 inx h
0946 mov m,d
0947 dcx h
0948 ;
0949 pop d
0950 pop b
0951 pop psw
0952 ret
0953 ;---------------------------------------------------------------
0954 ; NoteUse(Ln,On,Sn): A use of HL->SymRec has been found on line
0955 ; BC=Ln, using DE->OpRec for an opcode. Create a UseRec, fill
0956 ; it in, and chain it to the SymRec.
0957 ; preserves -- all
0958 ;---------------------------------------------------------------
0959 NoteUse:
0960 push psw
0961 push b
0962 push d
0963 push h
0964 ;
0965 push h ; save a copy of ->SymRec
0966 push b ; save the line number
0967 lxi b,LengthUseRec
0968 call GetMain ; HL->new UseRec (zeroed)
0969 pop b ; recover line number
0970 push h ; save ->UseRec
0971 ;
0972 inx h ; (UseNext = 0000, end of chain)
0973 inx h ; HL->UseLine
0974 mov m,c
0975 inx h
0976 mov m,b ; UseLine := Ln
0977 inx h ; HL->UseOp
0978 mov m,e
0979 inx h
0980 mov m,d ; UseOp := On
0981 ;
0982 pop d ; DE->UseRec
0983 pop h ; HL->SymRec
0984 lxi b,SymUse
0985 dad b ; HL->SymUse (chain head)
0986 ;
0987 NoteUse2: ; HL->pointer->UseRec
0988 mov c,m
0989 inx h
0990 mov b,m ; BC := pointer->UseRec
0991 mov a,b
0992 ora c ; is it nil?
0993 JRZ NoteUse3 ; (yes, tack new one here)
0994 mov h,b ; no, continue down chain
0995 mov l,c
0996 JMPR NoteUse2
0997 ;
0998 NoteUse3: ; (HL-1)->nil, end of chain
0999 mov m,d
1000 dcx h
1001 mov m,e ; HL->pointer->new UseRec
1002 ;
1003 pop h
1004 pop d
1005 pop b
1006 pop psw
1007 ret
1008 ;===============================================================
1009 ;
1010 ; Phase 3 : Append the cross-reference to the output
1011 ;
1012 ; Each line is kept within a limit of MaxCol columns. This could
1013 ; be done by preparing a complete line of all uses, then cutting
1014 ; at appropriate points as needed. Instead, a running column-
1015 ; count is kept, and the current line is ended whenever the next
1016 ; display unit MIGHT not fit. This will sometimes result in
1017 ; lines that are shorter than they have to be.
1018 ;
1019 ; preserves -- all
1020 ;---------------------------------------------------------------
1021 AddXref:
1022 push psw
1023 push b
1024 push d
1025 push h
1026 ;
1027 lxi h,XrefHead
1028 call PutString ; output the heading line
1029 ;
1030 lxi h,XrefLegend ; output the column legends
1031 call PutString
1032 ;
1033 call FirstSymbol ; FirstSymbol(Sn)
1034 ;
1035 AddXloop:
1036 JRZ AddXend ; while (Sn <> nil)
1037 ;
1038 call WriteXline ; WriteXline(Sn)
1039 call NextSymbol ; NextSymbol(Sn)
1040 JMPR AddXloop ; end while
1041 ;
1042 AddXend:
1043 pop h
1044 pop d
1045 pop b
1046 pop psw
1047 ret
1048 ;---------------------------------------------------------------
1049 ; WriteXline(Sn): write the cross-ref information for HL->SymRec
1050 ; to the output file. Write nothing if the symbol is neither
1051 ; defined nor used in the input file -- e.g. if it is a label
1052 ; internal to an included routine.
1053 ; preserves -- all
1054 ;---------------------------------------------------------------
1055 WriteXline:
1056 push psw
1057 push b
1058 push d
1059 push h
1060 xchg ; DE->SymRec, HL used for work
1061 ;
1062 lxi h,SymUse
1063 dad d ; HL->SymRec.Use...
1064 mov a,m
1065 inx h
1066 ora m
1067 lxi h,SymLine
1068 dad d ; HL->SymRec.Line...
1069 ora m
1070 inx h
1071 ora m
1072 JRZ WriteX9 ; if (no def. or uses) return
1073 ;
1074 mvi a,'*'
1075 call PutChar ; PutChar("*")
1076 call PutBlank ; PutBlank
1077 ;
1078 lxi h,SymLine
1079 dad d ; HL->symbol definition line
1080 mov a,m
1081 inx h
1082 mov h,m
1083 mov l,a ; HL=definition line number
1084 ora h
1085 cnz Put9999 ; if Sn.Line<>0 then Put9999
1086 lxi h,Dashes
1087 cz PutString ; else PutString("----")
1088 ;
1089 mvi a,AsciiBlank
1090 call PutChar ; PutChar(" ")
1091 ;
1092 lxi h,SymVal
1093 dad d ; HL->symbol's hex value
1094 mov a,m
1095 inx h
1096 mov h,m
1097 mov l,a ; HL=symbol's hex value
1098 call PutXXXX ; PutXXXX(Sn.Value)
1099 ;
1100 mvi a,AsciiBlank
1101 call PutChar ; PutChar(" ")
1102 ;
1103 xchg ; HL->SymRec
1104 call PutString ; PutString(Sn.Label)
1105 ;
1106 call StringLength ; length of label in A
1107 adi 13
1108 mov b,a ; Col := 13 + StringLength(Sn.Label)
1109 call WriteUses ; WriteUses(Col,Sn)
1110 ;
1111 call PutCRLF ; PutCRLF
1112 ;
1113 WriteX9:
1114 pop h
1115 pop d
1116 pop b
1117 pop psw
1118 ret
1119 ;---------------------------------------------------------------
1120 ; WriteUses(Col,Sn): write all the uses of HL->SymRec. A "use"
1121 ; is the appearance of that symbol on some line related to some
1122 ; opcode. Uses are recorded in a chain of UseRecs, the chain
1123 ; being anchored in Symbol.Use. B contains a column-count.
1124 ; preserves -- PSW, C, DE, HL
1125 ;---------------------------------------------------------------
1126 WriteUses:
1127 push psw
1128 push d
1129 push h
1130 ;
1131 lxi d,SymUse
1132 dad d ; HL->Symbol.Use
1133 mov e,m
1134 inx h
1135 mov d,m
1136 xchg ; HL->UseRec (or HL=0000)
1137 lxi d,String ; DE->scratch string space
1138 xra a
1139 stax d ; DE->the null string
1140 mov a,l
1141 WriteUloop:
1142 ora h
1143 JRZ WriteUend ; while (Un <> nil)
1144 ;
1145 call PutUse ; PutUse(Col,String,Un)
1146 mov a,m
1147 inx h
1148 mov h,m
1149 mov l,a ; Un := Un.Next
1150 JMPR WriteUloop ; end while
1151 ;
1152 WriteUend:
1153 pop h
1154 pop d
1155 pop psw
1156 ret
1157 ;---------------------------------------------------------------
1158 ; PutUse(Col,String,Un): write a description of HL->UseRec. The
1159 ; description is either " OPCODE-nnnn", or just " -nnnn" if the
1160 ; previous opcode was the same. If the description won't fit
1161 ; in MaxCol columns, start a new, indented, line.
1162 ; input -- B contains the column of the next output byte
1163 ; DE->string, name of the last opcode
1164 ; HL->UseRec
1165 ; preserves -- PSW, C, DE, HL
1166 ;---------------------------------------------------------------
1167 LengthUse equ 6 ; length " -nnnn", worst case
1168 PutUse:
1169 push psw
1170 push d
1171 push h
1172 ;
1173 ; get the usage information -- save the line number on the
1174 ; stack and get HL->the opcode string.
1175 ;
1176 inx h
1177 inx h ; HL->UseRec.Line
1178 push h ; (save that)
1179 mov a,m
1180 inx h
1181 mov h,m
1182 mov l,a ; HL=UseRec.Line
1183 xthl ; HL->UseRec.Line, Line on stack
1184 inx h
1185 inx h ; HL->UseRec.Opcode
1186 mov a,m
1187 inx h
1188 mov h,m
1189 mov l,a ; HL->OpRec (and OpName)
1190 ;
1191 ; ensure that there is enough room on the current line for
1192 ; the whole display: blank, hyphen, as many as four digits,
1193 ; and perhaps the opcode string.
1194 ;
1195 push d
1196 push h
1197 call CmpString
1198 pop h
1199 pop d
1200 push psw ; (save result of compare)
1201 JRZ PutUse2 ; if (String<>Un.Opcode) then
1202 call StringLength
1203 add b
1204 mov b,a ; Col := Col + Length(opcode)
1205 call CopyString ; String := Un.Opcode
1206 PutUse2:
1207 mov a,b
1208 cpi MaxCol-LengthUse; if (Col+6<=MaxCol) then
1209 JRNC PutUse3
1210 call PutBlank ; PutBlank
1211 inr b ; Col := Col+1
1212 JMPR PutUse4
1213 PutUse3: ; else
1214 call StartNewLine ; StartNewLine(Col)
1215 ;
1216 PutUse4:
1217 pop psw ; if (String<>Un.Opcode) then
1218 cnz PutString ; PutString(Un.Opcode)
1219 ;
1220 mvi a,'-'
1221 call PutChar ; PutChar("-")
1222 inr b ; Col := Col+1
1223 pop h ; (recover line number)
1224 call PutZZZZ9 ; Col := Col + PutZZZZ9(Un.Line)
1225 add b
1226 mov b,a
1227 ;
1228 pop h
1229 pop d
1230 pop psw
1231 ret
1232 ;---------------------------------------------------------------
1233 ; StartNewLine(Col): start an overflow line of cross-ref. uses,
1234 ; indented so that the uses stand out from the fixed info.
1235 ; preserves -- PSW, C, DE, HL
1236 ; returns -- B updated to the new column
1237 ;---------------------------------------------------------------
1238 StartNewLine:
1239 push h
1240 lxi h,XrefOverFlow
1241 call PutString
1242 mvi b,17 ; column for next out-byte
1243 pop h
1244 ret
1245 ;---------------------------------------------------------------
1246 ; FirstSymbol(Sn) and NextSymbol(Sn): find the first/next
1247 ; SymRec. These functions are isolated in deference to the
1248 ; principle of information hiding, so that the organization
1249 ; of the symbol table can be changed without changing AddXref.
1250 ; preserves -- BC, DE
1251 ; returns -- HL->SymRec and Z false, or HL=0000 and Z true
1252 ; uses scratch word "WM"
1253 ;---------------------------------------------------------------
1254 FirstSymbol:
1255 lhld Nsymbols
1256 shld WM ; set count of symbols to do
1257 mov a,h
1258 ora l
1259 rz ; if none, return with Z true
1260 lhld SymbolTable ; otherwise return the first
1261 ret
1262 ;
1263 NextSymbol:
1264 push b
1265 mov b,h
1266 mov c,l ; save current SymRec address
1267 lhld WM
1268 dcx h ; count of symbols to do
1269 mov a,h
1270 ora l
1271 JRZ NextSymZ ; if none, exit with Z
1272 shld WM ; update count to go,
1273 lxi h,LengthSymRec ; ..return ->next SymRec
1274 dad b
1275 NextSymZ:
1276 pop b
1277 ret
1278 ;===============================================================
1279 ;
1280 ; Phase 4 : Append a census of opcode use to output
1281 ;
1282 ; Continue the output file with a census of the use of opcodes
1283 ; in this file. This is a by-product of storing opcodes in a
1284 ; tree of OpRecs, rather than storing each as a unique string.
1285 ; An "inorder" (alphabetic) tour of the binary tree produces
1286 ; all opcodes in alphabetic order.
1287 ;
1288 ; As with the cross-reference, each line is ended prior to
1289 ; reaching a length of MaxCol.
1290 ; preserves -- all
1291 ;---------------------------------------------------------------
1292 AddOpUse:
1293 push psw
1294 push b
1295 push d
1296 push h
1297 ;
1298 lxi h,OpUseHead ; write header line
1299 call PutString
1300 call StartNewOp ; StartNewOp(Col)
1301 ;
1302 lhld OpRoot
1303 mov a,h
1304 ora l ; if (OpRoot is not null) then
1305 cnz Inorder ; Inorder(OpRoot->node)
1306 call PutCRLF
1307 ;
1308 pop h
1309 pop d
1310 pop b
1311 pop psw
1312 ret
1313 ;---------------------------------------------------------------
1314 ; Inorder(On) : display all the OpRecs represented by the tree
1315 ; rooted in On->OpRec, in alphabetic ("in-") order.
1316 ; preserves -- none..stack space could be critical in such
1317 ; recursive routine, and AddOpUse protects the
1318 ; caller's registers.
1319 ;---------------------------------------------------------------
1320 Inorder:
1321 push h ; save extra copy of HL->OpRec
1322 ;
1323 lxi d,OpLeft
1324 dad d
1325 mov a,m
1326 inx h
1327 mov h,m
1328 mov l,a ; HL-> left-descendant
1329 ora h ; if (On.Left is not null) then
1330 cnz Inorder ; Inorder(On.Left)
1331 ;
1332 pop h ; HL->this OpRec
1333 call PutOp ; PutOp(Col,On)
1334 ;
1335 lxi d,OpRight
1336 dad d
1337 mov a,m
1338 inx h
1339 mov h,m
1340 mov l,a ; HL-> right-descendant
1341 ora h ; if (On.Right is not null) then
1342 cnz InOrder ; Inorder(On.Right)
1343 ;
1344 ret
1345 ;---------------------------------------------------------------
1346 ; PutOp(Col,On): display the count of uses of the opcode
1347 ; described in HL->OpRec. Start a new line as needed.
1348 ; preserves -- PSW, C, DE, HL
1349 ; returns -- B updated to next column
1350 ;---------------------------------------------------------------
1351 OpCols equ 16 ; constant size of op display
1352 PutOp:
1353 push psw
1354 push d
1355 push h
1356 ;
1357 mov a,b
1358 cpi MaxCol-OpCols+1 ; if (Col+16<=MaxCol) then
1359 cnc StartNewOp ; StartNewOp
1360 ;
1361 call PutString ; PutString(On.Name)
1362 call StringLength ; if (Length(On.Name)<8) then
1363 cpi 8
1364 cc PutTab ; PutTab(Col)
1365 call PutBlank ; PutBlank
1366 ;
1367 lxi d,OpCount
1368 dad d ; HL->OpRec.Count
1369 mov a,m
1370 inx h
1371 mov h,m
1372 mov l,a ; HL = Count
1373 call PutZZZZ9 ; PutZZZZ9(On.Count)
1374 mvi a,OpCols ; Col := Col+16
1375 add b
1376 mov b,a
1377 cpi MaxCol-OpCols+1 ; if (room for another) then
1378 cc PutTab ; PutTab(Col)
1379 ;
1380 pop h
1381 pop d
1382 pop psw
1383 ret
1384 ;---------------------------------------------------------------
1385 ; StartNewOp(Col): start a new line of opcode-census display.
1386 ; preserves -- PSW, C, DE, HL
1387 ;---------------------------------------------------------------
1388 StartNewOp:
1389 push h
1390 lxi h,OpOverFlow
1391 call PutString
1392 mvi b,9 ; next byte to column 9
1393 pop h
1394 ret
1395 ;===============================================================
1396 ; General subroutines and included ones:
1397 ;---------------------------------------------------------------
1398 ; GetMain(Len,Ad): allocate Len=BC bytes of space from the pool
1399 ; and zero it out. Return the address of the space in HL.
1400 ; preserves -- PSW, BC, DE
1401 ; returns -- HL->space
1402 ;---------------------------------------------------------------
1403 GetMain:
1404 push psw
1405 push d
1406 ;
1407 lhld NextPool
1408 push h ; save ->allocated space
1409 dad b ; figure new NextPool
1410 xchg
1411 lhld PoolEnd ; ..versus limit of pool
1412 call CmpDH ; DE best be less than HL
1413 JRC GetMain2
1414 ABORT MsgNoPool
1415 GetMain2:
1416 xchg
1417 shld NextPool ; update NextPool,
1418 pop d ; recover ->space
1419 push d ; ..and save it again
1420 call FillZero ; clear the new space
1421 pop h ; HL->cleared space
1422 pop d
1423 pop psw
1424 ret
1425 ; #start SetUpInput --------------------------------------------
1426 ; SetUpInput: initialize the input file for utility I/O with
1427 ; GetChar. Requires definition of InFCB, InIndex (InVars).
1428 ; preserves -- all
1429 ; Note: This is a modified version of the library routine. It
1430 ; expects HL to contain the address of the message to be given
1431 ; in case the input file is not found.
1432 ;---------------------------------------------------------------
1433 SetUpInput:
1434 push psw
1435 push b
1436 push d
1437 push h
1438 ;
1439 ; Prepare the input file for reading
1440 ; Zero the input FCB
1441 ;
1442 lxi d,InFCB
1443 lxi b,35
1444 call FillZero
1445 ;
1446 ; if input filename omitted, abort
1447 ;
1448 lxi h,CpmFCB+1 ; the filename is omitted if
1449 call Delimiter ; it starts with any delimiter
1450 jnz SUI2
1451 ABORT MsgNoName
1452 ;
1453 ; get an input drivecode for the FCB
1454 ;
1455 SUI2:
1456 dcx h ! mov a,m ; A := given drivecode
1457 ora a ; was it omitted?
1458 jnz SUI3
1459 SERVICE BdosDrive ; get e.g. A=00, B=01
1460 inr a ; make it A=01, B=02, etc.
1461 SUI3:
1462 sta InFCB ; explicit drivecode in FCB
1463 ;
1464 ; move the input filename and filetype to the FCB
1465 ;
1466 inx h ; HL-->filename (source)
1467 lxi d,InFCB+1 ; DE-->FCB (destination)
1468 lxi b,8+3 ; B = length of name and type
1469 call MoveHtoD ; move-characters routine
1470 ;
1471 ; open the input FCB
1472 ;
1473 SERVICE BdosOpen,InFCB
1474 inr a ; if no file exists, abort
1475 ; *** jnz SUI4
1476 ; *** ABORT MsgNoFile
1477 jz ERROREXIT ; ->msg already on the stack
1478 ;
1479 ; InIndex := FFh
1480 ;
1481 SUI4:
1482 mvi a,0FFh
1483 sta InIndex
1484 ;
1485 pop h
1486 pop d
1487 pop b
1488 pop psw
1489 ret
1490 ; #include textlib.inc,MoveHtoD
1491 ; #include textlib.inc,Delimiter
1492 ; #end SetUpInput
1493 ; #include utilio.inc,GetChar
1494 ; #include utilio.inc,PutChar
1495 ; #include arithlib.inc,CmpDH
1496 ; #include utilio.inc,SetUpOutput
1497 ; #include textlib.inc,CheckDigit
1498 ; #include textlib.inc,CmpStrText
1499 ; #include textlib.inc,CmpString
1500 ; #include textlib.inc,CopyString
1501 ; #include textlib.inc,UpperCase
1502 ; #include textlib.inc,WhiteSpace
1503 ; #include textlib.inc,StringLength
1504 ; #include putsubs.inc (all of it)
1505 ;===============================================================
1506 ; line, getmain, output space allocated starting here.
1507 ProgEnd equ $
1508 ds MaxSym+MaxPool+LengthLine+1024
1509 ; enable the next line in MP/M
1510 ; db 0 ; define full size of program in hex file
1511 end
* CROSS-REFERENCE
* def. val. symbol and uses
* 0685 068B ACT LXI-681 EQU-689 -692 -702 -708 -713
* 0702 0008 ACT0 DB-588 -588 -589 -589 -592 -594 -594 -594 -595 -595
* -597 -597 -597 -597 -598 -598 -598
* 0692 0000 ACT1 DB-588 -591 -591 -591 -594
* 0689 0000 ACT1A DB-588
* 0713 000D ACT2 DB-591 -592 -592 -592
* 0708 000C ACT3 DB-589 -589 -595 -595 -598
* 1292 08AE ADDOPUSE CALL-160
* 1042 07B7 ADDXEND JRZ-1036
* 1035 07AB ADDXLOOP JMPR-1040
* 1021 0798 ADDXREF CALL-159
* ---- 0020 ASCIIBLANK CPI-247 MVI-321 -1089 -1100
* ---- 000D ASCIICR DB-101 -102 -104 -105 -107
* ---- 007F ASCIIDEL ANI-667
* ---- 000A ASCIILF DB-101 -102 -104 -105 -107 MVI-523 CPI-555
* CLASS-647
* ---- 0009 ASCIITAB DB-104 -104 -107
* ---- 0019 BDOSDRIVE SERVICE-1459
* ---- 000F BDOSOPEN SERVICE-1473
* ---- 0BF2 CHECKDIGIT CALL-511 -516
* 0610 0000 CLASS0 DB-632
* 0611 0002 CLASS1 CLASS-634 -635
* 0612 0004 CLASS2 CLASS-636 -637 -638 -639 -640 -641 -642 -643
* 0613 0006 CLASS3 CLASS-644
* 0614 0008 CLASS4 CLASS-645
* 0615 000A CLASS5 CLASS-646
* 0616 000C CLASS6 CLASS-648
* 0617 000E CLASS7 CLASS-647
* 0630 05E2 CLASSTABLE ORG-620 -649 LXI-669
* ---- 0A89 CMPDH CALL-1412
* ---- 0C1C CMPSTRING CALL-350 -762 -880 -1197
* ---- 0BFD CMPSTRTEXT CALL-485
* 0477 053A COPYLINE CALL-418
* ---- 0C26 COPYSTRING CALL-941 -1205
* ---- 001A CPMEOF CPI-214 -245 -271 -508
* ---- 005C CPMFCB LHLD-113 LDA-114 SHLD-119 STA-120 SHLD-147 STA-148
* LXI-1448
* 0103 035A DASHES LXI-1086
* ---- 09B1 DELIMITER CALL-1449
* ---- 0110 ERROREXIT JZ-1477
* ---- 0BC7 FILLZERO CALL-1420 -1444
* 0863 0713 FINDADD JMPR-891
* 0875 0722 FINDADD2 JRNZ-869
* 0889 0736 FINDADD3 JRC-886
* ---- 0B69 FINISHOUTPUT CALL-161
* 1254 088C FIRSTSYMBOL CALL-1033
* 0586 05A2 FSA EQU-587 -590 -593 -596 LXI-662 -677
* 0587 0000 FSA0 DB-588 -589 -589 -589 -591 -592 -592 -592 -594 -595
* -595 -598 -598
* 0590 0010 FSA1 DB-588 -588 -591 -591 -591 -592 -594
* 0593 0020 FSA2 DB-588 -594 -594 -595
* 0596 0030 FSA3 DB-589 -595 -597 -597 -597 -597 -598 -598
* ---- 09CC GETCHAR CALL-249 -289 -326 -506 -514 CZ-519 CALL-527
* 0665 066E GETFSA JMP-704
* 1403 0923 GETMAIN CALL-201 -221 -939 -968
* 1415 093C GETMAIN2 JRC-1413
* 0656 0662 GETWORD CALL-426 -434 -444
* 0716 0699 GETWORDEND
* ---- 018C INFCB LXI-152 -1442 STA-1462 LXI-1467 SERVICE-1473
* ---- 01AF ININDEX STA-1483
* 1320 08CB INORDER CNZ-1305 -1330 -1342
* 0089 0400 LENGTHLINE LXI-129 DS-1508
* 0080 000F LENGTHOPREC LXI-938
* 0066 0018 LENGTHSYMREC LXI-200 -220 -355 -370 -1273
* 1167 0006 LENGTHUSE CPI-1208
* 0074 0006 LENGTHUSEREC LXI-967
* 0050 0156 LINEBUFFER SHLD-125 LHLD-421 -483 -505 -550
* 0229 0428 LOADSEND JRZ-215
* 0211 0400 LOADSLOOP JMPR-227
* 0194 03E6 LOADSYMBOLS CALL-136
* 0110 0384 MAIN
* 0932 074F MAKENODE CALL-872
* 0090 0048 MAXCOL CPI-1208 -1358 -1377
* 0088 2000 MAXPOOL LXI-141 DS-1508
* 0087 2000 MAXSYM LXI-132 DS-1508
* 0818 06EF MDIV2 CALL-736 -778 -789
* ---- 09A2 MOVEHTOD CALL-374 -380 -387 -1469
* ---- 0247 MSGNOFILE LXI-149
* ---- 0228 MSGNONAME ABORT-1451
* 0099 01FD MSGNOPOOL ABORT-1414
* 0098 01E2 MSGNOSYM LXI-121
* 0053 015C NEXTPOOL SHLD-131 LHLD-140 -204 -1407 SHLD-1417
* 1263 0899 NEXTSYMBOL CALL-1039
* 1275 08AC NEXTSYMZ JRZ-1271
* 0839 0701 NOTEDEF CALL-432
* 0857 070D NOTEOPCODE CALL-439
* 0896 073A NOTEOPZ1 JMPR-873
* 0904 073E NOTEOPZ2 JRZ-883
* 0959 0766 NOTEUSE CALL-452
* 0987 0783 NOTEUSE2 JMPR-996
* 0998 0790 NOTEUSE3 JRZ-993
* 0052 015A NSYMBOLS SHLD-208 LHLD-217 SHLD-219 LHLD-734 -1255
* 1351 0010 OPCOLS CPI-1358 MVI-1374 CPI-1377
* 0082 0009 OPCOUNT LXI-909 -1367
* 0083 000B OPLEFT LXI-885 -1323
* 0081 0000 OPNAME
* 0106 037F OPOVERFLOW LXI-1390
* 0084 000D OPRIGHT LXI-1335
* 0055 0160 OPROOT LXI-861 LHLD-1302
* 0105 0365 OPUSEHEAD LXI-1298
* 0417 04D6 PERUSE1 JMPR-460
* 0438 0506 PERUSE2 JRZ-431
* 0441 050C PERUSE3 JRZ-448 JMPR-453
* 0455 052C PERUSE9 JRZ-424 -427 -435 -445
* 0409 04CF PERUSEFILE CALL-158
* 0462 0535 PERUSEZ JRZ-419
* 0054 015E POOLEND SHLD-134 -143 LHLD-153 -1411
* 1507 0D3D PROGEND LXI-124
* ---- 0CA7 PUT9999 CALL-547 CNZ-1085
* ---- 0C4E PUTBLANK CALL-1076 -1210 -1365
* ---- 09FF PUTCHAR CALL-553 -1075 -1090 -1101 -1221
* ---- 0C56 PUTCRLF CALL-1111 -1306
* 1352 08E9 PUTOP CALL-1333
* ---- 0C63 PUTSTRING CALL-1028 -1031 CZ-1087 CALL-1104 CNZ-1218
* CALL-1241 -1299 -1361 -1391
* ---- 0C74 PUTTAB CALL-548 CC-1364 -1378
* 1168 0839 PUTUSE CALL-1145
* 1206 085D PUTUSE2 JRZ-1201
* 1213 086A PUTUSE3 JRNC-1209
* 1216 086D PUTUSE4 JMPR-1212
* ---- 0C80 PUTXXXX CALL-1098
* ---- 0CDD PUTZZZZ9 CALL-1224 -1373
* 0253 0440 READFEND JRZ-246 JRNC-248
* 0240 042D READFILL CALL-212 -223
* 0243 042E READFLOOP JMPR-251
* 0293 0469 READHEND JRZ-272
* 0262 0442 READHEX CALL-222
* 0276 0451 READHOOP DJNZ-291
* 0285 045D READHOOP2 JRC-283
* 0315 0476 READLABEL CALL-224
* 0330 0488 READLEND JRNC-323
* 0502 0550 READLINE CALL-478
* 0513 0563 READLINE1 JRZ-517
* 0522 0574 READLINE3 JRNZ-512 JMPR-529
* 0531 0582 READLINENZ JRZ-525
* 0533 0584 READLINEZ JRZ-509
* 0320 0479 READLLOOP JMPR-328
* 0340 048C REORDER CALL-225
* 0346 0493 REORDER1 JMPR-357
* 0359 04A8 REORDERDONE JRC-353
* 0092 0001 SEQINCR LXI-456
* 0091 0001 SEQSTART LXI-415
* 1433 0949 SETUPINPUT CALL-122 -150
* ---- 0A95 SETUPOUTPUT CALL-156
* 1238 0881 STARTNEWLINE CALL-1214
* 1388 0918 STARTNEWOP CALL-1300 CNC-1359
* 0063 016C STRING LXI-372 -385 -425 -442 -1137
* ---- 0C3C STRINGLENGTH CALL-1106 -1202 -1362
* 1455 0967 SUI2 JNZ-1450
* 1461 0979 SUI3 JNZ-1458
* 1481 0998 SUI4
* 0368 04AC SWAPSYMS CALL-354
* 0731 06A0 SYMBOL CALL-430 -447
* 0788 06D7 SYMBOLESS JRC-774
* 0805 06EB SYMBOLFAIL JRZ-779 -790
* 0809 06EB SYMBOLHIT JRZ-768
* 0738 06AA SYMBOLOOP JMP-784 -801
* 0051 0158 SYMBOLTABLE SHLD-205 LHLD-755 -1260
* 0067 0000 SYMLABEL
* 0068 0011 SYMLINE LXI-842 -1067 -1078
* 0070 0015 SYMUSE LXI-984 -1062 -1131
* 0069 0013 SYMVAL LXI-295 -1092
* 0097 01DF TYPESYM LHLD-117 LDA-118
* ---- 0C13 UPPERCASE CALL-695
* 0076 0002 USELINE
* 0075 0000 USENEXT
* 0077 0004 USEOP
* 0060 0166 WF SHLD-663 LHLD-672 SHLD-679
* ---- 0C35 WHITESPACE CALL-518
* 0061 0168 WI SHLD-739 LHLD-782 -793
* 0062 016A WM SHLD-735 LHLD-819 SHLD-828 -1256 LHLD-1267 SHLD-1272
* 0059 0164 WO SHLD-440 LHLD-450
* 0058 0162 WP SHLD-202 -226 LHLD-345 SHLD-428 LHLD-433 SHLD-436
* LHLD-443 SHLD-446
* 0541 0586 WRITELINE CALL-490
* 0551 0594 WRITELINE2 JRNZ-556
* 1152 0835 WRITEUEND JRZ-1143
* 1141 0827 WRITEULOOP JMPR-1150
* 1126 0816 WRITEUSES CALL-1109
* 1113 0811 WRITEX9 JRZ-1072
* 1055 07BC WRITEXLINE CALL-1038
* 0101 0328 XREFHEAD LXI-484 -1027
* 0102 033C XREFLEGEND LXI-1030
* 0104 035F XREFOVERFLOW LXI-1240
* ---- 0000 Z80CPU IF-794
* CENSUS OF OPCODE USAGE
*
* ABORT 2 ADD 3 ADI 1
* ANI 2 CALL 91 CC 2
* CLASS 16 CMP 2 CNC 1
* CNZ 5 CPI 12 CZ 2
* DAD 35 DB 21 DCX 7
* DJNZ 1 DS 2 DW 11
* ELSE 2 END 1 ENDIF 2
* ENDM 3 EQU 41 IF 2
* INR 4 INX 39 JMP 3
* JMPR 13 JNZ 2 JRC 5
* JRNC 3 JRNZ 3 JRZ 23
* JZ 1 LDA 2 LDAX 2
* LHLD 26 LXI 53 MACLIB 2
* MOV 108 MVI 13 ORA 18
* ORG 2 ORI 1 PCHL 1
* POP 99 PRINT 2 PROLOG 1
* PUSH 99 REPT 2 RET 31
* RNC 1 RZ 4 SERVICE 2
* SHLD 23 STA 4 STAX 1
* SUI 1 XCHG 17 XRA 2
* XTHL 2