mirror of
https://github.com/a2stuff/prodos-drivers.git
synced 2024-12-22 05:29:54 +00:00
536ed14b60
ProDOS prefers disks sorted with the fastest (e.g. ramdisks) at the end and slowest (e.g. 5.25 floppies) at the start. This facilitates pathname resolution starting at the end of DEVLST, so misses are cheap.
714 lines
18 KiB
ArmAsm
714 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 :-
|
|
|
|
;; Otherwise, insert at end. ProDOS resolves names to devices
|
|
;; from last to first, so fast RAM disks should go last.
|
|
inc DEVCNT
|
|
ldy DEVCNT
|
|
|
|
;; 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 log_message
|
|
scrcode 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 log_message
|
|
scrcode PRODUCT, " - Not Found"
|
|
.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"
|
|
;;; ************************************************************
|