``8080CODETXTg{  ``DISA-RELCOM  ``DISA-RELPAS  ``DISASM DOC]nf  ``EXTRACT PAS x  ``OPCODES TXTt  `` 0 NOP 2 LXI B,% 0 STAX B 0 INX B 0 INR B 0 DCR B 1 MVI B,% 0 RLC 0 ??? 0 DAD B 0 LDAX B 0 DCX B 0 INR C 0 DCR C 1 MVI C,% 0 RRC 0 ??? 2 LXI D,% 0 STAX D 0 INX D 0 INR D 0 DCR D 1 MVI D,% 0 RAL 0 ??? 0 DAD D 0 LDAX D 0 DCX D 0 INR E 0 DCR E 1 MVI E,% 0 RAR 0 ??? 2 LXI H,% 2 SHLD 0 INX H 0 INR H 0 DCR H 1 MVI H,% 0 DAA 0 ??? 0 DAD H 2 LHLD % 0 DCX H 0 INR L 0 DCR L 1 MVI L,% 0 CMA 0 ??? 2 LXI SP,% 2 STA % 0 INX SP 0 INR M 0 DCR M 1 MVI M,% 0 STC 0 ??? 0 DAD SP 2 LDA % 0 DCX SP 0 INR A 0 DCR A 1 MVI A,% 0 CMC 0 MOV B,B 0 MOV B,C 0 MOV B,D 0 MOV B,E 0 MOV B,H 0 MOV B,L 0 MOV B,M 0 MOV B,A 0 MOV C,B 0 MOV C,C 0 MOV C,D 0 MOV C,E 0 MOV C,H 0 MOV C,L 0 MOV C,M 0 MOV C,A 0 MOV D,B 0 MOV D,C 0 MOV D,D 0 MOV D,E 0 MOV D,H 0 MOV D,L 0 MOV D,M 0 MOV D,A 0 MOV E,B 0 MOV E,C 0 MOV E,D 0 MOV E,E 0 MOV E,H 0 MOV E,L 0 MOV E,M 0 MOV E,A 0 MOV H,B 0 MOV H,C 0 MOV H,D 0 MOV H,E 0 MOV H,H 0 MOV H,L 0 MOV H,M 0 MOV H,A 0 MOV L,B 0 MOV L,C 0 MOV L,D 0 MOV L,E 0 MOV L,H 0 MOV L,L 0 MOV L,M 0 MOV L,A 0 MOV M,B 0 MOV M,C 0 MOV M,D 0 MOV M,E 0 MOV M,H 0 MOV M,L 0 HLT 0 MOV M,A 0 MOV A,B 0 MOV A,C 0 MOV A,D 0 MOV A,E 0 MOV A,H 0 MOV A,L 0 MOV A,M 0 MOV A,A 0 ADD B 0 ADD C 0 ADD D 0 ADD E 0 ADD H 0 ADD L 0 ADD M 0 ADD A 0 ADC B 0 ADC C 0 ADC D 0 ADC E 0 ADC H 0 ADC L 0 ADC M 0 ADC A 0 SUB B 0 SUB C 0 SUB D 0 SUB E 0 SUB H 0 SUB L 0 SUB M 0 SUB A 0 SBB B 0 SBB C 0 SBB D 0 SBB E 0 SBB H 0 SBB L 0 SBB M 0 SBB A 0 ANA B 0 ANA C 0 ANA D 0 ANA E 0 ANA H 0 ANA L 0 ANA M 0 ANA A 0 XRA B 0 XRA C 0 XRA D 0 XRA E 0 XRA H 0 XRA L 0 XRA M 0 XRA A 0 ORA B 0 ORA C 0 ORA D 0 ORA E 0 ORA H 0 ORA L 0 ORA M 0 ORA A 0 CMP B 0 CMP C 0 CMP D 0 CMP E 0 CMP H 0 CMP L 0 CMP M 0 CMP A 0 RNZ 0 POP B 2 JNZ % 2 JMP % 2 CNZ % 0 PUSH B 1 ADI % 0 RST 0 0 RZ 0 RET 2 JZ % 0 ??? 2 CZ % 2 CALL % 1 ACI % 0 RST 1 0 RNC 0 POP D 2 JNC % 1 OUT % 2 CNC % 0 PUSH D 1 SUI % 0 RST 2 0 RC 0 ??? 2 JC % 1 IN % 2 CC % 0 ??? 1 SBI % 0 RST 3 0 RPO 0 POP H 2 JPO % 0 XTHL 2 CPO % 0 PUSH H 1 ANI % 0 RST 4 0 RPE 0 PCHL 2 JPE % 0 XCHG 2 CPE % 0 ??? 1 XRI % 0 RST 5 0 RP 0 POP PSW 2 JP % 0 DI 2 CP % 0 PUSH PSW 1 ORI % 0 RST 6 0 RM 0 SPHL 2 JM % 0 EI 2 CM % 0 ??? 1 CPI % 0 RST 7!ÑÜéP-ëE,ë [  0[tÇ4GZ Ñ÷diHYâ!r^#V"$Q!r*$Qs#r!!s6J!"LCC!P!"LC*T!P[ !PO !PF !PI !P^ !PR !PU !PL !PX !Pa !Pd Es }    C"bC*b### tC>!Q6J*b###2:!00";!6# xƒ#!"""">22>22> 2r!r"!5"!O;"6!2"8#!!C!L!C"Ù*Ù#O$^$C"͙"ϙC!r^#V!rͳI)$!r!r^#V####s#r*ϙ!r^#Vs#r!r!r^#V*͙s#rC"љ"әC!l^#V!r^#V{ozg"ՙ*ՙ%Ç$7&C}2C:&o!aI:&o!zJ}$:&o!}o|g}2:&o!9I$:&o}2:&o!}o|g"*C"ߙ"""C!*s#r*ߙ!I*ߙ!J}E%6&!"!*O;s!*^!ͳI*!I}ҋ%*+"Ò%*#"*;~K%*8^!IҼ%O;}2!!*++s!"!!^+"#,C"!*#"*!+"!|'&*!͵L!*^́$"%!**ߙLC""" " C!* s#r! q&0123456789ABCDEF#!"L*!I*!J}Ҝ&ä'!!+! *l&!K^s!!+! *l&!K^s!!+! *&!K^s!!+! *&!K^s*!ͳI['!!+!!L!*!͵L+"#,C"#*#"*#+"#|ʤ'!*+^2}'ë'C"-"/CH*/^!ͳIk(*/'@:PASTMP00.$$$!\J!:!*/! !L!:!!:!^#s!:!^!9I\(!:!!0s!:!!:!^#s*/*-*/X/*/! !!ZL*-!ͳIҰ(*/@!s*-}/o|/g#"-þ(*/@!s*/<!s*/;!s*/8!s!*/s#r*/6!s#r*/9*-s#r*/=*/s#r*/4*-s#r*/?!s*/&=}/c)!!s#ró)*/#=q)ó)*/*-;!!*/Hs#r!^#V!Iҳ)!!s#rú)C"9";CH*;8^!ͳI҉**;*9!=͓,*=!ͳI҉*!s6JͷF!s6J3*Unable to automatically close: Ï,*;<!s*;;!sÙ,Õ.C"A"C"ECH!*Es#r*E8^!ͳI-*E9*E6^#Vs#r@*E?^!I*E@^}ү-*E4^#V!J-*E!!s*E9!s#r!!+"G#,C"O*G#"G*O+"O||-@c-ì-*E*E4^#V!ZL*E9!s#r@-*E?^!ͳI-*E!!s*E9!s#r@".*E8^!I".*E9*E6^#Vs#r@Ô.*E8^!ͳI*E8^!ͳI}w.!!*EHs#r*ACs#rÃ.*A!s#r*E8!sHC"I"K"MC*M8^!I..*M*K;!!s#r*I!s#r*M8!s.C"U"W"YC*U!I/!"U*Y*W!Q!*UL!Q!*Us!Q"Q*Q[/ð/C}2eC:e&o!aI:e&o!zJ}Ҟ/:e&o!}o|g}2e:e&o}2[:[&oC"g"i"kC!!s#r*g^!I*g!^! ͳI} 0*g!!w2/*k*g!\J*k^!I 2!@}2m!oA0 #!"L!wV0 #!"L*k!^!:ͳIҔ0!"*k!^U/}2mÚ0!"*"{!"}*k*^!.+D!:+D ED}/o**{J}o**k^J}%1!o*}+*k*^U/s*}#"}*#"ê0*k*^!.ͳI**k^J}ҫ1*#"!"}*}!J**k^J}ҫ1!w*}+*k*^U/s*}#"}*#"[1*k!!ZL*k!:m&o!@{ozgs!o*k!!L!w*k! !L*k8!s'2C""C*!}/o|/g#ͳI]2!<^}2l2*<^}2:&oz2C"""C******^**!{ozg{ozgL*!*^*{ozgs2C}2C!^#V!:&os!^#V!^#V4^#V<:3[448!!0!9uD!++D E!-+D E!$+D EC!^#V8^!ͳIқ3!^#V!! s!^#V!^}2!^#V<^}/o:&o!CD}/o}4!^#V!^#V4^#V;!^#V!^}2ó3!^#V<~04!}2S4:&o!$ͳIL4!}2S4!}2:&oC}2ϛC:ϛ&o!FIҌ4:ϛ&o!}o|g}2ϛ:ϛ&o!9IҸ4:ϛ&o!A{ozg "ś4:ϛ&o!}o|g"ś*śC"ћC!6^#V!O;I6*ћ!s#r!}2+!}2,ͧJ}2%:%&o!$ͳI*5!}2,h5:%&o!+ͳIG5ͧJ}2%h5:%&o!-ͳIh5!}2+ͧJ}2%:%&o! I5:,&Ҫ5*ћ*ћ^#V!͵L:%&o13s#r5*ћ*ћ^#V! ͵L:%&o!}o|gs#rͧJ}2%h5:+&5*ћ*ћ^#V}/o|/g#s#r8.3}2-:-&o!ͳI*6*ћ!s#r8:-&o!ͳIf6!!C!A!FuD!a!fuD E EC!!C!$+D-EC!ӛ͆6!P\J!^#V!^}2%:%&o! IZ7:%&o!ͳI6!ӛ^!I6!ӛ!!ӛ^+s&7:%&o!$I&7!ӛBF!ӛPF:%&o͖F=F!P\J!^#V!^#V4^#V;!^#V!^}2%å6!!ӛ^+"'#,C"3*'#"'*3+"3|7!ӛ*'^!CD}/7!^#V")!s6JͷF!s6J7Bad Integer, Try again:*@~<*<*!^!ͳI*<^}os*!^! ͳI}2:&o*<^}ҧ<*;!s:&o*8^!ͳI}<>*<^*;^}<*!! s=C""C!*s#r@)==C"ǜC!}2Ü*ǜF=CON:`Ce=*ǜ8!s!}2Ü=*ǜq=LST:`CҐ=*ǜ8!s!}2Ü=*ǜ͜=KBD:`C*ǜͫ=TRM:`C}=*ǜ8!s!}2Ü:Ü&oC"͜C!}2ɜ!ќ! !~uDC*͜^!I*͜^!J}>t>!*͜^+"Ϝ#,C"*Ϝ#"Ϝ*+"|m>*͜*Ϝ^!ќCD}/j>t>7>!}2ɜ:ɜ&oÂ>!^#V=!!L!^#V8^!ͳI ?*!!Hs*^! ͳI>!! H"?*^!ͳI?!! H"!!H"@b?!^#V8^!ͳIb?*!!Hs*^!I:?@!^#VA~҇?!^#V<!s@!!^#V9^#V+"#,C"*#"*+"|@!^#V"*?^!ͳIK@*?!s*A^}/K@!*BH"!!*Hs#r!^#V!IK@*A!s*A~҈@*!s*<!s*6!s#rH@è@**B*?^^s*?*?^#s*#"í?!^#V6!s#rH@ÌAC}2C!^#V" * ?^!ͳIUA!!* BHs#r!!* Hs#r* ?!s* B* ?^:&os* ?* ?^#s!!s#r!^#V=!!L!^#V8^!IB!^#V" !* 9^#V+"#,C" *#"* +" |B*^}2:&o! ͳI(BC* 8^!ͳItB!:&oH":&o! ͳIqB!! H"ýB!:&oH"* 8^!ͳIҽB:&o! ͳIҽB!! H"*#"AC!!^#V9^#V+"#,C"*#"*+"|C*^@*#"BýB!C*"*"IMC{ozg##*!*"çCXCnC!*!iCXCfCnCXCnCnCfCXCfCnCXCnCfCXCfCfCnCN2GڷCAyC#»C:"*" !9 "L* " ! 9^#V!9D" !9^#V!9 "L!"9*  6##D"""$!9!D*$|FDJD*":$_!9:$_!mD~Gw @"""*"(!9!D*(|FD"$**|FD}FDʺDJD*$#"$**{z¥DJD*"""!9DM! 9^#V"$|E}_F:$_!mD~E!"9*"D""!9! 9 w#E! 9*"""!9! 9 /w#pE}opEyʫEڱE!!pEyʫEڱEëEpEʱEæEpEʱEÿE"2^#V#"0^#V#".",*2*0J.F*2*,*0I.F*2*0{_zW*.^#V*,#)*.^#V!4!5"4242W"8ʆF2:O:4ڊF*4"L:4G::24_!5"4*8>O>2WeFy27>26!6PF";ͧJq*; qL qL*;~FͧJF6*8~ͧJ*~ ͧJA͋GzG-sG{/_z/WzRG'lKfGlKfGdlKfG lKfGfG!aG!0_0ʄG!jLÁGxsGxzG¦G{¦GxGüGz/W{/_ lKz®GxGG !jLGüGɯ2@">}2=2>*8~IH!=H:>H:?H*<6!>!\J*8~CH !jL*;6> *>!?>2>*; ~H!hH*6:>}H͎H#:><2>*; ~_HHy ʝHq:@<2@6 :><2>:@<2@ʹH#ÝH!=#5HC"AC! *AH"CHIC"O"QC*QM*Oo&"S*S"E*E!!H"U)CI {_#z=I @ @)CI N#F~w#~w)CI ~/O#~/G~w#~w~#ʰIIIJIJ!J}I|J!`i!JzI}|JIJI!JzI{zJI!J}J|I!`i!JI!JI&}o&}o""6"8!O;"6!2"8ɯ2W"X"ZYPzʚJʃJڃJ>2W}2\ÎJ!J*6"]"_"a#+n&*a*_J*]|J}o|gJ{L{NqL#zJ:c!2cyKVKx/Gy/O>=.KTK)HK)9K, BK&K33&K)OK, &K aKC DIV 0 $ɯ2c}KKKxKK K}ʣK{/oz/g# K{/_z/W!KɯO>GzK½KK K{/_z/W}o|g#ɯo>g Ko>gVK>2c!.lK.lK K K'Lx6L~# x,LDLxXL + +~+ xNL{q#{aL{LY͒L}-- ͒L-†L*8yHҨL)¡LyžLÙLMD͙L}lgPROGRAM dis_rel; { Disassemble a .ERL or .REL file. AUTHOR: Professor Ronald E. Bruck Department of Mathematics University of Southern California Los Angeles, CA 90089 } CONST tty_name = 'CON:'; tabu = 9; bs = 8; bell = 7; argMark = '%'; op_code_filename = 'A:OPCODES.TXT'; { Maximum code size. Maximum REL info size is 1/4 this: } maxpc = 16384; maxrel = 4096; TYPE op_code_type = RECORD follow : 0..2; { # bytes which follow opcode } name : STRING [ 15 ]; { mnemonic for opcode } END; tipe_type = (abslute, code_rel, data_rel, common_rel); a_field = RECORD tipe : tipe_type; value : INTEGER; END; name_type = STRING [ 8 ]; ms_item = RECORD rel : BOOLEAN; value : INTEGER; tipe : tipe_type; control : INTEGER; a : a_field; b : name_type; END; ref_type = RECORD tipe : BYTE; { Bit assignments: Bit 0 : 0 = code relative, 1 = data relative; Bit 1 : 0 = public name, 1 = private name. Bit 1 is irrelevant if the item is placed in the chain of external references. } value : INTEGER; name : name_type; ptr : ^ref_type; END; ptr_to_ref = ^ref_type; offset_type = RECORD sign : -1..+1; loc, offset : INTEGER; next : ^offset_type; END; VAR sysmem : EXTERNAL INTEGER; pgm_name, in_f_name, out_f_name : STRING; item : ms_item; ch,tab : CHAR; next_label, first_code_ref, last_code_ref, first_data_ref, last_data_ref, first_ext_ref, last_ext_ref : ptr_to_ref; first_offset, last_offset, next_offset : ^offset_type; n, pc, { program counter } final_pc, { last byte of code } old_mark, { mark top of heap } pgm_size, data_size, result, cur_bit : INTEGER; fbyte : FILE OF BYTE; { File of char does interpretation } fout : TEXT; code_buffer: ARRAY [ 0..maxpc ] OF BYTE; rel_info : ARRAY [ 0..maxrel ] OF BYTE; op_codes : ARRAY [ 0..255 ] OF op_code_type; EXTERNAL PROCEDURE @HLT; EXTERNAL FUNCTION keypress : BOOLEAN; { Procedure to extract two filenames from the command line. The CCP has already done this, of course, but this way we don't have to define an FCB: } EXTERNAL PROCEDURE xtrctcmd ( VAR name1, name2 : STRING ); PROCEDURE mark; BEGIN old_mark := sysmem; END; PROCEDURE release; BEGIN sysmem := old_mark; END; PROCEDURE syntax_error; VAR i : INTEGER; BEGIN WRITELN ( CHR ( bell ), '*** SYNTAX ERROR:' ); WRITELN; WRITELN ( 'Syntax should be either' ); WRITELN; WRITELN ( 'DISASM d:source.ext' ); WRITELN ( ' (routing output to CON:)' ); WRITELN; WRITELN ( ' OR' ); WRITELN; WRITELN ( 'DISASM d:source.ext d:dest.ext' ); WRITELN ( ' (routing output to d:dest.ext' ); END { syntax_error }; PROCEDURE halt ( message : STRING ); BEGIN CLOSE ( fbyte, result ); CLOSE ( fout, result ); WRITELN; WRITELN ( message ); @HLT; END; PROCEDURE open_files; VAR i : INTEGER; BEGIN xtrctcmd ( in_f_name, out_f_name ); IF (in_f_name = '') THEN syntax_error; IF (out_f_name = '') THEN out_f_name := tty_name; ASSIGN ( fbyte, in_f_name ); RESET ( fbyte ); IF IORESULT = 255 THEN halt ( CONCAT ( 'Unable to open file ', in_f_name, '.' ) ); cur_bit := 7; ASSIGN ( fout, op_code_file_name ); RESET ( fout ); IF IORESULT = 255 THEN halt ( 'Unable to open file of opcode names.' ); FOR i := 0 TO 255 DO BEGIN READ ( fout, op_codes [ i ].follow ); READ ( fout, ch ); READLN ( fout, op_codes [ i ].name ); END; CLOSE ( fout, result ); ASSIGN ( fout, out_f_name ); REWRITE ( fout ); IF IORESULT = 255 THEN halt ( 'Unable to REWRITE output file.' ); END; FUNCTION test_type ( n : INTEGER ) : INTEGER; { Each item in the code buffer has associated with it two bits, meaning: 00 = ABSOLUTE item, use this byte AS-IS; 01 = CODE-RELATIVE item; relative to program base; 10 = DATA-RELATIVE item; relative to data base; 11 = POINTER; the two bytes point to a REF item in the heap giving more information. } VAR dv, md, i : INTEGER; BEGIN dv := n DIV 4; md := 2 * (n MOD 4); i := 0; IF TSTBIT ( rel_info [ dv ], md ) THEN i := i + 1; IF TSTBIT ( rel_info [ dv ], md + 1 ) THEN i := i + 2; test_type := i; END; PROCEDURE set_type ( n, tipe : INTEGER ); { SETS the two bits mentioned above to reflect tipe. Uses only the bottom two bits of tipe. } VAR dv, md : INTEGER; BEGIN dv := n DIV 4; md := 2 * ( n MOD 4 ); IF TSTBIT ( tipe, 0 ) THEN SETBIT ( rel_info [ dv ], md ) ELSE CLRBIT ( rel_info [ dv ], md ); IF TSTBIT ( tipe, 1 ) THEN SETBIT ( rel_info [ dv ], md + 1 ) ELSE CLRBIT ( rel_info [ dv ], md + 1 ); END; FUNCTION bit : INTEGER; BEGIN IF cur_bit < 0 THEN BEGIN IF EOF ( fbyte ) THEN halt ( '*** ERROR: Last byte read...' ); get ( fbyte ); cur_bit := 7; END; IF TSTBIT ( fbyte^, cur_bit ) THEN bit := 1 ELSE bit := 0; cur_bit := cur_bit - 1; END; FUNCTION bits ( n : INTEGER ) : INTEGER; VAR i, m : INTEGER; BEGIN m := 0; FOR i := 1 TO n DO m := 2 * m + bit; bits := m; END; PROCEDURE get_a_field ( VAR a : a_field ); VAR n : INTEGER; BEGIN n := bits ( 2 ); CASE n OF 0 : a.tipe := abslute; 1 : a.tipe := code_rel; 2 : a.tipe := data_rel; 3 : a.tipe := common_rel; END; n := bits ( 16 ); a.value := SWAP ( n ); END; PROCEDURE get_b_field ( VAR b : name_type ); VAR i, n : INTEGER; BEGIN n := bits ( 3 ); MOVELEFT ( n, b, 1 ); FOR i := 1 TO n DO b [ i ] := CHR ( bits ( 8 ) ); END; PROCEDURE get_ms_item ( VAR item : ms_item ); VAR n : INTEGER; BEGIN FILLCHAR ( item, SIZEOF ( ms_item ), CHR ( 0 ) ); CASE bit OF 0 : BEGIN item.rel := FALSE; item.value := bits ( 8 ); END; 1 : BEGIN item.rel := TRUE; n := bits ( 2 ); CASE n OF 0 : BEGIN item.tipe := abslute; item.control := bits ( 4 ); CASE item.control OF 0,1,2,3,4 : get_b_field ( item.b ); 5,6,7 : BEGIN get_a_field ( item.a ); get_b_field ( item.b ); END; 8,9,10,11, 12,13,14 : get_a_field ( item.a ); END; IF item.control = 14 THEN cur_bit := -1; { force to byte boundary } END; 1 : BEGIN item.tipe := code_rel; n := bits ( 16 ); item.value := SWAP ( n ); END; 2 : BEGIN item.tipe := data_rel; n := bits ( 16 ); item.value := SWAP ( n ); END; 3 : BEGIN item.tipe := common_rel; n := bits ( 16 ); item.value := SWAP ( n ); END; END; END; END; END; PROCEDURE insert_ext_ref ( VAR p, root, last : ptr_to_ref ); { Appends a new REF item to the end of a chain beginning at root. We append at the END of the list, instead of the beginning, so we keep the EXTERNAL items in the correct order of appearance in the .REL file. } BEGIN IF last = NIL THEN { nothing in chain } BEGIN root := p; root^.ptr := NIL; last := root; END ELSE BEGIN p^.ptr := NIL; last^.ptr := p; last := p; END; END; PROCEDURE linear_insert ( VAR p, first, last : ptr_to_ref ); { Assuming there is a chain of ref_type, beginning with sentinel values first and ending with last, linearly ordered by value, this procedure breaks the chain and inserts p^. } VAR w1, w2 : ptr_to_ref; BEGIN w2 := first; w1 := w2^.ptr; last^.value := p^.value; WHILE w1^.value < p^.value DO BEGIN w2 := w1; w1 := w2^.ptr; END; { Insert if the value is new, or if it is repeated but the name is more specific. } IF (p^.value <> w1^.value) OR ( w1 = last ) THEN BEGIN p^.ptr := w1; w2^.ptr := p; END ELSE IF (p^.name <> '') THEN BEGIN w2^.ptr := p; p^.ptr := w1^.ptr; END ELSE p := w1; END; PROCEDURE write_memavail; BEGIN IF out_f_name <> tty_name THEN BEGIN WRITE ( CHR (bs), CHR (bs), CHR (bs), CHR (bs) ); WRITE_HEX ( OUTPUT, MEMAVAIL, 2 ); END; END; PROCEDURE test_for_create_error; BEGIN IF (MEMAVAIL < SIZEOF ( ref_type ) ) THEN halt ( '*** HALT -- insufficient MEMORY' ); write_memavail; END; PROCEDURE create_ref ( VAR p : ptr_to_ref; tipe : tipe_type; value : INTEGER; name : name_type; public : INTEGER { 0 for public, 1 for private } ); BEGIN new ( p ); test_for_create_error; p^.value := value; p^.name := name; IF tipe = code_rel THEN BEGIN p^.tipe := CHR ( 2 * public ); linear_insert ( p, first_code_ref, last_code_ref ); END ELSE BEGIN p^.tipe := CHR ( 1 + 2 * public ); linear_insert ( p, first_data_ref, last_data_ref ); END; END { create_ref }; PROCEDURE chain_external ( item : ms_item ); VAR p : ptr_to_ref; q, q1 : INTEGER; { Indices into code buffer } stop : BOOLEAN; BEGIN new ( p ); test_for_create_error; IF item.a.tipe = code_rel THEN p^.tipe := CHR ( 0 ) { code, public } ELSE p^.tipe := CHR ( 1 ); { data, public } p^.value := item.a.value; p^.name := item.b; insert_ext_ref ( p, first_ext_ref, last_ext_ref ); q := item.a.value; REPEAT { Replace code-file REL quantities with pointers to REF } stop := ( test_type ( q ) = 0 ) AND (code_buffer [ q ] = CHR ( 0 )) AND (test_type ( q + 1 ) = 0) AND (code_buffer [ q + 1 ] = CHR ( 0 )); set_type ( q, 3 ); set_type ( q + 1, 3 ); MOVELEFT ( code_buffer [ q ], q1, 2 ); MOVELEFT ( p, code_buffer [ q ], 2 ); q := q1; UNTIL stop; END; PROCEDURE define_entry_point ( item : ms_item ); VAR p : ptr_to_ref; BEGIN create_ref ( p, item.a.tipe, item.a.value, item.b, 0 { public } ); END; PROCEDURE chain_address ( item : ms_item ); VAR p : ptr_to_ref; q, q1 : INTEGER; stop : BOOLEAN; BEGIN create_ref ( p, code_rel, pc, '', 1 { private } ); q := item.a.value; REPEAT { Replace code-file REL quantities with pointers to REF } stop := ( test_type ( q ) = 0 ) AND (code_buffer [ q ] = CHR ( 0 )) AND (test_type ( q + 1 ) = 0) AND (code_buffer [ q + 1 ] = CHR ( 0 )); set_type ( q, 3 ); set_type ( q + 1, 3 ); MOVELEFT ( code_buffer [ q ], q1, 2 ); MOVELEFT ( p, code_buffer [ q ], 2 ); q := q1; UNTIL stop; END; PROCEDURE program_name ( item : ms_item ); BEGIN pgm_name := item.b; END; PROCEDURE define_data_size ( item : ms_item ); BEGIN data_size := item.a.value; END; PROCEDURE set_load ( item : ms_item ); BEGIN IF item.a.tipe = code_rel THEN pc := item.a.value; END; PROCEDURE define_program_size ( item : ms_item ); BEGIN pgm_size := item.a.value; END; PROCEDURE name_for_search ( item : ms_item ); BEGIN END; PROCEDURE offset ( item : ms_item ); VAR p : ^offset_type; BEGIN new ( p ); test_for_create_error; IF item.control = 8 { - offset } THEN p^.sign := -1 ELSE p^.sign := +1; p^.loc := pc; p^.offset := item.a.value; p^.next := NIL; { Now insert the item at the END of the offset chain. Because the pc increases, the chain will be ordered on its LOC field. } IF last_offset = NIL THEN BEGIN first_offset := p; last_offset := first_offset; END ELSE BEGIN last_offset^.next := p; last_offset := p; END; END; PROCEDURE end_pgm ( item : ms_item ); BEGIN END; PROCEDURE end_file ( item : ms_item ); BEGIN END; PROCEDURE handle_special ( item : ms_item ); BEGIN CASE item.control OF 0 : name_for_search ( item ); { 1 : ignore SELECT COMMON BLOCK } 2 : program_name ( item ); { 3 : ignore REQUEST LIBRARY SEARCH } { 4 : ignore RESERVED FOR FUTURE EXPANSION } { 5 : ignore DEFINE COMMON SIZE } 6 : chain_external ( item ); 7 : define_entry_point ( item ); 8, 9 : offset ( item ); 10 : define_data_size ( item ); 11 : set_load ( item ); 12 : chain_address ( item ); 13 : define_program_size ( item ); 14 : end_pgm ( item ); 15 : end_file ( item ); END; END { handle_special }; PROCEDURE write_name ( p : ptr_to_ref ); VAR i : INTEGER; BEGIN IF p^.name <> '' THEN WRITE ( fout, p^.name) ELSE BEGIN IF TSTBIT ( p^.tipe, 0 ) THEN WRITE ( fout, 'D$' ) ELSE WRITE ( fout, 'C$' ); WRITE_HEX ( fout, p^.value, 2 ); END; END; PROCEDURE write_ref_name ( pc : INTEGER ); VAR p : ptr_to_ref; BEGIN MOVELEFT ( code_buffer [ pc ], p, 2 ); write_name ( p ); END; PROCEDURE write_next_label; BEGIN write_name ( next_label ); WRITE( fout, ':', tab ); END; PROCEDURE w_hex ( n, nbytes : INTEGER ); { Writes the integer n (one or two bytes) to file f in hex form, in M80-readable form; e.g., 0FFFFh. } BEGIN IF ( (nbytes = 1) AND (LO ( n ) >= $a0) ) OR ( (nbytes = 2) AND (HI ( n ) >= $a0) ) THEN WRITE ( fout, '0' ); WRITE_HEX ( fout, n, nbytes ); WRITE ( fout, 'H' ); END; PROCEDURE write_offset ( pc : INTEGER ); BEGIN IF next_offset <> NIL THEN WITH next_offset^ DO BEGIN IF loc = pc THEN BEGIN IF sign = 1 THEN BEGIN IF offset >= 0 THEN WRITE ( fout, '+', offset ) ELSE WRITE ( fout, offset ); END ELSE IF offset >= 0 THEN WRITE ( fout, '-', offset ) ELSE WRITE ( fout, '+', -offset ); next_offset := next_offset^.next; END; END; END; PROCEDURE WriteDB; BEGIN WRITE ( fout, 'DB', TAB ); w_hex ( ord ( code_buffer [ pc ] ), 1 ); WRITELN ( fout ); WRITELN ( fout, TAB, '; *** SYNC ERROR: inconsistent REL type' ); END; (* WriteDB *) PROCEDURE dis_asm ( VAR pc : INTEGER ); VAR a : a_field; p : ptr_to_ref; expect, n, t, temp : INTEGER; PROCEDURE WriteOpCode; VAR i,len: INTEGER; ch: CHAR; BEGIN WITH opCodes[ codeBuffer[ pc ] ] DO BEGIN i := 1; len := ORD( name[0] ); (* length *) ch := name[1]; WHILE (i <= len) AND (ch <> argMark) DO BEGIN IF ch = ' ' THEN WRITE( fout, TAB ) ELSE WRITE ( fout, ch ); i := i + 1; ch := name[i]; END; IF follow <> 0 THEN BEGIN IF follow = 1 THEN Whex( ORD( codeBuffer[ pc + 1 ] ), 1 ) ELSE IF follow = 2 THEN BEGIN IF t = 0 THEN BEGIN MOVELEFT ( codeBuffer[ pc + 1 ], temp, 2 ); w_hex ( temp, 2 ) END ELSE IF t = 3 THEN BEGIN write_ref_name ( pc + 1 ); END; write_offset ( pc + 1 ); END; i := i + 1; (* move behind % *) WHILE i <= len DO BEGIN IF ch = ' ' THEN WRITE( fout, tab ) ELSE WRITE ( fout, name[i] ); i := i + 1; END; END; (* follow # 0 *) WRITELN ( fout ); END; (* WITH *) END; (* WriteOpCode *) BEGIN IF next_label^.ptr <> NIL THEN BEGIN IF pc = next_label^.value THEN BEGIN write_next_label; next_label := next_label^.ptr; END ELSE WRITE ( fout, TAB ); END ELSE WRITE ( fout, TAB); CASE test_type ( pc ) OF 0 :BEGIN expect := op_codes [ code_buffer [ pc ] ].follow; CASE expect OF 0 : BEGIN IF op_codes [ code_buffer [ pc ] ].name <> '???' THEN BEGIN WriteOpCode; END ELSE BEGIN WriteDB; END; pc := pc + 1; END; 1 : BEGIN IF test_type ( pc + 1 ) <> 0 THEN BEGIN WriteDB; pc := pc + 1; END ELSE BEGIN WriteOpCode; pc := pc + 2; END; END; 2 : BEGIN t := test_type ( pc + 1 ); IF t <> test_type ( pc + 2 ) THEN BEGIN WriteDB; pc := pc + 1; END ELSE BEGIN WriteOpCode; pc := pc + 3; END; END; END; { case expect of } END; 1,2: BEGIN WRITELN ( fout, TAB, '; *** WOW!! HOW DID THAT HAPPEN?!!' ); pc := pc + 1; END; 3 : BEGIN MOVELEFT ( code_buffer [ pc ], p, 2 ); WRITE ( fout, 'DW', TAB ); write_ref_name ( pc ); WRITELN ( fout ); pc := pc + 2; END; END { case test_type of } END; PROCEDURE print_publics ( VAR p : ptr_to_ref ); VAR count : INTEGER; BEGIN count := 0; WHILE p^.ptr <> NIL DO BEGIN IF p^.name <> '' THEN BEGIN IF count = 0 THEN WRITE ( fout, TAB, 'PUBLIC', TAB, p^.name ) ELSE WRITE ( fout, ',' , p^.name ); count := count + 1; IF count = 6 THEN BEGIN WRITELN ( fout ); count := 0; END; END; p := p^.ptr; END; IF count > 0 THEN WRITELN ( fout ); END { print_publics }; PROCEDURE print_it; VAR p : ptr_to_ref; count : INTEGER; BEGIN IF out_f_name <> tty_name THEN BEGIN WRITELN; WRITELN ( 'Disassembling ', pgm_name ); END; pc := 0; p := first_ext_ref; IF pgm_name <> '' THEN WRITELN ( fout, TAB, 'TITLE', TAB, pgm_name ); count := 0; WHILE p <> NIL DO BEGIN IF count = 0 THEN WRITE ( fout, TAB, 'EXTRN', TAB, p^.name ) ELSE WRITE ( fout, ',' , p^.name ); count := count + 1; IF count = 6 THEN BEGIN WRITELN ( fout ); count := 0; END; p := p^.ptr; END; IF count > 0 THEN WRITELN ( fout ); p := first_code_ref^.ptr; print_publics ( p ); p := first_data_ref^.ptr; print_publics ( p ); WRITELN ( fout, TAB, 'CSEG' ); next_label := first_code_ref^.ptr; next_offset := first_offset; WHILE pc < final_pc DO dis_asm ( pc ); IF pc < pgm_size THEN WRITELN ( fout, TAB, 'DS', TAB, pgm_size - pc ); WHILE next_label <> last_code_ref DO BEGIN WRITE ( fout, TAB, 'ORG', TAB ); w_hex ( next_label^.value, 2 ); WRITELN ( fout ); write_next_label; WRITELN ( fout ); next_label := nextlabel^.ptr; END; IF data_size > 0 THEN BEGIN WRITELN ( fout, TAB, 'DSEG', TAB ); next_label := first_data_ref^.ptr; pc := 0; WHILE next_label^.ptr <> NIL DO BEGIN IF pc = next_label^.value THEN BEGIN write_next_label; WRITE ( fout, 'DS', TAB ); next_label := next_label^.ptr; IF next_label^.ptr <> NIL THEN BEGIN WRITELN ( fout, next_label^.value - pc ); pc := next_label^.value; END ELSE WRITELN ( fout, data_size - pc ); END ELSE BEGIN WRITELN ( fout, TAB, 'DS', TAB, next_label^.value - pc ); pc := next_label^.value; END; END; END; WRITELN ( fout, TAB, 'END' ); END; PROCEDURE replace_names; VAR p : ptr_to_ref; found : BOOLEAN; n, value : INTEGER; BEGIN pc := 0; WHILE pc < final_pc DO BEGIN n := test_type ( pc ); CASE n OF 0 : pc := pc + 1; 3 : pc := pc + 2; 1,2 : BEGIN MOVELEFT ( code_buffer [ pc ], value, 2 ); IF n = 1 THEN p := first_code_ref^.ptr ELSE p := first_data_ref^.ptr; found := FALSE; WHILE (p <> last_code_ref) AND (p <> last_data_ref) AND NOT found DO BEGIN found := (p^.value = value); IF NOT found THEN p := p^.ptr; END; IF (p = last_code_ref) THEN create_ref ( p, code_rel, value, '', 1 { private } ) ELSE IF (p = last_data_ref) THEN create_ref ( p, data_rel, value, '', 1 ); { Now that p points to an appropriate reference -- one already in the chain, or one we just added -- we can push its address into the code buffer and adjust the REL bits to POINTER. } MOVELEFT ( p, code_buffer [ pc ], 2 ); set_type ( pc, 3 ); set_type ( pc + 1, 3 ); pc := pc + 2; END { Case 1,2 }; END { Case n of }; END { While pc < final_pc }; END { replace_names }; PROCEDURE initialize; BEGIN pc := 0; pgm_name := ''; pgm_size := 0; data_size := 0; release; { free all space in the heap } new ( first_code_ref ); new ( last_code_ref ); new ( first_data_ref ); new ( last_data_ref ); first_code_ref^.ptr := last_code_ref; last_code_ref^.ptr := NIL; first_data_ref^.ptr := last_data_ref; last_data_ref^.ptr := NIL; first_ext_ref := NIL; last_ext_ref := NIL; first_offset := NIL; last_offset := NIL; END; PROCEDURE one_program ( VAR item : ms_item ); BEGIN initialize; IF out_f_name <> tty_name THEN WRITE ( 'Memory remaining: ' ); REPEAT get_ms_item ( item ); IF (item.rel) AND (item.tipe = abslute) AND (item.control = 15) THEN EXIT; { End-of-file } IF NOT item.rel THEN BEGIN MOVELEFT ( item.value, code_buffer [ pc ], 1 ); set_type ( pc, 0 ); pc := pc + 1; IF pc > maxpc THEN halt ( '*** ERROR: Code file overflow.' ); END ELSE CASE item.tipe OF code_rel, data_rel, common_rel : BEGIN IF pc <= maxpc - 2 THEN MOVELEFT ( item.value, code_buffer [ pc ], 2 ) ELSE halt ( '*** ERROR: Code file overflow.' ); CASE item.tipe OF code_rel : n := 1; data_rel : n := 2; common_rel: n := 0; END; set_type ( pc, n ); set_type ( pc+1, n ); pc := pc + 2; END; abslute : handle_special ( item ); END; UNTIL (item.rel) AND (item.tipe=abslute) AND (item.control IN [14,15]); final_pc := pc; { save program counter } replace_names; print_it; END; BEGIN { >>> MAIN PROGRAM <<< } tab := CHR( tabu ); open_files; mark; { mark heap space } REPEAT one_program ( item ) UNTIL (item.rel) AND (item.tipe = abslute) AND (item.control = 15 ); halt ( 'End of file - Normal termination.' ); END.  DOCUMENTATION FOR DISASM by Ronald Bruck This program accepts as input a .ERL or .REL (Microsoft-format relocatable) file and disassembles it to the output file. The original file may very well be a library file, containing more than one module. The result is almost in a form for assembly via M80 or RMAC -- see the example below. USAGE In addition to the DISASM.COM file, you must have the file OPCODES.TXT on drive A:. The program is invoked by DISASM inputfile.ext outputfile.ext (for translating inputfile to the textfile outputfile) or DISASM inputfile.ext (for translating inputfile to the default CON:) It is an error to invoke DISASM without any parameters, and the user is given a message to that effect. For example, consider the very simple (if useless) program PROGRAM dummy; VAR col, line : INTEGER; EXTERNAL PROCEDURE gotoxy ( col, line ); BEGIN gotoxy ( col, line ); END. After compilation by MT+ version 5.6 and disassembly of the .ERL file by DISASM we obtain the following actual output: NAME ('DUMMY ') EXTRN @INI,@HLT,GOTOXY PUBLIC LINE,COL CSEG 0000' 00 NOP 0001' 00 NOP 0002' 00 NOP 0003' 00 NOP 0004' 00 NOP 0005' 00 NOP 0006' 00 NOP 0007' 00 NOP 0008' 00 NOP 0009' 00 NOP 000A' 00 NOP 000B' 00 NOP 000C' 00 NOP 000D' 00 NOP 000E' 00 NOP 000F' 00 NOP 0010' C3 JMP C$0013 0013' 2A C$0013: LHLD 0006h 0016' F9 SPHL 0017' CD CALL @INI 001A' 2A LHLD COL 001D' E5 PUSH H 001E' 2A LHLD LINE 0021' E5 PUSH H 0022' CD CALL GOTOXY 0025' CD CALL @HLT DSEG 0000" LINE: DS 2 0002" COL: DS 2 END Note that the EXTERNAL declaration in the original program is mirrored by the EXTRN declaration in the disassembled file. Note also that PUBLICLY declared items -- arising from the global variables and global procedures/functions in the MT+ program -- are declared in a PUBLIC statement in the disassembled file. Finally, internal entrypoints and data are denoted by C$nnnn and D$nnnn, respectively. The disassembler does not construct DB's in the DSEG, only DS's, so it may fail on certain .REL files. In the MT+ environment this is not a problem, since LINKMT does not accept DB's in the DSEG. The first five characters at the left of the disassembly specify whether the bytes are in the code-relative or data-relative sections (a single quote stands for code-relative, a double-quote for data-relative). DISASM does not support COMMON. The FIRST BYTE of each item is given because the disassembly is not perfect -- I have not attempted to have it disassemble embedded strings or data in the code-relative segment. It will treat this as code as long as it can, otherwise will use DB. Since the first byte is available, you can manually change these items to DB. Note that CSEG and DSEG are specified, and that in contrast to the output of DIS8080, all reference chains have been filled in. If there is no data, the DSEG statement will not be generated. The first 12 columns are always present -- they may be blanks (no tabs are used) -- so you can go into ED and do a macro: BM12DL to remove all of that information. The resulting file can be reassembled by M80 to give a perfect copy of the original .ERL or .REL file. (There may be a problem with seven-character names which coincide in the first six characters.) Also, if the original file was a library file, the first END encountered will terminate assembly. The companion program BREAKLIB can be used on the OUTPUT of DISASM. (Strange things will happen if you use it on other files!) It is expecially intended for use on files generated from DISASM applied to libraries. (In particular, PASLIB.ERL.) When invoked by BREAKLIB filename.ext, the following are created: (1) The file filename.REF, which contains (for each module in the original file) the NAME of the module, followed by (indented) lines identifying the PUBLIC entrypoints of those modules. (2) For each module in the original file, having name "NAME", a file of name NAME.MAC, containing the lines of the original file pertaining to that module, eliminating the first 7 characters, but leaving the first byte of the item so you can manually edit strings. For safety's sake, BREAKLIB will never destroy an existing file. When you apply it to PASLIB, expect LOTS of text, and a two-inch printout. WHY A DISASSEMBLER (a) A disassembler and a code optimizer together can give more compact, faster programs. Now all we're lacking is the optimizer! (b) You can disassemble files such as REALIO.ERL, for which source is not available from DRI, and write your own modules based on what you find. For example, I plan to write a "rational arithmetic" module which will replace the 80-bit BCD form of real numbers with an exact rational arithmetic format. This will allow, for example, exact 73-bit integer arithmetic (including the sign bit). (c) When ordinary single-precision reals are used, the compiler allocates 4 bytes per real number. When BCD reals are used, it allocates 10 bytes per real. Disassembling the code, we can replace these 10 bytes by 8. (To do this automatically will require a special program which parses the original source file in parallel with the disassembled code.) This is a kludge to overcome DRI's negligence in not providing double-precision arithmetic. I intend to write a double-precision real number package using the Hudson 8087 board with my Compupro 8085/8088 board. 8080 MT+ will then be able to do double-precision reals. (I own MT+86 for the 8088 side of my machine, but unfortunately DRI modified the compiler so it will no longer run in 128K. My money wasn't exactly wasted -- eventually I'll get more memory -- but it is certainly lying fallow.) (d) You can disassemble PASLIB and rewrite the parts that you need rewritten. I have done a similar job with BLAZEIO, trying to correct a bug and provide file-locking. RENAMING RELOCATABLE FILE ITEMS Also provided is a program (RENREL) to RENAME the entrypoints of a .ERL or .REL file. You can also use this to remove the NAME FOR SEARCH items (unless your file is a library -- leave them in in that case!) The main advantage of this is that M80 output files are limited to six- character names, whereas MT+ may emit up to seven-character names. Using RENREL, you can change the six-character names to seven. A RELOCATABLE FILE READING MODULE Also provided is MSMODULE (and MSTYPE) for sequentially reading Microsoft- format items from the bit-stream of a .ERL or .REL file and translating them to a format more easily used in Pascal. (If I were more of a Pascal purits, I would have written the type "ms_item" found in MSTYPE in terms of variant records. In fact, I did. Keeping track of the variants was such a headache I went to the type declared here.) MSTYPE is separated out of MSMODULE since any program which uses the "get_ms" and "put_ms" procedures is also going to need the type declarations. Just $I MSTYPE.PAS somewhere in the types of your main program. DISASM was written before MSMODULE, and I never got around to rewriting it to use the module. A TUTORIAL ON THE MICROSOFT FORMAT A relocatable format is one which contains assembled code and relocation information. In contrast with the 8086, 8080 code is not naturally relocatable: JMP's and CALL's are to ABSOLUTE addresses, and if the code location is shifted all of these addresses become invalid. One way to overcome this is to assemble the code as if it started at $0000, then store (in addition to the code) one bit for each byte of code, indicating whether the item is relocatable or not. The Microsoft format goes several steps further: it contains not only relocation information, but also a good deal of symbolic information (external references and names of entry-points). The format is intended to be used with a linker. The Microsoft relocatable-file format consists of a bit stream: individual fields are not aligned on byte boundaries, except as noted below. There are two types of items: ABSOLUTE and RELOCATABLE. The first bit of an item distinguishes the type: if a 0, the next 8 bits are loaded as ABSOLUTE; if a 1, the next 2 bits are used to determine the relocatable TYPE: 00 = special item (see below); 01 = Code relative. Load the next 16 bits after adding the current Program base. 10 = Data relative. Load the next 16 bits after adding the current Data base. 11 = Common relative. Load the next 16 bits after adding the current Common base. A special item has a bit stream looking like this: 100 xxxx yy bb zzz + symbol name ---- ----- ----------------- Control A-field B-field Field where both the A-field and B-field are optional. The A-field yy bb consists of a two-bit address yy similar to the relocatable type code above, except that 00 denotes ABSOLUTE address; followed by the 16-bit field bb described by yy. The B-field consists of a 3- bit field zzz which defines the length of a string, and as many bytes thereafter (up to 7) as are needed to define the symbol name. The following types specified by the control field have a B-FIELD ONLY: xxxx Type ---- ------------------------------ 0000 Entry symbol (name for search) 0001 Select COMMON block 0010 Program name 0011 Request library search 0100 Reserved (future expansion) The following types have both an A-field and a B-field: xxxx Type ---- ------------------------------ 0101 Define COMMON size 0110 Chain external (A is head of address chain, B is name of external symbol) 0111 Define entry point (A=address, B=name) The following types have an A-field only: xxxx Type ---- ------------------------------ 1000 External - offset. Used for JMP and CALL to externals. 1001 External + offset. A value will be added to the two bytes starting at the current location counter immediately before execution. 1010 Define size of Data area = A. 1011 Set loading location counter to A 1100 Chain address. A=head of chain, replace all entries in chain with current location counter. Last entry in chain has an address field of absolute 0. 1101 Define program size = A. 1110 End program -- forces to BYTE BOUNDARY. The following type has neither an A nor a B field: xxxx Type ---- ------------------------------ 1111 End of file. A library will contain one "End program" for each module assembled into it; and only one End of file. FINAL REMARKS I have provided source code for DISASM although I intend to eventually modify and improve it, making it more interactive. Users are advised to get a copy of Ward Christiansen's public- domain RESOURCE (from CPMUG) if they want to disassemble .COM files. (This is much more difficult, because you have NO symbol information available.) A companion module, MSMODULE, contains procedures to read/write Microsoft-format files. This was (essentially) extracted from DISASM. The .ERL or .REL file is limited to 10K bytes. Any more and an error message is displayed. When DISASM is tricked in the CSEG and must produce a DB, it issues a SYNCHRONIZATION ERROR line following the DB -- since this is a comment line, it will not interfere with the assembler. If the output file is not CON:, you will be shown a running summary of MEMAVAIL. DISASM is not entirely bug-free. In particular, sometimes it hangs -- I have no idea why. It checks MEMAVAIL before every use of NEW, and it never seems to be anywhere near the stack. Despite the new manual's promise about "@HERR", I can't get the compiler or linker to recognize it. Also, since I make only one pass through the file, and don't back up when a synchronization error is found (e.g., an opcode which requires an absolute byte as the next byte, but has instead a relocatable word), some address references are missed. I arrange for these with ORG's near the end of the CSEG. Finally, when the output file is not CON:, it finishes with a spurious display of memory remaining. This is such a minor bug I haven't bothered to try to squash it. COMPLAINT DEPARTMENT Please address all remarks, queries, complaints, suggestions for improvement, etc. to: Professor Ronald E. Bruck Department of Mathematics University of Southern California Los Angeles, CA 90089  MODULE extract_cmd; { Extracts first and second file-names from the "command tail". Written by: Ronald E. Bruck Department of Mathematics University of Southern California Los Angeles, CA 90089 } CONST space = ' '; TYPE ptrstring = ^STRING; EXTERNAL FUNCTION @cmd : ptrstring; {$E- : private } PROCEDURE strip_leading_blanks ( VAR s : STRING ) ; BEGIN WHILE ( POS ( space, s ) = 1 ) DO DELETE ( s, 1, 1 ) ; END; {$E+ : public} PROCEDURE xtrct_cmd ( VAR first_id, second_id : STRING ) ; VAR m, n : INTEGER; ptr : ptrstring; ch : CHAR; BEGIN ptr := @cmd; first_id := ptr^; strip_leading_blanks ( first_id ) ; n := POS ( space, first_id ); IF n = 0 THEN second_id := '' ELSE BEGIN m := LENGTH ( first_id ) - n + 1; second_id := COPY ( first_id, n, m ); DELETE ( first_id, n, m ) ; strip_leading_blanks ( second_id ) ; END; END; MODEND. 0 NOP 2 LD BC,% 0 LD (BC),A 0 INC BC 0 INC B 0 DEC B 1 LD B,% 0 RLCA 0 EX AF,AF' 0 ADD HL,BC 0 LD A,(BC) 0 DEC BC 0 INC C 0 DEC C 1 LD C,% 0 RRC 1 DJNZ,% 2 LD DE,% 0 LD (DE),A 0 INC DE 0 INC D 0 DEC D 1 LD D,% 0 RLA 1 JR,% 0 ADD HL,DE 0 LD A,(DE) 0 DEC DE 0 INC E 0 DEC E 1 LD E,% 0 RRA 1 JR NZ,% 2 LD HL,% 2 LD (%),HL 0 INC HL 0 INC H 0 DEC H 1 LD H,% 0 DAA 1 JR Z,% 0 ADD HL,HL 2 LD HL,(%) 0 DEC HL 0 INC L 0 DEC L 1 LD L,% 0 CPL 1 JR NC,% 2 LD SP,% 2 LD (%),A 0 INC SP 0 INC (HL) 0 DEC (HL) 1 LD (HL),% 0 SCF 1 JR C,% 0 ADD HL,SP 2 LD A,(%) 0 DEC SP 0 INC A 0 DEC A 1 LD A,% 0 CCF 0 LD B,B 0 LD B,C 0 LD B,D 0 LD B,E 0 LD B,H 0 LD B,L 0 LD B,(HL) 0 LD B,A 0 LD C,B 0 LD C,C 0 LD C,D 0 LD C,E 0 LD C,H 0 LD C,L 0 LD C,(HL) 0 LD C,A 0 LD D,B 0 LD D,C 0 LD D,D 0 LD D,E 0 LD D,H 0 LD D,L 0 LD D,(HL) 0 LD D,A 0 LD E,B 0 LD E,C 0 LD E,D 0 LD E,E 0 LD E,H 0 LD E,L 0 LD E,(HL) 0 LD E,A 0 LD H,B 0 LD H,C 0 LD H,D 0 LD H,E 0 LD H,H 0 LD H,L 0 LD H,(HL) 0 LD H,A 0 LD L,B 0 LD L,C 0 LD L,D 0 LD L,E 0 LD L,H 0 LD L,L 0 LD L,(HL) 0 LD L,A 0 LD (HL),B 0 LD (HL),C 0 LD (HL),D 0 LD (HL),E 0 LD (HL),H 0 LD (HL),L 0 HALT 0 LD (HL),A 0 LD A,B 0 LD A,C 0 LD A,D 0 LD A,E 0 LD A,H 0 LD A,L 0 LD A,(HL) 0 LD A,A 0 ADD A,B 0 ADD A,C 0 ADD A,D 0 ADD A,E 0 ADD A,H 0 ADD A,L 0 ADD A,(HL) 0 ADD A,A 0 ADC A,B 0 ADC A,C 0 ADC A,D 0 ADC A,E 0 ADC A,H 0 ADC A,L 0 ADC A,(HL) 0 ADC A,A 0 SUB B 0 SUB C 0 SUB D 0 SUB E 0 SUB H 0 SUB L 0 SUB (HL) 0 SUB A 0 SBC A,B 0 SBC A,C 0 SBC A,D 0 SBC A,E 0 SBC A,H 0 SBC A,L 0 SBC A,(HL) 0 SBC A,A 0 AND B 0 AND C 0 AND D 0 AND E 0 AND H 0 AND L 0 AND (HL) 0 AND A 0 XOR B 0 XOR C 0 XOR D 0 XOR E 0 XOR H 0 XOR L 0 XOR (HL) 0 XOR A 0 OR B 0 OR C 0 OR D 0 OR E 0 OR H 0 OR L 0 OR (HL) 0 OR A 0 CP B 0 CP C 0 CP D 0 CP E 0 CP H 0 CP L 0 CP (HL) 0 CP A 0 RET NZ 0 POP BC 2 JP NZ,% 2 JP % 2 CALL NZ,% 0 PUSH BC 1 ADD A,% 0 RST 0 0 RET Z 0 RET 2 JP Z,% 0 ??? 2 CALL Z,% 2 CALL % 1 ADC A,% 0 RST 8 0 RET NC 0 POP DE 2 JP NC,% 1 OUT A,(%) 2 CALL NC,% 0 PUSH DE 1 SUB % 0 RST 10H 0 RET C 0 EXX 2 JP C,% 1 IN A,(%) 2 CALL C,% 0 ??? 1 SBC A,% 0 RST 18H 0 RET PO 0 POP HL 2 JP PO,% 0 EX (SP),HL 2 CALL PO,% 0 PUSH HL 1 AND % 0 RST 20H 0 RET PE 0 JP (HL) 2 JP PE,% 0 EX DE,HL 2 CALL PE,% 0 ??? 1 XOR % 0 RST 28H 0 RET P 0 POP AF 2 JP P,% 0 DI 2 CALL P,% 0 PUSH AF 1 OR % 0 RST 30H 0 RET M 0 LD SP,HL 2 JP M,% 0 EI 2 CALL M,% 0 ??? 1 CP % 0 RST 38H