mirror of
https://github.com/SEPPDROID/Digital-Research-Source-Code.git
synced 2025-10-24 00:44:23 +00:00
739 lines
16 KiB
NASM
739 lines
16 KiB
NASM
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 401h ;[JCE] was 41A before patches
|
||
;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
|
||
|