; LOCATE.ASM - Relocator Program by George D. Massar - Version 1.0 ; ; To generate a relocatable program to be located at the offset below ; the base address of the CP/M BDOS. ; ; NOTE: The program is in the public domain, but not to be ; used for commercial benefit. DONOT remove the ; author's name and his credit. ; ; Command synthax: ; ; LOCATE [(-/+)] ; ; where may be optionally chosen below or above ; the base of BDOS expressing respectively in negative ; or positive hex-value with its sign (required). The ; value must be on the page boundary. ; ; Input file(s) expected: .COZ (ORG 000H) ; .COM (ORG 100H) ; ; Output file(s) generated: .COM (relocatable) ; ; Please see LOCATE.DOC for complete description. ; ; ; 1 Nov 81 - First implementation ; 7 Nov 81 - Page offset inquiry added (GDM) ; 5 Dec 81 - Program ID added (GDM) ; 12 Dec 81 - First release, Vers. 1.0 TPA EQU 0100H BDOS EQU 5 WRCON EQU 2 ; write console function PSTRF EQU 9 ; print string function OPENF EQU 15 ; open file function MAKEF EQU 22 ; make file funciton DELETF EQU 19 ; delete file function CLOSF EQU 16 ; close file function READF EQU 20 ; disk read function WRITF EQU 21 ; write file function SETDMA EQU 26 ; set DMA address FCB EQU 05CH ; file control block FTYP EQU FCB+9 ; file type FCBCR EQU FCB+32 ; current record # FCB2 EQU FCB+16 ; second file field DBUF EQU 80H ; default buffer CTL EQU 1FH CR EQU 0DH LF EQU 0AH EOT EQU CTL AND 'Z' ; end of ASCII text ORG TPA GENERATOR: JMP START DB CR,LF,'Relocator - Vers 1.0' DB CR,LF,'George D. Massar' DB CR,LF,'Woodland Hills, CA' DB EOT START: LXI D,FCB2+1 ; get page offset value from ; the command line LDAX D ; look at its sign CPI ' ' ; zero offset if not given JZ ZEROFF CPI '-' ; offset below the BDOS base JZ NEGOFF CPI '+' ; offset above the BDOS base JZ POSOFF GETERR: LXI D,SYNERR ; synthax error JMP SENDMSG ZEROFF: XRA A ; zero offset JMP STOFF NEGOFF: CALL CONVERT ; to binary value in HL MOV A,L ORA A ; at page boundary? JNZ GETERR ; no MOV A,H CMA INR A ; negate page offset JMP STOFF POSOFF: CALL CONVERT ; to binary value in HL MOV A,L ORA A ; at page boundary? JNZ GETERR ; no MOV A,H STOFF: STA PGOFF ; store page offset value ; Open program file with org 0000, .COZ OPENZ: LXI H,0 SHLD CSIZE ; reset counter LXI H,BUFFER SHLD BUFPTR ; at the beg. of the buffer LXI H,FTYP MVI M,'C' INX H MVI M,'O' INX H MVI M,'Z' ; FTYP = 'COZ' XRA A STA FCBCR ; clear current record # LXI D,FCB MVI C,OPENF CALL BDOS CPI 255 ; file present? JNZ LOADZ ; yes - go on LXI D,OPNERR JMP SENDMSG LOADZ: LXI D,FCB MVI C,READF CALL BDOS ; read next record in DBUF ORA A ; end of file? JNZ OPENX ; yes - open the next file LHLD CSIZE LXI D,128 DAD D SHLD CSIZE ; CSIZE = CSIZE + 128 LXI D,DBUF LHLD BUFPTR MVI C,128 LOADZ1: LDAX D ; get next byte from DBUF INX D MOV M,A ; and put it in the buffer INX H DCR C ; end of record? JZ LOADZ2 ; yes MOV A,C ANI 07H ; mod 8 = 0? JNZ LOADZ1 ; no - not yet INX H ; leave a room for bit pattern JMP LOADZ1 LOADZ2: INX H ; last room for bit pattern SHLD BUFPTR CALL CKBPTR ; check memory space JMP LOADZ ; ok - go on ; Open program file with org 0100H, .COM OPENX: LXI H,BUFFER SHLD BUFPTR MVI A,'M' STA FTYP+2 ; FTYP = 'COM' XRA A STA FCBCR ; clear current record # MVI C,OPENF LXI D,FCB CALL BDOS CPI 255 ; file present? JNZ LOADX ; yes LXI D,OPNERR JMP SENDMSG LOADX: LXI D,FCB MVI C,READF CALL BDOS ; read next record ORA A ; end of file? JNZ END$LOAD ; yes LXI D,DBUF LHLD BUFPTR MVI C,128 LOADX0: XRA A STA BITPAT ; clear bit pattern MVI B,10000000B LOADX1: LDAX D INX D CMP M ; compare contents bet. files INX H JZ LOADX2 ; jump if both are the same LDA BITPAT ORA B ; set bit in the pattern STA BITPAT LOADX2: MOV A,B RAR ; advance bit position MOV B,A DCR C ; end of record? JZ LOADX3 ; yes MOV A,C ANI 07H ; mod 8 = 0? JNZ LOADX1 ; no - not yet LDA BITPAT MOV M,A ; store the pattern in buffer INX H JMP LOADX0 LOADX3: LDA BITPAT MOV M,A ; store the last pattern before INX H ; reading the next record SHLD BUFPTR CALL CKBPTR ; check memory space JMP LOADX ; ok - go on END$LOAD: ; Print statistic results and also store them in program ID CALL PRINTA DB CR,LF,'CODE size: $' PRINTA: POP D MVI C,PSTRF CALL BDOS LXI H,CODSIZ SHLD ID$PTR ; ptr to code size in the ID LHLD CSIZE CALL PVALUE ; print CSIZE value CALL PRINTB DB CR,LF,'BDOS offset: $' PRINTB: POP D MVI C,PSTRF CALL BDOS LXI H,BDOFF SHLD ID$PTR ; ptr to BDOS offset in the ID LDA PGOFF MOV H,A MVI L,0 CALL PVALUE ; print BDOFF value ; Write the buffer beginning at the MOVER location MAKEY: LHLD BUFPTR DCX H SHLD LBYTE ; loc. of the last byte in buf LXI D,-DELTA ; DELTA is the size of Generator DAD D SHLD BUFPTR ; ptr to last byte to be moved MVI A,'C' STA FTYP MVI A,'O' STA FTYP+1 MVI A,'M' STA FTYP+2 ; FTYP = 'COM' LXI D,FCB MVI C,DELETF CALL BDOS ; delete input file .COM XRA A STA FCBCR ; clear current rec # LXI D,FCB MVI C,MAKEF CALL BDOS ; re-create the file .COM CPI 255 ; disk full? (do we need this?) JNZ WRITY ; no LXI D,DSKERR JMP SENDMSG WRITY: LXI H,MOVER ; set DMA at the beginning of SHLD DMA ; the buffer XCHG WRITY1: MVI C,SETDMA CALL BDOS LXI D,FCB MVI C,WRITF CALL BDOS ; write the next record CPI 255 ; write error? JNZ WRITY2 ; no - go on LXI D,DSKERR JMP SENDMSG WRITY2: LHLD DMA LXI D,128 DAD D ; next DMA SHLD DMA XCHG LHLD LBYTE CALL DSUB ; HL = LBYT-DMA JNC WRITY1 ; jump if need to write more CLOSY: LXI D,FCB MVI C,CLOSF CALL BDOS ; close the output file JMP 0 ; warmboot ; SUPPORT SUBROUTINES ; Convert a value in ASCII pointed-to by DE to binary val. in HL CONVERT: LXI H,0 MOV B,H CONV1: INX D LDAX D CPI ' ' ; end of ASCII string? RZ ; yes SUI '0' ; A < '0'? JNC CONV2 ; no LXI D,SYNERR ; synthax error JMP SENDMSG CONV2: CPI 10 JM CONV3 SUI 7 ; A = hex digit CONV3: CPI 17 ; A > 16? JC CONV4 ; no LXI D,SYNERR ; synthax error JMP SENDMSG CONV4: DAD H DAD H DAD H DAD H MOV C,A DAD B ; HL = 16*HL + digit JMP CONV1 ; continue for next digit ; Print address value in HL PVALUE: MOV A,H PUSH H CALL PHEX ; print MSB of addr POP H MOV A,L CALL PHEX ; print LSB of addr MVI A,'H' CALL PCHAR RET ; print hex char in reg. A PHEX: PUSH PSW RRC RRC RRC RRC ; shift high 4 bits to right CALL PNIB POP PSW PNIB: ANI 0FH CPI 10 ; A < 10 ? JNC PNIB1 ; no ADI '0' JMP PCHAR PNIB1: ADI 'A'-10 ; print a char in reg. A PCHAR: LHLD ID$PTR MOV M,A ; put char in program ID INX H SHLD ID$PTR MOV E,A MVI C,WRCON CALL BDOS ; and print it on console, too RET ; Check memory space left CKBPTR: PUSH D LXI D,10 DAD D ; HL = BUFPTR+10 XCHG LHLD 6 ; HL = TOPMEM+1 CALL DSUB ; HL = TOPMEM-(BUFPTR+9) LXI D,MEMERR ; memory full? JC SENDMSG ; yes POP D RET ; Send error message and go home SENDMSG: MVI C,PSTRF CALL BDOS JMP 0 ; warmboot ; DATA SPACE FOR GENERATOR ROUTINE ; ID$PTR: DS 2 BITPAT: DS 1 ; bit pattern LBYTE: DS 2 ; loc of the last byte DMA: DS 2 ; addr of DMA DS 32*2 STACK: OPNERR: DB CR,LF,'>> File not present <<$' DSKERR: DB CR,LF,'>> Disk full <<$' SYNERR: DB CR,LF,'>> Synthax error <<$' MEMERR: DB CR,LF,'>> Memory overflow <<$' DELTA EQU $-GENERATOR ; size of generator program ; <<<<<<<<< THE FOLLOWING WILL BE WRITTEN INTO DISK >>>>>>>>>> ; The program ID below is to indicate the name of program, ; its size & location*, and the author of the program when- ; ever a user wishes to see by TYPE-ing .COM. ; The dashed line may be substituted with a program name and ; its version. Your name as an author of your own program ; may replace the existing name of the LOCATE creator. ; NOTE: this is the only place you can remove the orginal ; author's name! ; The PASS$ID vector should be at the page boundary or some ; other location where the equivalent ASCII characters of ; the vector would not jeopardize while TYPE-ing the program ; ID heading. ; * The location of the relocatable program can be computed ; as follows, for example: ; ; Say BDOS vector is E406H (located at 0006H), ; the offset is F800 (-800H), and the program ; size is 180H. ; ; So, the "ending" of the program is E400+F800-1 = DBFFH ; and the starting is DBFF+1-200 = DA00H. ; ; Note: the value of 200H is the next page greater than 180H. ; The actual ending of the program may be far below than ; DBFFH but no more than a page. MOVER: JMP PASS$ID DB CR,LF,'Relocatable Program' DB CR,LF,'----------------------' DB CR,LF,'CODE size: ' CODSIZ: DS 5 DB CR,LF,'BDOS offset: ' BDOFF: DS 5 DB CR,LF,'George D. Massar' DB CR,LF,'Woodland Hills, CA' DB EOT ; DONOT remove EOT here DS 128-(($-MOVER) MOD 128) PASS$ID EQU $-DELTA ; The DELTA offset as indicated here are the special feature ; to locate the MOVER program with the relocatable program ; at 0100H when loading the program under the CCP command. LHLD CSIZE-DELTA ; program size XCHG LHLD 6 ; HL = base of BDOS+6 LDA PGOFF-DELTA ADD H ; add page offset MOV H,A CALL DSUB-DELTA MOV B,H ; B = displacement MVI L,0 ; HL = new program base DAD D ; HL = dest. of program code XCHG LHLD BUFPTR-DELTA XCHG ; DE = CODE ptr in buffer MOVEUP EQU $-DELTA DCX H LDAX D ; get next pattern byte STA PATTERN DCX D MVI C,8 ; count of 8 bytes to move MOVE EQU $-DELTA LDA PATTERN RAR ; put the corresp. bit in CY STA PATTERN LDAX D ; get next byte in the buffer DCX D JNC SKIP ; jump if bit is not set ADD B ; displacement SKIP EQU $-DELTA MOV M,A ; put CODE byte in memory above DCX H DCR C ; 8th byte? JNZ MOVE ; no - go on INX H MOV A,L ORA A ; at page boundary? JNZ MOVEUP ; no - continue to move MOV A,H CMP B ; HL = ptr to the first byte? JNZ MOVEUP ; no - not yet PCHL ; jump to the relocated program ; SUPPORT SUBROUTINE(S) common to both routines DSUB: MOV A,L SUB E MOV L,A MOV A,H SBB D MOV H,A ; HL = HL - DE RET ; DATA SPACE for MOVER routine PATTERN EQU $-DELTA DS 1 ; bit pattern hold ; DATA ALLOCATION common to both routines CSIZE: DS 2 ; size of movable program code PGOFF: DS 1 ; page offset BUFPTR: DS 2 ; buffer pointer hold BUFFER: ; start of "big" buffer END