title Checking .REL file format name ('REL') maclib base80 maclib reldef ; Test and display .REL file ; Copyright (C) Werner Cirsovius ; Hohe Weide 44 ; D-20253 Hamburg ; Tel.: 040/4223247 ; Version 2.4 September 1997 $$VER macro db '2.4' endm $$PRG macro db 'REL' endm ; Call it: REL filename{.ext} {module} {[option]} {>log_file} ; Default extension is .REL ; may be selected if filename is a library ; [option] may be [B] for switching to binary ; output instead of text output and/or [P] for ; page mode for pausing after one screen ; Output may be redirected ; ===== LIB linkage ===== ext open,string,ascbyt,indexa,combrk,cmdarg ext parse,getver,dskget,wcard,scbpb,scbfun ext bit1,bit2,bit3,bit4,bit8,bit16,bitcnt,biterr ext AField,BField,dskpc ; ; REDIR structure: REDIR-2 File desriptor ; ext redir,LOGfile,Fconout,Fstring,Fcrlf,FOS .string equ 9 MAX$ARG equ 3 _Width equ 1ah _PagLen equ 1ch MOD$LEN equ 8 ; ===== Start program ===== illCPU: ld de,$ILL.CPU ld c,.string call BDOS ; Tell invalid machine jp OS REL: sub a ; Test right machine jp pe,illCPU ; .. nope ld sp,LocStk ld de,$VERS call string ; Give version call getver ; Verify CP/M 3.x ld de,$ILL.OS jp c,comstr ld hl,rederr ld (biterr),hl ; Set error linkage ld hl,help push hl ld a,(CCPlen) ; Test file here or a ret z ; .. must be call redir ; Set redirection ld de,CCPlen ld b,MAX$ARG ld hl,ARGV call cmdarg ; Get arguments ld (ARGC),a ; .. save count ret c ; .. error pop hl ; .. clean stack ld hl,BegREL push hl ; Set return ld hl,ARGVex-2 call indexa ; .. get pointer ex de,hl jp (hl) ; .. parse line BegREL: ld a,_Width call SCB ld (Width),a ; Save columns on screen call LOGfile ; Activate logging ld hl,ENOUT ld a,(hl) push af ; Save state of enable ld (hl),TRUE ; .. enable ld de,$STRT call strchk ; Tell running pop af ; .. get old state of enable ld (hl),a ; .. reset ld a,FALSE ld (ConstCode),a ; Reset code flag ld hl,dskget ld (dskpc),hl ; Enable disk I/O newbit: ld a,-1 ld (bitcnt),a ; Init bit count ; ; Decode the bitstream ; loop: call bit1 ; Get a bit jr c,ctrl1 ; It's special call bit8 ld de,$DATSEP call print8 ; Print byte call inccol ; Increment column ld a,TRUE ld (ConstCode),a ld bc,1 call incPC ; Increment PC jr loop ; ; First bit is not 0, test next two bits ; ctrl1: xor a ld (Col),a ; .. clear count ld a,(ConstCode) ; Test previous code cp TRUE jr nz,nocod ld a,(BinMode) ; Test binary cp TRUE ld de,$DATA8 call nz,strchk ; Tell constants call .Fcrlf ld a,FALSE ld (ConstCode),a nocod: call bit2 ; Get control bits ld c,a or a ; Test special jr z,ctrl2 ld a,(actSEG) push af ; Save segment ld a,c ld (actSEG),a ; Set new segment ld a,(BinMode) cp TRUE ; Test mode jr nz,@bin1 ld a,2 call prbit ; Print the bits call pri16 ; Get and print bits ..loop: ld bc,2 call incPC ; Increment PC pop af ld (actSEG),a ; Reset segment jr loop @bin1: dec c ; Fix index call bit16 ; Get word ld (Word),hl ld hl,tab2 call indx16 ; Get pointer, print 16 bit jr ..loop ; ; The observed two bits are 00, await four control bits ; ctrl2: ld a,(BinMode) ; Test to print bits cp TRUE ld a,2 call z,prbit ; Do it, if requested call bit4 ; Get control bits push af call prCTRLstate ; Print control state pop af ld hl,ctrltb call indexa ; Get index ex de,hl jp (hl) ; -->> Jump ; ; Print the control state ; ENTRY Accu holds state ; prCTRLstate: ld c,a ld a,(BinMode) ; Test binary cp TRUE jr nz,@bin2 ld a,4 call prbit ; Print the bits ret @bin2: ld a,c ld hl,tab4 call indexa call strchk ; Print control string ret ; Control code decoder ; ; Code 0000, 0001, 0010, 0011, 0100 ; Format : Name field ; field0: field1: field3: f.0_4: call .BF.getN ; Get field ld a,FALSE ; Set normal call .BF.putN ; Put field jp loop ; ; Name of module - used for syncing display ; field2: call .BF.getN ; Get field call Oenable ; Enable output on match push hl ld a,0010b call z,prCTRLstate ; Print control state again pop hl ld a,FALSE ; Set normal call .BF.putN ; Put field jp loop ; ; Special MS item ; field4: call .BF.getN ; Get field ld a,TRUE ; Set special call .BF.putN ; Put field jr MS.item ; ; Code 0101, 0110, 0111 ; Format : Value field - name field ; field5: field6: field7: call .AField ; Print code and 16 bit ld a,' ' call conchk jr f.0_4 ; Then name ; ; Code : 1000, 1001, 1010, 1011, 1100, 1101 ; Format : Value field ; field8: call GivePC ; Tell program counter call .AField MS.item: ld de,$MS.SPEC ; Indicate special MS-Soft call strchk call .Fcrlf jp loop fieldb: call .AField ; Get code call setPC jp loop field9: fielda: fieldc: fieldd: call .AField ; Print code jp loop ; ; Code : 1110 ; Format : Value field (Reset to byte boundary) ; fielde: call .AField ; Print code call Odisabled? ; .. test output disabled jr z,fieldf ; Fall into total end ld de,$EOM ; End of module call strchk call .Fcrlf jp newbit ; Set byte boundary ; ; Code : 1111 ; Format : End of file ; fieldf: ld a,TRUE ld (ENOUT),a ; Force final output call .Fcrlf call .Fcrlf call modmatch? ; Call module matched ld de,$EOREL ; End of file jr z,..fF ld de,$NOMODULE ..fF: call strchk call .Fcrlf jp FOS ; ; ===== Subroutines ===== ; ; Error handler ; help: ld de,$HELP jr comstr ; .. give help rederr: ld de,$FILERR ; Simple error message comstr: call Fstring call Fcrlf jp FOS ; ; Get two control bit fields and print the meaning ; Then get and print 16 bit ; .AField: call AFIeld ; .. get values ld (actSEG),a ; Set segment ld c,a ld a,(BinMode) ; Test mode cp TRUE jr nz,@bin4 ld a,2 call prbit ; Print bits call .pri16 ; Then 16 bit ret @bin4: ld (Word),hl ld hl,tabc2 indx16: ld a,c ; Get index call indexa ld hl,(Word) call prnt16 ; Print it ret ; ; Process the BField ; ; Part 1 : Get the BField ; EXIT Reg HL points to name ; .BF.getN: ld hl,$NAME push hl call BField ; Get name pop hl ret ; ; Part 2 : Put the BField ; ENTRY Reg HL points to name ; Accu holds TRUE if special link ; .BF.putN: push af ; .. save flag ld d,(hl) ; Get three bits as count ld a,(BinMode) ; Test mode cp TRUE ld c,d ld a,3 call z,prbit ; Print as bits pop af cp TRUE jr z,spec.MS ; Check special ld a,'"' call conchk ld b,d ; .. get length call PrName ; .. print name ld a,'"' call conchk call .Fcrlf ret ; ; Get special MS-link ; spec.MS: inc hl ld b,d ; Save length ld de,$SPC.TYP call strchk ld a,'''' call conchk ld a,(hl) call GetASCII call conchk ; Print the type ld a,'''' call conchk ld a,(hl) call tell.spec dec b push hl push bc spc.bin.loop: inc hl ld a,(hl) ; Get character ld de,$DATSEP call print8 ; Print djnz spc.bin.loop ld de,$SEPAR call strchk pop bc pop hl ld a,(hl) cp 'C' ; Test type C jr z,spc.C. cp 'A' ; Test type A jr z,spc.A. spc.ASC.loop: call PrName call .Fcrlf ret spc.A.: ld a,b cp 1 ; Test proper length jr nz,spc.ASC.loop inc hl ld a,(hl) ; .. get type ld de,$SPC.? cp 8+1 ; Check max jr nc,spc.A.in.val ld hl,SpcTab call indexa ; Get function spc.A.in.val: call _Fstring ; .. print call .Fcrlf ret spc.C.: ld a,b cp 3 ; Test proper length jr nz,spc.ASC.loop inc hl ld a,(hl) ; Get type push hl ld hl,tabc2 call indexa ; Get address type inc de ; .. skip delimiters inc de call _Fstring ; .. print pop hl inc hl ld a,(hl) ; .. get value inc hl ld h,(hl) ld l,a ld de,$DATSEP jr prnt16 ; .. print ; ; The prxxx package, get either 16 or 8 bits and print it ; pri16: call bit16 ; Fetch bits .pri16: ld de,$DUMMY ; Set dummy prnt16: call pr16 call .Fcrlf ret pr16: ld a,h ld (HexWrd),a ; Save hi ld a,l ld (HexWrd+1),a ; And low ld a,2 jr compr print8: ld (HexWrd),a ; Save byte ld a,1 compr: push bc push de push hl ld b,a ; Set count ld de,$ASC.BUF ld hl,HexWrd ; Init buffers prlop: call ascbyt ; Convert to ASCII djnz prlop ex de,hl ld (hl),eot ld de,$ASC.BUF call strchk ; Print pop hl pop de pop bc call strchk ; Entry string follows ret ; ; Print bits as binary stream ; ENTRY Accu holds number of bits to be printed ; Reg C holds bit(s) to be printed ; prbit: ld b,a ; Get count ld a,c ; Get bits ld c,b ; And count twice pshbit: rra ; Get a bit ld e,a ld a,'0' ; Get "0" or "1" adc a,0 push af ; Onto stack, cause it's LSB ld a,e djnz pshbit popbit: pop af ; Now get and print call conchk dec c jr nz,popbit ld a,'.' call conchk ; Close output ret ; ; Tell type of special MS-LINK item ; ENTRY Accu holds type ; tell.spec: push bc push hl ld hl,$SPEC.TAB ld bc,SpcLen cpir ; Find character jr z,spc.fnd ld bc,SpcLen ; Set unknown spc.fnd: ld a,c cp SpcLen-1 ; Test special arithmetic ld de,$ARITH call c,strchk ; .. special prefix ld hl,SpcTxt add hl,bc ; Set pointer add hl,bc ld e,(hl) inc hl ld d,(hl) call strchk ; .. type call .Fcrlf pop hl pop bc ret ; ; Increment actual segment ; ENTRY Reg BC holds incremental value ; incPC: call getSEG ; Get segment pointer add hl,bc ; .. add jr PC.. ; ; Set segment location counter ; ENTRY Reg HL holds value ; setPC: push hl call getSEG ; Get segment pointer pop hl PC..: ld (ix+0),l ; Set new value ld (ix+1),h ret ; ; Get pointer to segment ; EXIT Reg IX holds pointer ; Accu holds code type ; Reg HL holds value ; getSEG: push bc ld bc,(actSEG) ; Get index ld a,c ld hl,SEGptr add hl,bc ; .. to pointer add hl,bc pop bc call getIX getIX: push hl pop ix ld l,(ix+0) ; Fetch value ld h,(ix+1) ret ; ; Tell actual program counter ; GivePC: ld de,$ATTENT call strchk ; Give attention call getSEG ld c,a call @bin4 ; .. print PC ret ; ; Print string, test BREAK ; ENTRY Reg pair DE holds string ; strchk: call _Fstring ; Print jr @c$chk ; ; Print character, test BREAK ; ENTRY Accu holds character ; conchk: call _Fconout ; Print @c$chk: call combrk ; Test BREAK ret nc ; No, exit IsBreak?: cp CtrlC ; Test CTRL-C ret nz ld de,$BREAK call string ; Tell BREAK jp FOS ; HARD STOP ; ; Print string if enabled ; ENTRY Reg pair DE holds string ; _Fstring: call IO.ena? ; Test enabled call nz,Fstring ; .. yeap ret ; ; Print character if enabled ; ENTRY Accu holds character ; _Fconout: call IO.ena? ; Test enabled call nz,Fconout ; .. yeap ret ; ; Print new line if enabled ; _Fcrlf: call IO.ena? ; Test enabled call nz,Fcrlf ; .. yeap ret ; ; Test output enabled ; EXIT Zero flag set if not ; IO.ena?: push hl ld hl,ENOUT ; Test enabled bit 0,(hl) pop hl ret ; ; Enable output on match ; ENTRY Reg HL points to name ; EXIT Zero flag set indicates output just enabled ; Oenable: ld a,(MODSRC) ; Test to be searched cp TRUE ret nz ; .. nope ld de,$MODULE ; Point to name to start with ld a,(de) cp (hl) ; Test same length ret nz ; .. nope push hl cp 0 ; Test zero jr nz,Olen ld a,MOD$LEN ; .. map length Olen: ld b,a inc b Ocmp: ld a,(de) cp (hl) ; Compare jr nz,O.NE inc hl inc de djnz Ocmp O.NE: pop hl ret nz ; .. no match ld a,TRUE ld (ENOUT),a ; .. enable output ld (SMATCH),a ; .. set match ret ; ; Test output disabled ; EXIT Zero flag set says yes ; Odisabled?: ld a,(MODSRC) ; Test to be searched cp TRUE ret nz ; .. nope ld a,(ENOUT) ; .. test output enabled cp TRUE ret ; ; Test match on search of module ; EXIT Zero flag set says yes ; modmatch?: ld a,(MODSRC) ; Test to be searched cp FALSE ret z ; .. nope ld a,(SMATCH) ; .. test match cp TRUE ret ; ; Unpack module name ; ENTRY Reg pair HL points to module parameter ; module: ld de,1+$MODULE ; Point to name start ld b,MOD$LEN ; .. set max length ld c,0 ; Clear count ..mod: ld a,(hl) ; Get character or a ; Test end jr z,mod.st ; .. yeap ld (de),a ; .. unpack inc de inc hl inc c ; Update length djnz ..mod LenErr: ld de,$TOOLONG jp comstr ; .. error mod.st: ld a,c ; Get length or a ; Verify not empty jr z,LenErr cp MOD$LEN ; Test max jr nz,mod.stL ld a,0 ; Map 8 -> 0 mod.stL: ld ($MODULE),a ; Save length ld a,TRUE ld (MODSRC),a ; .. select module cpl ld (ENOUT),a ; .. disable output ret ; ; Print ASCII name from buffer ; ENTRY Reg HL points to name-1 ; Reg B holds length ; PrName: inc hl ld a,(hl) ; Get character call GetASCII ; Get valid ASCII call conchk ; Print djnz PrName ret ; ; Get valid ASCII character ; ENTRY Accu holds character ; EXIT Accu mapped to valid character ; GetASCII: cp ' ' ; Test control jr c,spc.Ctrl cp '~'+1 ; .. and 8 bit ret c spc.Ctrl: ld a,'*' ; Indicate hex ret ; ; Give new line, check screen filled ; .Fcrlf: call _Fcrlf ; .. new line ld a,(PageMode) cp TRUE ; Test check page ret nz ; .. nope ld a,(Line) inc a ld (Line),a ; .. bump line ld b,a ld a,(PageLen) cp b ; Test screen filled ret nc ; .. nope ld de,$MORE call string ; Tell more wtKey: call combrk ; Wait for key jr nc,wtKey call IsBreak? ; .. watch for break ld de,$BS call string xor a ld (Line),a ; Clear line ret ; ; Increment screen column ; inccol: ld a,(Col) ; Get column add a,3 ; .. advance ld (Col),a add a,3 ld b,a ld a,(Width) cp b ; Test line filled ret nc ; .. nope call .Fcrlf ; .. close line xor a ld (Col),a ; Clear column ret ; ; Get SCB value - Offset in Accu - Return HL,Accu ; SCB: ld (scbpb),a ; Set offset call scbfun ; Get value ret ; ; The command line parser ; ; Got three parameters of the form ; FILE NAME [OPTION] ; CCP.3: ld hl,(ARGV+2*(MAX$ARG-1)) ld a,(hl) ; Verify option cp '[' jp nz,help ; .. must be call ..opt ; Get option jr ..module ; .. get module ; ; Got two parameters of the forms ; FILE [OPTION] ; FILE NAME ; FILE NAME[OPTION] ; CCP.2: ld hl,(ARGV+2*(MAX$ARG-2)) ld a,(hl) ; Test option cp '[' jr nz,module? ; .. nope, get module call ..opt ; Get option jr getFN ; .. then file module?: call prcopt ; Process option if there ..module: ld hl,(ARGV+2*(MAX$ARG-2)) call module ; .. unpack module jr getFN ; .. get file ; ; Got one parameters of the forms ; FILE ; FILE[OPTION] ; CCP.1: ld hl,(ARGV) ; Get 1st parameter call prcopt ; Process option if there ; ; Now get file name ; getFN: ld hl,(ARGV) ; Get 1st parameter ld (FIB),hl ; .. into block ld de,FIB call parse ; Parse file ld de,$ILLFIL jp c,comstr ; .. error ld a,h ; Verify end of string or l jp nz,comstr ; .. should be ex de,hl ld de,FCB call wcard ; Verify no wild card ex de,hl jp z,comstr ld de,FCBext ld a,(de) ; Test extension cp ' ' jr nz,noex ld hl,$REL ld bc,.fext ldir ; Set .REL noex: ld de,FCB call open ; Find file ret nc ; .. error ld de,$NOFIL jp comstr ; Should be here ; ; Test if option embedded in string ^HL and process if so ; prcopt: ld a,(hl) ; Get character or a ; Test end ret z ; .. yeap cp '[' ; Test option jr z,opt.fnd ; .. yeap inc hl jr prcopt opt.fnd: ld (hl),null ; Overwrite '[' ..opt: inc hl ld a,(hl) ; Fetch option cp 'B' ; Test verbose jr nz,optP? call opt.B ; .. do it jr z,opt.ex ; .. no more cp 'P' ; Verify page mode jr nz,noop call opt.P ; Do it jr z,opt.ex ; .. verify end noop: ld de,$ILLOP jp comstr optP?: cp 'P' ; Verify page mode jr nz,noop call opt.P ; Do it jr z,opt.ex ; .. end cp 'B' ; Test verbose jr nz,noop call opt.B ; .. do it jr nz,noop ; .. verify end opt.ex: inc hl ld a,(hl) or a ; Verify real end jr nz,noop ; .. verify end ret ; ; Execute option P ; opt.P: push hl ld a,TRUE ld (PageMode),a ; Set mode ld a,_PagLen call SCB ; .. get page length dec a dec a ld (PageLen),a ; .. save pop hl jr opt.? ; ; Execute option B ; opt.B: ld a,TRUE ld (BinMode),a ; Switch mode opt.?: inc hl ld a,(hl) ; Test end cp ']' ret dseg $VERS: $$PRG db ' ' $$VER db cr,lf,lf,eot $HELP: db 'Call it:',tab,'REL filename{.ext} {module}' db ' {[option]} {>log_file}',cr,lf,lf db tab,tab db ' may be selected if filename is a library' db cr,lf,lf db tab,tab,'Default extension is .REL',cr,lf,lf db tab,tab,'[option] may be [B] for switching ' db 'to binary',cr,lf db tab,tab,'output instead of text output ' db 'and/or [P] for',cr,lf db tab,tab db 'page mode for pausing after one screen' db cr,lf,lf db tab,tab,'Output may be redirected',cr,lf,eot $ILL.OS: db 'Requires CP/M 3.x',eot $ILL.CPU: db 'Requires Z80 CPU',eot $ILLFIL: db 'File name illegal',eot $ILLOP: db 'Option [B] or [P] expected',eot $TOOLONG: db 'Invalid length of module name',eot $NOFIL: db 'File not present',eot $FILERR: db cr,lf,'Abnormal termination',eot $EOM: db '############### END OF MODULE ###############' db eot $EOREL: db 'Normal end of file',eot $NOMODULE: db 'Selected module not found',eot $STRT: db 'Analyzing ...',cr,lf,lf,eot $DUMMY: db eot $BREAK: db cr,lf,lf,'*** USER BREAK ***',cr,lf,eot $MORE: db 'Press RETURN for more',eot $BS: db cr,' ',cr,eot $REL: db 'REL' $DATSEP: db ' ',eot $DATA8: db ' Constant byte',eot $TAB21: db ' Code ',eot $TAB22: db ' Data ',eot $TAB23: db ' Common ',eot $TABC20: db ', Abs ',eot $TABC21: db ', Prg rel ',eot $TABC22: db ', Dat rel ',eot $TABC23: db ', Com rel ',eot $TAB40: db 'Entry ',eot $TAB41: db 'Common ',eot $TAB42: db 'Module ',eot $TAB43: db 'Lib req ',eot $TAB44: db 'Ext MSLnk ',eot $TAB45: db 'Com size ',eot $TAB46: db 'Chn ext ',eot $TAB47 equ $TAB40 $TAB48: db 'Ext -offs ',eot $TAB49: db 'Ext +offs ',eot $TAB4a: db 'Data size ',eot $TAB4b: db 'Loc cnt ',eot $TAB4c: db 'Chn adr ',eot $TAB4d: db 'Prg size ',eot $TAB4e: db 'Strt adr ',eot $TAB4f: db '>> End of code <<',eot $SPC.TYP: db 'Subtype found : ',eot $MS.SPEC: db '[ *** Special MS-Soft item *** ]',eot $SPEC.TAB: db '5ABC' SpcLen equ $-$SPEC.TAB SpcTxt: dw $SPC.C,$SPC.B,$SPC.A,$SPC.5,$SPC.? $ARITH: db ', Arithmetic Fixup (',eot $SPC.C: db 'Area Base + Offset)',eot $SPC.B: db 'External Reference)',eot $SPC.A: db 'Arithmetic operator)',eot $SPC.5: db ', COBOL Overlay Segment Sentinel',eot $SPC.?: db ' *** Undocumented Function ***',eot $SEPAR: db ' ->> ',eot $ATTENT: db '*** Attention, at location counter ',eot $STB: db 'Store Byte',eot $HIGH: db 'HIGH',eot $LOW: db 'LOW',eot $SUB: db 'SUB',eot $ADD: db 'ADD',eot ARGVex: dw CCP.1,CCP.2,CCP.3 SpcTab: dw $SPC.?,$STB,$SPC.?,$HIGH,$LOW dw $SPC.?,$SPC.?,$SUB,$ADD tab2: dw $TAB21,$TAB22,$TAB23 tabc2: dw $TABC20,$TABC21,$TABC22,$TABC23 ctrltb: dw field0,field1,field2,field3 dw field4,field5,field6,field7 dw field8,field9,fielda,fieldb dw fieldc,fieldd,fielde,fieldf tab4: dw $TAB40,$TAB41,$TAB42,$TAB43 dw $TAB44,$TAB45,$TAB46,$TAB47 dw $TAB48,$TAB49,$TAB4a,$TAB4b dw $TAB4c,$TAB4d,$TAB4e,$TAB4f actSEG: dw 0 ; 0,1 CSEG ; 2 DSEG ; 3 COMMON SEGptr: dw CSEG.PC,CSEG.PC,DSEG.PC,COM.PC CSEG.PC: dw 0 DSEG.PC: dw 0 COM.PC: dw 0 FIB: dw CCP,FCB BinMode: db FALSE ; Binary mode ConstCode: ds 1 ; Constant flag HexWrd: ds 2 ; Hex bytes $ASC.BUF: ds 4+1 ; ASCII hex Word: ds 2 $NAME: ds 8+1 PageMode: db FALSE Line: db 0 PageLen: ds 1 Col: db 0 Width: ds 1 ARGV: ds 2*MAX$ARG ARGC: ds 1 $MODULE: ds 1+MOD$LEN MODSRC: db FALSE ENOUT: db TRUE SMATCH: db FALSE ; ds 2*48 LocStk: end REL