prodos-drivers/ram.drv/ram.drv.system.s
2020-12-21 13:27:54 -08:00

719 lines
18 KiB
ArmAsm

;;; Disassembly of "RAM.SYSTEM" found on Mouse Desk 2.0 images
;;; Based on Glen E. Bredon's "RAM.DRV.SYSTEM"
;;; Some details c/o http://boutillon.free.fr/Underground/Outils/Ram_Drv_System/Ram_Drv_System.html
;;;
;;; Modifications:
;;; * Chain to next .SYSTEM file dynamically
.setcpu "6502"
.linecont +
.feature string_escapes
.include "apple2.inc"
.include "apple2.mac"
.include "opcodes.inc"
.include "../inc/apple2.inc"
.include "../inc/macros.inc"
.include "../inc/prodos.inc"
;;; ************************************************************
.include "../inc/driver_preamble.inc"
;;; ************************************************************
;;; ============================================================
;;;
;;; Driver Installer
;;;
;;; ============================================================
;;; ============================================================
;;; Configuration Parameters
.define PRODUCT "RAMWorks RAM Disk"
zp_sig_addr := $06
zpproc_addr := $B0
zpproc_relay_addr := $2D0
data_buf := $1C00 ; I/O when chaining to next SYS file
driver_target := $FF00 ; Install location in ProDOS
kMaxUsableBanks = 24 ; Why is this capped so low???
; (driver has room for another ~20?)
banks_to_reserve: .byte 0 ; banks to reserve (e.g. for AppleWorks)
unitnum: .byte $03 ; S3D1; could be $B for S3D2
;;; ============================================================
;;; Install the driver
.proc maybe_install_driver
sta CLR80COL
ldy #0
sty BANKSEL
sta ALTZPON ; Use ZP to probe banks
;; Clear map1 / map2 (256 bytes) to $FF
lda #$FF
: sta map1,y
iny
bne :-
;; Stash first two bytes of each bank (128 possible banks)
: sty BANKSEL
lda $00
sta stash_00,y
lda $01
sta stash_01,y
iny
bpl :-
dey
;; Write bank num/complement at $0/$1
: sty BANKSEL
sty $00
tya
eor #$FF
sta $01
dey
bne :-
;; Y = 0
;; Reset signature bytes on main/aux banks
sty BANKSEL
sty $00
sty $01
sta ALTZPOFF
sty $00
sty $01
sta ALTZPON
lda banks_to_reserve
sta reserved_banks
;;; ============================================================
;; Copy into every bank
ldy #1
bank_loop:
;; Check bank for signature bytes (bank num/complement at $0/$1)
sty BANKSEL
cpy $00
bne next_bank
tya
eor #$FF
eor $01
bne next_bank
cpy $00 ; Bank 0 (aux) is reserved for 128k apps
bne next_bank
;; Flag as available in map2
;; (map2,N = N if available, $FF otherwise)
tya
sta map2,y
;; Skip over reserved banks, then start storing them in the map
ldx reserved_banks
bne :+
sta first_used_bank
: dec reserved_banks
bpl next_bank
sta map1,y
;; (map1,N = N if available, $FF otherwise - also???)
;; Copy helper proc into bank's ZP
ldx #sizeof_zpproc
: lda zpproc-1,x
sta zpproc_addr-1,x
dex
bne :-
next_bank:
iny
bpl bank_loop
;;; ============================================================
;; Y = $80
;; Restore stashed $0/$1 bytes of back
;; (except first, in first_used_bank ???)
loop0: lda map2-1,y
bmi :+
cmp first_used_bank
beq :+
sta BANKSEL
lda stash_00-1,y
sta $00
lda stash_01-1,y
sta $01
: dey
bne loop0
;; Y = 0
sty BANKSEL
sty $00
;; Count number of available banks, and populate
;; driver_bank_list with list of banks.
ldx #$FF
loop1: inx
cpx #kMaxUsableBanks
bcs break
loop2: iny
bmi break
lda map1,y
bmi loop2
sta driver_bank_list,x
bpl loop1
break:
;; Patch driver with block-specific data
;; X = number of available banks
;; Compute number of blocks
txa
lsr a
sta vol_dir_header+VolumeDirectoryHeader::total_blocks+1
ror vol_dir_header+VolumeDirectoryHeader::total_blocks
stx driver_block_x ; num banks
dex ; -1
stx num_banks_minus_one
bmi fail ; 0 banks? give up.
lda vol_dir_header+VolumeDirectoryHeader::total_blocks
sec
sbc driver_block_x
and #$F8
sta vol_dir_header+VolumeDirectoryHeader::total_blocks
sta driver_blocks_lo
bcs :+
dec vol_dir_header+VolumeDirectoryHeader::total_blocks+1
: lda vol_dir_header+VolumeDirectoryHeader::total_blocks+1
sta driver_blocks_hi
lda driver_bank_list
sta BANKSEL
lda $00
beq fail
;; Check for ZP signature - if not found, set it and install.
ldx #sig_len-1
: lda sig,x
cmp zp_sig_addr,x
bne set_sig
dex
bpl :-
bit BUTN1 ; escape hatch in case of loop ???
bmi L21F0
jmp do_install
fail: jmp install_failure
sloop: lda sig,x
set_sig:
sta zp_sig_addr,x
dex
bpl sloop
;;; ============================================================
;;; Prepare key blocks in
L21F0: sta ALTZPOFF
;; Stamp current date/time into vol_dir_header
ldy #3
: lda DATELO,y
sta vol_dir_header+VolumeDirectoryHeader::creation_date,y
dey
bpl :-
;; Fill pages $06-$0F with 00-FF
sta RAMWRTON
iny
tya
: sta $0600,y ; Block 2 - volume dir
sta $0700,y
sta $0800,y ; Block 3 - volume dir
sta $0900,y
sta $0A00,y ; Block 4 - volume dir
sta $0B00,y
sta $0C00,y ; Block 5 - volume dir
sta $0D00,y
sta $0E00,y ; Block 6 - volume bitmap
sta $0F00,y
iny
bne :-
;; Copy vol_dir_header into page $06
ldy #.sizeof(VolumeDirectoryHeader)-1
: lda vol_dir_header,y
sta $0600,y
dey
bpl :-
ldy #$02
sty $0800
iny
sty $0A00
iny
sty $0C00
sty $0802
iny
sty $0A02
ptr := $3C
lda vol_dir_header+VolumeDirectoryHeader::total_blocks
sta ptr
lda vol_dir_header+VolumeDirectoryHeader::total_blocks+1
lsr a
ror ptr
lsr a
ror ptr
lsr a
ror ptr
clc
adc #$0E
sta ptr+1
ldy #0
tya
: sta (ptr),y
lda ptr
sec
sbc #1
sta ptr
lda #$FF
bcs :-
dec ptr+1
ldx ptr+1
cpx #$0E
bcs :-
lda #$01
sta $0E00
;;; ============================================================
do_install:
lda #0
sta RAMWRTOFF
sta ALTZPOFF
sta BANKSEL
bit LCBANK1
bit LCBANK1
lda #OPC_CLD ; signature
cmp driver_target
beq copy_driver
sta ALTZPON ; Maybe in AUX?
cmp driver_target
beq copy_driver
cmp $DE00 ; ???
beq copy_driver
sta ALTZPOFF
;; Copy driver into place
copy_driver:
ldy #0
: lda driver_src,y
sta driver_target,y
iny
cpy #sizeof_driver
bcc :-
;; Check if unitnum already has a device
ldy DEVCNT
: lda DEVLST,y
lsr a
lsr a
lsr a
lsr a
cmp unitnum
beq install_device
dey
bpl :-
;; Shift devices up by one
inc DEVCNT
ldy DEVCNT
: lda DEVLST-1,y
sta DEVLST,y
dey
bne :-
;; Install device in ProDOS via DEVLST/DEVADR.
;; (Y has index in DEVLST)
install_device:
lda unitnum
asl a
tax
asl a
asl a
asl a
sta on_line_params+1 ; unit_number
ora #$0E ; $3E - signature byte used by DeskTop
sta DEVLST,y
copy16 #(driver_target+1), DEVADR,x
;; Did we install into S3D2?
lda unitnum
cmp #$0B ; Slot 3 Drive 2
beq finish
;; No, so uninstall S3D2 (regular /RAM)
ldy DEVCNT
: lda DEVLST,y
and #$F0
cmp #$B0 ; Slot 3 drive 2 i.e. normal /RAM
beq found
dey
bpl :-
bmi finish ; always
;; Actually remove from DEVLST
slot3d2_devadr := DEVADR + $10 + 3*2
found: ldx slot3d2_devadr + 1
inx
bne finish
: copy DEVLST+1,y, DEVLST,y
iny
cpy DEVCNT
bcc :-
beq :-
dec DEVCNT
copy16 NODEV, slot3d2_devadr ; clear driver
finish: bit ROMIN2
MLI_CALL ON_LINE, on_line_params
ldx #$00
lda on_line_params_buffer
ora L239F
bne install_success
bcc install_success
copy #$FF, L239F
sta ALTZPON
copy driver_bank_list, BANKSEL
stx $06
stx BANKSEL
stx vol_dir_header+VolumeDirectoryHeader::total_blocks
jmp maybe_install_driver ; retry???
install_success:
sta ALTZPOFF
jsr HOME
jsr zstrout
scrcode "\r\r\r", PRODUCT, " - "
.byte 0
;; Initialize Applesoft zero page locations required by LINPRNT
copy #0, SHIFT_SIGN_EXT ; required by FP routines
copy #TEMPST, TEMPPT ; string descriptor pointer
copy #1, SPEEDZ ; output delay
copy #0, FLASH_BIT ; character set mask
ldx vol_dir_header+VolumeDirectoryHeader::total_blocks
lda vol_dir_header+VolumeDirectoryHeader::total_blocks+1
jsr LINPRNT
jsr zstrout
scrcode " Blocks"
.byte 0
rts
install_failure:
sta ALTZPOFF
jsr HOME
jsr zstrout
scrcode "\r\r\r", PRODUCT, " - Not Installed"
.byte 0
rts
;;; ============================================================
;;; Installed on zero page of each bank at $B0
.proc zpproc
pushorg ::zpproc_addr
sta $E0 ; dst1 hi
bcs :+
sty $E0 ; dst1 hi
tay
: lda #$00
sta RAMWRTON
bcc :+
txa
ldx #$00
sta RAMWRTOFF
sta RAMRDON
;; One block = two pages
: sty $DD ; src1 hi
iny
sty $E3 ; src2 hi
sta $DF ; dst1 lo
sta $E5 ; dst2 lo
stx $DC ; src1 lo
stx $E2 ; src2 lo
ldy $E0 ; dst1 hi
iny
sty $E6 ; dst2 hi
ldy #$00
: lda $1000,y ; src1
sta $1000,y ; dst1
lda $1000,y ; src2
sta $1000,y ; dst2
iny
bne :-
sta RAMWRTOFF
sta RAMRDOFF
clc
bit $02E4
rts
poporg
.endproc
sizeof_zpproc := .sizeof(zpproc)
;;; ============================================================
on_line_params_buffer := $220
DEFINE_ON_LINE_PARAMS on_line_params, $30, on_line_params_buffer
num_banks_minus_one:
.byte 0
L239F: .byte 0
sig: scrcode "GEB" ; signature sequence - Glen E. Bredon
sig_len = * - sig
;; Volume Directory Header
.proc vol_dir_header
.word 0 ; preceding block number
.word $03 ; succeeding block number
.byte ST_VOLUME_DIRECTORY << 4 | 3 ; storage type / name length
.byte "RAM" ; name field is 15 bytes
.res 15-3
.res 8, 0 ; reserved (8 bytes)
.word 0, 0 ; creation date/time
.byte 1 ; version (1 = ProDOS 2.0)
.byte 0 ; min_version
.byte ACCESS_DEFAULT ; access
.byte $27 ; entry_length
.byte $D ; entries_per_block
.word 0 ; file_count
.word 6 ; bit_map_pointer
blocks: .word 0 ; total_blocks
.endproc
.assert .sizeof(vol_dir_header) = .sizeof(VolumeDirectoryHeader), error, "Size mismatch"
.endproc
;;; ============================================================
;;; Ram Disk Driver - installed at $FF00
;;; ============================================================
.proc driver_src
pushorg ::driver_target
driver_start := *
start: cld ; used as a signature
lda DRIVER_COMMAND
bne not_status
driver_blocks_lo := *+1
ldx #0 ; self-modified - blocks low
driver_blocks_hi := *+1
ldy #0 ; self-modified - blocks high
LFF09: clc
bcc LFF83 ; always
not_status:
cmp #DRIVER_COMMAND_FORMAT
beq LFF09
;; COMMAND_READ or COMMAND_WRITE
LFF10: lda #$27
bcs rts1
lda RD80STORE
pha
sta CLR80COL
;; Save $40/41
lda $40
pha
lda $41
pha
lda DRIVER_BUFFER
sta $40
ldx DRIVER_BUFFER+1
inx
stx $41
jsr install_zpproc_relay
zpproc_relay_patch1_offset := $04
stx zpproc_relay_addr + zpproc_relay_patch1_offset
lda RDALTZP
zpproc_relay_patch2_offset := $14
sta zpproc_relay_addr + zpproc_relay_patch2_offset
lda DRIVER_BLOCK_NUMBER+1
pha
tax
lda DRIVER_BLOCK_NUMBER
LFF3C: sec
: iny
sbc #$7F
bcs :-
dex
bpl LFF3C
tya
adc DRIVER_BLOCK_NUMBER
bcc :+
inc DRIVER_BLOCK_NUMBER+1
: asl a
tay
lda DRIVER_BLOCK_NUMBER+1
rol a
tax
pla
sta DRIVER_BLOCK_NUMBER+1
driver_block_x := *+1
cpx #$0 ; self-modified - ???
bcs LFF74
tya
sbc #191
cmp #16
bcs :+
adc #208
tay
bit LCBANK2
: lda DRIVER_COMMAND
lsr a ; carry set = READ, clear = WRITE
lda bank_list,x
ldx DRIVER_BUFFER
jsr zpproc_relay_addr
bit LCBANK1
LFF74: jsr install_zpproc_relay
;; Restore $40/41
pla
sta $41
pla
sta $40
pla
bpl LFF83
sta SET80COL
LFF83: lda #$00
bcs LFF10
rts1: rts
install_zpproc_relay:
ldy #sizeof_zpproc_relay+1
: ldx zpproc_relay-1,y
lda zpproc_relay_addr-1,y
sta zpproc_relay-1,y
txa
sta zpproc_relay_addr-1,y
dey
bne :-
ldx DRIVER_BUFFER+1
bpl done
bit DRIVER_BUFFER+1
bvc done
: ldx $8000,y
lda (DRIVER_BUFFER),y
sta $8000,y
txa
sta (DRIVER_BUFFER),y
ldx $8100,y
lda ($40),y
sta $8100,y
txa
sta ($40),y
iny
bne :-
ldx #$80
done: rts
bank_list:
.res ::kMaxUsableBanks, 0
.proc zpproc_relay
sta BANKSEL
patch_loc1 := *+1
lda #$00
sta ALTZPON
jsr zpproc_addr
sty BANKSEL
bmi :+
sta ALTZPOFF
: rts
patch_loc2 := *
.endproc
sizeof_zpproc_relay := .sizeof(zpproc_relay)
patch_loc1_offset := zpproc_relay::patch_loc1 - zpproc_relay
patch_loc2_offset := zpproc_relay::patch_loc2 - zpproc_relay
;; These offsets can't be used directly due to ca65 addressing mode
;; assumptions, so just verify they are correct.
.assert zpproc_relay_patch1_offset = patch_loc1_offset, error, "Offset mismatch"
.assert zpproc_relay_patch2_offset = patch_loc2_offset, error, "Offset mismatch"
.byte 0
poporg
.endproc
sizeof_driver := .sizeof(driver_src)
driver_blocks_lo := driver_src + driver_src::driver_blocks_lo - driver_src::driver_start
driver_blocks_hi := driver_src + driver_src::driver_blocks_hi - driver_src::driver_start
driver_block_x := driver_src + driver_src::driver_block_x - driver_src::driver_start
driver_bank_list := driver_src + driver_src::bank_list - driver_src::driver_start
;;; ============================================================
;;; Scratch space beyond code used during driver install
reserved_banks := *
first_used_bank := *+1
map1 := *+2 ; len: $80
map2 := *+2+$80 ; len: $80
stash_00 := *+2+$100 ; len: $80
stash_01 := *+2+$180 ; len: $80
.assert stash_01+$80 < data_buf, error, "Too long"
;;; ************************************************************
.include "../inc/driver_postamble.inc"
;;; ************************************************************