title 'CP/M 3 - PROGRAM LOADER RSX - November 1982' ; version 3.0b Nov 04 1982 - Kathy Strutynski ; version 3.0c Nov 23 1982 - Doug Huskey ; Dec 22 1982 - Bruce Skidmore ; ; ; copyright (c) 1982 ; digital research ; box 579 ; pacific grove, ca. ; 93950 ; **************************************************** ***** The following values must be placed in *** ***** equates at the front of CCP3.ASM. *** ***** *** ***** Note: Due to placement at the front these *** ***** equates cause PHASE errors which can be *** ***** ignored. *** equ1 equ rsxstart +0100h ;set this equate in the CCP equ2 equ fixchain +0100h ;set this equate in the CCP equ3 equ fixchain1+0100h ;set this equate in the CCP equ4 equ fixchain2+0100h ;set this equate in the CCP equ5 equ rsx$chain+0100h ;set this equate in the CCP equ6 equ reloc +0100h ;set this equate in the CCP equ7 equ calcdest +0100h ;set this equate in the CCP equ8 equ scbaddr +0100h ;set this equate in the CCP equ9 equ banked +0100h ;set this equate in the CCP equ10 equ rsxend +0100h ;set this equate in the CCP ccporg equ CCP ;set origin to this in CCP patch equ patcharea+0100h ;LOADER patch area CCP equ 41Ah ;ORIGIN OF CCP3.ASM **************************************************** ; conditional assembly toggles: true equ 0ffffh false equ 0h spacesaver equ true stacksize equ 32 ;16 levels of stack version equ 30h tpa equ 100h ccptop equ 0Fh ;top page of CCP osbase equ 06h ;base page in BDOS jump off$nxt equ 10 ;address in next jmp field currec equ 32 ;current record field in fcb ranrec equ 33 ;random record field in fcb ; ; ; dsect for SCB ; bdosbase equ 98h ; offset from page boundary ccpflag1 equ 0b3h ; offset from page boundary multicnt equ 0e6h ; offset from page boundary rsx$only$clr equ 0FDh ;clear load RSX flag rsx$only$set equ 002h rscbadd equ 3ah ;offset of scbadd in SCB dmaad equ 03ch ;offset of DMA address in SCB bdosadd equ 62h ;offset of bdosadd in SCB ; loadflag equ 02H ;flag for LOADER in memory ; ; dsect for RSX entry equ 06h ;RSX contain jump to start ; nextadd equ 0bh ;address of next RXS in chain prevadd equ 0ch ;address of previous RSX in chain warmflg equ 0eh ;remove on wboot flag endchain equ 18h ;end of RSX chain flag ; ; readf equ 20 ;sequential read dmaf equ 26 ;set DMA address scbf equ 49 ;get/set SCB info loadf equ 59 ;load function ; ; maxread equ 64 ;maximum of 64 pages in MULTIO ; ; wboot equ 0000h ;BIOS warm start bdos equ 0005h ;bdos entry point print equ 9 ;bdos print function vers equ 12 ;get version number module equ 200h ;module address ; ; DSECT for COM file header ; comsize equ tpa+1h scbcode equ tpa+3h rsxoff equ tpa+10h rsxlen equ tpa+12h ; ; cr equ 0dh lf equ 0ah ; ; cseg ; ; ; ********* LOADER RSX HEADER *********** ; rsxstart: jmp ccp ;the ccp will move this loader to db 0,0,0 ;high memory, these first 6 bytes ;will receive the serial number from ;the 6 bytes prior to the BDOS entry ;point tojump: jmp begin next db 0c3h ;jump to next module nextjmp dw 06 prevjmp dw 07 db 0 ;warm start flag db 0 ;bank flag db 'LOADER ' ;RSX name db 0ffh ;end of RSX chain flag db 0 ;reserved db 0 ;patch version number ; ********* LOADER RSX ENTRY POINT *********** begin: mov a,c cpi loadf jnz next beginlod: pop b push b ;BC = return address lxi h,0 ;switch stacks dad sp lxi sp,stack ;our stack shld ustack ;save user stack address push b ;save return address xchg ;save address of user's FCB shld usrfcb mov a,h ;is .fcb = 0000h ora l push psw cz rsx$chain ;if so , remove RSXs with remove flag on pop psw cnz loadfile pop d ;return address lxi h,tpa mov a,m cpi ret jz rsxfile mov a,d ;check return address dcr a ; if CCP is calling ora e ; it will be 100H jnz retuser1 ;jump if not CCP retuser: lda prevjmp+1 ;get high byte ora a ;is it the zero page (i.e. no RSXs present) jnz retuser1 ;jump if not lhld nextjmp ;restore five....don't stay arround shld osbase shld newjmp call setmaxb retuser1: lhld ustack ;restore the stack sphl xra a mov l,a mov h,a ;A,HL=0 (successful return) ret ;CCP pushed 100H on stack ; ; ; BDOS FUNC 59 error return ; reterror: lxi d,0feh reterror1: ;DE = BDOS error return lhld ustack sphl pop h ;get return address push h dcr h ;is it 100H? mov a,h ora l xchg ;now HL = BDOS error return mov a,l mov b,h rnz ;return if not the CCP ; ; loaderr: mvi c,print lxi d,nogo ;cannot load program call bdos ;to print the message jmp wboot ;warm boot ; ; ;; ;************************************************************************ ; ; MOVE RSXS TO HIGH MEMORY ; ;************************************************************************ ; ; ; RSX files are present ; rsxf1: inx h mov c,m inx h mov b,m ;BC contains RSX length lda banked ora a ;is this the non-banked system? jz rsxf2 ;jump if so inx h ;HL = banked/non-banked flag inr m ;is this RSX only for non-banked? jz rsxf3 ;skip if so rsxf2: push d ;save offset call calcdest ;calculate destination address and bias pop h ;rsx offset in file call reloc ;move and relocate file call fixchain ;fix up rsx address chain rsxf3: pop h ;RSX length field in header rsxfile: ;HL = .RSX (n-1) descriptor lxi d,10h ;length of RSX descriptor in header dad d ;HL = .RSX (n) descriptor push h ;RSX offset field in COM header mov e,m inx h mov d,m ;DE = RSX offset mov a,e ora d jnz rsxf1 ;jump if RSX offset is non-zero ; ; ; comfile: ;RSXs are in place, now call SCB setting code call scbcode ;set SCB flags for this com file ;is there a real COM file? lda module ;is this an RSX only cpi ret jnz comfile2 ;jump if real COM file lhld scbaddr mvi l,ccpflag1 mov a,m ori rsx$only$set ;set if RSX only mov m,a comfile2: lhld comsize ;move COM module to 100H mov b,h mov c,l ;BC contains length of COM module lxi h,tpa+100h ;address of source for COM move to 100H lxi d,tpa ;destination address call move jmp retuser1 ;restore stack and return ;; ;************************************************************************ ; ; ADD AN RSX TO THE CHAIN ; ;************************************************************************ ; ; fixchain: lhld osbase ;next RSX link mvi l,0 lxi b,6 call move ;move serial number down mvi e,endchain stax d ;set loader flag=0 mvi e,prevadd+1 stax d ;set previous field to 0007H dcx d mvi a,7 stax d ;low byte = 7H mov l,e ;HL address previous field in next RSX mvi e,nextadd ;change previous field in link mov m,e inx h mov m,d ;current <-- next ; fixchain1: ;entry: H=next RSX page, ; DE=.(high byte of next RSX field) in current RSX xchg ;HL-->current DE-->next mov m,d ;put page of next RSX in high(next field) dcx h mvi m,6 ; fixchain2: ;entry: H=page of lowest active RSX in the TPA ;this routine resets the BDOS address @ 6H and in the SCB mvi l,6 shld osbase ;change base page BDOS vector shld newjmp ;change SCB value for BDOS vector ; ; setmaxb: lxi d,scbadd2 scbfun: mvi c,scbf jmp bdos ; ; ;; ;************************************************************************ ; ; REMOVE TEMPORARY RSXS ; ;************************************************************************ ; ; ; rsx$chain: ; ; Chase up RSX chain, removing RSXs with the ; remove flag on (0FFH) ; lhld osbase ;base of RSX chain mov b,h rsx$chain1: ;B = current RSX mov h,b mvi l,endchain inr m dcr m ;is this the loader? rnz ;return if so (m=0ffh) mvi l,nextadd ;address of next node mov b,m ;DE -> next link ; ; check$remove: ; mvi l,warmflg ;check remove flag mov a,m ;warmflag in A ora a ;FF if remove on warm start jz rsx$chain1 ;check next RSX if not ; remove: ;remove this RSX from chain ; ;first change next field of prior link to point to next RSX ;HL = current B = next ; mvi l,prevadd mov e,m ;address of previous RSX link inx h mov d,m mov a,b ;A = next (high byte) stax d ;store in previous link dcx d ;previous RSX chains to next RSX mvi a,6 ;initialize low byte to 6 stax d ; inx d ;DE = .next (high byte) ; ;now change previous field of next link to address previous RSX mov h,b ;next in HL...previous in DE mvi l,prevadd mov m,e inx h mov m,d ;next chained back to previous RSX mov a,d ;check to see if this is the bottom ora a ;RSX... push b cz fixchain2 ;reset BDOS BASE to page in H pop b jmp rsx$chain1 ;check next RSX in the chain ; ; ;; ;************************************************************************ ; ; PROGRAM LOADER ; ;************************************************************************ ; ; ; loadfile: ; entry: HL = .FCB push h lxi d,scbdma call scbfun xchg pop h ;.fcb push h ;save .fcb lxi b,currec dad b mvi m,0 ;set current record to 0 inx h mov c,m ;load address inx h mov h,m mov l,c dcr h inr h jz reterror ;Load address < 100h push h ;now save load address push d ;save the user's DMA push h call multio1 ;returns A=multio pop h push psw ;save A = user's multisector I/O mvi e,128 ;read 16k ;stack: |return address| ; |.FCB | ; |Load address | ; |users DMA | ; |users Multio | ; loadf0: ;HL= next load address (DMA) ; E= number of records to read lda osbase+1 ;calculate maximum number of pages dcr a sub h jc endload ;we have used all we can inr a cpi maxread ;can we read 16k? jnc loadf2 rlc ;change to sectors mov e,a ;save for multi i/o call mov a,l ;A = low(load address) ora a jz loadf2 ;load on a page boundary mvi b,2 ;(to subtract from # of sectors) dcr a ;is it greater than 81h? jm subtract ;080h < l(adr) <= 0FFh (subtract 2) dcr b ;000h < l(adr) <= 080h (subtract 1) subtract: mov a,e ;reduce the number of sectors to sub b ;compensate for non-page aligned ;load address jz endload ;can't read zero sectors mov e,a ; loadf2: ;read the file push d ;save number of records to read push h ;save load address call multio ;set multi-sector i/o pop h push h call readb ;read sector pop h pop d ;restore number of records push psw ;zero flag set if no error mov a,e ;number of records in A inr a rar ;convert to pages add h mov h,a ;add to load address shld loadtop ;save next free page address pop psw jz loadf0 ;loop if more to go loadf4: ;FINISHED load A=1 if successful (eof) ; A>1 if a I/O error occured ; pop b ;B=multisector I/O count dcr a ;not eof error? mov e,b ;user's multisector count call multio mvi c,dmaf ;restore the user's DMA address pop d push psw ;zero flag => successful load call bdos ; user's DMA now restored pop psw lhld bdosret ;BDOS error return xchg jnz reterror1 pop d ;load address pop h ;.fcb lxi b,9 ;is it a PRL? dad b ;.fcb(type) mov a,m ani 7fh ;get rid of attribute bit cpi 'P' ;is it a P? rnz ;return if not inx h mov a,m ani 7fh cpi 'R' ;is it a R rnz ;return if not inx h mov a,m ani 7fh sui 'L' ;is it a L? rnz ;return if not ;load PRL file mov a,e ora a ;is load address on a page boundary jnz reterror ;error, if not mov h,d mov l,e ;HL,DE = load address inx h mov c,m inx h mov b,m mov l,e ;HL,DE = load address BC = length ; jmp reloc ;relocate PRL file at load address ; ;; ;************************************************************************ ; ; PAGE RELOCATOR ; ;************************************************************************ ; ; reloc: ; HL,DE = load address (of PRL header) ; BC = length of program (offset of bit map) inr h ;offset by 100h to skip header push d ;save destination address push b ;save length in bc call move ;move rsx to correct memory location pop b pop d push d ;save DE for fixchain...base of RSX mov e,d ;E will contain the BIAS from 100h dcr e ;base address is now 100h ;after move HL addresses bit map ; ;storage moved, ready for relocation ; HL addresses beginning of the bit map for relocation ; E contains relocation bias ; D contain relocation address ; BC contains length of code rel0: push h ;save bit map base in stack mov h,e ;relocation bias is in e mvi e,0 ; rel1: mov a,b ;bc=0? ora c jz endrel ; ; not end of the relocation, may be into next byte of bit map dcx b ;count length down mov a,e ani 111b ;0 causes fetch of next byte jnz rel2 ; fetch bit map from stacked address xthl mov a,m ;next 8 bits of map inx h xthl ;base address goes back to stack mov l,a ;l holds the map as we process 8 locations rel2: mov a,l ral ;cy set to 1 if relocation necessary mov l,a ;back to l for next time around jnc rel3 ;skip relocation if cy=0 ; ; current address requires relocation ldax d add h ;apply bias in h stax d rel3: inx d ;to next address jmp rel1 ;for another byte to relocate ; endrel: ;end of relocation pop d ;clear stacked address pop d ;restore DE to base of PRL ret ; ;; ;************************************************************************ ; ; PROGRAM LOAD TERMINATION ; ;************************************************************************ ; ;; ;; endload: call multio1 ;try to read after memory is filled lxi h,80h ;set load address = default buffer call readb jnz loadf4 ;eof => successful lxi h,0feh ;set BDOSRET to indicate an error shld bdosret jmp loadf4 ;unsuccessful (file to big) ; ;; ; ;; ;************************************************************************ ; ; SUBROUTINES ; ;************************************************************************ ; ; ; ; Calculate RSX base in the top of the TPA ; calcdest: ; ; calcdest returns destination in DE ; BC contains length of RSX ; lda osbase+1 ;a has high order address of memory top dcr a ;page directly below bdos dcx b ;subtract 1 to reflect last byte of code sub b ;a has high order address of reloc area inx b ;add 1 back get bit map offset cpi ccptop ;are we below the CCP jc loaderr lhld loadtop cmp h ;are we below top of this module jc loaderr mov d,a mvi e,0 ;d,e addresses base of reloc area ret ; ;; ;;----------------------------------------------------------------------- ;; ;; move memory routine move: ; move source to destination ; where source is in HL and destination is in DE ; and length is in BC ; mov a,b ;bc=0? ora c rz dcx b ;count module size down to zero mov a,m ;get next absolute location stax d ;place it into the reloc area inx d inx h jmp move ;; ;;----------------------------------------------------------------------- ;; ;; Multi-sector I/O ;; (BDOS function #44) ; multio1: mvi e,1 ;set to read 1 sector ; multio: ;entry: E = new multisector count ;exit: A = old multisector count lhld scbaddr mvi l,multicnt mov a,m mov m,e ret ;; ;;----------------------------------------------------------------------- ;; ;; read file ;; (BDOS function #20) ;; ;; entry: hl = buffer address (readb only) ;; exit z = set if read ok ;; readb: xchg setbuf: mvi c,dmaf push h ;save number of records call bdos mvi c,readf lhld usrfcb xchg call bdos shld bdosret ;save bdos return pop d ;restore number of records ora a rz ;no error on read mov e,h ;change E to number records read ret ; ; ;************************************************************************ ; ; DATA AREA ; ;************************************************************************ ; nogo db cr,lf,'Cannot load Program$' patcharea: ds 36 ;36 byte patch area scbaddr dw 0 banked db 0 scbdma db dmaad db 00h ;getting the value scbadd2 db bdosadd ;current top of TPA db 0feh ;set the value ; if not spacesaver newjmp ds 2 ;new BDOS vector loadtop ds 2 ;page above loaded program usrfcb ds 2 ;contains user FCB add ustack: ds 2 ; user stack on entry bdosret ds 2 ;bdos error return ; rsxend : stack equ rsxend+stacksize else rsxend: newjmp equ rsxend loadtop equ rsxend+2 usrfcb equ rsxend+4 ustack equ rsxend+6 bdosret equ rsxend+8 stack equ rsxend+10+stacksize endif end