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 ; SSymRec 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