Files
86-DOS-0.11/CPM Tools/DOSGEN.Z80
T
2024-11-23 09:47:10 +00:00

595 lines
12 KiB
Z80 Assembly

; DOSGEN The SYSGEN for 86-DOS version 1.10
; by Broken Pipe
; Runs on the Z80 under CP/M
;
; Disk Format Parameters
;
SECSIZ: EQU 128 ;128 bytes per sector
SPT: EQU 26 ;26 sectors per track
SYSTRK: EQU 2 ;2 tracks for just system
NEWTRK: EQU 4 ;4 tracks for new disk
CLSSIZ: EQU 4 ;4 records per cluster
;
; File System Parameters
;
OBOOT: EQU 0 ;Sector 0
OBIOS: EQU 128 ;Sector 1
ODOS: EQU 1152 ;Sector 9
OFAT1: EQU 6656 ;Sector 52
OFAT2: EQU 7424 ;Sector 58
ODIR: EQU 8192 ;Sector 64
OUSER: EQU 9216 ;Sector 72
FATSIZ: EQU OFAT2-OFAT1 ;FAT size in bytes
DIRSIZ: EQU OUSER-ODIR ;Directory size in bytes
;
; BIOS call offsets.
;
SELDSK: EQU 24
SETTRK: EQU 27
SETSEC: EQU 30
SETDMA: EQU 33
WRITE: EQU 39
;
; BDOS call numbers.
;
WRITESTR: EQU 9
DRVSET: EQU 14
OPEN: EQU 15
READ: EQU 20
DRVGET: EQU 25
DMAOFF: EQU 26
;
; Zero Page and FCB fields.
;
BDOS: EQU 5
FCB: EQU 5CH
FCB2: EQU 6CH
EX: EQU 12
S1: EQU 13
S2: EQU 14
RC: EQU 15
;
; Program entry point.
;
ORG 100H
LD SP,STACK ;Setup new stack
LD C,DRVGET
CALL BDOS ;Get current drive
LD (CURDRV),A ;Save it for EXIT
LD A,(FCB) ;Get dest drive letter
OR A ;Default drive?
JR NZ,DRVOK ;No, all good
LD DE,DRVBAD ;Cannot transfer system to default drive
JR EXIT
DRVOK: DEC A ;Convert to actual drive number
LD (DRIVE),A
CALL SETSYS ;Setup system area
LD IX,BRCFCB ;Load boot record FCB
CALL FOPEN ;Open it
LD HL,PBOOT ;Set DMA to boot record in memory
CALL FREAD ;Read it
LD IX,BIOFCB ;Load BIOS FCB
CALL FOPEN ;Open it
LD HL,PBIOS ;Set DMA to BIOS in memory
CALL FREAD ;Read it
LD IX,DOSFCB ;Load DOS FCB
CALL FOPEN ;Open it
LD HL,PDOS ;Set DMA to DOS in memory
CALL FREAD ;Read it
LD A,(FCB2+1) ;Get first char of second param
OR 20H ;Convert to lower case
CP 'n' ;New disk?
JR NZ,SKIP ;No, skip writing file system
CALL SETFAT ;Setup FAT image
LD IX,CMDFCB ;Load COMMAND FCB
CALL ADDFIL ;Add it to FAT and directory
LD IX,RDCFCB ;Load RDCPM FCB
CALL ADDFIL ;Add it to FAT and directory
SKIP: CALL WRTSYS ;Write system in memory to disk
LD DE,DONE ;Fall through to EXIT
;
; Prints the string in DE and exits. Does not return.
;
EXIT:
LD C,WRITESTR
CALL BDOS ;Print message
LD A,(CURDRV)
LD C,A
PUSH BC ;Save drive number
CALL SEL ;Restore old drive
LD C,DRVSET
POP DE ;Restore drive number
CALL BDOS
JP 0
;
; Sets up the system area in memory. AF, BC, DE and HL destroyed.
;
SETSYS:
LD HL,PBOOT ;Fill system area with 0
LD DE,PBOOT+1
LD BC,PFAT1-PBOOT-1
LD (HL),0
LDIR
LD A,SYSTRK ;Set track count to SYSTRK
LD (NUMTRK),A
RET
;
; Sets up a FAT image after the system area. AF, BC, DE and HL
; destroyed.
;
SETFAT:
LD HL,PFAT1 ;Fill FATs with 0
LD DE,PFAT1+1
LD BC,FATSIZ+FATSIZ-1
LD (HL),0
LDIR
LD HL,PDIR ;Fill directory with 0E5H
LD DE,PDIR+1
LD BC,DIRSIZ-1
LD (HL),0E5H
LDIR
LD HL,0 ;Set current cluster to 0
LD (CURCLS),HL
LD HL,PDIR ;Set current directory entry
LD (CURDIR),HL
LD HL,PUSER ;Set current data pointer
LD (CURPTR),HL
LD A,NEWTRK ;Set track count to NEWTRK
LD (NUMTRK),A
LD DE,0FFFH ;Write the first 2 FAT entries
CALL PUTCLS
CALL PUTCLS
RET
;
; Opens a file with its FCB in IX. IX preserved, all other
; registers destroyed. Does not return if the requested file
; cannot be opened.
;
FOPEN:
LD (IX+EX),0 ;Set EX to 0
LD (IX+S1),0 ;Set S1 to 0
LD (IX+S2),0 ;Set S2 to 0
LD (IX+RC),0 ;Set RC to 0
LD C,OPEN
PUSH IX
POP DE ;DE points to FCB
PUSH IX ;Save registers
CALL BDOS ;Open file
POP IX ;Restore registers
CP -1 ;Error?
RET NZ ;No, return
LD DE,OFAIL
LD C,WRITESTR
PUSH IX ;Save registers
CALL BDOS ;Print first part of open fail message
POP IX ;Restore registers
LD (IX+12),'$' ;Turn FCB file name into a string
INC IX ;Point to the file name
PUSH IX
POP DE ;DE points to file name
LD C,WRITESTR
CALL BDOS ;Print it
LD DE,OPENER
JP EXIT ;Exit with last part of the message.
;
; Reads all records of a file pointed to by its FCB in IX to the
; DMA address in HL. IX preserved, all other registers destroyed.
; Does not return if reading fails. Returns number of records
; read in BC.
;
FREAD:
LD IY,0 ;Set record count to 0
RDLOOP: PUSH HL
POP DE ;DE points to DMA
LD C,DMAOFF
PUSH IX ;Save registers
PUSH HL
PUSH IY
CALL BDOS ;Set DMA before reading
POP IY
POP HL ;Restore registers
POP IX
LDREC: LD C,READ
PUSH IX
POP DE
PUSH IX ;Save registers
PUSH HL
PUSH IY
CALL BDOS ;Read a record
POP IY
POP HL ;Restore registers
POP IX
CP 0 ;Success?
JR NZ,CHKEND ;No, check if EOF
INC IY ;Increment record count
LD DE,128
ADD HL,DE ;Increment DMA by 128 bytes
JR RDLOOP ;Read next record
CHKEND: PUSH AF ;Save AF
PUSH IX ;Save IX
PUSH IY ;Save record count
LD C,DMAOFF
LD DE,80H
CALL BDOS ;Reset DMA to default
POP BC ;Restore record count to BC
POP IX ;Restore IX
POP AF ;Restore AF
CP 1 ;EOF?
RET Z ;Yes, done
LD DE,RFAIL
LD C,WRITESTR
PUSH IX ;Save registers
CALL BDOS ;Print first part of read fail message
POP IX ;Restore registers
LD (IX+12),'$' ;Turn FCB file name into a string
INC IX ;Point to the file name
PUSH IX
POP DE ;DE points to file name
LD C,WRITESTR
CALL BDOS ;Print it
LD DE,RDERR
JP EXIT ;Exit with last part of the message.
;
; Multiplies BC by DE and returns the result in HL. AF and BC destroyed.
;
MLTPLY:
LD A,B
LD B,16
MLOOP: ADD HL,HL
SLA C
RLA
JR NC,MNOADD
ADD HL,DE
MNOADD: DJNZ MLOOP
RET
;
; Divides BC by DE and returns the result in BC, with remainder in HL.
; AF and BC destroyed.
;
DIVIDE:
LD HL,0
LD A,B
LD B,8
DIVLP1: RLA
ADC HL,HL
SBC HL,DE
JR NC,DNADD1
ADD HL,DE
DNADD1: DJNZ DIVLP1
RLA
CPL
LD B,A
LD A,C
LD C,B
LD B,8
DIVLP2: RLA
ADC HL,HL
SBC HL,DE
JR NC,DNADD2
ADD HL,DE
DNADD2: DJNZ DIVLP2
RLA
CPL
LD B,C
LD C,A
RET
;
; Writes the lowest 12 bits of DE to the BCth entry of the FAT pointed
; to by HL. AF and HL destroyed.
;
FATPUT:
PUSH BC ;Save registers
PUSH DE
LD A,D ;Make DE a 12-bit number
AND 0FH
LD D,A
BIT 0,C ;Is cluster even?
PUSH AF ;Save flags
ADD HL,BC ;HL = HL + BC * 1.5
SRL B
RR C
ADD HL,BC
POP AF ;Restore flags
JR Z,NOTODD ;Jump if even
LD A,(HL) ;Odd, get byte at HL
AND 0FH ;Clear high bits
PUSH HL ;Save HL
LD H,D ;Shift DE by 4 bits
LD L,E
ADD HL,HL
ADD HL,HL
ADD HL,HL
ADD HL,HL
LD D,H
LD E,L
POP HL ;Restore HL
OR E ;Combine with high bits of E
LD (HL),A ;Save it back
INC HL ;Point to next byte
LD (HL),D ;Save high byte of DE
JR PUTDON ;Done, pop registers and return
NOTODD: LD (HL),E ;Even, save low byte of DE
INC HL ;Point to next byte
LD A,(HL) ;Get byte at HL
AND 0F0H ;Clear low 4 bits
OR D ;Combine with high byte of DE
LD (HL),A ;Save it back
PUTDON: POP DE ;Restore registers
POP BC
RET
;
; Writes the lowest 12 bits of DE to next entry of the FATs at
; PFAT1 and PFAT2. AF, BC and HL destroyed. (CURCLS) automatically
; incremented.
;
PUTCLS:
LD HL,PFAT1
LD BC,(CURCLS)
CALL FATPUT
LD HL,PFAT2
CALL FATPUT
INC BC
LD (CURCLS),BC
RET
;
; Converts the record count in BC to cluster count. AF and HL
; destroyed.
;
RECTOCLS:
LD HL,CLSSIZ-1 ;Add CLSSIZ-1
ADD HL,BC
LD B,H
LD C,L
LD DE,CLSSIZ
CALL DIVIDE ;Divide by CLSSIZ
RET
;
; Adds a file pointed to by its FCB in IX to the system FAT image.
; All registers destroyed. Does not return if the file cannopt be
; opened and read. (CURCLS), (CURDIR) and (CURPTR) automatically
; incremented.
;
ADDFIL:
CALL FOPEN ;Open the file
LD HL,(CURPTR) ;Set DMA to current file location
CALL FREAD ;Read it
PUSH BC ;Save record count
CALL RECTOCLS ;Convert to number of clusters
PUSH BC ;Save cluster count
LD H,B ;Multiply by 128
LD L,C
XOR A
SRL H
RR L
RRA
LD H,L
LD L,A
LD B,H ;Multiply by CLSSIZ for bytes
LD C,L
LD DE,CLSSIZ
CALL MLTPLY
LD BC,(CURPTR) ;Load current data pointer
ADD HL,BC ;Add size and pointer
LD (CURPTR),HL ;Save new data pointer
POP BC ;Restore cluster count
LD H,B ;Save cluster count to HL
LD L,C
LD DE,(CURCLS) ;Load current cluster
PUSH DE ;Save as starting cluster
FATLP: OR A ;Decrement cluster count
LD DE,1
SBC HL,DE
JR Z,LSTCLS ;Handle last cluster if 0
JP M,EMPTY ;Handle empty file if negative
LD DE,(CURCLS) ;Get current cluster
INC DE ;Next cluster = current + 1
PUSH HL
CALL PUTCLS ;Write FAT entry
POP HL
JR FATLP ;Go to next cluster
LSTCLS: LD DE,0FFFH ;Load end-of-chain value
CALL PUTCLS ;Write FAT entry
JR WRTDIR ;Go write directory entry
EMPTY: POP DE ;Discard saved starting cluster
LD DE,0 ;Save 0 as new starting cluster
PUSH DE
WRTDIR: LD DE,(CURDIR) ;Load pointer to current dir entry
PUSH IX
POP HL ;HL points to FCB
INC HL ;Increment to point to file name
LD BC,11 ;11 characters to copy
LDIR ;Copy file name over
POP HL ;Restore starting cluster number
LD A,L ;Write it to directory entry
LD (DE),A
INC DE
LD A,H
LD (DE),A
INC DE
POP HL ;Restore record count
XOR A ;Multiply by 128 to get file size
SRL H
RR L
RRA
LD H,L
LD L,A
LD A,L ;Write file size
LD (DE),A
INC DE
LD A,H
LD (DE),A
INC DE
XOR A ;Write 0 for high byte of file size
LD (DE),A
INC DE
LD (CURDIR),DE ;Update current directory pointer
RET
;
; BIOS drive select routine, drive in C. All registers destroyed.
;
SEL:
LD HL,(1)
LD DE,SELDSK
ADD HL,DE
JP (HL)
;
; BIOS set track routine, track in C. All registers destroyed.
;
TRK:
LD HL,(1)
LD DE,SETTRK
ADD HL,DE
JP (HL)
;
; BIOS set sector routine, sector in C. All registers destroyed.
;
SEC:
LD HL,(1)
LD DE,SETSEC
ADD HL,DE
JP (HL)
;
; BIOS set DMA routine, DMA in BC. All registers destroyed.
;
DMA:
LD HL,(1)
LD DE,SETDMA
ADD HL,DE
JP (HL)
;
; BIOS disk write routine. All registers destroyed.
;
DWRITE:
LD HL,(1)
LD DE,WRITE
ADD HL,DE
JP (HL)
;
; Writes the system in memory at LOADP to the first (NUMTRK) tracks of
; the disk in (DRIVE). All registers destroyed.
;
WRTSYS:
LD HL,DRIVE
LD C,(HL)
CALL SEL ;Select dest drive
LD HL,LOADP-SECSIZ ;Load point in RAM for system - SECSIZ
LD (DMADDR),HL ;Save it
LD A,-1 ;Start with track = -1
LD (TRACK),A
WRTTRK: LD HL,TRACK
INC (HL) ;Next track
LD A,(NUMTRK) ;Number of tracks to write
CP (HL) ;All tracks done?
RET Z ;Yes, system transfer complete
LD C,(HL)
CALL TRK ;Set track number
LD A,-1 ;Start with sector = -1
LD (SECTOR),A
WRTSEC: LD A,SPT ;Sectors per track
LD HL,SECTOR
INC (HL) ;Next sector
CP (HL) ;All sectors done?
JR Z,WRTTRK ;Yes, track write complete
LD C,(HL) ;Get sector number
INC C ;One-based indexing
CALL SEC ;Set sector number
LD HL,(DMADDR)
LD BC,SECSIZ
ADD HL,BC ;Increment DMA
LD (DMADDR),HL
LD B,H
LD C,L ;BC = HL for DMA call
CALL DMA ;Set DMA
XOR A
LD (RETRY),A ;Clear retry count
TRYSEC: LD A,(RETRY) ;Get retry count
CP 10 ;Too many (more than 10) retries?
JR C,TRYOK ;No, can perform write
LD DE,WRTER ;Load write error message
JP EXIT ;Print and quit
TRYOK: INC A
LD (RETRY),A ;Increment retry count
CALL DWRITE ;Raw disk write
OR A ;Success?
JR Z,WRTSEC ;Yes, next sector
JR TRYSEC ;Retry the write
;
; Messages.
;
DRVBAD: DB 'Bad drive specification',13,10,'$'
OFAIL: DB 'Failed to open "$'
OPENER: DB '"',13,10,'$'
RFAIL: DB 'Failed to read "$'
RDERR: DB '"',13,10,'$'
WRTER: DB 'Disk write error',13,10,'$'
DONE: DB 'System transfered',13,10,'$'
;
; FCBs. Open these from bottom to top, one at a time.
;
RDCFCB: DB 0,'RDCPM COM'
CMDFCB: DB 0,'COMMAND COM'
DOSFCB: DB 0,'86DOS COM'
BIOFCB: DB 0,'DOSIO COM'
BRCFCB: DB 0,'BOOT COM'
DS 24
;
; Global variables.
;
CURDRV: DS 1
DRIVE: DS 1
TRACK: DS 1
SECTOR: DS 1
DMADDR: DS 2
RETRY: DS 1
CURCLS: DS 2
CURDIR: DS 2
CURPTR: DS 2
NUMTRK: DS 1
;
; Stack of 128 bytes.
;
DS 128
STACK:
;
; Load point of the 86-DOS system and pointers to different system
; components.
;
LOADP:
PBOOT: EQU $+OBOOT ;Relative offset of BOOT
PBIOS: EQU $+OBIOS ;Relative offset of BIOS
PDOS: EQU $+ODOS ;Relative offset of 86-DOS
PFAT1: EQU $+OFAT1 ;Relative offset of FAT 1
PFAT2: EQU $+OFAT2 ;Relative offset of FAT 2
PDIR: EQU $+ODIR ;Relative offset of directory
PUSER: EQU $+OUSER ;Relative offset of user area