prodos-drivers/ns.clock.system.s

719 lines
18 KiB
ArmAsm
Raw Normal View History

2017-11-27 03:51:35 +00:00
;;; NS.CLOCK.SYSTEM
;;; Original by "CAP" 04/21/91
;;; http://www.apple2.org.za/gswv/a2zine/GS.WorldView/v1999/Oct/MISC/NSC.Disk.TXT
;;; Modification history available at:
;;; https://github.com/inexorabletash/cricket
2017-11-27 00:52:28 +00:00
.setcpu "6502"
2017-11-26 20:57:47 +00:00
.linecont +
2017-11-27 00:52:28 +00:00
.include "apple2.inc"
.include "opcodes.inc"
.include "./common.inc"
2017-11-27 00:52:28 +00:00
;;; ------------------------------------------------------------
2017-11-26 20:57:47 +00:00
2017-11-26 00:15:42 +00:00
data_buffer = $1800
2017-11-26 20:57:47 +00:00
.define SYSTEM_SUFFIX ".SYSTEM"
.define PRODUCT "No-Slot Clock"
2017-11-27 00:52:28 +00:00
;;; ------------------------------------------------------------
.org $1000
;; Loaded at $2000 but relocates to $1000
2017-11-27 00:52:28 +00:00
;;; ------------------------------------------------------------
2017-11-26 20:57:47 +00:00
sys_start:
sec
bcs relocate
2017-11-26 08:27:03 +00:00
2017-12-02 04:17:14 +00:00
.byte MM, DD, YY ; version date stamp
2017-11-27 00:52:28 +00:00
;;; ------------------------------------------------------------
2017-11-26 20:57:47 +00:00
;;; Relocate this code from $2000 (.SYSTEM start location) to $1000
;;; and start executing there. This is done so that the next .SYSTEM
;;; file can be loaded/run at $2000.
.proc relocate
src := SYS_ADDR
dst := $1000
2017-11-26 20:57:47 +00:00
ldx #(sys_end - sys_start + $FF) / $100 ; pages
ldy #0
load: lda src,y ; self-modified
2017-11-26 08:27:03 +00:00
load_hi := *-1
sta dst,y ; self-modified
store_hi := *-1
iny
bne load
2017-11-26 08:27:03 +00:00
inc load_hi
inc store_hi
dex
2017-11-26 20:57:47 +00:00
beq find_self_name ; done
jmp load
.endproc
2017-11-27 00:52:28 +00:00
;;; ------------------------------------------------------------
2017-11-26 20:57:47 +00:00
;;; Identify the name of this SYS file, which should be present at
;;; $280 with or without a path prefix. This is used when searching
;;; for the next .SYSTEM file to execute.
.proc find_self_name
;; Search pathname buffer backwards for '/', then
;; copy name into |self_name|; this is used later
;; to find/invoke the next .SYSTEM file.
;; Find '/' (which may not be present, prefix is optional)
lda #0
sta $A8
ldx PATHNAME
2017-11-26 01:35:48 +00:00
beq pre_install
floop: inc $A8
dex
beq copy
lda PATHNAME,x
eor #'/'
asl a
bne floop
2017-11-26 20:57:47 +00:00
;; Copy name into |self_name| buffer
copy: ldy #0
cloop: iny
inx
lda PATHNAME,x
sta self_name,y
cpy $A8
bcc cloop
sty self_name
.endproc
2017-11-26 20:57:47 +00:00
;; Fall through...
2017-11-27 00:52:28 +00:00
;;; ------------------------------------------------------------
;;; Before installing, get the system to a known state and
;;; ensure there is not a previous clock driver installed.
2017-11-26 01:35:48 +00:00
.proc pre_install
cld
bit ROMIN2
2017-11-26 01:35:48 +00:00
2017-11-26 20:57:47 +00:00
;; Update reset vector - re-invokes this code.
2017-11-26 08:03:11 +00:00
lda #<pre_install
sta $03F2
2017-11-26 08:03:11 +00:00
lda #>pre_install
sta $03F3
eor #$A5
sta $03F4
2017-11-26 01:35:48 +00:00
2017-11-27 00:52:28 +00:00
;; Quit 80-column firmware
2017-11-26 08:03:11 +00:00
lda #$95 ; Ctrl+U (quit 80 col firmware)
jsr COUT
2017-11-26 01:35:48 +00:00
2017-11-27 00:52:28 +00:00
;; Reset stack
ldx #$FF
txs
2017-11-26 08:03:11 +00:00
2017-11-27 00:52:28 +00:00
;; Reset I/O
sta CLR80VID
sta CLRALTCHAR
jsr SETVID
jsr SETKBD
jsr SETNORM
jsr INIT
2017-11-26 01:35:48 +00:00
2017-11-27 00:52:28 +00:00
;; Update System Bit Map
ldx #BITMAP_SIZE-1
lda #%00000001 ; protect page $BF
2017-11-26 00:15:42 +00:00
: sta BITMAP,x
2017-11-27 00:52:28 +00:00
lda #%00000000 ; nothing else protected until...
dex
2017-11-26 00:15:42 +00:00
bne :-
2017-11-27 00:52:28 +00:00
lda #%11001111 ; ZP ($00), stack ($01), text page 1 ($04-$07)
sta BITMAP
2017-11-26 01:35:48 +00:00
lda MACHID
2017-11-27 00:52:28 +00:00
and #$88 ; IIe or IIc (or IIgs) ?
2017-11-26 01:35:48 +00:00
bne :+
lda #$DF
sta lowercase_mask ; lower case to upper case
2017-11-27 00:52:28 +00:00
2017-11-26 01:35:48 +00:00
: lda MACHID
and #$01 ; existing clock card?
beq detect_nsc ; nope, check for NSC
;; Chain with no message
2017-11-26 20:57:47 +00:00
jmp launch_next_sys_file
2017-11-26 01:35:48 +00:00
.endproc
2017-11-27 00:52:28 +00:00
;;; ------------------------------------------------------------
2017-11-26 20:57:47 +00:00
;;; Detect NSC. Scan slot ROMs and main ROMs. Try reading
;;; each location several times, and validate results before
;;; installing driver.
2017-11-26 00:15:42 +00:00
2017-11-26 08:03:11 +00:00
.proc detect_nsc
2017-11-26 01:35:48 +00:00
;; Preserve date/time
ldy #3 ; copy 4 bytes
: lda DATELO,y
2017-11-26 20:57:47 +00:00
sta saved,y
dey
2017-11-26 01:35:48 +00:00
bpl :-
2017-11-26 08:03:11 +00:00
;; Check slot ROMs
2017-11-26 20:57:47 +00:00
lda #>$CFFF
ldy #<$CFFF
sta ld4+2
sty ld4+1
sta st4+2
sty st4+1
2017-11-26 20:57:47 +00:00
lda #0
2017-11-26 08:03:11 +00:00
sta slot
2017-11-26 20:57:47 +00:00
lda #3 ; treat slot 0 as slot 3
sloop: ora #$C0 ; A=$Cs
sta st1+2
2017-11-26 20:57:47 +00:00
rloop: sta ld1+2
sta ld2+2
sta st2+2
2017-11-26 20:57:47 +00:00
lda #3 ; 3 tries - need valid results each time
2017-11-26 08:03:11 +00:00
sta tries
2017-11-26 20:57:47 +00:00
try: jsr driver ; try reading date/time
lda DATELO+1 ; check result
ror a
lda DATELO
rol a
rol a
rol a
rol a
and #$0F
2017-11-26 08:03:11 +00:00
beq next
2017-11-26 20:57:47 +00:00
cmp #13 ; month
2017-11-26 08:03:11 +00:00
bcs next
lda DATELO
2017-11-26 08:03:11 +00:00
and #$1F
beq next
2017-11-26 20:57:47 +00:00
cmp #32 ; day
2017-11-26 08:03:11 +00:00
bcs next
2017-11-26 20:57:47 +00:00
lda TIMELO+1
cmp #24 ; hours
2017-11-26 08:03:11 +00:00
bcs next
lda TIMELO
2017-11-26 20:57:47 +00:00
cmp #60 ; minutes
2017-11-26 08:03:11 +00:00
bcs next
dec tries
bne try
2017-11-26 20:57:47 +00:00
beq install_driver ; all tries look valid
2017-11-26 08:03:11 +00:00
next: inc slot
lda slot
cmp #8
2017-11-26 20:57:47 +00:00
bcc sloop ; next slot
2017-11-26 08:03:11 +00:00
bne not_found
2017-11-26 20:57:47 +00:00
;; Not found in slot ROM, try main ROMs ???
lda #>$C015
ldy #<$C015
sta ld4+2
sty ld4+1
ldy #$07
sta st1+2
sty st1+1
dey
sta st4+2
sty st4+1
2017-11-26 20:57:47 +00:00
lda #>$C800
bne rloop
2017-11-26 01:35:48 +00:00
;; Restore date/time
2017-11-26 08:03:11 +00:00
not_found:
ldy #3
2017-11-26 20:57:47 +00:00
: lda saved,y
sta DATELO,y
dey
2017-11-26 01:35:48 +00:00
bpl :-
;; Show failure message
jsr MON_HOME
jsr zstrout
HIASCIIZ CR, CR, CR, PRODUCT, " - Not Found."
2017-11-26 20:57:47 +00:00
jmp launch_next_sys_file
2017-11-26 20:57:47 +00:00
saved: .byte 0, 0, 0, 0
2017-11-26 08:03:11 +00:00
tries: .byte 3
slot: .byte 0
.endproc
2017-11-27 00:52:28 +00:00
;;; ------------------------------------------------------------
2017-11-26 20:57:47 +00:00
;;; Install NSC Driver. Copy into address at DATETIME vector,
;;; update the vector and update MACHID bits to signal a clock
;;; is present.
.proc install_driver
2017-11-26 01:35:48 +00:00
ptr := $A5
lda DATETIME+1
2017-11-26 01:35:48 +00:00
sta ptr
clc
2017-11-27 00:52:28 +00:00
adc #(unlock - driver - 1)
sta ld3+1
lda DATETIME+2
2017-11-26 01:35:48 +00:00
sta ptr+1
adc #0
sta ld3+2
lda RWRAM1
lda RWRAM1
2017-11-26 01:35:48 +00:00
ldy #sizeof_driver-1
2017-11-26 01:35:48 +00:00
loop: lda driver,y
sta (ptr),y
dey
bpl loop
2017-11-26 00:15:42 +00:00
;; Set the "Recognizable Clock Card" bit
lda MACHID
ora #$01
sta MACHID
2017-11-26 00:15:42 +00:00
2017-11-27 00:52:28 +00:00
lda #OPC_JMP_abs
sta DATETIME
;; Invoke the driver to init the time
jsr DATETIME
;; Display success message
bit ROMIN2
jsr MON_HOME
jsr zstrout
HIASCIIZ CR, CR, CR, PRODUCT, " - Installed "
;; Display the current date
2017-11-26 00:15:42 +00:00
lda DATELO+1 ; month
ror a
pha
lda DATELO
pha
rol a
rol a
rol a
rol a
and #%00001111
jsr cout_number
2017-11-26 00:15:42 +00:00
lda #(HI '/') ; /
jsr COUT
2017-11-26 00:15:42 +00:00
pla ; day
and #%00011111
jsr cout_number
2017-11-26 00:15:42 +00:00
lda #(HI '/') ; /
jsr COUT
2017-11-26 00:15:42 +00:00
pla ; year
jsr cout_number
jsr CROUT
.endproc
2017-11-27 00:52:28 +00:00
;;; ------------------------------------------------------------
2017-11-26 20:57:47 +00:00
;;; Find and invoke the next .SYSTEM file
2017-11-26 20:57:47 +00:00
.proc launch_next_sys_file
;; Update reset vector - now terminates.
2017-11-26 08:03:11 +00:00
lda #<quit
sta $03F2
2017-11-26 08:03:11 +00:00
lda #>quit
sta $03F3
eor #$A5
sta $03F4
2017-11-26 00:15:42 +00:00
ptr := $A5
num := $A7
2017-11-26 01:35:48 +00:00
len := $A8
2017-11-26 00:15:42 +00:00
2017-11-26 01:35:48 +00:00
lda DEVNUM ; stick with most recent device
sta read_block_params_unit_num
jsr read_block
2017-11-26 01:35:48 +00:00
lda data_buffer + VolumeDirectoryBlockHeader::entry_length
2017-11-26 08:27:03 +00:00
sta entry_length_mod
lda data_buffer + VolumeDirectoryBlockHeader::entries_per_block
2017-11-26 08:27:03 +00:00
sta entries_per_block_mod
2017-11-26 00:15:42 +00:00
lda #1
sta num
2017-11-26 01:35:48 +00:00
lda #<(data_buffer + VolumeDirectoryBlockHeader::header_length)
2017-11-26 00:15:42 +00:00
sta ptr
lda #>(data_buffer + VolumeDirectoryBlockHeader::header_length)
2017-11-26 00:15:42 +00:00
sta ptr+1
2017-11-26 01:35:48 +00:00
;; Process directory entry
entry: ldy #FileEntry::file_type ; file_type
2017-11-26 00:15:42 +00:00
lda (ptr),y
2017-11-26 01:35:48 +00:00
cmp #$FF ; type=SYS
bne next
ldy #FileEntry::storage_type
2017-11-26 00:15:42 +00:00
lda (ptr),y
2017-11-26 01:35:48 +00:00
and #$30 ; regular file (not directory, pascal)
beq next
2017-11-26 00:15:42 +00:00
lda (ptr),y
2017-11-26 01:35:48 +00:00
and #$0F ; name_length
sta len
tay
2017-11-26 01:35:48 +00:00
;; Compare suffix - is it .SYSTEM?
ldx #.strlen(SYSTEM_SUFFIX)-1
2017-11-26 01:35:48 +00:00
: lda (ptr),y
cmp suffix,x
2017-11-26 01:35:48 +00:00
bne next
dey
dex
2017-11-26 01:35:48 +00:00
bpl :-
;; Yes; is it *this* .SYSTEM file?
ldy self_name
2017-11-26 01:35:48 +00:00
cpy len
bne handle_sys_file
2017-11-26 00:15:42 +00:00
: lda (ptr),y
cmp self_name,y
2017-11-26 01:35:48 +00:00
bne handle_sys_file
dey
bne :-
sec
ror found_self_flag
2017-11-26 01:35:48 +00:00
;; Move to the next entry
next: lda ptr
clc
2017-11-26 08:27:03 +00:00
adc #$27 ; self-modified: entry_length
entry_length_mod := *-1
2017-11-26 00:15:42 +00:00
sta ptr
2017-11-26 08:03:11 +00:00
bcc :+
2017-11-26 00:15:42 +00:00
inc ptr+1
: inc num
lda num
2017-11-26 08:27:03 +00:00
cmp #$0D ; self-modified: entries_per_block
entries_per_block_mod := *-1
2017-11-26 01:35:48 +00:00
bcc entry
lda data_buffer + VolumeDirectoryBlockHeader::next_block
sta read_block_params_block_num
lda data_buffer + VolumeDirectoryBlockHeader::next_block + 1
sta read_block_params_block_num+1
ora read_block_params_block_num
2017-11-26 08:03:11 +00:00
beq not_found ; last block has next=0
jsr read_block
lda #0
sta num
2017-11-26 00:15:42 +00:00
lda #<(data_buffer + $04)
sta ptr
lda #>(data_buffer + $04)
sta ptr+1
2017-11-26 01:35:48 +00:00
jmp entry
2017-11-26 01:35:48 +00:00
;; Found a .SYSTEM file which is not this one; invoke
;; it if follows this one.
handle_sys_file:
bit found_self_flag
bpl next
2017-11-26 01:35:48 +00:00
;; Compose the path to invoke. First walk self path
;; backwards to '/'.
ldx PATHNAME
2017-11-26 01:35:48 +00:00
beq append
: dex
beq append
lda PATHNAME,x
eor #'/'
asl a
2017-11-26 01:35:48 +00:00
bne :-
2017-11-26 00:15:42 +00:00
2017-11-26 01:35:48 +00:00
;; Now append name of found file.
append: ldy #0
: iny
inx
2017-11-26 01:35:48 +00:00
lda (ptr),y
sta PATHNAME,x
2017-11-26 01:35:48 +00:00
cpy len
bcc :-
stx PATHNAME
jmp invoke_system_file
2017-11-26 01:35:48 +00:00
not_found:
jsr zstrout
2017-11-26 20:57:47 +00:00
HIASCIIZ CR, CR, CR, "* Unable to find next '.SYSTEM' file *", CR
bit KBDSTRB
: lda KBD
bpl :-
bit KBDSTRB
jmp quit
2017-11-26 00:15:42 +00:00
.endproc
2017-11-27 00:52:28 +00:00
;;; ------------------------------------------------------------
;;; Output a high-ascii, null-terminated string.
;;; String immediately follows the JSR.
.proc zstrout
2017-11-26 00:15:42 +00:00
ptr := $A5
2017-11-26 08:27:03 +00:00
pla ; read address from stack
2017-11-26 00:15:42 +00:00
sta ptr
pla
2017-11-26 00:15:42 +00:00
sta ptr+1
bne skip ; always (since data not on ZP)
2017-11-26 08:27:03 +00:00
next: cmp #(HI 'a') ; lower-case?
bcc :+
and lowercase_mask ; make upper-case if needed
: jsr COUT
2017-11-26 08:27:03 +00:00
skip: inc ptr
bne :+
2017-11-26 00:15:42 +00:00
inc ptr+1
: ldy #0
2017-11-26 00:15:42 +00:00
lda (ptr),y
2017-11-26 08:27:03 +00:00
bne next
lda ptr+1 ; restore address to stack
pha
2017-11-26 00:15:42 +00:00
lda ptr
pha
rts
.endproc
2017-11-27 00:52:28 +00:00
;;; ------------------------------------------------------------
;;; COUT a 2-digit number in A
.proc cout_number
ldx #(HI '0')
cmp #10 ; >= 10?
bcc tens
;; divide by 10, dividend(+'0') in x remainder in a
: sbc #10
inx
cmp #10
bcs :-
tens: pha
cpx #(HI '0')
beq units
txa
jsr COUT
units: pla
ora #(HI '0')
jsr COUT
rts
.endproc
2017-11-27 00:52:28 +00:00
;;; ------------------------------------------------------------
lowercase_mask:
.byte $FF ; Set to $DF on systems w/o lower-case
2017-11-27 00:52:28 +00:00
;;; ------------------------------------------------------------
;;; Invoke ProDOS QUIT routine.
.proc quit
PRODOS_CALL MLI_QUIT, quit_params
2017-11-26 20:57:47 +00:00
.byte 0 ; crash if QUIT fails
rts
.proc quit_params
.byte 4 ; param_count
.byte 0 ; quit_type
.word 0000 ; reserved
.byte 0 ; reserved
.word 0000 ; reserved
.endproc
.endproc
2017-11-27 00:52:28 +00:00
;;; ------------------------------------------------------------
;;; Read a disk block.
.proc read_block
PRODOS_CALL MLI_READ_BLOCK, read_block_params
bcs on_error
rts
.endproc
.proc read_block_params
.byte 3 ; param_count
unit_num: .byte $60 ; unit_num
2017-11-26 00:15:42 +00:00
.addr data_buffer ; data_buffer
2017-11-26 08:03:11 +00:00
block_num: .word 2 ; block_num - block 2 is volume directory
.endproc
read_block_params_unit_num := read_block_params::unit_num
read_block_params_block_num := read_block_params::block_num
2017-11-27 00:52:28 +00:00
;;; ------------------------------------------------------------
;;; Load/execute the system file in PATHNAME
.proc invoke_system_file
PRODOS_CALL MLI_OPEN, open_params
bcs on_error
lda open_params_ref_num
sta read_params_ref_num
PRODOS_CALL MLI_READ, read_params
bcs on_error
PRODOS_CALL MLI_CLOSE, close_params
bcs on_error
jmp SYS_ADDR ; Invoke loaded SYSTEM file
.endproc
2017-11-27 00:52:28 +00:00
;;; ------------------------------------------------------------
;;; Error handler - invoked if any ProDOS error occurs.
.proc on_error
pha
jsr zstrout
2017-11-26 20:57:47 +00:00
HIASCIIZ CR, CR, CR, "** Disk Error $"
pla
jsr PRBYTE
jsr zstrout
2017-11-26 20:57:47 +00:00
HIASCIIZ " **", CR
bit KBDSTRB
: lda KBD
bpl :-
bit KBDSTRB
jmp quit
.endproc
2017-11-27 00:52:28 +00:00
;;; ------------------------------------------------------------
.proc open_params
.byte 3 ; param_count
.addr PATHNAME ; pathname
2017-11-26 00:15:42 +00:00
.addr data_buffer ; io_buffer
ref_num:.byte 1 ; ref_num
.endproc
open_params_ref_num := open_params::ref_num
.proc read_params
.byte 4 ; param_count
ref_num:.byte 1 ; ref_num
.addr SYS_ADDR ; data_buffer
.word MAX_DW ; request_count
.word 0 ; trans_count
.endproc
read_params_ref_num := read_params::ref_num
.proc close_params
.byte 1 ; param_count
ref_num:.byte 0 ; ref_num
.endproc
2017-11-27 00:52:28 +00:00
;;; ------------------------------------------------------------
found_self_flag:
.byte 0
suffix: .byte SYSTEM_SUFFIX
self_name:
2017-11-26 01:35:48 +00:00
PASCAL_STRING "NS.CLOCK.SYSTEM"
2017-11-27 00:52:28 +00:00
;;; ------------------------------------------------------------
;;; NSC driver - modified as needed and copied into ProDOS
2017-11-26 01:35:48 +00:00
driver:
php
sei
2017-11-26 08:27:03 +00:00
ld4: lda $CFFF ; self-modified
pha
st1: sta $C300 ; self-modified
ld1: lda $C304 ; self-modified
ldx #8
2017-11-27 00:52:28 +00:00
;; Unlock the NSC by bit-banging.
uloop:
2017-11-26 08:27:03 +00:00
ld3: lda unlock-1,x ; self-modified
sec
2017-11-27 00:52:28 +00:00
ror a ; a bit at a time
: pha
lda #0
rol a
tay
ld2: lda $C300,y ; self-modified
pla
lsr a
2017-11-27 00:52:28 +00:00
bne :-
dex
2017-11-27 00:52:28 +00:00
bne uloop
;; Read 8 bytes * 8 bits of clock data into $200...$207
ldx #8
2017-11-27 00:52:28 +00:00
bloop: ldy #8
st2:
2017-11-26 08:27:03 +00:00
: lda $C304 ; self-modified
ror a
ror $01FF,x
dey
2017-11-26 08:27:03 +00:00
bne :-
2017-11-27 00:52:28 +00:00
lda $01FF,x ; got 8 bits
lsr a ; BCD to binary
lsr a ; shift out tens
lsr a
lsr a
tay
2017-11-27 00:52:28 +00:00
beq donebcd
lda $01FF,x
2017-11-27 00:52:28 +00:00
and #$0F ; mask out units
clc
2017-11-27 00:52:28 +00:00
: adc #10 ; and add tens as needed
dey
2017-11-26 08:27:03 +00:00
bne :-
sta $01FF,x
2017-11-27 00:52:28 +00:00
donebcd:
dex
bne bloop
;; Now $200...$207 is y/m/d/w/H/M/S/f
;; Update ProDOS date/time.
lda $0204 ; hour
sta TIMELO+1
2017-11-27 00:52:28 +00:00
lda $0205 ; minute
sta TIMELO
2017-11-27 00:52:28 +00:00
lda $0201 ; month
asl a
asl a
asl a
asl a
asl a
2017-11-27 00:52:28 +00:00
ora $0202 ; day
sta DATELO
2017-11-27 00:52:28 +00:00
lda $0200 ; year
rol a
sta DATELO+1
2017-11-27 00:52:28 +00:00
pla
2017-11-26 08:27:03 +00:00
bmi done
st4: sta $CFFF ; self-modified
2017-11-26 08:27:03 +00:00
done: plp
rts
2017-11-26 01:35:48 +00:00
unlock:
;; NSC unlock sequence
.byte $5C, $A3, $3A, $C5
.byte $5C, $A3, $3A, $C5
.byte $00
sizeof_driver := * - driver
2017-12-03 05:35:50 +00:00
.assert sizeof_driver <= 125, error, "Clock code must be <= 125 bytes"
2017-11-26 01:35:48 +00:00
2017-11-27 00:52:28 +00:00
;;; ------------------------------------------------------------
2017-11-26 20:57:47 +00:00
sys_end: