initial snapshot

This commit is contained in:
Joshua Bell 2019-02-24 20:03:51 -08:00
commit fbd79badee
7 changed files with 1443 additions and 0 deletions

29
Makefile Normal file
View File

@ -0,0 +1,29 @@
CC65 = ~/dev/cc65/bin
CAFLAGS = --target apple2enh --list-bytes 0
CCFLAGS = --config apple2-asm.cfg
OUTDIR = out
HEADERS = $(wildcard inc/*.inc)
TARGETS = $(OUTDIR)/ram.system.SYS
.PHONY: clean all
all: $(OUTDIR) $(TARGETS)
$(OUTDIR):
mkdir -p $(OUTDIR)
clean:
rm -f $(OUTDIR)/*.o
rm -f $(OUTDIR)/*.list
rm -f $(TARGETS)
$(OUTDIR)/%.o: %.s $(HEADERS)
$(CC65)/ca65 $(CAFLAGS) --listing $(basename $@).list -o $@ $<
# System Files .SYS
$(OUTDIR)/%.SYS: $(OUTDIR)/%.o
$(CC65)/ld65 $(CCFLAGS) -o '$@' $<
xattr -wx prodos.AuxType '00 20' $@

27
inc/apple2.inc Normal file
View File

@ -0,0 +1,27 @@
;;; ============================================================
;;;
;;; More Apple II Symbols
;;;
;;; ============================================================
;;; Soft Switches
ROMIN2 := $C082
;;; ============================================================
;;; Aux Memory / Extended 80 Column Card
;;; ============================================================
;;; Softswitches
RAMRDOFF := $C002
RAMRDON := $C003
RAMWRTOFF := $C004
RAMWRTON := $C005
ALTZPOFF := $C008
ALTZPON := $C009
CLR80VID := $C00C
SET80VID := $C00D
RDALTZP := $C016
RD80STORE := $C018
RDPAGE2 := $C01C

101
inc/macros.inc Normal file
View File

@ -0,0 +1,101 @@
;;; ============================================================
;;; Generic Macros
;;; ============================================================
.define _is_immediate(arg) (.match (.mid (0, 1, {arg}), #))
.define _is_register(arg) (.match ({arg}, x) .or .match ({arg}, y))
.define _is_y_register(arg) (.match ({arg}, y))
.define _immediate_value(arg) (.right (.tcount ({arg})-1, {arg}))
.macro _op_lo op, arg
.if _is_immediate {arg}
op #<_immediate_value {arg}
.else
op arg
.endif
.endmacro
.macro _op_hi op, arg
.if _is_immediate {arg}
op #>_immediate_value {arg}
.else
op arg+1
.endif
.endmacro
;;; ============================================================
;;; Length-prefixed string
;;;
;;; Can include control chars by using:
;;;
;;; PASCAL_STRING {"abc",$0D,"def"}
.macro PASCAL_STRING str,res
.local data
.local end
.byte end - data
data: .byte str
end:
.if .paramcount > 1
.res res - (end - data), 0
.endif
.endmacro
;;; ============================================================
;;; Common patterns
.macro copy arg1, arg2, arg3, arg4
.if _is_register {arg2} && _is_register {arg4}
;; indexed load/indexed store
lda arg1,arg2
sta arg3,arg4
.elseif _is_register {arg2}
;; indexed load variant (arg2 is x or y)
lda arg1,arg2
sta arg3
.elseif _is_register {arg3}
;; indexed store variant (arg3 is x or y)
lda arg1
sta arg2,arg3
.else
lda arg1
sta arg2
.endif
.endmacro
;;; Copy 16-bit value
;;; copy16 #$1111, $2222 ; immediate, absolute
;;; copy16 $1111, $2222 ; absolute, absolute
;;; copy16 $1111,x, $2222 ; indirect load, absolute store
;;; copy16 $1111, $2222,x ; absolute load, indirect store
;;; copy16 $1111,x $2222,x ; indirect load, indirect store
;;; copy16 #$1111, $2222,x ; immediate load, indirect store
.macro copy16 arg1, arg2, arg3, arg4
.if _is_register {arg2} && _is_register {arg4}
;; indexed load/indexed store
lda arg1,arg2
sta arg3,arg4
lda arg1+1,arg2
sta arg3+1,arg4
.elseif _is_register {arg2}
;; indexed load variant (arg2 is x or y)
lda arg1,arg2
sta arg3
lda arg1+1,arg2
sta arg3+1
.elseif _is_register {arg3}
;; indexed store variant (arg3 is x or y)
_op_lo lda, {arg1}
sta arg2,arg3
_op_hi lda, {arg1}
sta arg2+1,arg3
.else
_op_lo lda, {arg1}
sta arg2
_op_hi lda, {arg1}
sta arg2+1
.endif
.endmacro

473
inc/prodos.inc Normal file
View File

@ -0,0 +1,473 @@
;;; ============================================================
;;;
;;; ProDOS MLI
;;;
;;; ============================================================
;;; Entry point / Global Page
MLI := $BF00 ; Entry point
DATETIME := $BF06 ; JMP to clock routine
DEVADR := $BF10 ; Device driver addresses ($BF10-$BF2F)
NODEV := $BF10 ; "No Device Connected" entry (slot 0)
DEVNUM := $BF30 ; Most recent accessed device
DEVCNT := $BF31 ; Number of on-line devices minus 1
DEVLST := $BF32 ; Up to 14 units ($BF32-$BF3F)
BITMAP := $BF58 ; System memory bitmap
BITMAP_SIZE = $18 ; Bits for pages $00 to $BF
DATELO := $BF90 ; Date lo
DATEHI := $BF91 ; Date hi
TIMELO := $BF92 ; Time lo
TIMEHI := $BF93 ; Time hi
LEVEL := $BF94 ; File level
MACHID := $BF98 ; Machine ID
SLTBYT := $BF99 ; '1' bits indicate rom in slot (bit#)
IVERSION := $BFFD ; Interpreter Version
KVERSION := $BFFF ; ProDOS Kernel Version
;;; Patch Locations
SELECTOR := $D100
BLOCK_SIZE = $200
;;; ============================================================
;;; MLI Calls
;;; ============================================================
;;; Housekeeping Calls
CREATE = $C0
DESTROY = $C1
RENAME = $C2
SET_FILE_INFO = $C3
GET_FILE_INFO = $C4
ON_LINE = $C5
SET_PREFIX = $C6
GET_PREFIX = $C7
;;; Filing Calls
OPEN = $C8
NEWLINE = $C9
READ = $CA
WRITE = $CB
CLOSE = $CC
FLUSH = $CD
SET_MARK = $CE
GET_MARK = $CF
SET_EOF = $D0
GET_EOF = $D1
SET_BUF = $D2
GET_BUF = $D3
;;; System Calls
GET_TIME = $82
ALLOC_INTERRUPT = $40
DEALLOC_INTERRUPT = $41
QUIT = $65
;;; Direct Disk Access Commands
READ_BLOCK = $80
WRITE_BLOCK = $81
;;; ============================================================
;;; File Types
;;; ============================================================
FT_TYPELESS = $00
FT_BAD = $01
FT_TEXT = $04 ; ASCII Text File *
FT_BINARY = $06 ; Generic Binary File *
FT_GRAPHICS = $08 ; Graphics File
FT_DIRECTORY = $0F ; Directory *
FT_ADB = $19 ; AppleWorks Database *
FT_AWP = $1A ; AppleWorks Word Processing *
FT_ASP = $1B ; AppleWorks Spreadsheet *
FT_SRC = $B0 ; IIgs system type; re-used?
FT_S16 = $B3 ; IIgs Application Program
FT_PAS = $EF ; Pascal Area *
FT_CMD = $F0 ; ProDOS Command File *
FT_INT = $FA ; Integer BASIC Program *
FT_IVR = $FB ; Integer BASIC Variable File *
FT_BASIC = $FC ; Applesoft BASIC Program *
FT_VAR = $FD ; Applesoft BASIC Variable File *
FT_REL = $FE ; EDASM/Contiki Relocatable File *
FT_SYSTEM = $FF ; ProDOS System File *
;;; Types marked with * are known to BASIC.SYSTEM and have an
;;; associated three-letter abbreviation.
;;; ============================================================
;;; Access
;;; ============================================================
ACCESS_DEFAULT = %11000011
ACCESS_LOCKED = %00100001
;;; ============================================================
;;; Storage Types
;;; ============================================================
ST_STANDARD_FILE = $01
ST_LINKED_DIRECTORY = $0D
ST_VOLUME_DIRECTORY = $0F
;;; ============================================================
;;; Errors
;;; ============================================================
ERR_DEVICE_NOT_CONNECTED = $28
ERR_WRITE_PROTECTED = $2B
ERR_INVALID_PATHNAME = $40
ERR_INVALID_REFERENCE = $43
ERR_PATH_NOT_FOUND = $44
ERR_VOL_NOT_FOUND = $45
ERR_FILE_NOT_FOUND = $46
ERR_DUPLICATE_FILENAME= $47
ERR_OVERRUN_ERROR = $48
ERR_VOLUME_DIR_FULL = $49
ERR_END_OF_FILE = $4C
ERR_ACCESS_ERROR = $4E
ERR_DUPLICATE_VOLUME = $57
ERR_NETWORK_ERROR = $88
;;; ============================================================
;;; Directory Structures
;;; ============================================================
STORAGE_TYPE_MASK = $F0
NAME_LENGTH_MASK = $0F
;;; Volume Directory Header structure
.struct VolumeDirectoryHeader
prev_block .word
next_block .word
storage_type_name_length .byte
file_name .byte 15
reserved .byte 8
creation_date .word
creation_time .word
version .byte
min_version .byte
access .byte
entry_length .byte
entries_per_block .byte
file_count .word
;; same through here ---------
bit_map_pointer .word
total_blocks .word
.endstruct
.assert .sizeof(VolumeDirectoryHeader) = $2B, error, "incorrect struct size"
;;; Subdirectory Header structure
.struct SubdirectoryHeader
prev_block .word
next_block .word
storage_type_name_length .byte
file_name .byte 15
reserved .byte 8
creation_date .word
creation_time .word
version .byte
min_version .byte
access .byte
entry_length .byte
entries_per_block .byte
file_count .word
;; same through here ---------
parent_pointer .word
parent_entry_number .byte
parent_entry_length .byte
.endstruct
.assert .sizeof(SubdirectoryHeader) = $2B, error, "incorrect struct size"
;; File Entry structure
.struct FileEntry
storage_type_name_length .byte
file_name .byte 15
file_type .byte
key_pointer .word
blocks_used .word
eof .faraddr
creation_date .word
creation_time .word
version .byte
min_version .byte
access .byte
aux_type .word
mod_date .word
mod_time .word
header_pointer .word
.endstruct
.assert .sizeof(FileEntry) = $27, error, "incorrect struct size"
;;; ============================================================
;;; ProDOS Driver Protocol
;;; ============================================================
;;; Addresses for command parameters
DRIVER_COMMAND := $42
DRIVER_UNIT_NUMBER := $43
DRIVER_BUFFER := $44
DRIVER_BLOCK_NUMBER := $46
;;; Commands
DRIVER_COMMAND_STATUS = 0
DRIVER_COMMAND_READ = 1
DRIVER_COMMAND_WRITE = 2
DRIVER_COMMAND_FORMAT = 3
;;; ============================================================
;;; Macros
;;; ============================================================
.macro MLI_CALL op, addr
jsr MLI
.byte op
.addr addr
.endmacro
.macro DEFINE_OPEN_PARAMS name, pn, io, rn
.if .xmatch(.string(pn), "pathname")
;; If 'pathname' is passed then expansion yields a circular reference.
.error "Can't pass 'pathname' label to DEFINE_*_PARAMS"
.endif
.if .xmatch(.string(io), "io_buffer")
.error "Can't pass 'io_buffer' label to DEFINE_*_PARAMS"
.endif
.proc name
param_count: .byte 3
pathname: .addr pn
io_buffer: .addr io
.ifnblank rn
ref_num: .byte rn
.else
ref_num: .byte 0
.endif
.endproc
.endmacro
.macro DEFINE_READ_PARAMS name, db, rc
.proc name
param_count: .byte 4
ref_num: .byte 0
data_buffer: .addr db
request_count: .word rc
trans_count: .word 0
.endproc
.endmacro
.macro DEFINE_WRITE_PARAMS name, db, rc
.proc name
param_count: .byte 4
ref_num: .byte 0
data_buffer: .addr db
request_count: .word rc
trans_count: .word 0
.endproc
.endmacro
.macro DEFINE_CLOSE_PARAMS name
.proc name
param_count: .byte 1
ref_num: .byte 0
.endproc
.endmacro
.macro DEFINE_FLUSH_PARAMS name
.proc name
param_count: .byte 1
ref_num: .byte 0
.endproc
.endmacro
.macro DEFINE_GET_FILE_INFO_PARAMS name, pn
.if .xmatch(.string(pn), "pathname")
;; If 'pathname' is passed then expansion yields a circular reference.
.error "Can't pass 'pathname' label to DEFINE_*_PARAMS"
.endif
.proc name
param_count: .byte $A
pathname: .addr pn
access: .byte 0
file_type: .byte 0
aux_type: .word 0
storage_type: .byte 0
blocks_used: .word 0
mod_date: .word 0
mod_time: .word 0
create_date: .word 0
create_time: .word 0
.endproc
.endmacro
.macro DEFINE_SET_MARK_PARAMS name, pos
.proc name
param_count: .byte 2
ref_num: .byte 0
position: .faraddr pos
.endproc
.endmacro
.macro DEFINE_ON_LINE_PARAMS name, un, db
.proc name
param_count: .byte 2
.ifnblank un
unit_num: .byte un
.else
unit_num: .byte 0
.endif
data_buffer: .addr db
.endproc
.endmacro
.macro DEFINE_READ_BLOCK_PARAMS name, db, bn
.proc name
param_count: .byte 3
unit_num: .byte 0
data_buffer: .addr db
block_num: .word bn
.endproc
.endmacro
.macro DEFINE_WRITE_BLOCK_PARAMS name, db, bn
.proc name
param_count: .byte 3
unit_num: .byte 0
data_buffer: .addr db
block_num: .word bn
.endproc
.endmacro
.macro DEFINE_ALLOC_INTERRUPT_PARAMS name, ic
.proc alloc_interrupt_params
param_count: .byte 2
int_num: .byte 0
int_code: .addr ic
.endproc
.endmacro
.macro DEFINE_DEALLOC_INTERRUPT_PARAMS name
.proc dealloc_interrupt_params
param_count: .byte 1
int_num: .byte 0
.endproc
.endmacro
.macro DEFINE_QUIT_PARAMS name, ext, pathname
.proc name
param_count: .byte 4
.ifnblank ext
.byte ext
.else
.byte 0
.endif
.ifnblank pathname
.word pathname
.else
.word 0
.endif
.byte 0
.word 0
.endproc
.endmacro
.macro DEFINE_SET_PREFIX_PARAMS name, pn
.if .xmatch(.string(pn), "pathname")
;; If 'pathname' is passed then expansion yields a circular reference.
.error "Can't pass 'pathname' label to DEFINE_*_PARAMS"
.endif
.proc name
param_count: .byte 1
pathname: .addr pn
.endproc
.endmacro
.macro DEFINE_GET_PREFIX_PARAMS name, pn
.if .xmatch(.string(pn), "pathname")
;; If 'pathname' is passed then expansion yields a circular reference.
.error "Can't pass 'pathname' label to DEFINE_*_PARAMS"
.endif
.proc name
param_count: .byte 1
pathname: .addr pn
.endproc
.endmacro
.macro DEFINE_DESTROY_PARAMS name, pn
.if .xmatch(.string(pn), "pathname")
;; If 'pathname' is passed then expansion yields a circular reference.
.error "Can't pass 'pathname' label to DEFINE_*_PARAMS"
.endif
.proc name
param_count: .byte 1
pathname: .addr pn
.endproc
.endmacro
.macro DEFINE_CREATE_PARAMS name, pn, ac, ft, at, st
.if .xmatch(.string(pn), "pathname")
;; If 'pathname' is passed then expansion yields a circular reference.
.error "Can't pass 'pathname' label to DEFINE_*_PARAMS"
.endif
.proc name
param_count: .byte 7
pathname: .addr pn
.ifnblank ac
access: .byte ac
.else
access: .byte 0
.endif
.ifnblank ft
file_type: .byte ft
.else
file_type: .byte 0
.endif
.ifnblank at
aux_type: .word at
.else
aux_type: .word 0
.endif
.ifnblank st
storage_type: .byte st
.else
storage_type: .byte 0
.endif
create_date: .word 0
create_time: .word 0
.endproc
.endmacro
.macro DEFINE_SET_EOF_PARAMS name, eo
.proc name
param_count: .byte 2
ref_num: .byte 0
eof: .faraddr eo
.endproc
.endmacro
.macro DEFINE_GET_EOF_PARAMS name
.proc name
param_count: .byte 2
ref_num: .byte 0
eof: .faraddr 0
.endproc
.endmacro
.macro DEFINE_RENAME_PARAMS name, pn, np
.if .xmatch(.string(pn), "pathname")
;; If 'pathname' is passed then expansion yields a circular reference.
.error "Can't pass 'pathname' label to DEFINE_*_PARAMS"
.endif
.proc name
param_count: .byte 2
pathname: .addr pn
new_pathname: .addr np
.endproc
.endmacro

780
ram.system.s Normal file
View File

@ -0,0 +1,780 @@
;;; Disassembly of "RAM.SYSTEM" found on Mouse Desk 2.0 images
.setcpu "6502"
.include "apple2.inc"
.include "inc/macros.inc"
.include "inc/apple2.inc"
.include "inc/prodos.inc"
.include "opcodes.inc"
zpproc_addr := $B0
zpproc_relay_addr := $2D0
chain_path := $280
driver_target = $FF00
kMaxUsableBanks = 24 ; Why is this capped so low???
; (driver has room for another ~20?)
;;; ============================================================
;;; RamWorks I/O
RWBANK := $C073
;;; ============================================================
.org $2000
jmp relocate_chain
;;; ============================================================
;; Interpreter signature
.byte $EE,$EE
;; Buffer size
.byte 64
chain_path_orig:
PASCAL_STRING "/MOUSE.DESK/MD.SYSTEM", 63
.byte $FF, $FF
;;; Configuration Parameters
banks_to_reserve: .byte 0 ; banks to reserve (e.g. for AppleWorks)
slot: .byte 3 ; S3D1; could be $B for S3D2
;;; ============================================================
;;; Chain to the next system file (path above)
;;; ============================================================
;;;
;;; Relocated to $BD00 to leave room at $2000 for the sys file.
chain_target = $BD00
saved_org .set *
.proc chain
.org ::chain_target
;; Copy path to $280
ldx chain_path_orig
beq quit
: lda chain_path_orig,x
sta chain_path,x
dex
bpl :-
MLI_CALL GET_FILE_INFO, get_file_info_params
bcs quit
ldx get_file_info_params_file_type
inx
bne quit
MLI_CALL OPEN, open_params
bcs close
lda open_params_ref_num
sta read_params_ref_num
sta get_eof_params_ref_num
MLI_CALL GET_EOF, get_eof_params
bcs close
;; Ensure there is room for chained file
lda get_eof_params_eof+2
bne close
lda get_eof_params_eof+1
cmp #$98 ; $2000+$97FF=$B7FF (have up to $BF00)
bcs close
sta read_params_request_count+1
lda get_eof_params_eof
sta read_params_request_count
;; Read
MLI_CALL READ, read_params
bcs close
;; Close
MLI_CALL CLOSE, close_params
bcs close
;; Invoke it
jmp sys_target
close: MLI_CALL CLOSE, close_params
quit: MLI_CALL QUIT, quit_params
DEFINE_QUIT_PARAMS quit_params
sys_target := $2000
io_buff := $1C00
DEFINE_GET_FILE_INFO_PARAMS get_file_info_params, chain_path
get_file_info_params_file_type := get_file_info_params::file_type
DEFINE_OPEN_PARAMS open_params, chain_path, io_buff
open_params_ref_num := open_params::ref_num
DEFINE_CLOSE_PARAMS close_params
DEFINE_READ_PARAMS read_params, sys_target, 0
read_params_ref_num := read_params::ref_num
read_params_request_count := read_params::request_count
DEFINE_GET_EOF_PARAMS get_eof_params
get_eof_params_ref_num := get_eof_params::ref_num
get_eof_params_eof := get_eof_params::eof
.endproc
.assert .sizeof(chain) <= $100, error, "Chain routine must fit in one page"
.org (saved_org + .sizeof(chain))
;;; ============================================================
;;; Copy chain code to final location
.proc relocate_chain
ldy #$00
: lda chain,y
sta chain_target,y
iny
bne :-
;; fall through
.endproc
;;; ============================================================
;;; Install the driver
.proc install_driver
sta CLR80COL
ldy #0
sty RWBANK
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 RWBANK
lda $00
sta stash_00,y
lda $01
sta stash_01,y
iny
bpl :-
dey
;; Write bank num/complement at $0/$1
: sty RWBANK
sty $00
tya
eor #$FF
sta $01
dey
bne :-
;; Y = 0
;; Reset signature bytes on main/aux banks
sty RWBANK
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 RWBANK
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 RWBANK
lda stash_00-1,y
sta $00
lda stash_01-1,y
sta $01
: dey
bne loop0
;; Y = 0
sty RWBANK
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 RWBANK
lda $00
beq fail
;; Check for ZP signature - if not found, set it and install.
ldx #2
: lda L23A0,x
cmp $06,x
bne set_sig
dex
bpl :-
bit BUTN1 ; escape hatch in case of loop ???
bmi L21F0
jmp do_install
fail: jmp do_chain
sloop: lda L23A0,x
set_sig:
sta $06,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
sta $0700,y
sta $0800,y
sta $0900,y
sta $0A00,y
sta $0B00,y
sta $0C00,y
sta $0D00,y
sta $0E00,y
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 RWBANK
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 slot already has a device
ldy DEVCNT
: lda DEVLST,y
lsr a
lsr a
lsr a
lsr a
cmp slot
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 slot
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 slot
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 do_chain
bcc do_chain
copy #$FF, L239F
sta ALTZPON
copy driver_bank_list, RWBANK
stx $06
stx RWBANK
stx vol_dir_header+VolumeDirectoryHeader::total_blocks
jmp install_driver ; retry???
do_chain:
sta ALTZPOFF
jmp chain_target
;;; ============================================================
;;; Installed on zero page of each bank at $B0
saved_org .set *
.proc zpproc
.org ::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
.endproc
sizeof_zpproc := .sizeof(zpproc)
.org (saved_org + .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
L23A0: .byte $C7, $C5, $C2 ; signature sequence ???
;; 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
;;; ============================================================
saved_org .set *
.proc driver_src
.org ::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 RWBANK
patch_loc1 := *+1
lda #$00
sta ALTZPON
jsr zpproc_addr
sty RWBANK
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
.endproc
sizeof_driver := .sizeof(driver_src)
.org (saved_org + .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 install
reserved_banks := *
first_used_bank := *+1
map1 := *+2
map2 := *+2+$80
stash_00 := *+2+$100
stash_01 := *+2+$180

18
res/go.sh Executable file
View File

@ -0,0 +1,18 @@
#!/bin/bash
# Run this from the ram.system directory
set -e
source "res/util.sh"
function verify {
diff "orig/$1" "out/$2" \
&& (cecho green "diff $2 good" ) \
|| (tput blink ; cecho red "DIFF $2 BAD" ; return 1)
}
#do_make clean
do_make all
verify "RAM.SYSTEM.SYS" "ram.system.SYS"

15
res/util.sh Normal file
View File

@ -0,0 +1,15 @@
function cecho {
case $1 in
red) tput setaf 1 ; shift ;;
green) tput setaf 2 ; shift ;;
yellow) tput setaf 3 ; shift ;;
esac
echo -e "$@"
tput sgr0
}
function do_make {
make $MAKE_FLAGS "$1" \
&& (cecho green "make $1 good") \
|| (tput blink ; cecho red "MAKE $1 BAD" ; return 1)
}