ORG 00100H WBOOT EQU 00000H BDOS EQU 00005H DEFFCB EQU 00080H ; CR EQU 00DH LF EQU 00AH ; CHROUT EQU 002H OPNFIL EQU 00FH CLSFIL EQU 010H FNDFIL EQU 011H DELFIL EQU 013H RDREC EQU 014H WRTREC EQU 015H MAKFIL EQU 016H SETDMA EQU 01AH ; START: LXI H,0 DAD SP SHLD TMPSTK ;Save STACK for later (self-modifying code) LXI SP,LOADER+080H ; LXI D,DEFFCB MVI C,SETDMA ;Use default FCB for DIR searches CALL BDOS ; LXI D,CO0FCB MVI C,FNDFIL CALL BDOS ;Go search for LUX.CO0 INR A ;Is it there? JNZ FNDCO1 ;Yes CALL ILPRT DB 'Can''t find LUX.CO0 - Aborting',CR,LF,0 JMP EXIT ;Nope - display error and return ; FNDCO1: LXI D,CO1FCB MVI C,FNDFIL CALL BDOS ;Go search for LUX.CO1 INR A ;Is it there? JNZ OPNCO0 ;Yes CALL ILPRT DB 'Can''t find LUX.CO1 - Aborting',CR,LF,0 JMP EXIT ;Nope - display error and return ; OPNCO0: LXI D,CO0FCB MVI C,OPNFIL CALL BDOS ;Open LUX.CO0 for input CPI 0FFH ;Open OK? JNZ GETCO0 ;Yes - go read it then CALL ILPRT DB 'LUX.CO0 open error',CR,LF,0 JMP EXIT ;Nope - display error and return ; GETCO0: LXI H,RDSPC LXI D,080H GETC0A: DAD D ;Calc next sector address XCHG PUSH D PUSH H MVI C,SETDMA CALL BDOS ;Tell BDOS where to put LUX.CO0 sector LXI D,CO0FCB MVI C,RDREC CALL BDOS ;Read a record of LUX.CO0 POP H POP D XCHG ORA A ;All of file in yet? JZ GETC0A ;Nope - go read another sector SHLD CO0END ;Yes. Save "end of LUX.CO0" pointer ; LXI D,CO1FCB XRA A STA CO1EXT STA CO1REC ;Init current extent and record to read MVI C,OPNFIL CALL BDOS ;Open LUX.CO1 CPI 0FFH ;Open OK? JNZ GETCO1 ;Yes CALL ILPRT DB 'LUX.CO1 open error',CR,LF,0 JMP EXIT ;Nope - display error and return ; GETCO1: LXI H,CO0DAT ; points to LUX.CO0 data LXI D,LNKMAP ; points to our link map ; GETC1A: XRA A ;Clear STAX D ;Zero out a byte of the link map MVI B,008H ;Prepare to process 8 bytes of LUX.CO0 ; GETC1B: CALL GETBYT ;Fetch a byte of LUX.CO1 JC MAKLEN ;Jump if end of file on LUX.CO1 CMP M ;Is this byte of LUX.CO1 same as LUX.CO0? JZ GETC1C ;Yes - so it's absolute STC ;No - it's relative ; GETC1C: LDAX D RAL STAX D ;Tag the bit corresponding to this byte INX H ;Next byte of LUX.CO0... DCR B JNZ GETC1B ;Process all 8 bits of this link map entry INX D ;Point to next byte of link map JMP GETC1A ; and do it all over again ; MAKLEN: LHLD CO0END LXI D,CO0DAT MOV A,L SUB E MOV L,A MOV A,H SBB D MOV H,A ;Calc length of LUX.CO0 (in ) MOV A,L ORA A ;Is length of form 0XX00h? JZ MKLEN1 ;Yup MVI L,0 INR H ;Nope, so force it that way (add 128 bytes) MKLEN1: SHLD CO0LEN ; and save that length away LXI D,LODLEN DAD D ;Add the length of the loader to LUX.CO0 length MOV A,L ORA A ;Is length in the form 0XX00h? JZ MKLEN2 ;Yup CPI 080H ;Well then, is it in the form 0XX80? JZ MKLEN2 ;Yup MVI L,0 INR H ;Nope, so force it to be a multiple of sector ; MKLEN2: MVI B,7 MKLEN3: MOV A,H RRC MOV H,A MOV A,L RAR MOV L,A DCR B JNZ MKLEN3 ;Divide this length by the sector size (080h) ; MOV A,L STA LODKNT ;Save the sector count ; LXI D,COMFCB MVI C,DELFIL CALL BDOS ;Delete any old LUX.COM file LXI D,COMFCB MVI C,MAKFIL CALL BDOS ;Create a new LUX.COM file CPI 0FFH ;Could we create the file? JNZ WRTCOM ;Yes - go write it then CALL ILPRT DB 'No directory space',CR,LF,0 JMP EXIT ;Nope - display error and return ; WRTCOM: LXI H,LOADER LXI D,080H LDA LODKNT MOV B,A ;Sector count to ; WTCOM1: DAD D ;Calc next sector address XCHG PUSH B PUSH D PUSH H ;We use all regs, so save em MVI C,SETDMA CALL BDOS ;Tell BDOS where to get the data LXI D,COMFCB MVI C,WRTREC CALL BDOS ;Write a record of LUX.COM POP H POP D POP B ;Get all our regs back ; XCHG ORA A ;Did we get an error on that last write? JNZ WTCOM2 ;Yes - disk must be full then DCR B JNZ WTCOM1 ;Nope - go write another record then ; LXI D,COMFCB MVI C,CLSFIL CALL BDOS ;Go close the file now CPI 0FFH ;Sucessful close? JNZ EXIT ;Yes - all done. LUX.COM is ready!!! ; WTCOM2: CALL ILPRT DB 'Disk full - aborting',CR,LF,0 ; LXI D,COMFCB MVI C,CLSFIL CALL BDOS ;Try to close the file again LXI D,COMFCB MVI C,DELFIL CALL BDOS ;Delete the bad file EXIT: LXI SP,$-$ TMPSTK EQU $-2 RET ;We now return you to the system ; GETBYT: PUSH B PUSH D PUSH H GTBYT1: LDA BYTKNT INR A STA BYTKNT CPI 081H JZ GETREC LHLD LODADR MOV A,M INX H SHLD LODADR POP H POP D POP B ORA A RET ; GETREC: XRA A STA BYTKNT LXI H,DEFFCB SHLD LODADR ;Reset data address XCHG ; gets default FCB address MVI C,SETDMA CALL BDOS LXI D,CO1FCB MVI C,RDREC CALL BDOS ORA A JZ GTBYT1 POP H POP D POP B STC RET ; ILPRT: XTHL ILPRT1: MOV A,M ORA A JZ ILPRT2 CALL CTYPE INX H JMP ILPRT1 ; ILPRT2: XTHL RET ; CTYPE: PUSH PSW PUSH B PUSH D PUSH H ANI 07FH MOV E,A MVI C,CHROUT CALL BDOS POP H POP D POP B POP PSW RET ; LODADR: DW 0 BYTKNT: DB 128 LODKNT: DB 0 CO0END: DW 0 CO0FCB: DB 0 ;Current drive DB 'LUX CO0' ;File name DB 0,0,0,0,0,0,0,0 ;Rest of FCB DB 0,0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0,0 CO1FCB: DB 0 ;Current drive DB 'LUX CO1' ;File name CO1EXT: DB 0,0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0,0 DB 0,0,0,0 CO1REC: DB 0,0,0,0 COMFCB: DB 0 ;Current drive DB 'LUX COM' ;File name DB 0,0,0,0,0,0,0,0 ;Rest of FCB DB 0,0,0,0,0,0,0,0 DB 0,0,0,0,0,0,0,0 ; ; Start the loader at 0XX00h just to make it look nice ; ORG ($+0FFH) AND 0FF00h ; ; The following loader is placed in the beginning of LUX.COM, ; followed by the LINK MAP and finally, LUX.CO0. The loader ; examines top of memory, allows enough space for LUX to ; reside in, and relocates LUX.CO0 (the 0 absolute version) ; to this area, changing all relative bytes to absolute by ; adding in the offset of where LUX.CO0 is being moved. The ; loader accomplishes this by using the LINK MAP created above. ; OFFSET EQU $-100H LOADER EQU $-080H ; LODBEG EQU $ CALL LOCATE ;Locate a place for LUX and link it there ; LHLD C0LENR MOV B,H MOV C,L LHLD LDADDR XCHG LXI H,C0DATR ; TMP010 EQU $-OFFSET MOV A,M INX H XCHG MOV M,A INX H XCHG DCR C JNZ TMP010 DCR B JNZ TMP010 ;Move LUX to our new-found area ; LHLD LDADDR PCHL ; and go run it!!! ; LOCATE EQU $-OFFSET LDA BDOS+1 CPI 006H ;Has LUX modified BIOS vectors already? JNZ LINK ;Yes - just link it then ; LHLD WBOOT+1 MVI B,004H ;Prepare to find a place for LUX to reside ; LOCAT1: EQU $-OFFSET INX H ;Skip the JMP opcode byte MOV E,M INX H MOV D,M ; has address of a BIOS routine INX H ; points to next BIOS jump vector ; PUSH H ; so save it ; MOV A,L SUB E MOV A,H SBB D ;Is this BIOS routine below its jump vector? ; POP H JNC LINK ;Yes - use it then for top of memory DCR B JNZ LOCAT1 ;No - better find another then ; ; None of the vectors reference routine below their vectors, so ; we can assume all of memory below the Warm Start vector is free ; for use. ; LHLD WBOOT+1 ;Get address of Warm Boot vector LXI D,0F203H DAD D ;Point back to where BDOS vectors should be ; XCHG LHLD BDOS+1 ;Get BDOS vector address XCHG ; into MOV A,E CMP L ;Are the two the same? JNZ LINK ;Nope. Better use BDOS address then MOV A,D CMP H ;Are they *really* the same? JNZ LINK ;Nope. Better use BDOS address then JMP LINK1 ;Yes, so we know BDOS is safe to use ; LINK EQU $-OFFSET LHLD BDOS+1 DCR H ;Use BDOS address - 256 for safety JMP LINK2 ; LINK1 EQU $-OFFSET LHLD BDOS+1 ;Safe to use BDOS address directly LINK2 EQU $-OFFSET MVI L,0 ;Start at an even boundary ; XCHG LHLD C0LENR ;Get LUX length XCHG ; into MOV A,L SUB E MOV L,A MOV A,H SBB D MOV H,A ;Subtract LUX length from our address SHLD LDADDR ; which becomes the load point ; MOV C,H ; has LOAD offset for linking LHLD C0LENR XCHG ; has LUX length LXI H,LKMAPR SHLD IXPTR ; has LINK MAP address LXI H,C0DATR ; has LUX address ; LINK3 EQU $-OFFSET MVI B,8 ;Prepare for 8 bytes of processing LINK4 EQU $-OFFSET PUSH H LHLD IXPTR ;Get MOV A,M RAL ;Is this byte absolute or relative MOV M,A POP H JNC LINK5 ;Absolute - don't offset it then MOV A,M ADD C MOV M,A ;Relative - offset it by the load address LINK5 EQU $-OFFSET INX H ;Next byte of raw LUX code DCX D MOV A,E ORA D ;Done all of LUX yet? RZ ;Yes - linking is complete DCR B JNZ LINK4 ;No - go do another byte of LUX ; PUSH H LHLD IXPTR INX H ;Point to next byte of LINK MAP SHLD IXPTR POP H JMP LINK3 ; and go process 8 more bytes of LUX ; C0LENR EQU $-OFFSET CO0LEN: DW 0 LDADDR EQU $-OFFSET DW 0 IXPTR EQU $-OFFSET DW 0 ; ; ADDRESS = 00480h ; LKMAPR EQU $-OFFSET LNKMAP: DS 128 DS 128 DS 128 DS 128 DS 128 DS 128 ; ; LUX.CO0 follows this program and goes below: ; C0DATR EQU $-OFFSET CO0DAT EQU $ RDSPC EQU CO0DAT-080H LODLEN EQU CO0DAT-LODBEG END