title LINKPRL - Making RSXes Easy name ('LINKPRL') ; DASMed version of H.F.Bower's LINKPRL.COM ; DASmed by W.Cirsovius ; Original labels changed for better reading... ; Some labels taken by H.F.Bower's LINKPRL article OS equ 0000h BDOS equ 0005h TPATOP equ BDOS+1 FCB equ 005ch CCP equ 0080h BUFF equ 0080h TPA equ 0100h .conout equ 2 .string equ 9 .kbdrd equ 10 .open equ 15 .close equ 16 .delete equ 19 .rdseq equ 20 .wrseq equ 21 .create equ 22 .setdma equ 26 OSerr equ -1 reclng equ 128 .drv equ 1 .nam equ 8 .ext equ 3 FCBlen equ 36 bell equ 07h tab equ 09h lf equ 0ah cr equ 0dh eot equ '$' UPPMASK equ 01011111b LOMASK equ 00001111b ALLBITS equ 11111111b INPLEN equ 32 ; Max length of console input PRLHDL equ 256 ; Length of .PRL header ld (StkSav),sp ; Save caller's stack ld sp,LocStk ; Load our local stack call pr$ID db cr,lf db 'Bit-map Linker V3.3',tab,'19 Aug 89 (C) H.F.Bower' db cr,lf,lf,eot pr$ID: pop de call String ; Tell what we are ld a,(FCB+.drv) ; Get name of file cp ' ' ; Test empty jp z,NoName ; Tell missing name cp '/' ; Test help request jp nz,GoLink ; Nope, proceed call pr$HELP db cr,lf db 'LINKPRL' db cr,lf,lf db ' Purpose:' db cr,lf db tab,'Produce COM or PRL file from MicroSoft REL' db cr,lf,lf db ' Usage:' db cr,lf db tab,'LINKPRL //',tab,'- ','Print this message' db cr,lf db tab,'LINKPRL FOO',tab,'- Link FOO.REL' db cr,lf,lf,eot pr$HELP: pop de call String ; Print help ld sp,(StkSav) ; Get back caller's stack ret ; ; Proceed linking ; GoLink: ld hl,FCB+.drv+.nam ld a,(hl) cp ' ' ; Test extension given jr nz,GotExt ; Yeap ld hl,$$REL call SetExt ; Force .REL if not GotExt: call OpenREL ; Open source file ld hl,(TPATOP) ; Get top of memory ld l,0 dec hl ld de,Temp push de or a sbc hl,de ; Calculate free memory ld c,l ld b,h pop hl ld (hl),0 ld e,l ld d,h inc de ldir ; Clear memory ld a,reclng ld (RecIdx),a ; Force read record the first time ld ix,CodStrt ; Init start of code ld iy,Control ; Init flag pointer AskType: call pr$Link db cr,lf db 'Link to .COM or .PRL (C/P) :',eot pr$Link: pop de call String ; Ask for destination type ld a,INPLEN call ReadKeyboard ; Read line ld a,(hl) ; Get character call Upcase ; Get as upper case cp 'C'-'@' ; Test abort jp z,OS ; Yeap, do it cp 'C' ; Test .COM file res 3,(iy+0) ld de,TPA ; Init laod address jr z,TypeOk ; Yeap cp 'P' ; Test .PRL file jr nz,AskType ; Wait for correct input set 3,(iy+0) ; Set bit TypeOk: push de call pr$Load db cr,lf db 'Enter Hex load addr (Default = ',eot pr$Load: pop de call String ; Ask for load address pop hl ; Get back load address call PrHL ; Print hex push hl call pr$Paren db 'H) :',eot pr$Paren: pop de call String ; Close line ld a,INPLEN call ReadKeyboard ; Read line dec hl ; Point to length of line ld b,(hl) ; Get length inc hl ex de,hl ld a,b ; Get length or a ; Test default jr z,Default ; Yeap ld hl,0 ; Clear result call ASCIIhex ; Convert ASCII to hex ex (sp),hl ; Put onto stack ld a,b or a ; Verify no error jr z,Default ; Ok ex (sp),hl call pr$HexErr db cr,lf,bell db '++ Hex Digit Error ++' db cr,lf,eot pr$HexErr: pop de call String ; Tell error pop de jr TypeOk ; Retry input ; ; ####################################################### ; # ORIGINAL CODE PART 1 FROM HAROLD F. BOWER'S ARTICLE # ; ####################################################### ; Default: pop hl ; Get address from stack ld (OrgAdr),hl ld hl,BUFF call Read ; Set bit position jr Loop0 ; ..and jump to test bit ; ;===============================================; ; M a i n P r o g r a m L o o p ; ;===============================================; ; Loop: call GetBit ; Get a source bit into position Loop0: jr nz,Loop1 ; ..jump if 1x form and test next call Byte0 ; 0 = load 8 bits absolute jr Loop ; ..and back for more ; ; We have 1x form. Check the second bit ; Loop1: call GetBit ; Get a source bit into position jr z,Loop2 ; ..jump if it is 10x form call GetBit ; We have 11x, Check 3rd bit jr nz,COMREL ; 1 = Common, 0 = Data ; ; We have 110 = Data Relative. ; call Addr16 ; Get 16 bits, data relative exx ; Do the math in alternate regs ld hl,(Temp) ; Load the offset add hl,de ; ..and add DSEG base from DE' jr Outv ; Write a 01 to Bit Map ; ; We have 111 = Common Relative. ; COMREL: call Addr16 ; Get 16 bits, common rel push hl ; Write 01 to map push de ld de,(COMMad) ; Add COMMON Base address ld hl,(Temp) ; ..to accumulated offset add hl,de pop de ex (sp),hl exx pop hl jr Outv ; Save value & write 01 to Bit Map ; ; We have 10x form. Check the third bit ; Loop2: call GetBit ; Get a bit in position jr z,Specl ; ..jump if 100 (Special Link Item) ; ; We have 101 = Program Relative. ; call Addr16 ; Get 16 bits, prog relative exx ; ..writing 01 in bit map ld hl,(Temp) add hl,bc Outv: ld a,l ; Vector here to output exx ; ..relative addresses call Byte0v ; Low byte has 0 Map Bit exx ld a,h ; Get Hi byte exx call Byte1v ; ..write with 1 Map Bit jr Loop ; ; Arrive here if special link (100xxxxxxxx) ; We don't do much with these, most just print information ; Specl: call GetTyp ; Get 4 bit type exx ; Swap to alternates to get free HL' ld hl,SplTbl ; Offset from table start add a,a ; Double value for 2-byte entries add a,l ld l,a jr nc,Specl0 ; Bypass next if no Overflow inc h Specl0: ld a,(hl) ; Addr.low to A.. inc hl ld h,(hl) ; Addr.high to H ld l,a ; Complete address to HL push hl ; Address to stack to simulate CALL exx ; ..back to primary registers ret ; Jump to Address on stack ; SplTbl: dw Entry ; 0 = Entry Symbol dw COMnam ; 1 = Common Block Name dw PgName ; 2 = Program Name dw Search ; 3 = Library Search dw Undef0 ; 4 = (undefined) dw COMMON ; 5 = Common Size dw ChnExt ; 6 = Chain External dw EntrPoint ; 7 = Entry Point dw Undef1 ; 8 = (undefined) dw ExtOff ; 9 = External + offset dw DatSiz ; 10 = Data Size dw LodLoc ; 11 = Load Location dw ChnAddr ; 12 = Chain Address dw PrgSiz ; 13 = Program Size dw Fini ; 14 = End of Program dw Fini ; 15 = Module End ; ; ##################################################### ; # END OF CODE PART 1 FROM HAROLD F. BOWER'S ARTICLE # ; ##################################################### ; ; Special link item 1.00.1110 - end module ; Special link item 1.00.1111 - end file ; Fini: bit 3,(iy+0) ; Test .COM file jr z,CloseCOM ; Yeap ld a,(Count) ; Get bit count or a ; Test bits remaining jr z,ClosePRL ; Nope FixBitMap: or a rl e ; Left justify bit map inc a cp 8 ; Test all bits filled jr c,FixBitMap ; Nope ld hl,(BitMap) ; Get pointer to bit map ld (hl),e ; Store last bit map byte inc hl ld (BitMap),hl ClosePRL: call pr$MapBeg db cr,lf db 'Bit Map begins @ ORG + ',eot pr$MapBeg: pop de call String CloseCOM: ld de,(Clen) ; Get length of program code ld hl,(Dsize) ; Get size of data area add hl,de ; Build start address of bit map bit 3,(iy+0) ; Test .COM file jr z,SkipCOM ; Yeap, skip message call PrHL ; Print hex address ld hl,(BitMap) ; Get pointer to bit map dec h ; Fix for header ld de,CodStrt ; Get start of code or a sbc hl,de ; Build end address of bit map call pr$MapEnd db cr,lf db 'Bit Map ends @ ORG + ',eot pr$MapEnd: pop de call String ; Tell address call PrHL ; Print hex address inc h ; One more page if .PRL file SkipCOM: ld a,l ; Get last page pointer and reclng-1 ; Mask it push af rl l ; Build record count ld l,h ld h,0 adc hl,hl pop af jr z,RecLenOk inc hl RecLenOk: ld c,l ; Copy records to be written ld b,h call WriteFile ; Write data to file ld de,FCB ld c,.close call BDOS ; Close file inc a ; Verify success jr nz,WBOOT ; Yeap call pr$ClosErr db cr,lf db 'Cannot Close .PRL file !',bell db cr,lf,eot pr$ClosErr: pop de call String WBOOT: jp OS ; Exit program ; ; Special link item 1.00.0000 - entry symbol ; Entry: push de call pr$ExtName db 'Entry point',tab,': ',eot ; ; Special link item 1.00.0001 - select common block ; COMnam: push de call pr$ExtName db 'Common name',tab,': ',eot ; ; Special link item 1.00.0010 - program name ; PgName: push de call pr$ExtName db cr,lf db 'Program Name',tab,': ',eot ; ; Special link item 1.00.0011 - library request ; Search: push de call pr$ExtName db 'Search Library',tab,': ',eot ; ; Special link item 1.00.0100 - extension MS-LINK ; Undef0: push de call pr$ExtName db cr,lf,'UNDEFINED',tab,': ',eot pr$ExtName: pop de call String ; Print string pop de call BField ; Tell name jp MapREL ; ; Special link item 1.00.0101 - define common size ; COMMON: call AField ; Read address field push de call pr$ExtSize db 'COMMON',tab,tab,': ',eot pr$ExtSize: pop de call String ; Tell type of size call BField ; Tell name call pr$Assign db ' = ',eot pr$Assign: pop de call String ; Indicate assignment pop de call PrAddr ; Print address read ld hl,(Temp) ; Get address read ld (COMMad),hl ; Save it jp CloseREL ; ; Special link item 1.00.0110 - chain external ; ChnExt: call AField ; Read address field push de call pr$ExtSize db 'Chain EXTERNAL',tab,': ',eot ; ; Special link item 1.00.0111 - define entry point ; EntrPoint: call AField ; Read address field push de call pr$ExtSize db 'Entry Point',tab,': ',eot ; ; Special link item 1.00.1000 - external minus offset ; Undef1: call AField ; Read address field push de call pr$ExtSize db 'Undefined !!!',tab,': ',eot ; ; Special link item 1.00.1001 - external plus offset ; ExtOff: push de call pr$ExtValue db 'External + Offset = ',eot ; ; Special link item 1.00.1100 - chain address ; ChnAddr: push de call pr$ExtValue db 'Chain Address',tab,'= ',eot pr$ExtValue: pop de call String ; Tell address mode pop de call PrAField ; Read and print address jr MapREL ; ; Special link item 1.00.1010 - define data size ; DatSiz: push de call pr$DatSize db 'Data Area Size',tab,': ',eot pr$DatSize: pop de call String ; Tell size type pop de call PrAField ; Read and print size ld (Dsize),hl ; Store size of data area set 0,(iy+0) ; Indicate data size read jr MapREL ; ; Special link item 1.00.1101 - define program size ; PrgSiz: push de call pr$PrgSize db 'Program Size',tab,': ',eot pr$PrgSize: pop de call String pop de call PrAField ; Read and print size ld (Clen),hl ; Store length of program code set 1,(iy+0) ; Indicate program size read MapREL: call CalcMapAd ; Calculate address of bit map CloseREL: call NewLine ; Put new line on console jp Loop ; ; Special link item 1.00.1011 - set location counter ; LodLoc: call AField ; Read address field ld (LocPC),hl ; Save address bit 7,(iy+0) ; Test load address determined jr nz,_ByteFill ; Yeap bit 3,(iy+0) ; Test .COM file jr z,_ByteFill ; Yeap push de call pr$Location db 'Load Location',tab,': ',eot pr$Location: pop de call String ; Tell load address call PrAddr ; Print address read pop de call NewLine ; Put new line on console _ByteFill: jp ByteFill ; Fill bytes ; ; Read name field and print on console ; BField: ld d,3 xor a call RdRELxbit ; Read length or a ; Test 1..7 jr nz,Blen ; Yeap ld a,8 ; Map 0 -> 8 Blen: ld d,a ; Set length Bname: push de ld d,8 call RdRELxbit ; Read character call Conout ; Print it pop de dec d jr nz,Bname ret ; ; Read and print address ; PrAField: call AField ; Read address field ; ; Print address read ; PrAddr: ld hl,(Temp) ; Get address read ; ; Print value in HL as hex ; PrHL: ld a,h ; Get hi call prHexByte ; Print as hex ld a,l ; Then lo jp prHexByte ; ; Read next bit from .REL file, Z flag shows bit state ; GetBit: rl c ; Shift bit into right place djnz RdREL$0_1 ; Not all attached ; ; Read first bit from .REL file, Z flag shows bit state ; Read: push af push de ld a,(RecIdx) ; Get record index cp reclng ; Test record scanned call nc,ReadRec ; Read record from file if so ld e,a ld d,0 ld hl,BUFF add hl,de ; Build buffer address inc a ld (RecIdx),a ; Update index ld c,(hl) ; Get bits ld b,8 ; Init count pop de pop af RdREL$0_1: bit 7,c ; Get state of bit ret ; ; Read address field ; AField: xor a ld d,2 call RdRELxbit ; Read address type ld (AdrType),a ; Save it ; ; Read address ; Addr16: call GetByt ; Read lo byte ex af,af' call GetByt ; Read hi byte ld h,a ex af,af' ld l,a ld (Temp),hl ; Save address ret ; ; ####################################################### ; # ORIGINAL CODE PART 2 FROM HAROLD F. BOWER'S ARTICLE # ; ####################################################### ; ; Output a byte with a 1 Map Bit ; Byte1v: scf ; Set Carry flag for 1 in Bit Map jr ChkWrt ; ..and do it ; ; Accumulate 8 bits into a byte and output with a 0 Map Bit ; Byte0: call GetByt ; Gather 8 bits into a byte Byte0v: or a ; Reset Carry for 0 in Bit Map ;..fall thru to.. ; ; Check for output write status on Map Bit ; Carry Flag unaffected until shifted into E Register ; ChkWrt: bit 2,(iy+0) ; Check ok-to-load jr z,ChkWr2 ; ..Error if flag = 0 ld (ix+0),a ; Save Code byte inc ix ; ..and bump code pointer push hl ; Preserve regs ld hl,(Pcntr) inc hl ; Increment Pseudo-Program Counter ld (Pcntr),hl rl e ; Rotate Map from Carry into E ld hl,Count inc (hl) ; Bump count.. bit 3,(hl) ; ..check = 8? jr z,ChkWr1 ; Exit if < 8 ld (hl),0 ; ..else reset counter to 0 ld hl,(BitMap) ; Write 8 Map bits out ld (hl),e inc hl ; ..bumping address ld (BitMap),hl ld e,0 ; Preset next map byte to 0 ChkWr1: pop hl ; Restore regs ret ; ChkWr2: call Errv ; Print message & Abort db cr,lf,bell db 'Write attempt before areas sized !',eot ; ; ##################################################### ; # END OF CODE PART 2 FROM HAROLD F. BOWER'S ARTICLE # ; ##################################################### ; ; Read control bits ; GetTyp: ld d,4 xor a jr RdRELxbit ; Read control bits ; ; Read byte from .REL file ; GetByt: ld d,8 ; Set bit count ; ; Read D bits from .REL file ; RdRELxbit: call GetBit ; Read next bit from .REL file scf jr nz,RdRELlop ; Shift 1 in ccf ; Change to 0 RdRELlop: rla ; Shift bit in dec d jr nz,RdRELxbit ret ; ; Fill bytes ; ByteFill: push hl push bc push de ld hl,(LocPC) ; Get address of location counter setted to ld a,(AdrType) ; Get address type cp 10b ; Test range jr c,FillCSEG ; 00 or 01 (ASEG or CSEG) jr z,FillDSEG ; 10 (DSEG) ld de,(Dsize) ; Got 11b (COMMON), load size of data area add hl,de FillDSEG: ld de,(Clen) ; Get length of program code add hl,de FillCSEG: set 7,(iy+0) ; Indicate load address determined ld de,(Pcntr) ; Get byte count or a sbc hl,de ; Calculate free area pop de FillIt: ld a,h or l jr z,FillDone call Byte0v ; Store byte and do not touch bit map dec hl jr FillIt FillDone: pop bc pop hl jp Loop ; ; Calculate address of bit map ; CalcMapAd: bit 0,(iy+0) ; Test data size read ret z ; Nope bit 1,(iy+0) ; Test program size read ret z ; Nope set 2,(iy+0) ; Indicate sizes read push hl push de exx ld bc,(OrgAdr) ; Get load address ld hl,(Clen) ; Get length of program code add hl,bc ex de,hl exx ld de,(Clen) ; Get length of program code ld hl,(Dsize) ; Get size of data area add hl,de ; Build length of file bit 3,(iy+0) ; Test .COM file jr z,ComMap ; Yeap ld (ix+1),l ; Store length into .PRL header ld (ix+2),h ld ix,CodStrt+PRLHDL ld de,PRLHDL ; Change program counter add hl,de ; Add PRL page to length ComMap: ld de,CodStrt ; Get start of code add hl,de ; Build address ld (BitMap),hl ; Set pointer to bit map pop de pop hl ret ; ; Open source file ; OpenREL: ld de,FCB ld c,.open call BDOS ; Open file cp OSerr ; Test success ret nz ; Yeap call Errv ; Give error message db '++ Cannot Open File ++' db cr,lf,bell,eot ; ; Read record from file ; ReadRec: ld de,BUFF ld c,.setdma call BDOS ; Set disc buffer ld c,.rdseq ld de,FCB call BDOS ; Read record or a ; Test success ret z ; Yeap jr WrtErr ; Ups, eraly end of file ; ; Write data to file ; WriteFile: push bc ; Save record count bit 3,(iy+0) ; Test .COM file ld hl,$$COM jr z,InitFileExt ; Yeap ld hl,$$PRL InitFileExt: call SetExt ; Set .COM or .PRL ex de,hl ld b,FCBlen-(.drv+.nam+.ext) InitFile: ld (hl),0 ; Clear remainder of FCB inc hl djnz InitFile ld de,FCB push de ld c,.delete call BDOS ; Delte file pop de ld c,.create call BDOS ; Create file pop bc inc a ; Test ok jr z,DirErr ; Creation error ld de,reclng ld hl,CodStrt ; Init start of code WrLoop: ld a,b or c ; Test all written ret z ; Yeap push hl push de push bc ex de,hl ld c,.setdma call BDOS ; Set dusc buffer ld de,FCB ld c,.wrseq call BDOS ; Write record to file or a ; Verify success jr nz,WrtErr ; Nope write error pop bc pop de pop hl dec bc add hl,de ; Advance buffer address jr WrLoop WrtErr: call Errv db cr,lf db '++ ERROR writing file ++',bell db cr,lf db '...Aborting !',eot ; ; Give error message and give up ; Errv: pop de call String ; Give message jp WBOOT ; Give up ; ; Tell no free space on disc ; DirErr: call Errv db cr,lf db bell,'Directory Full - Abort !',eot ; ; Tell missing file name and abort ; NoName: call Errv db cr,lf db 'No File Name !' db cr,lf,bell,eot ; ; Read line from keyboard ; Returns pointer to 1st character ; ReadKeyboard: ld de,CCP push de ld (de),a ; Store max length ld c,.kbdrd call BDOS pop hl inc hl inc hl ret ; ; Put string ^DE to console ; String: push hl push bc ld c,.string call BDOS ; Print pop bc pop hl ret ; ; Put character in Accu to console ; Conout: push hl push bc ld e,a ld c,.conout call BDOS ; Put to console pop bc pop hl ret ; ; Set extension ^HL ; SetExt: ld de,FCB+.drv+.nam ld bc,.ext ldir ; Simple move ret ; ; Convert ASCII to hex ; Z set on valid input ; ASCIIhex: ld a,(de) ; Load character call Upcase ; Get as upper case call ASCIIdig ; Convert to digit jr c,ASCIIhexErr ; Invalid push de ld e,a ld d,0 add hl,hl ; Old *16 add hl,hl add hl,hl add hl,hl add hl,de ; Insert digit pop de inc de djnz ASCIIhex ret ASCIIhexErr: or ALLBITS ret ; ; Convert character to upper case ; Upcase: cp 'a' ; Test lower case ret c cp 'z'+1 ret nc and UPPMASK ; Convert if so ret ; ; Convert hex ASCII character to digit ; C set on error ; ASCIIdig: sub '0' ; Strip off ASCII offset ret c ; Invalid cp 9+1 ; Test decimal ccf ret nc ; Yeap sub 'A'-10-'0' ; Fix it for hex cp 10 ; Verify correct range ret c cp 15+1 ccf ret ; ; Put new line on console ; NewLine: push hl push de push bc ld a,cr call Conout ; Simple line closure ld a,lf call Conout pop bc pop de pop hl ret ; ; Print byte as ASCII ; prHexByte: push hl push de push bc push af rrca ; Get hi bits rrca rrca rrca call Hexdig ; Print pop af call Hexdig ; Then lo bits pop bc pop de pop hl ret ; ; Print hex nibble as ASCII ; Hexdig: and LOMASK ; Mask bits add a,90h ; Convert to ASCII daa adc a,40h daa jr Conout ; Print it ; $$REL: db 'REL' $$COM: db 'COM' $$PRL: db 'PRL' ; ds 2*32 LocStk equ $ ; StkSav: ds 2 ; Stack save location ; ; Dynamic data area begins ; Temp: ds 2 ; Address read from .REL file COMMad: ds 2 ; COMMON address ds 2 Clen: ds 2 ; Length of program code Dsize: ds 2 ; Size of data area Count: ds 1 ; Bit count in bit map BitMap: ds 2 ; Pointer to bit map Pcntr: ds 2 ; Byte count LocPC: ds 2 ; Address of location counter AdrType: db 00b ; Type of address ; ; Control flags ; ; 76543210 Meaning ; -------- -------------------------- ; 00000001 Data size read ; 00000010 Program size read ; 00000100 Program and data size read ; 00001000 .PRL file selected ; Control: db 00000000b ; Flag OrgAdr: ds 2 ; Load address RecIdx: ds 1 ; Record index of .REL file ; ; Code starts here ; CodStrt equ $ end