diff --git a/CONFIG.md b/CONFIG.md index 95ef6ffb..800e646d 100644 --- a/CONFIG.md +++ b/CONFIG.md @@ -151,10 +151,16 @@ Needs about 700 bytes in KERNAL segment (if both normal and turbo are enabled, a ### `CONFIG_TAPE_TURBO` -Adds a minimal turbo tape support - just LOAD command (device 7, like on _Action Replay_ and _Final_ cartridges) +Adds a minimal turbo tape support - just LOAD command (device 7, like on _Action Replay_ and _Final_ cartridges), up to 250 blocks Needs about 650 bytes in KERNAL segment (if both normal and turbo are enabled, about 950 bytes are needed, as they share some code). If unsure - enable. +### `CONFIG_TAPE_TURBO_AUTOCALIBRATE` + +Extends the tape turbo load functionality by ability to adjust to tape speed differences. + +Needs about 100 bytes in KERNAL segment. If unsure - enable for Datasette or other tape recorder, disable for digital solutions like Tapuino. + ### `CONFIG_TAPE_NO_KEY_SENSE` Enable this option if you are using a tape interface adapter with some audio signal source connected. These adapters lack key sense functionality, so the computer is unable to tell whether Play got pressed or not - with this option ROM will assume Play got pressed after imppulses start arriving from the tape. diff --git a/STATUS.md b/STATUS.md index a4cfd1c7..acbe51e6 100644 --- a/STATUS.md +++ b/STATUS.md @@ -11,7 +11,7 @@ Here are the features of the Open ROMs not found in the original ROMs from the 8 * JiffyDOS and DolphinDOS protocols support * DOS wedge (direct mode only) - `@`, `@`, `@$`, `@$`, `@` -* turbo tape load support (as device 7, or using `←L`), up to 250 blocks (can store bytes under I/O) +* turbo tape load support (as device 7, or using `←L`), quite sophisticated: up to 250 blocks (can store bytes under I/O), can use sync signal for autocalibration (less load errors than simple turbos) * improved support for tape adapters (for using regular casette players and other audio devices instead of Datasette) * extended `LOAD` command @@ -70,7 +70,7 @@ The following ROM features are currently missing: | Driver | Status | Remarks | | :----------------: | :------: | :------------------------------------------------: | | normal | PARTIAL | no tape speed calibratrion, no error correction | -| turbo (250 blocks) | PARTIAL | no tape speed calibratrion | +| turbo (250 blocks) | DONE | | ## IEC bus diff --git a/c64/,,config_custom.s b/c64/,,config_custom.s index 95bfda75..40fa8d18 100644 --- a/c64/,,config_custom.s +++ b/c64/,,config_custom.s @@ -56,6 +56,7 @@ #define CONFIG_TAPE_NORMAL #define CONFIG_TAPE_TURBO +#define CONFIG_TAPE_TURBO_AUTOCALIBRATE // #define CONFIG_TAPE_NO_KEY_SENSE // #define CONFIG_TAPE_NO_MOTOR_CONTROL diff --git a/c64/,,config_generic.s b/c64/,,config_generic.s index 4195ef46..e091a2af 100644 --- a/c64/,,config_generic.s +++ b/c64/,,config_generic.s @@ -50,6 +50,7 @@ #define CONFIG_TAPE_NORMAL #define CONFIG_TAPE_TURBO +#define CONFIG_TAPE_TURBO_AUTOCALIBRATE // #define CONFIG_TAPE_NO_KEY_SENSE // #define CONFIG_TAPE_NO_MOTOR_CONTROL diff --git a/c64/,,config_mega65.s b/c64/,,config_mega65.s index add9c08e..ea322e26 100644 --- a/c64/,,config_mega65.s +++ b/c64/,,config_mega65.s @@ -50,6 +50,7 @@ // #define CONFIG_TAPE_NORMAL // #define CONFIG_TAPE_TURBO +// #define CONFIG_TAPE_TURBO_AUTOCALIBRATE // #define CONFIG_TAPE_NO_KEY_SENSE // #define CONFIG_TAPE_NO_MOTOR_CONTROL diff --git a/c64/,,config_testing.s b/c64/,,config_testing.s index 0f7a164f..59e2dbaa 100644 --- a/c64/,,config_testing.s +++ b/c64/,,config_testing.s @@ -50,6 +50,7 @@ // #define CONFIG_TAPE_NORMAL #define CONFIG_TAPE_TURBO +// #define CONFIG_TAPE_TURBO_AUTOCALIBRATE // #define CONFIG_TAPE_NO_KEY_SENSE // #define CONFIG_TAPE_NO_MOTOR_CONTROL diff --git a/c64/,,config_ultimate64.s b/c64/,,config_ultimate64.s index f2927c60..24045b8a 100644 --- a/c64/,,config_ultimate64.s +++ b/c64/,,config_ultimate64.s @@ -50,6 +50,7 @@ #define CONFIG_TAPE_NORMAL #define CONFIG_TAPE_TURBO +#define CONFIG_TAPE_TURBO_AUTOCALIBRATE // #define CONFIG_TAPE_NO_KEY_SENSE // #define CONFIG_TAPE_NO_MOTOR_CONTROL diff --git a/c64/aliases/,aliases_config.s b/c64/aliases/,aliases_config.s index 083bb394..6d97e40c 100644 --- a/c64/aliases/,aliases_config.s +++ b/c64/aliases/,aliases_config.s @@ -102,12 +102,12 @@ // Check that memory model and ROM layout configurations is correct { -#if ROM_LAYOUT_M65 && !CONFIG_CPU_M65_45GS02 - .error "Mega65 ROM layout requires CONFIG_CPU_M65_45GS02" -#endif -#if ROM_LAYOUT_M65 && !CONFIG_MB_MEGA_65 - .error "Mega65 ROM layout requires CONFIG_MB_MEGA_65" -#endif +//#if ROM_LAYOUT_M65 && !CONFIG_CPU_M65_45GS02 +// .error "Mega65 ROM layout requires CONFIG_CPU_M65_45GS02" +//#endif +//#if ROM_LAYOUT_M65 && !CONFIG_MB_MEGA_65 +// .error "Mega65 ROM layout requires CONFIG_MB_MEGA_65" +//#endif .var selected = 0; @@ -166,6 +166,15 @@ +// Check that tape configuration is correct +{ +#if !CONFIG_TAPE_TURBO && CONFIG_TAPE_TURBO_AUTOCALIBRATE + .error "CONFIG_TAPE_TURBO_AUTOCALIBRATE requires CONFIG_TAPE_TURBO*" +#endif +} + + + // Check that RS-232 configuration is correct { #if CONFIG_RS232_UP9600 && (CONFIG_TAPE_NORMAL || CONFIG_TAPE_TURBO) diff --git a/c64/aliases/,aliases_ram_lowmem.s b/c64/aliases/,aliases_ram_lowmem.s index 1f0c37a7..50f63017 100644 --- a/c64/aliases/,aliases_ram_lowmem.s +++ b/c64/aliases/,aliases_ram_lowmem.s @@ -214,7 +214,7 @@ .label RIDBS = $29C // -- WIP -- read pointer into RS-232 receive buffer .label RODBS = $29D // -- WIP -- read pointer into RS-232 send buffer .label RODBE = $29E // -- WIP -- write pointer into RS-232 send buffer - .label IRQTMP = $29F // $29F-$2A0 -- NOT IMPLEMENTED -- + .label IRQTMP = $29F // $29F-$2A0 temporary IRQ vector storage [!] we use it for tape speed calibration instead .label ENABL = $2A1 // -- NOT IMPLEMENTED -- .label TODSNS = $2A2 // -- NOT IMPLEMENTED -- .label TRDTMP = $2A3 // -- NOT IMPLEMENTED -- diff --git a/c64/kernal/iostack/f1ca.chrout.s b/c64/kernal/iostack/f1ca.chrout.s index c76d7498..ebc31152 100644 --- a/c64/kernal/iostack/f1ca.chrout.s +++ b/c64/kernal/iostack/f1ca.chrout.s @@ -67,7 +67,7 @@ chrout_done_unknown_device: chrout_done_success: plp - cli // XXX certainly needed for screen, what about other devices? + cli // needed for screen (checked calling the routine on original ROMs), XXX what about other devices? // Restore X and Y ply_trash_a diff --git a/c64/kernal/tape/load_tape_normal.s b/c64/kernal/tape/load_tape_normal.s index ec06918a..1d4f18b4 100644 --- a/c64/kernal/tape/load_tape_normal.s +++ b/c64/kernal/tape/load_tape_normal.s @@ -32,6 +32,7 @@ // XXX use pilot to calibrate reading speed +// XXX add error correction #if CONFIG_TAPE_NORMAL @@ -41,22 +42,6 @@ load_tape_normal: jsr tape_ditch_verify // only LOAD is supported, no VERIFY - // Setup CIA #2 timers - - ldx #$03 // set timer A to 4 ticks - stx CIA2_TIMALO // $DD04 - ldx #$00 - stx CIA2_TIMAHI // $DD05 - - stx CIA2_TIMBHI // $DD07 - dex // puts $FF - for running timer B as long as possible - stx CIA2_TIMBLO // $DD06 - - // Let timer A run continuously - - ldx #%00010001 // start timer, force latch reload - stx CIA2_CRA // $DD0E - // Temporarily store MEMUSS in EAL lda MEMUSS+1 @@ -66,6 +51,7 @@ load_tape_normal: // Start playing + jsr tape_common_prepare_cia jsr tape_ask_play // FALLTROUGH diff --git a/c64/kernal/tape/load_tape_turbo.s b/c64/kernal/tape/load_tape_turbo.s index e85999e9..44af58c9 100644 --- a/c64/kernal/tape/load_tape_turbo.s +++ b/c64/kernal/tape/load_tape_turbo.s @@ -4,13 +4,9 @@ // Tape (turbo) part of the LOAD routine // -// Based on routine by enthusi/Onslaught, found here: +// Initially based on routine by enthusi/Onslaught, found here: // - https://codebase64.pokefinder.org/doku.php?id=base:turbotape_loader_source - - -// XXX use calibration bits to improve reading -// XXX find out how to use B0 tape timing -// XXX put header type into (TAPE1) +// but heavily reworked since then. Still, original authors deserve credits! #if CONFIG_TAPE_TURBO @@ -20,15 +16,11 @@ load_tape_turbo: jsr tape_ditch_verify // only LOAD is supported, no VERIFY - lda #$00 - sta CIA2_TIMAHI // $DD05 - lda #$FE // timer threshold for TurboTape - sta CIA2_TIMALO // $DD04 - // Prepare for sound effects jsr tape_clean_sid // Start playing + jsr tape_common_prepare_cia jsr tape_ask_play // FALLTROUGH @@ -76,7 +68,7 @@ load_tape_turbo_header_loop: // Handle the header jsr tape_clean_sid - jsr tape_handle_header // XXX handle non-relocatable programs - any non-0 even header type + jsr tape_handle_header bcs load_tape_turbo_header // if name does not match, look for other header // FALLTROUGH diff --git a/c64/kernal/tape/tape_common_prepare_cia.s b/c64/kernal/tape/tape_common_prepare_cia.s new file mode 100644 index 00000000..ac141cee --- /dev/null +++ b/c64/kernal/tape/tape_common_prepare_cia.s @@ -0,0 +1,30 @@ +#if ROM_LAYOUT_STD || (ROM_LAYOUT_M65 && SEGMENT_KERNAL_0) + + +#if CONFIG_TAPE_NORMAL || CONFIG_TAPE_TURBO + + +tape_common_prepare_cia: + + // Setup CIA #2 timers + + ldx #$03 // set timer A to 4 ticks XXX consider setting to 8 + stx CIA2_TIMALO // $DD04 + ldx #$00 + stx CIA2_TIMAHI // $DD05 + + stx CIA2_TIMBHI // $DD07 + dex // puts $FF - for running timer B as long as possible + stx CIA2_TIMBLO // $DD06 + + // Let timer A run continuously + + ldx #%00010001 // start timer, force latch reload + stx CIA2_CRA // $DD0E + + rts + +#endif + + +#endif // ROM layout diff --git a/c64/kernal/tape/tape_normal_get_pulse.s b/c64/kernal/tape/tape_normal_get_pulse.s index 82a41858..95d9e939 100644 --- a/c64/kernal/tape/tape_normal_get_pulse.s +++ b/c64/kernal/tape/tape_normal_get_pulse.s @@ -18,7 +18,7 @@ tape_normal_get_pulse: // implementation is probably just shorter lda #$10 -!: +!: bit CIA1_ICR // $DC0D beq !- // busy loop to detect signal, restart timer afterwards lda CIA2_TIMBLO // $DD06 diff --git a/c64/kernal/tape/tape_turbo_get_bit.s b/c64/kernal/tape/tape_turbo_get_bit.s index 2f616008..31c5a82f 100644 --- a/c64/kernal/tape/tape_turbo_get_bit.s +++ b/c64/kernal/tape/tape_turbo_get_bit.s @@ -13,26 +13,34 @@ tape_turbo_get_bit: - lda #$10 + lda #$10 !: - bit CIA1_ICR // $DC0D - beq !- // busy loop to detect signal - lda CIA2_ICR // $DD0D - pha - lda #%00011001 // start timer, one-shot, force latch reload - sta CIA2_CRA // $DD0E - pla - - pha // audio/video effects - asl - sta SID_SIGVOL - beq !+ - lda #$06 -!: - sta VIC_EXTCOL - pla + bit CIA1_ICR // $DC0D + beq !- // busy loop to detect signal, restart timer afterwards + lda CIA2_TIMBLO // $DD06 + ldx #%01010001 // start timer, force latch reload, count timer A underflows + stx CIA2_CRB // $DD0F - lsr +#if CONFIG_TAPE_TURBO_AUTOCALIBRATE + tax + cmp IRQTMP+0 +#else + cmp #$BF // threshold calculated by autocalibration routines under VICE +#endif + + bcs !+ + + lda #$01 + sta SID_SIGVOL + lda #$06 + sta VIC_EXTCOL + sec + rts +!: + lda #$00 + sta SID_SIGVOL + sta VIC_EXTCOL + clc rts diff --git a/c64/kernal/tape/tape_turbo_get_byte.s b/c64/kernal/tape/tape_turbo_get_byte.s index ec8683aa..9c2c22a6 100644 --- a/c64/kernal/tape/tape_turbo_get_byte.s +++ b/c64/kernal/tape/tape_turbo_get_byte.s @@ -15,14 +15,16 @@ tape_turbo_get_byte: + phx_trash_a lda #$01 sta INBIT // init the to-be-read byte with 1 (canary bit to mark loop end) !: jsr tape_turbo_get_bit rol INBIT bcc !- // is the initial 1 shifted into carry already? - lda INBIT // much nicer than ldx #8: dex: loop - + plx_trash_a + lda INBIT + rts diff --git a/c64/kernal/tape/tape_turbo_sync.s b/c64/kernal/tape/tape_turbo_sync.s index a81c272c..d442789f 100644 --- a/c64/kernal/tape/tape_turbo_sync.s +++ b/c64/kernal/tape/tape_turbo_sync.s @@ -22,18 +22,78 @@ tape_turbo_sync_common: tape_turbo_sync_header: - ldx #$FF -!: +#if CONFIG_TAPE_TURBO_AUTOCALIBRATE + + // Initial pulse threshold + lda #$BF // auto-calibration under VICE often returns this value + sta IRQTMP+0 + + // Zero temporary stack area + lda #$00 ldy #$03 +!: + sta STACK, y + dey + bpl !- + +#endif // CONFIG_TAPE_TURBO_AUTOCALIBRATE + + // Perform synchronization + ldx #$C0 +!: + phx_trash_a + ldy #$04 !: jsr tape_turbo_sync_common dey bne !- + plx_trash_a dex bne !-- // FALLTROUGH +#if CONFIG_TAPE_TURBO_AUTOCALIBRATE + +tape_turbo_sync_calibrate: + + ldy #$80 + + // FALLTROUGH + +tape_turbo_sync_calibrate_loop: + + // Skip 5 bits + phy_trash_a + ldy #$04 +!: + jsr tape_turbo_get_bit + bcs tape_turbo_sync_header + dey + bpl !- + + jsr tape_turbo_sync_measure_0 + jsr tape_turbo_sync_measure_1 + jsr tape_turbo_sync_measure_0 + + ply_trash_a + dey + bne tape_turbo_sync_calibrate_loop + + // Calculate new pulse threshold as arithmetic mean + // We accumulated 256 zeros and 128 ones, so calculating average value is easy + + lda STACK+0 + clc + ror + clc + adc STACK+1 + sta IRQTMP+0 + + // FALLTROUGH + +#endif // CONFIG_TAPE_TURBO_AUTOCALIBRATE + tape_turbo_sync_payload: jsr tape_turbo_sync_common diff --git a/c64/kernal/tape/tape_turbo_sync_measure.s b/c64/kernal/tape/tape_turbo_sync_measure.s new file mode 100644 index 00000000..d68349db --- /dev/null +++ b/c64/kernal/tape/tape_turbo_sync_measure.s @@ -0,0 +1,46 @@ +#if ROM_LAYOUT_STD || (ROM_LAYOUT_M65 && SEGMENT_KERNAL_0) + +// +// Tape (turbo) helper routine - measure bit length for calibration purposes +// + +#if CONFIG_TAPE_TURBO && CONFIG_TAPE_TURBO_AUTOCALIBRATE + + +tape_turbo_sync_measure_0: + + // Measure bit '0' + jsr tape_turbo_get_bit + bcs tape_turbo_sync_measure_fail + txa + adc STACK+2 + sta STACK+2 + bcc !+ + inc STACK+0 +!: + rts + +tape_turbo_sync_measure_1: + + // Measure bit '1' + jsr tape_turbo_get_bit + bcc tape_turbo_sync_measure_fail + txa + adc STACK+3 + sta STACK+3 + bcc !+ + inc STACK+1 +!: + rts + +tape_turbo_sync_measure_fail: + + pla + pla + jmp tape_turbo_sync_header + + +#endif + + +#endif // ROM layout