Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ An internal clock on the Apple side is required for this functionality to be ena
2. NoSlotClock
3. Thunderclock
4. ROMX family
5. MegaFlash (Apple IIc/IIc+ with [MegaFlash](https://github.com/ThomasFok/MegaFlash))

The reason why SPF can't/doesn't use the built-in ProDOS clock interface is it only gives information to the minute
resolution - and we need seconds. (More resolution would be nice, but seconds seems to be the best we can hope for in the universe of Apple II clocks.)
Expand Down
75 changes: 75 additions & 0 deletions doc/MegaFlash_Clock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# MegaFlash Clock Support - Design Notes

This document captures the reasoning and design decisions behind adding MegaFlash clock support to SPF.

## Background

**MegaFlash** is an internal storage device for Apple IIc/IIc+ computers ([ThomasFok/MegaFlash](https://github.com/ThomasFok/MegaFlash)). It includes:
- Flash storage (128MB/256MB)
- Real-time clock with ProDOS driver
- NTP time sync capability

SPF's benchmark feature requires second-resolution timing. The standard ProDOS clock interface only provides minute resolution, so SPF uses a compatibility layer that detects and uses various hardware clocks directly.

## Research Process

1. **Identification**: MegaFlash was identified from the GitHub repo. The README lists "Real Time Clock with ProDOS clock driver" as a feature.

2. **Architecture**: MegaFlash is IIc/IIc+ only—it replaces the system ROM. Unlike slot-based cards (Thunderclock), it uses fixed I/O addresses $C0C0-$C0C3. Activation is handled in MegaFlash ROM; SPF does not perform the magic address sequence—it only checks for presence and reads the time.

3. **API Discovery**: From `common/defines.inc` and `firmware/megaflash.s`:
- I/O: `cmdreg`/`statusreg`=$C0C0, `paramreg`=$C0C1, `datareg`=$C0C2, `idreg`=$C0C3
- `CMD_GETDEVINFO` ($10) returns signature bytes $88, $74 in paramreg for detection
- `CMD_GETPRODOS25TIME` ($18) returns 6-byte ProDOS 2.5 timestamp (includes seconds)

4. **Time Format**: From `pico/rtc.c` and `pico/cmdhandler.c`:
- ProDOS legacy (4 bytes): date_lo, date_hi, minute, hour — **no seconds**
- ProDOS 2.5 (6 bytes): t4ms, second, time_lo, time_hi, date_lo, date_hi
- The time word: `time = mday<<11 | hour<<6 | min` (min=bits 0-5, hour=bits 6-10, mday=bits 11-15)

## Design Decisions

### 1. Detection Order

MegaFlash is checked **after** ROMX and **before** slotted clocks. Rationale:
- IIgs and NoSlotClock are checked first (common)
- ROMX is another "replacement" device
- MegaFlash (IIc-specific) before generic slotted clocks
- Thunderclock remains last for slot-based detection

### 2. ProDOS 2.5 vs ProDOS Legacy Format

We use `CMD_GETPRODOS25TIME` exclusively because:
- SPF needs **seconds** for elapsed-time benchmarks
- ProDOS legacy format only provides hour and minute
- ProDOS 2.5 format includes the second byte

### 3. Bit Extraction for Hour and Minute

The time word layout: `[mday:5][hour:5][min:6]`

- **Minutes**: `time & $3F` (low 6 bits)
- **Hours**: `(time >> 6) & $1F` (next 5 bits)

Decomposed for 6502:
- `hour_lo2 = (time_lo >> 6)` — bits 6–7 of time_lo become bits 0–1 of hour
- `hour_hi3 = (time_hi & $07) << 2` — bits 0–2 of time_hi (time bits 8–10) become bits 2–4 of hour
- `hour = hour_lo2 | hour_hi3` (5 bits, 0–23)

### 4. Hundredths of Seconds

MegaFlash provides `t4ms` (0–249, units of 4ms), so hundredths could be approximated. We use 0 for consistency with other clocks (GS, ROMX, Thunderclock) that don't expose sub-second resolution.

### 5. Error Handling

- **Detection timeout**: If the busy flag stays set for 100 polls, we assume no MegaFlash.
- **GetTime error**: If the status register error bit is set after the time command, we skip updating TimeNow and return (leaving prior values).

### 6. RTC Not Initialized

If MegaFlash's RTC has never been set (no NTP, no manual set), the firmware returns all zeros. SPF will show 00:00:00 and benchmarks would report ~0 elapsed time. This is a user configuration issue; we document it in the README.

## Implementation Notes

- **MFTime buffer**: 4 bytes for temp storage during extraction. [0] holds intermediate hour bits, [1]=sec, [2–3]=time word.
- **Activation**: Handled in MegaFlash ROM; SPF does not perform the magic address sequence.
107 changes: 107 additions & 0 deletions src/prodos/gettime.asm
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ InitTime:
bcc FoundClockNoSlot
jsr CheckForROMX
bcc FoundClockROMX
jsr CheckForMegaFlash
bcc FoundClockMegaFlash
jsr CheckForSlottedClocks
rts

Expand Down Expand Up @@ -59,6 +61,16 @@ FoundClockROMX:
sta GetTime+2
rts

FoundClockMegaFlash:
;---------------------------------------------------------
; Patch the entry point of GetTime to the MegaFlash version
;---------------------------------------------------------
lda #<GetTimeMegaFlash
sta GetTime+1
lda #>GetTimeMegaFlash
sta GetTime+2
rts

GetTime:
jsr $0000
rts
Expand Down Expand Up @@ -176,6 +188,101 @@ GetTimeROMX:
sta TimeNow+3
rts

;---------------------------------------------------------
; MegaFlash clock support (Apple IIc/IIc+ with MegaFlash)
; https://github.com/ThomasFok/MegaFlash
;
; Design notes: doc/MegaFlash_Clock.md
;
; Activation is handled in MegaFlash ROM; we only check presence and read time.
; Detection: CMD_GETDEVINFO returns signature $88,$74 in paramreg.
;
; GetTime: CMD_GETPRODOS25TIME returns 6 bytes. Time word (bytes 2-3)
; packs [mday:5][hour:5][min:6]; we extract hour=(time>>6)&$1F,
; min=time&$3F. Byte 1 = seconds.
;---------------------------------------------------------
MF_CMDSTATUS = $C0C0
MF_PARAM = $C0C1
MF_CMD_GETDEVINFO = $10
MF_CMD_GETPRODOS25TIME = $18
MF_SIGNATURE1 = $88
MF_SIGNATURE2 = $74

CheckForMegaFlash:
; ROM activates MegaFlash if present; just check for presence
; Send GETDEVINFO command
lda #MF_CMD_GETDEVINFO
sta MF_CMDSTATUS
; Wait for busy to clear (with timeout)
ldx #100
: bit MF_CMDSTATUS
bpl :+
dex
bne :-
; Timeout - MegaFlash not present
sec
rts
: ; Check for error (bit 6)
bvs CheckMegaFlashFail
; Verify signature bytes
lda MF_PARAM
cmp #MF_SIGNATURE1
bne CheckMegaFlashFail
lda MF_PARAM
cmp #MF_SIGNATURE2
bne CheckMegaFlashFail
; MegaFlash found
clc
rts
CheckMegaFlashFail:
sec
rts

GetTimeMegaFlash:
; Request ProDOS 2.5 format time (includes seconds)
lda #MF_CMD_GETPRODOS25TIME
sta MF_CMDSTATUS
; Wait for completion
: bit MF_CMDSTATUS
bmi :-
bvs GetTimeMFDone ; Error - skip update
; Read 6 bytes: [0]=4ms units, [1]=sec, [2-3]=time word, [4-5]=date
lda MF_PARAM ; t4ms - discard
lda MF_PARAM ; seconds
sta MFTime+1
lda MF_PARAM ; time lo
sta MFTime+2
lda MF_PARAM ; time hi
sta MFTime+3
lda MF_PARAM ; date lo - discard
lda MF_PARAM ; date hi - discard
; Extract: min = time & $3F, hour = (time >> 6) & $1F
lda MFTime+2
and #$3F
sta TimeNow+1 ; Minutes
lda MFTime+2
lsr a
lsr a
lsr a
lsr a
lsr a
lsr a
sta MFTime+0 ; temp: hour low 2 bits
lda MFTime+3
and #$07 ; hour bits from high byte
asl a
asl a
ora MFTime+0
sta TimeNow ; Hours
lda MFTime+1
sta TimeNow+2 ; Seconds
lda #$00
sta TimeNow+3 ; Hundredths (MegaFlash has 4ms, we use 0 for simplicity)
GetTimeMFDone:
rts

MFTime: .res 4 ; Temp: [0]=hour bits, [1]=sec, [2]=time_lo, [3]=time_hi

CheckForNoSlotClock:
jsr PrepNoSlotClock ; Prepare the driver
jsr NSCEntry ; Call it to get the time
Expand Down