diff --git a/apps/basic/basic.asm b/apps/basic/basic.asm index 8e8a8fe..1ac03f4 100644 --- a/apps/basic/basic.asm +++ b/apps/basic/basic.asm @@ -4667,7 +4667,6 @@ CLOADB: inc hl ; skip # call PRS ; print filename ; de contains the nr of blocks to load ; TODO check de>0 - pop de 1$: push de ; save repeat counter call CLREADBLOCK ; hl = new data @@ -4675,7 +4674,6 @@ CLOADB: inc hl ; skip # inc hl ; goto start of data pop de ; get repeat counter push hl ; save stored data - exx pop hl ; get stored data back pop de ; get destination @@ -4683,7 +4681,6 @@ CLOADB: inc hl ; skip # ldir ; copy data to memory push de ; store destination address exx - dec de ld a,e cp 0 @@ -4691,7 +4688,6 @@ CLOADB: inc hl ; skip # ld a,d cp 0 jr nz,1$ ; repeat if not done yet - pop de pop bc pop hl @@ -4699,19 +4695,31 @@ CLOADB: inc hl ; skip # CLREADBLOCK: call BIOS_CAS_READ_BLOCK - jr nc,1$ + jr c,3$ + ld a,'.' ; block read correctly + call OUTC + jr 1$ +3$: ld a,'x' ; block failed, try redundant block + call OUTC call BIOS_CAS_WAIT_SILENCE call BIOS_CAS_READ_BLOCK push hl - jr nc,2$ + jr c,4$ + ld a,'.' ; redundant block read correctly + call OUTC + jr 2$ +4$: push af ; preserve error code + push bc ; preserve specific error code + ld a,'x' ; redundant block failed + call OUTC ; error, make sure it is silent again - push af - push bc ; save specific error code call BIOS_CAS_WAIT_SILENCE pop bc pop af jr CLERR 1$: push hl ; save hl + ld a,'o' ; skip redundant block + call OUTC call BIOS_CAS_READ_BLOCK_DRY ; Ignore errors on this block 2$: ; but make sure it is silent again @@ -4734,9 +4742,15 @@ CLECAL: ld h,0 call PRNTHL ; print specific error code ld hl, CLECALMSG jr CLEPRINT -CLESYN: ld hl, CLESYNMSG +CLESYN: ld h,0 + ld l,b ; get specific error code + call PRNTHL ; print specific error code + ld hl, CLESYNMSG jr CLEPRINT -CLERED: ld hl, CLEREDMSG +CLERED: ld h,0 + ld l,b ; get specific error code + call PRNTHL ; print specific error code + ld hl, CLEREDMSG jr CLEPRINT CLECHS: ld hl, CLECHSMSG jr CLEPRINT @@ -4750,12 +4764,12 @@ CLEPRINT: jp FCERR CLSTARTMSG: .db "Play tape...",0ah,0dh,00h -CLECALMSG: .db " - Calibration failed",0ah,0dh,00h -CLESYNMSG: .db "Synchronization failed",0ah,0dh,00h -CLEREDMSG: .db "Read failed",0ah,0dh,00h -CLECHSMSG: .db "Checksum error",0ah,0dh,00h -CLNOHDRMSG: .db "No header found", 0ah,0dh,00h -CLBLOCKSMSG: .db " blocks to load", 0ah,0dh,00h +CLECALMSG: .db "CE",0ah,0dh,00h +CLESYNMSG: .db "SE",0ah,0dh,00h +CLEREDMSG: .db "RE",0ah,0dh,00h +CLECHSMSG: .db "CSE",0ah,0dh,00h +CLNOHDRMSG: .db "HE", 0ah,0dh,00h +CLBLOCKSMSG: .db " blocks", 0ah,0dh,00h CLCLRF: .db 0ah,0dh,00h .end diff --git a/hardware/CUPL.PLD b/hardware/CUPL.PLD new file mode 100644 index 0000000..1668645 --- /dev/null +++ b/hardware/CUPL.PLD @@ -0,0 +1,199 @@ +Name casette; +PartNo 00; +Date 2026-10-03; +Revision 01; +Designer joppe; +Company unknown; +Assembly None; +Location unknown; +Device g22v10; + + +/* ------------------------------------------------------------ + Constants + ------------------------------------------------------------ */ + +ar = 'b'0; +sp = 'b'0; + + +/* ------------------------------------------------------------ + Pin assignments + + Physical signals IOSEL, RD and WR are active-low. + Their polarity is expressed in the equations with !. + ------------------------------------------------------------ */ + +/* inputs */ + +PIN 1 = CLK; + +PIN 2 = A1; +PIN 3 = A2; + +PIN 4 = IOSEL; +PIN 5 = RD; +PIN 6 = A0; + +PIN 7 = COMP; +PIN 8 = WR; + + +/* data bus */ + +PIN 14 = D0; +PIN 15 = D1; +PIN 16 = D2; +PIN 17 = D3; +PIN 18 = D4; +PIN 19 = D5; +PIN 20 = D6; +PIN 21 = D7; + + +/* registered output */ + +PIN 22 = LED; + + +/* ------------------------------------------------------------ + Read register at offset 0 + + Conditions: + + /IOSEL = 0 + /RD = 0 + /WR = 1 + A2:A0 = 000 + + Result: + + D0 = COMP + D7..D1 = 0 + ------------------------------------------------------------ */ + +D0 = COMP; + +D1 = 'b'0; +D2 = 'b'0; +D3 = 'b'0; +D4 = 'b'0; +D5 = 'b'0; +D6 = 'b'0; +D7 = 'b'0; + + +D0.OE = + !IOSEL + & !RD + & WR + & !A2 + & !A1 + & !A0; + +D1.OE = + !IOSEL + & !RD + & WR + & !A2 + & !A1 + & !A0; + +D2.OE = + !IOSEL + & !RD + & WR + & !A2 + & !A1 + & !A0; + +D3.OE = + !IOSEL + & !RD + & WR + & !A2 + & !A1 + & !A0; + +D4.OE = + !IOSEL + & !RD + & WR + & !A2 + & !A1 + & !A0; + +D5.OE = + !IOSEL + & !RD + & WR + & !A2 + & !A1 + & !A0; + +D6.OE = + !IOSEL + & !RD + & WR + & !A2 + & !A1 + & !A0; + +D7.OE = + !IOSEL + & !RD + & WR + & !A2 + & !A1 + & !A0; + + +/* ------------------------------------------------------------ + Writable LED register + + Write conditions: + + /IOSEL = 0 + /RD = 1 + /WR = 0 + A2:A0 = 000 + + During such a write: + + LED.D = D0 + + Otherwise: + + LED.D = LED + + Pin 1 (CLK) clocks the registered macrocell. + ------------------------------------------------------------ */ + +LED.D = + ( + !IOSEL + & RD + & !WR + & !A2 + & !A1 + & !A0 + & D0 + ) + + # + + ( + ( + IOSEL + # !RD + # WR + # A2 + # A1 + # A0 + ) + & LED + ); + + +LED.ar = ar; +LED.sp = sp; \ No newline at end of file diff --git a/hardware/casette.jed b/hardware/casette.jed new file mode 100644 index 0000000..4ae757a --- /dev/null +++ b/hardware/casette.jed @@ -0,0 +1,54 @@ + +CUPL(WM) 5.0a Serial# MW-10400000 +Device g22v10 Library DLIB-h-40-1 +Created Sat Oct 03 16:22:02 2026 +Name casette +Partno 00 +Revision 01 +Date 2026-10-03 +Designer joppe +Company unknown +Assembly None +Location unknown +*QP24 +*QF5892 +*G1 +*F0 +*L00416 00000000000000000000000011111111 +*L00448 11111111111111111111111111111111 +*L00480 11111111101110111011011110111111 +*L00512 10111111110111111111111011110111 +*L00544 11111111111111111111111111111111 +*L00576 11101111111110111111111111111111 +*L00608 11111111111111101111111111111111 +*L00640 11110111111111111111111111100111 +*L00672 11111111111111111111111111111111 +*L00704 11110110111111111111111111111111 +*L00736 11111111111111111110111111111111 +*L00768 01111111111111111111111100000000 +*L00896 00000000000000000000000000001111 +*L00928 10111011101110111011111101111111 +*L00960 11111111000000000000000000000000 +*L01472 00000000000000000000000011111011 +*L01504 10111011101110111111011111111111 +*L01536 11110000000000000000000000000000 +*L02144 00000000000011111011101110111011 +*L02176 10111111011111111111111100000000 +*L02880 00000000000000000000000011111011 +*L02912 10111011101110111111011111111111 +*L02944 11110000000000000000000000000000 +*L03648 00001111101110111011101110111111 +*L03680 01111111111111110000000000000000 +*L04288 00000000000000000000000011111011 +*L04320 10111011101110111111011111111111 +*L04352 11110000000000000000000000000000 +*L04864 00000000000000000000111110111011 +*L04896 10111011101111110111111111111111 +*L05344 00000000000000000000000011111011 +*L05376 10111011101110111111011111111111 +*L05408 11111111111111111111111111110111 +*L05440 11111111111111110000000000000000 +*L05792 00000000000000000010111111111111 +*L05824 11110011000000110000000000000000 +*C5BE4 +*6A87 \ No newline at end of file diff --git a/scripts/encode.py b/scripts/encode.py index 1b0271d..5a59e15 100644 --- a/scripts/encode.py +++ b/scripts/encode.py @@ -66,8 +66,9 @@ class PulseWriter: self._level(self.low, self.short_samples) def marker(self) -> None: - self._level(self.high, 2*self.long_samples) - self._level(self.low, 2*self.long_samples) + for _ in range(4): + self._level(self.high, self.short_samples) + self._level(self.low, self.short_samples) def bit(self, value: int) -> None: if value: @@ -101,6 +102,7 @@ class PulseWriter: block_data = struct.pack(" None: fname = path.name diff --git a/tape.asm b/tape.asm index aba9e92..8ab56a6 100644 --- a/tape.asm +++ b/tape.asm @@ -95,6 +95,11 @@ sample_pulse: ; returns a (average) ; cary is timeout ; uses b,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 ; ------------------------------------------------------- average8: ld e,0 @@ -118,27 +123,33 @@ average8: 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 non-overlapping short and long pulse windows +; Boundary is the midpoint between both averages +; Short minimum and long maximum allow a 50% margin +; needs SHORT_AVG and LONG_AVG +; uses a,c ; ------------------------------------------------------- -make_window: - ld e,a +make_windows: + ld a,(SHORT_AVG) 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% + ld (SHORT_MIN),a ; short minimum = 50% of short + + ld a,(LONG_AVG) + ld c,a + inc a + srl a ; ceil(long/2) + add c + ld (LONG_MAX),a ; long maximum = 150% of long + + ld a,(SHORT_AVG) + ld c,a + ld a,(LONG_AVG) + add c + inc a + srl a ; ceil((short+long)/2) + ld (LONG_MIN),a ; midpoint belongs to long + dec a + ld (SHORT_MAX),a ; short ends below midpoint ret ; ------------------------------------------------------- @@ -148,7 +159,7 @@ make_window: ; carry is error, a contains error code ; ------------------------------------------------------- calibrate: - ld a,(TAPE_PORT) ; get current state + in a,(TAPE_PORT) ; get current state ld d,a call sample_pulse ; make sure we start in silence jp nc,cal_err ; should timeout @@ -169,11 +180,6 @@ calibrate: 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) @@ -201,11 +207,6 @@ calibrate: 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 @@ -215,29 +216,31 @@ calibrate: 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) + call make_windows + ; Find the marker: 8 consecutive short intervals + ; Classify them using short*1.5 + ld a,(SHORT_AVG) ld c,a srl a add c - ld c,a ; c = 1.5*long - ; Find two consecutive marker-width intervals + ld c,a ; c = 1.5*short + ld e,0 ; consecutive short counter 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 + jr c,6$ ; short interval found + ld e,0 ; long interval, restart marker + jr 5$ +6$: + inc e ld a,e - cp 2 ; we need two of them - jr c,6$ ; not 2 yet, measure next - ; both marker halves found + cp 8 + jr c,5$ + ; marker found ; we are at the beginning of the first data pulse - ; next 8 bytes is the training byte 0x55 + ; next byte is the training byte 0x55 call read_byte jr c,cal_err_training_read cp a,055h @@ -348,22 +351,23 @@ read_byte: ; ------------------------------------------------------- ; Read and check sync bytes -; carry is error +; carry is error, error value in a ; ------------------------------------------------------- read_sync: call read_byte - ret c + jr c,sync_err_read_1 cp 05ah - jr nz,9$ + jr nz,sync_err_value_1 call read_byte - ret c + jr c,sync_err_read_2 cp 0c3h - jr nz,9$ + jr nz,sync_err_value_2 or a ret -9$: - scf - ret + create_error_return sync_err_read_1, 001h + create_error_return sync_err_read_2, 002h + create_error_return sync_err_value_1, 003h + create_error_return sync_err_value_2, 004h ; ------------------------------------------------------- ; Read a block into BLOCK_BUFFER @@ -379,6 +383,8 @@ read_block: push de push hl exx + call BIOS_FLUSH + di ; calibrate reader call calibrate jr nc,1$ ; if no error jump @@ -390,6 +396,7 @@ read_block: ; calibrated, read sync bytes call read_sync jr nc,2$ ; if no error jump + ld b,a ld a,BLOCK_ERROR_SYNC scf jr 101$ @@ -400,10 +407,13 @@ read_block: ld hl,BLOCK_BUFFER exx 4$: - call read_byte + call read_byte ; byte in a jr nc,5$ ; read error exx + ld a,e + exx + ld b,a ; store count in block in specific error ld a,BLOCK_ERROR_READ scf jr 101$ @@ -440,6 +450,7 @@ read_block: pop hl pop de exx + ei ret ; ------------------------------------------------------- @@ -455,6 +466,7 @@ read_block_dry: push de push hl exx + di ; calibrate reader call calibrate jr nc,1$ ; if no error jump @@ -496,6 +508,7 @@ read_block_dry: pop hl pop de exx + ei ret ; ------------------------------------------------------- ; CRC16 diff --git a/tapes/recording_makefile.wav b/tapes/recording_makefile.wav index 4f9e5b7..4224de6 100644 Binary files a/tapes/recording_makefile.wav and b/tapes/recording_makefile.wav differ diff --git a/tapes/test.wav b/tapes/test.wav index b399c15..f050e80 100644 Binary files a/tapes/test.wav and b/tapes/test.wav differ