BLOCK_SIZE .equ 1+128+2 ; type + 64 bytes + crc-16 BLOCK_ERROR_CALIBRATION .equ 1 BLOCK_ERROR_SYNC .equ 2 BLOCK_ERROR_READ .equ 3 BLOCK_ERROR_CHSUM .equ 4 TAPE_WORKSPACE .equ END_OF_BIOS_WS SHORT_AVG .equ TAPE_WORKSPACE + 000h ; byte SHORT_MIN .equ TAPE_WORKSPACE + 001h ; byte SHORT_MAX .equ TAPE_WORKSPACE + 002h ; byte LONG_AVG .equ TAPE_WORKSPACE + 003h ; byte LONG_MIN .equ TAPE_WORKSPACE + 004h ; byte LONG_MAX .equ TAPE_WORKSPACE + 005h ; byte BLOCK_BUFFER .equ TAPE_WORKSPACE + 006h ; BLOCK_SIZE ; ------------------------------------------------------- ; Error return callback macro ; creates a simple macro which puts a return code in a ; and sets the carry flag ; ------------------------------------------------------- create_error_return MACRO &name, &code &name: ld a,&code scf ret ENDM ; ------------------------------------------------------- ; Measure edge-to-edge pulse ; needs d (comparator state) ; returns b (loop count) ; carry is timeout ; uses a ; loop is 36T extra 22T (0.6 loops) ; ------------------------------------------------------- measure_pulse: ld b,0 ; 4T 1$: ; use b as timeout counter (246 loops) inc b ; 4T jr z,9$ ; 12/7T in a,(TAPE_PORT) ; 11T cp d ; 4T jp z,1$ ; 10T ; loop has 36T ; edge found ld d,a ; 4T or a ; 4T, clear carry ret ; 10T 9$: scf ret ; ------------------------------------------------------- ; Ignore one pulse and then measure the next ; needs d (comparator state) ; returns b (loop count) ; carry is timeout ; uses a ; loop is 36T extra 26T (0.7 loops) ; ------------------------------------------------------- sample_pulse: ld b,0 ; 4T 1$: ; use b as timeout counter (246 loops) inc b ; 4T jr z,9$ ; 12/7T in a,(TAPE_PORT) ; 11T cp d ; 4T jp z,1$ ; 10T ; loop has 36T ; edge found ld d,a ; 4T ; start timing from now ld b,0 ; 4T 2$: ; use b as timeout counter (246 loops) inc b ; 4T jr z,9$ ; 12/7T in a,(TAPE_PORT) ; 11T cp d ; 4T jp z,2$ ; 10T ; loop has 36T ; edge found ld d,a ; 4T or a ; 4T, clear carry ret 9$: scf ret ; ------------------------------------------------------- ; Average 8 pulse measurements ; needs d (comparator state) ; returns a (average) ; cary is timeout ; uses b,e,h ; ------------------------------------------------------- average8: ld e,0 ld h,8 1$: ; synchronize to next pulse and ignore measurement call sample_pulse ret c ; return when timeout call sample_pulse ret c ; return when timeout ld a,e ; add loop count to e add b ld e,a dec h jr nz,1$ ; repeat this 8 times ld a,e srl a srl a srl a ; divide by 8 or a ; cear carry ret ; ------------------------------------------------------- ; Make a +/-25% window around a ; needs a ; returns b (minimum) c (maximum) ; uses e,h ; NOTE: USES 8 BIT COUNTER ; @1MHZ t_long<1.14ms ; @2MHZ t_long<571us ; @4MHZ t_long<285us ; TODO: Check feasibility of using 16bit accumulator ; ------------------------------------------------------- make_window: ld e,a srl a srl a ld h,a ; h = a/4 ld a,e sub h ld b,a ; b = a-25% ld a,e add h ld c,a ; c = a+25% ret ; ------------------------------------------------------- ; Calibration ; d needs to be kept from now on ; uses a,b,c,e,h,l ; carry is error, a contains error code ; ------------------------------------------------------- calibrate: ld a,(TAPE_PORT) ; get current state ld d,a call sample_pulse ; make sure we start in silence jp nc,cal_err ; should timeout ; wait for the first edge ld l,0 1$: call sample_pulse jr nc,2$ ; use l as timout counter ; inc l ; jr nx,1$ jr 1$ ; timeout jp cal_err ; TODO ignored timeout for now 2$: ; edge found ; get short leader timing call average8 jr c,cal_err_short_leader_average ld (SHORT_AVG),a call make_window ld a,b ld (SHORT_MIN),a ld a,c ld (SHORT_MAX),a ; find the long leader ; need 8 consecutive pulses > short*1.5 ld a,(SHORT_AVG) ld c,a srl a add c ; a = 1.5*short ld c,a ; TODO no overall timeout here 3$: ld e,4 ; repeat 4 times, each first pulse ; is ignored 4$: call sample_pulse jr c,cal_err_wait_for_long call sample_pulse jr c,cal_err_wait_for_long ld a,b ; try again if its a short pulse cp c jr c,3$ ; we found a long pulse dec e jr nz,4$ ; repeat this 4 times ; long leader found call average8 jr c,cal_err_long_leader_average ld (LONG_AVG),a call make_window ld a,b ld (LONG_MIN),a ld a,c ld (LONG_MAX),a ; A quick sanity check: long>1.5*short ld a,(SHORT_AVG) ld c,a srl a add c ld c,a ; c = 1.5*short ld a,(LONG_AVG) cp c jr c,cal_err_sanity_check ; Find the marker. marker is 2*long ; threshold is 1.5*long ld a,(LONG_AVG) ld c,a srl a add c ld c,a ; c = 1.5*long ; Find two consecutive marker-width intervals 5$: ld e,0 6$: call measure_pulse jr c,cal_err_marker_measure ld a,b cp c jr c,5$ ; not a marker inc e ; marker interval found ld a,e cp 2 ; we need two of them jr c,6$ ; not 2 yet, measure next ; both marker halves found ; we are at the beginning of the first data pulse ; next 8 bytes is the training byte 0x55 call read_byte jr c,cal_err_training_read cp a,055h jr nz,cal_err_training_incorrect ; calibration complete or a ; clear carry ret create_error_return cal_err, 000h create_error_return cal_err_short_leader_average, 001h create_error_return cal_err_wait_for_long, 002h create_error_return cal_err_long_leader_average, 003h create_error_return cal_err_sanity_check, 004h create_error_return cal_err_marker_measure, 005h create_error_return cal_err_training_read, 006h create_error_return cal_err_training_incorrect, 007h ; ------------------------------------------------------- ; Read a single bit ; needs d (comparator state) ; valid: z=1 c=bit ; error: z=0 ; uses a,b,c ; ------------------------------------------------------- read_bit: ; entry may be late into first pulse ; wait for first edge to measure ld b,0 1$: inc b jr z,bit_bad in a,(TAPE_PORT) cp d jp z,1$ ld d,a ; ignored first edge ; measure second pulse ld b,0 2$: inc b jr z,bit_bad in a,(TAPE_PORT) cp d jp z,2$ ld d,a ; if in short window (second half is short pulse) ; 1 ld a,(SHORT_MIN) ld c,a ld a,b cp c jr c,bit_bad ; pulse no silence ret ; ------------------------------------------------------- ; Strings ; ------------------------------------------------------- msg_start: .db "PLAY TAPE", 0ah,0dh,00h