Files
Digital-Research-Source-Code/CPM OPERATING SYSTEMS/CPM 3.X/CPM 3.0/SOURCE/save.asm
Sepp J Morris 31738079c4 Upload
Digital Research
2020-11-06 18:50:37 +01:00

820 lines
13 KiB
NASM
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

title 'SAVE.RSX - CP/M 3.0 save routine. July 1982'
; *************************************************
; *
; * Title: SAVE.RSX Resident System eXtension
; * Date: 7/28/82
; * Author: Thomas J. Mason
; *
; * Modified:
; * 11/30/82 - Thomas J. Mason
; * Added trap for function 60 to fix PUT and SAVE
; * bios vector mods.
; *
; * Modified:
; * 17 May 1998 - John Elliott
; * Apply DRI patch 18 and "multiple calls" bug fix
; *
; *********************************************************
;
; Copyright (c) 1982
; Digital Research
; PO Box 579
; Pacific Grove, Ca. 93950
;
TRUE equ 0FFFFh
FALSE equ not TRUE
;
; BIOS and BDOS Jump vectors
;
WBOOT equ 0
WBTADR equ 1 ;address of boot in BIOS
BDOS equ 5 ;BDOS jump vector
BDOSAD equ 6 ;location of instructions
DFCB equ 05Ch ;default FCB
;
; BDOS Function calls
;
BDOSAD equ 6 ;BDOS jump address
PSTRING equ 9 ;print string
BUFIN equ 10 ;console buffer input
CFILE equ 16 ;file close
DFILE equ 19 ;file delete
WFILE equ 21 ;file write
MFILE equ 22 ;make file
SETDMA equ 26 ;set DMA function
BDOSER equ 45 ;Set BDOS error mode
GETSCB equ 49 ;get/set scb func #
LDRSX equ 59 ;function for RSX load
CALRSX equ 60 ;call rsx func #
CONMOD equ 109 ;GET/SET Console Mode
;
; Non Printable ASCII characters
;
CTL$C equ 03 ;CONTROL-C
CR equ 13 ;ASCII Carrige Return
LF equ 10 ;ASCII Line Feed
;
VERSION equ 31 ;[JCE] Version 3.1
;
; Buffer size
;
CONMAX equ 14 ;[JCE] Patch 18: console buffer maximum should be 14
STKSZE equ 010h ;size fo stack
SCBOST equ 068h ;page boundary + to jmp instr
RETDSP equ 0FEh ;RETurn and DiSPlay mode
JUMP equ 0C3h ;opcode for jump
LXIH equ 21h ;lxi instr to poke
BSNLY equ 07Fh ;restore bios jump table only
CMMON equ 0F9h ;offset of common memory base from pg. bound
;
; *********************************
; * *
; * The Save Program *
; * *
; *********************************
;
db 0,0,0,0,0,0
jmp PREFIX
NEXTJ:
db JUMP ;jump
NEXT:
db 0,0 ;next module in line
PREV:
dw 5 ;previous, initialized to 5
STKYBT: db 00h ;for warm start
db 0
db 'SAVE '
ds 3
;
;
; This is the check performed every time the BDOS is
; called to see if the RSX is to be invoked
;
PREFIX:
mov a,c ;set up for compare
cpi CALRSX
jnz GETGOING
push b
push d
push h
lxi h,0000h ;zero out HL
dad d ; <HL> -> RSXPB
mov a,m ;get the byte
cpi 160 ; sub function defined
pop h
pop d
pop b
jz GOODBYE ;remove this RSX
GETGOING:
;
cpi LDRSX ;do the compare
NOPME: jz START ;[JCE] For the bug fix, see below
lhld NEXT ;get address for continue
pchl ;get going.....
;
;
;
START:
;
;[JCE] Bug. This rewires the jump vectors every time the Loader is called,
; and some programs call the Loader more than once to load overlays.
; The second time it is called, SAVE is left pointing at itself rather
; than the real BIOS.
;
; They are equal so get the BIOS address to point here
; in case of a Func 0 call
;
push b ;save state
push d ; of registers
;
; check for jump byte before the SCB
call GETSET$SCB
shld SCBADR ;save address for later
;
mvi l,CMMON+1 ;offset into scb to check BIOS
mov a,m ;get byte
ora a ;check for zero
mvi a,FALSE ;store for insurance
sta CHGJMP ;non-banked = FALSE
jz NBNKED ;high byte zero if non-banked
;
lhld SCBADR ;restor SCB
mvi l,SCBOST ;offset from page for instr
mov a,m ;get byte
cpi JUMP ;is it a jump?
jnz MORRSX ;we are not alone
mvi a,TRUE
sta CHGJMP ;set flag
mvi m,LXIH ;put in lxi h,xxxx mnemonic
;
MORRSX:
; continue with processing
NBNKED:
;
;
lhld WBTADR ;get address at 01h
inx h ;now points to address of jmp xxxx
mov a,m ;get low order byte
sta BIOSAD
inx h ;next byte
mov a,m
sta BIOSAD+1 ;high order byte
;
; Now poke the BIOS address to point to
; the save routine.
;
lxi d,BEGIN ;begining of routine
mov m,d
dcx h ;point back to first byte
mov m,e ;low order
;
mvi c,BDOSER ;now set BDOS errormode
mvi e,RETDSP ;to trap any hard
call BDOS ;errors
;
;
; [JCE] Fix for the bug I mentioned earlier
;
lxi h,0 ;[JCE] Nop out the jump to this routine. Crude
shld NOPME ;[JCE] but effective!
shld NOPME+1 ;[JCE]
pop d
pop b
lhld NEXT
pchl ;continue on
;
BEGIN:
; Start of the save routine
; Notify the user which program is running
;
lxi sp,STACK ;initialize stack
lxi d,SIGNON ;prompt
call PSTR
;
; Get the file from the user
;
FLEGET:
lxi d,FLEPRMPT ;ask for file name
call PSTR
call GETBUF
; zero at end of string for parser
lxi h,CONBUF-1 ;address of #
mov a,m ;get it
cpi 0
jz REPLCE
inx h ;HL->CONBUF
mvi d,0 ;zero out high order
mov e,a ;fill low
dad d ;add to h
mvi m,00 ;zero out byte for parse
push h
;
;
call PARSE
mov a,h
cpi 0FFh
jz FLEGET
;
pop h ;get end of string address back
inx h
mvi m,'?' ;put in question mark
inx h ;bump
mvi m,' ' ;blank in string
inx h ;bump
mvi m,'$' ;end of string
;
mvi c,17 ;Search for first
lxi d,DFCB
call BDOS ;find it
inr a ;bump Acc
jz FLECLR ;file no present skip prompt
;
lxi d,DELFLE
call PSTR ;print out delete prompt
lxi d,CONBUF ;buffer address
call PSTR ;print out filename
call GETBUF ;get answer
call GNC ;get the next char
cpi 'Y' ;is it yes
jnz FLEGET ;another name if not
;
; Delete any existing file, then make a new one
FLECLR:
mvi c,DFILE ;file delete func
lxi d,DFCB ;default FCB
call BDOS ;real BDOS call
;
mvi a,0
lxi h,07ch ;M -> record count in FCB
mov m,a ;zero out record count
;
mvi c,MFILE ;make file function
lxi d,DFCB ;default FCB
call BDOS
; Get the address of start of write
;
STRADD:
lxi d,SPRMPT ;first address
call PSTR
call GETBUF
;
lda BUFFER+1 ;get # of chars read
cpi 0
jz STRADD
;
call SCANAD ;get address
jc STRADD
;
shld SADDR ;store in SADDR
;
; Get the finish address
ENDADD:
lxi d,FPRMPT ;load prompt
call PSTR ;print
call GETBUF ;read in
;
lda BUFFER+1
cpi 0
jz ENDADD
;
call SCANAD ;get finish address
jc ENDADD
;
shld FADDR ;store it
xchg
lhld SADDR
xchg
;
call CHECK
jc STRADD
;
;
lhld SADDR ;beginning DMA address
xchg ;DE=DMA address
;
; Write the first record then check the beginning address
; if DMA address ends up larger exit
;
WLOOP:
call WFLAG
push d ;save DMA address
mvi c,SETDMA
call BDOS ;set DMA address
;
mvi c,WFILE
lxi d,DFCB
call BDOS ;write
;
; Check for directory space on disk for extents
lxi d,NODIR
cpi 01h ;no more directory
jz FINIS
;
; CHECK data block error
lxi d,NOBLK
cpi 02h
jz FINIS ;out of disk space!
; final check
ora a ;if bad write occured...
jnz REPLCE ;restore BIOS address
;
; Write OK now check write address
pop d ;get DMA address
lxi h,080h
dad d
xchg
lhld FADDR ;HL=end of write
;
call CHECK
;
lda ONEFLG
cpi TRUE
jnz WLOOP ;WLOOP if not done
;
; Else, Close file and print out ending prompt
CLOSE:
mvi c,CFILE ;close function
lxi d,DFCB ;get filename
call BDOS
;
inr a ;check for close error
lxi d,CERROR
jz FINIS ;maybe write protected
;
;good copy
lxi d,ENDMSG
FINIS:
call PSTR
;
; Replace the BIOS Address to correct one
REPLCE:
lhld BIOSAD ;HL=BIOS warm jump
xchg ;DE=" " "
lhld WBTADR
inx h
mov m,e
inx h
mov m,d
;
GOODBYE:
mvi a,0FFh
sta STKYBT ;change sticky byte for
; ; removal of RSX
;
; check to see if JMP changed for BANKED system
lda CHGJMP
cpi TRUE ;has it been done?
jnz CHGBIOS
lhld SCBADR ;retreive SCB address
mvi l,SCBOST ;points to page + offset
mvi m,JUMP ;restore original code
;
CHGBIOS:
mvi c,13 ;reset the disk system
call BDOS
;
mvi c,0 ;set up for wboot
call BDOS
;****************************************
;* *
;* Logical end of the program *
;* *
;****************************************
;
GETSET$SCB:
mvi c,GETSCB
lxi d,SCBPB
call BDOS
ret
;
WFLAG:
mvi a,FALSE
sta ONEFLG
lda RSLT+1
cpi 00h
rnz
lda RSLT
cpi 080h
jc WFLAG1
jz WFLAG1
ret
;
WFLAG1:
mvi a,TRUE
sta ONEFLG
ret
;
;
;
CHECK:
; Subtract the two to find out if finished
mov a,l ;low order
sub e ;subtraction
sta RSLT
mov a,h ;now ...
sbb d ;high order subtraction
sta RSLT+1 ;saved
ret
;
GETBUF:
;buffer input routine
;
lxi h,CONBUF ;address of buffer
shld NEXTCOM ;store it
mvi c,BUFIN
lxi d,BUFFER
call BDOS
ret
;
PSTR:
; String output routine for messages
;
mvi c,PSTRING
call BDOS
ret
;
PARSE:
; General purpose parser
;
; Filename = [d:]file[.type][;password]
;
; FCB assignments
;
; 0 => drive, 0=default, 1=A, 2=B
; 1-8 => file, converted to upper case,
; padded with blanks
; 9-11 => type, converted to upper case,
; padded with blanks
; 12-15 => set to zero
; 16-23 => passwords, converted to upper case,
; padded with blanks
; 24-25 => address of password field in "filename",
; set to zero if password length=0.
; 26 => length of password (0-8)
;
; Upon return, HL is set to FFFFh if BC locates
; an invalid file name;
; otherwise, HL is set to 0000h if the delimiter
; following the file name is a 00h (null)
; or a 0Dh (CR);
; otherwise, HL is set to the address of the delimiter
; following the file name.
;
;
lxi h,0
push h
push h
lxi d,CONBUF ;set up source address
lxi h,DFCB ;set up dest address
call DEBLNK ;scan the blanks
call DELIM ;check for delimeter
jnz PARSE1
mov a,c
ora a
jnz PARSE9
mov m,a
jmp PARSE3
;
PARSE1:
mov b,a
inx d
ldax d
cpi ':'
jnz PARSE2
;
mov a,b
sui 'A'
jc PARSE9
cpi 16
jnc PARSE9
inr a
mov m,a
inx d
call DELIM
jnz PARSE3
cpi '.'
jz PARSE9
cpi ':'
jz PARSE9
cpi ';'
jz PARSE9
jmp PARSE3
;
PARSE2:
dcx d
mvi m,0
PARSE3:
mvi b,8
call SETFLD
mvi b,3
cpi '.'
jz PARSE4
call PADFLD
jmp PARSE5
;
PARSE4:
inx d
call SETFLD
PARSE5:
mvi b,4
PARSE6:
inx h
mvi m,0
dcr b
jnz PARSE6
mvi b,8
cpi ';'
jz PARSE7
call PADFLD
jmp PARSE8
PARSE7:
inx d
call PWFLD
PARSE8:
push d
call DEBLNK
call DELIM
jnz PARSE81
inx sp
inx sp
jmp PARSE82
PARSE81:
pop d
PARSE82:
mov a,c
ora a
pop b
mov a,c
pop b
inx h
mov m,c
inx h
mov m,b
inx h
mov m,a
xchg
rnz
lxi h,0
ret
PARSE9:
pop h
pop h
lxi h,0FFFFh
ret
;
SETFLD:
call DELIM
jz PADFLD
inx h
cpi '*'
jnz SETFD1
mvi m,'?'
dcr b
jnz SETFLD
jmp SETFD2
SETFD1:
mov m,a
dcr b
SETFD2:
inx d
jnz SETFLD
SETFD3:
call DELIM
rz
pop h
jmp PARSE9
;
PWFLD:
call DELIM
jz PADFLD
inx sp
inx sp
inx sp
inx sp
inx sp
inx sp
push d
push h
mvi l,0
xthl
dcx sp
dcx sp
PWFLD1:
inx sp
inx sp
xthl
inr l
xthl
dcx sp
dcx sp
inx h
mov m,a
inx d
dcr b
jz SETFD3
call DELIM
jnz PWFLD1
;
PADFLD:
inx h
mvi m,' '
dcr b
jnz PADFLD
ret
;
DELIM:
ldax d
mov c,a
ora a
rz
mvi c,0
cpi 0Dh
rz
mov c,a
cpi 09h
rz
cpi ' '
jc DELIM2
rz
cpi '.'
rz
cpi ':'
rz
cpi ';'
rz
cpi '='
rz
cpi ','
rz
cpi '/'
rz
cpi '['
rz
cpi ']'
rz
cpi '<'
rz
cpi '>'
rz
cpi 'a'
rc
cpi 'z'+1
jnc DELIM1
ani 05Fh
DELIM1:
ani 07Fh
ret
DELIM2:
pop h
jmp PARSE9
;
DEBLNK:
ldax d
cpi ' '
jz DBLNK1
cpi 09h
jz DBLNK1
ret
DBLNK1:
inx d
jmp DEBLNK
; End of the Parser
;
; GET a character from the console buffer
GNC:
push h
lxi h,CONBUF-1 ;get length
mov a,m
ora a ;zero?
mvi a,CR ;return with CR if so
jz GNCRET
dcr m ;lenght = length-1
lhld NEXTCOM ;next char address
mov a,m
inx h ;bump to next
shld NEXTCOM ;update
GNCRET:
pop h
TRANS:
cpi 7Fh ;Rubout?
rz
cpi ('A' or 0100000b)
rc
ani 1011111b ; clear upper case bit
ret
;
;
; Scan the buffer for the address read in ASCII from the terminal
;
SCANAD:
lxi d,00h ;zero out address
push d ;and save
;
lda CONBUF-1 ;get character count
cpi 05 ;5 is too many
jc SCAN0
stc ;set carry for routine
jmp SCNRET
SCAN0:
call GNC ;get a char
cpi CR ;end?
jz SCNRET ;to scnret if so
cpi '0' ;is it >0?
jnc SCAN01 ;bad character
jmp SCNRET
SCAN01:
cpi '@'
jnz SCAN02 ;bad character
stc
jmp SCNRET ;return on bad file
SCAN02:
jnc SCAN1 ;must be A-F
sui 030h ;normalize 0-9
jmp SCAN2
SCAN1:
cpi 'G' ;is it out of range?
jc SCAN11
stc
jmp SCNRET
SCAN11:
sui 037h ;normalize
SCAN2:
mov l,a ;character in low of DE
lda CONBUF-1 ;get # left
adi 1 ;readjust
mov c,a
mvi h,00 ;zero out high order
SCAN3:
dcr c ;dec to set flag
jz SCAN4 ;were done
dad h ;shift 1bit left
dad h ;same
dad h ;same
dad h ;finally
jmp SCAN3 ;back for more
;
SCAN4:
pop d ;ready for or
mov a,d ;high order
ora h ;
mov d,a
mov a,e ;low order
ora l ;ORed
mov e,a ;back
push d ;save
jmp SCAN0 ;get more characters
SCNRET:
pop d ;hl = address
xchg ;DE->HL
ret
;
;
; *********************************
; * *
; * Data Structures *
; * *
; *********************************
;
SCBPB:
db 03Ah ;SCB address
db 0
;
SADDR: dw 0 ;write start address
FADDR: dw 0 ;write finish address
BIOSAD: dw 0 ;WarmBOOT bios address
NEXTCOM: dw 0 ;address of next character to read
ONEFLG: db 0
RSLT: dw 0
CHGJMP db FALSE
;
SCBADR: dw 0 ;Scb address
;
BIOSMD: db 0 ;if non-zero change LXI @jmpadr to
;JUMP when removed.
;
BUFFER: db CONMAX
db 0 ;# of console characters read
CONBUF: ds CONMAX
;
SIGNON: db CR,LF,'CP/M 3 SAVE - Version ',VERSION/10+'0','.',VERSION mod 10+'0','$'
FLEPRMPT: db CR,LF,'Enter file '
db '(type RETURN to exit): $'
DELFLE: db CR,LF,'Delete $'
SPRMPT: db CR,LF,'Beginning hex address $'
FPRMPT: db CR,LF,'Ending hex address $'
ENDMSG: db CR,LF,'$'
;
; Error messages......
CERROR: db CR,LF,'ERROR: Bad close.$'
NODIR: db CR,LF,'ERROR: No directory space.$'
NOBLK: db CR,LF,'ERROR: No disk space.$'
;
; Stack for program
ds STKSZE
STACK:
end ;Physical end of program