prodos-drivers/clocks/dclock/dclock.system.s
Joshua Bell f2ea11fcde Add "jumbo" clock driver, an amalgamation of the others
This one driver pulls in the installers for each other clock driver,
and invokes each in turn:

* No-Slot Clock
* ROMX
* FujiNet
* DClock
* Cricket!

This requires adding `.ifndef JUMBO` guards in the other drivers for
when they pull in include files (symbols, macros, etc). The other
drivers are adjusted to return with carry clear on successful install,
failure otherwise.
2022-11-26 20:29:38 -08:00

406 lines
14 KiB
ArmAsm

; Fully disassembled and analyzed source to AE
; DCLOCK.SYSTEM by M.G. - 04/18/2017
; https://gist.github.com/mgcaret/7f0d7aeec169e90809c7cfaab9bf183b
; Further modified by @inexorabletash - 12/21/2020
; There are critical bugs in the original AE code:
; * When driver loader is initially probing it corrupts the
; Apple //c Memory Expansion Card:
; - it saves, but fails to restore, data at address $080000
; - it fails to reset slinky pointer, and *will* trash $080000-$080007
; * When the clock is read, it corrupts data at address $08xx01
; - John Brooks spotted this, [M.G.] totally missed this.
; This version of the code has fixes permanently applied.
.ifndef JUMBO_CLOCK_DRIVER
.setcpu "6502" ; changed below
.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"
.endif ; JUMBO_CLOCK_DRIVER
; zero page locations
SCRATCH := $0B ; scratch value for BCD range checks
SAVEBYTE := $0C ; slinky overwritten byte save location
BCDTMP := $3A ; location clock driver uses for BCD->Binary
; buffers & other spaces
INBUF := $0200 ; input buffer
CLOCKBUF := $0300 ; clock buffer
; I/O and hardware
C8OFF := $CFFF ; C8xx ROM off
SLOT4ROM := $C400 ; Slot 4 ROM space
SLOT4IO := $C0C0 ; Slot 4 I/O space
DPTRL := SLOT4IO+0 ; Slinky data ptr low
DPTRM := SLOT4IO+1 ; Slinky data ptr middle
DPTRH := SLOT4IO+2 ; Slinky data ptr high
DATA := SLOT4IO+3 ; Slinky data byte
;;; ************************************************************
.ifndef JUMBO_CLOCK_DRIVER
.include "../../inc/driver_preamble.inc"
.endif ; JUMBO_CLOCK_DRIVER
;;; ************************************************************
;;; ============================================================
;;;
;;; Driver Installer
;;;
;;; ============================================================
.ifndef JUMBO_CLOCK_DRIVER
.define PRODUCT "DClock"
.endif ; JUMBO_CLOCK_DRIVER
;;; ============================================================
;;; Ensure there is not a previous clock driver installed.
;;; And that this is a IIc. And that the clock is present.
;;; NOTE: Safe to run on a non-IIc (6502 opcodes, etc)
.proc maybe_install_driver
lda MACHID
and #$01 ; existing clock card?
bne done
lda VERSION ; IIc identification byte 1
cmp #$06
bne done
lda ZIDBYTE ; IIc identification byte 2
cmp #$00
bne done
;;; Since this is a IIc, okay to rely on 65C02 opcodes from here.
.pushcpu
.setcpu "65C02"
jsr ClockRead
jsr ValidTime
bcc InstallDriver
.ifndef JUMBO_CLOCK_DRIVER
;; Show failure message
jsr log_message
scrcode PRODUCT, " - Not Found."
.byte 0
.endif ; JUMBO_CLOCK_DRIVER
done: sec ; failure
rts
.endproc
; ----------------------------------------------------------------------------
; Install clock driver
.proc InstallDriver
;; Copy into address at DATETIME vector, update the vector and
;; update MACHID bits to signal a clock is present.
ptr := $A5
;; Update absolute addresses within driver
lda DATETIME+1
sta ptr
clc
adc #(regulk - driver)
sta regulk_addr
lda DATETIME+2
sta ptr+1
adc #0
sta regulk_addr+1
;; Copy driver into appropriate bank
lda RWRAM1
lda RWRAM1
ldy #sizeof_driver-1
loop: lda driver,y
sta (ptr),y
dey
bpl loop
;; Set the "Recognizable Clock Card" bit
lda MACHID
ora #$01
sta MACHID
lda #OPC_JMP_abs
sta DATETIME
;; Invoke the driver to init the time
jsr DATETIME
lda ROMIN2
.ifndef JUMBO_CLOCK_DRIVER
;; Display success message
jsr log_message
scrcode PRODUCT, " - "
.byte 0
;; Display the current date
jsr cout_date
.endif ; JUMBO_CLOCK_DRIVER
clc ; success
rts ; done!
.endproc
; ----------------------------------------------------------------------------
; enable slinky registers, set adddress and save byte we intend to trash
.proc SlinkyEnable
lda C8OFF ; not needed on //c, but release $C8xx firmware
lda SLOT4ROM ; enable slinky registers
lda #$08 ; set addr $080000
sta DPTRH
stz DPTRM
stz DPTRL
lda DATA ; read data byte
sta SAVEBYTE ; save it to restore later
rts
.endproc
; ----------------------------------------------------------------------------
; Routine to restore trashed byte in slinky RAM
.proc SlinkyRestore
lda #$08 ; set adddr $080000
sta DPTRH
stz DPTRM
stz DPTRL
lda SAVEBYTE ; get saved byte
sta DATA ; and put it back
lda C8OFF ; not needed on //c, but release $C8xx firmware
rts
.endproc
; ----------------------------------------------------------------------------
; Write 8 bits to clock
.proc ClockWrite8b
ldx #$08 ; set adddr $080000
stx DPTRH
stz DPTRM
: stz DPTRL ; restore low byte to 0
sta DATA ; write byte
lsr a ; next bit into 0 position
dex
bne :-
rts
.endproc
; ----------------------------------------------------------------------------
; unlock the clock by writing the magic bit sequence
.proc ClockUnlock
ldy #$08
: lda unlock,y
jsr ClockWrite8b ; write 8 bits
dey
bne :-
rts
unlock = * - 1
.byte $5c, $a3, $3a, $c5, $5c, $a3, $3a, $c5
.endproc
; ----------------------------------------------------------------------------
; Read 8 bits from the clock
.proc ClockRead8b
ldx #$08 ; set adddr $080000
stz DPTRL
stz DPTRM
stx DPTRH
: pha ; save accumulator
lda DATA ; get data byte
lsr a ; bit 0 into carry
pla ; restore accumulator
ror a ; put read bit into position
dex
bne :-
rts
.endproc
; ----------------------------------------------------------------------------
; read the clock data into memory at CLOCKBUF
; WARNING: unfixed code never restores byte we trashed
.proc ClockRead
jsr SlinkyEnable
jsr ClockUnlock
ldy #$00
: jsr ClockRead8b
sta CLOCKBUF,y
iny
cpy #$08 ; have we read 8 bytes?
bcc :- ; nope
jsr SlinkyRestore
rts
.endproc
; ----------------------------------------------------------------------------
; validate the DClock data makes sense
; return carry clear if it does, carry set if it does not
.proc ValidTime
; validate ms
ldx #$00
ldy #$99
lda CLOCKBUF
jsr CheckBCD
bcs :+
; validate seconds
ldx #$00
ldy #$59
lda CLOCKBUF+$01
jsr CheckBCD
bcs :+
; validate minutes
ldx #$00
ldy #$59
lda CLOCKBUF+$02
jsr CheckBCD
bcs :+
; validate hours
ldx #$00
ldy #$23
lda CLOCKBUF+$03
jsr CheckBCD
bcs :+
; validate day of week
ldx #$01
ldy #$07
lda CLOCKBUF+$04
jsr CheckBCD
bcs :+
; validate day of month
ldx #$01
ldy #$31
lda CLOCKBUF+$05
jsr CheckBCD
bcs :+
; validate month
ldx #$01
ldy #$12
lda CLOCKBUF+$06
jsr CheckBCD
bcs :+
; validate year
ldx #$00
ldy #$99
lda CLOCKBUF+$07
jsr CheckBCD
bcs :+
clc ; all good
rts
: sec ; problem
rts
.endproc
; ----------------------------------------------------------------------------
; Check BCD number in range of [x,y]
; return carry clear if it is, carry set if it is not
.proc CheckBCD
sed ; decimal mode
stx SCRATCH ; lower bound into scratch
cmp SCRATCH ; compare it
bcc :++ ; fail if out of range
sty SCRATCH ; upper bound into scratch
cmp SCRATCH ; compare it
beq :+ ; OK if equal
bcs :++ ; fail if out of range
: cld ; in range
clc
rts
: cld ; not in range
sec
rts
.endproc
; ----------------------------------------------------------------------------
; clock driver code inserted into ProDOS
driver:
lda #$08 ; useless instruction
php
sei
lda SLOT4ROM ; activate slinky registers
; ($08 from above overwritten)
stz DPTRL ; set slinky address to $08xx00
ldy #$08 ; also counter for unlock bytes
sty DPTRH
lda DATA ; get destroyed byte
; (slinky now at $08xx01)
pha ; save value on stack
; unlock dclock registers
regulk_addr := *+1
ubytlp: lda regulk,y ; self-modified
ldx #$08 ; bit counter
ubitlp: stz DPTRL ; reset pointer to $08xx00
sta DATA ; write to $08xx00
lsr a ; next bit into 0 position
dex
bne ubitlp
dey
bne ubytlp
; now read 64 bits (8 bytes) from dclock
ldx #$08 ; byte counter
rbytlp: ldy #$08 ; bit counter
rbitlp: pha
lda DATA ; data byte
lsr a ; bit 0 into carry
pla
ror a ; carry into bit 7
dey
bne rbitlp
; got 8 bits now, convert from BCD to binary
pha
and #$0F
sta BCDTMP
pla
and #$F0
lsr a
pha
adc BCDTMP
sta BCDTMP
pla
lsr a
lsr a
adc BCDTMP
; place in input buffer, which is OK because the ThunderClock driver does this
sta INBUF-1,x
dex
bne rbytlp
; done copying, now put necessary values into ProDOS time locations
; copy hours to ProDOS hours
lda INBUF+4
sta TIMEHI
; copy minutes to ProDOS minutes
lda INBUF+5
sta TIMELO
; copy month ...
lda INBUF+1
lsr a
ror a
ror a
ror a
; ... and day of month to ProDOS month/day
ora INBUF+2
sta DATELO
; copy year and final bit of month to ProDOS year/month
lda INBUF
rol a
sta DATEHI
stz DPTRL ; set slinky back to $08xx00
pla ; get saved byte
sta DATA ; put it back
plp
rts
; DS1215 unlock sequence (in reverse)
regulk = * - 1
.byte $5C, $A3, $3A, $C5, $5C, $A3, $3A, $C5
sizeof_driver := * - driver
.assert sizeof_driver <= 125, error, "Clock code must be <= 125 bytes"
.popcpu
;;; ************************************************************
.ifndef JUMBO_CLOCK_DRIVER
.include "../../inc/driver_postamble.inc"
.endif ; JUMBO_CLOCK_DRIVER
;;; ************************************************************