working irl decoding

This commit is contained in:
Joppe Blondel
2026-10-09 14:51:56 +02:00
parent b8c6eaf4c9
commit c84b3fdc97
7 changed files with 352 additions and 70 deletions
+30 -16
View File
@@ -4667,7 +4667,6 @@ CLOADB: inc hl ; skip #
call PRS ; print filename call PRS ; print filename
; de contains the nr of blocks to load ; de contains the nr of blocks to load
; TODO check de>0 ; TODO check de>0
pop de pop de
1$: push de ; save repeat counter 1$: push de ; save repeat counter
call CLREADBLOCK ; hl = new data call CLREADBLOCK ; hl = new data
@@ -4675,7 +4674,6 @@ CLOADB: inc hl ; skip #
inc hl ; goto start of data inc hl ; goto start of data
pop de ; get repeat counter pop de ; get repeat counter
push hl ; save stored data push hl ; save stored data
exx exx
pop hl ; get stored data back pop hl ; get stored data back
pop de ; get destination pop de ; get destination
@@ -4683,7 +4681,6 @@ CLOADB: inc hl ; skip #
ldir ; copy data to memory ldir ; copy data to memory
push de ; store destination address push de ; store destination address
exx exx
dec de dec de
ld a,e ld a,e
cp 0 cp 0
@@ -4691,7 +4688,6 @@ CLOADB: inc hl ; skip #
ld a,d ld a,d
cp 0 cp 0
jr nz,1$ ; repeat if not done yet jr nz,1$ ; repeat if not done yet
pop de pop de
pop bc pop bc
pop hl pop hl
@@ -4699,19 +4695,31 @@ CLOADB: inc hl ; skip #
CLREADBLOCK: CLREADBLOCK:
call BIOS_CAS_READ_BLOCK 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_WAIT_SILENCE
call BIOS_CAS_READ_BLOCK call BIOS_CAS_READ_BLOCK
push hl 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 ; error, make sure it is silent again
push af
push bc ; save specific error code
call BIOS_CAS_WAIT_SILENCE call BIOS_CAS_WAIT_SILENCE
pop bc pop bc
pop af pop af
jr CLERR jr CLERR
1$: push hl ; save hl 1$: push hl ; save hl
ld a,'o' ; skip redundant block
call OUTC
call BIOS_CAS_READ_BLOCK_DRY call BIOS_CAS_READ_BLOCK_DRY
; Ignore errors on this block ; Ignore errors on this block
2$: ; but make sure it is silent again 2$: ; but make sure it is silent again
@@ -4734,9 +4742,15 @@ CLECAL: ld h,0
call PRNTHL ; print specific error code call PRNTHL ; print specific error code
ld hl, CLECALMSG ld hl, CLECALMSG
jr CLEPRINT 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 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 jr CLEPRINT
CLECHS: ld hl, CLECHSMSG CLECHS: ld hl, CLECHSMSG
jr CLEPRINT jr CLEPRINT
@@ -4750,12 +4764,12 @@ CLEPRINT:
jp FCERR jp FCERR
CLSTARTMSG: .db "Play tape...",0ah,0dh,00h CLSTARTMSG: .db "Play tape...",0ah,0dh,00h
CLECALMSG: .db " - Calibration failed",0ah,0dh,00h CLECALMSG: .db "CE",0ah,0dh,00h
CLESYNMSG: .db "Synchronization failed",0ah,0dh,00h CLESYNMSG: .db "SE",0ah,0dh,00h
CLEREDMSG: .db "Read failed",0ah,0dh,00h CLEREDMSG: .db "RE",0ah,0dh,00h
CLECHSMSG: .db "Checksum error",0ah,0dh,00h CLECHSMSG: .db "CSE",0ah,0dh,00h
CLNOHDRMSG: .db "No header found", 0ah,0dh,00h CLNOHDRMSG: .db "HE", 0ah,0dh,00h
CLBLOCKSMSG: .db " blocks to load", 0ah,0dh,00h CLBLOCKSMSG: .db " blocks", 0ah,0dh,00h
CLCLRF: .db 0ah,0dh,00h CLCLRF: .db 0ah,0dh,00h
.end .end
+199
View File
@@ -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;
+54
View File
@@ -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
+4 -2
View File
@@ -66,8 +66,9 @@ class PulseWriter:
self._level(self.low, self.short_samples) self._level(self.low, self.short_samples)
def marker(self) -> None: def marker(self) -> None:
self._level(self.high, 2*self.long_samples) for _ in range(4):
self._level(self.low, 2*self.long_samples) self._level(self.high, self.short_samples)
self._level(self.low, self.short_samples)
def bit(self, value: int) -> None: def bit(self, value: int) -> None:
if value: if value:
@@ -101,6 +102,7 @@ class PulseWriter:
block_data = struct.pack("<B",type) + data[:BLOCK_SIZE].ljust(BLOCK_SIZE, b'\0') block_data = struct.pack("<B",type) + data[:BLOCK_SIZE].ljust(BLOCK_SIZE, b'\0')
self.bytes(block_data) self.bytes(block_data)
self.bytes(struct.pack("<H", crc16(block_data))) self.bytes(struct.pack("<H", crc16(block_data)))
self.byte(0x00) # Make sure the last byte has its last edge
def header(pulses: PulseWriter, path:Path) -> None: def header(pulses: PulseWriter, path:Path) -> None:
fname = path.name fname = path.name
+65 -52
View File
@@ -95,6 +95,11 @@ sample_pulse:
; returns a (average) ; returns a (average)
; cary is timeout ; cary is timeout
; uses b,e,h ; 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: average8:
ld e,0 ld e,0
@@ -118,27 +123,33 @@ average8:
ret ret
; ------------------------------------------------------- ; -------------------------------------------------------
; Make a +/-25% window around a ; Make non-overlapping short and long pulse windows
; needs a ; Boundary is the midpoint between both averages
; returns b (minimum) c (maximum) ; Short minimum and long maximum allow a 50% margin
; uses e,h ; needs SHORT_AVG and LONG_AVG
; NOTE: USES 8 BIT COUNTER ; uses a,c
; @1MHZ t_long<1.14ms
; @2MHZ t_long<571us
; @4MHZ t_long<285us
; TODO: Check feasibility of using 16bit accumulator
; ------------------------------------------------------- ; -------------------------------------------------------
make_window: make_windows:
ld e,a ld a,(SHORT_AVG)
srl a srl a
srl a ld (SHORT_MIN),a ; short minimum = 50% of short
ld h,a ; h = a/4
ld a,e ld a,(LONG_AVG)
sub h ld c,a
ld b,a ; b = a-25% inc a
ld a,e srl a ; ceil(long/2)
add h add c
ld c,a ; c = a+25% 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 ret
; ------------------------------------------------------- ; -------------------------------------------------------
@@ -148,7 +159,7 @@ make_window:
; carry is error, a contains error code ; carry is error, a contains error code
; ------------------------------------------------------- ; -------------------------------------------------------
calibrate: calibrate:
ld a,(TAPE_PORT) ; get current state in a,(TAPE_PORT) ; get current state
ld d,a ld d,a
call sample_pulse ; make sure we start in silence call sample_pulse ; make sure we start in silence
jp nc,cal_err ; should timeout jp nc,cal_err ; should timeout
@@ -169,11 +180,6 @@ calibrate:
call average8 call average8
jr c,cal_err_short_leader_average jr c,cal_err_short_leader_average
ld (SHORT_AVG),a 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 ; find the long leader
; need 8 consecutive pulses > short*1.5 ; need 8 consecutive pulses > short*1.5
ld a,(SHORT_AVG) ld a,(SHORT_AVG)
@@ -201,11 +207,6 @@ calibrate:
call average8 call average8
jr c,cal_err_long_leader_average jr c,cal_err_long_leader_average
ld (LONG_AVG),a 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 ; A quick sanity check: long>1.5*short
ld a,(SHORT_AVG) ld a,(SHORT_AVG)
ld c,a ld c,a
@@ -215,29 +216,31 @@ calibrate:
ld a,(LONG_AVG) ld a,(LONG_AVG)
cp c cp c
jr c,cal_err_sanity_check jr c,cal_err_sanity_check
; Find the marker. marker is 2*long call make_windows
; threshold is 1.5*long ; Find the marker: 8 consecutive short intervals
ld a,(LONG_AVG) ; Classify them using short*1.5
ld a,(SHORT_AVG)
ld c,a ld c,a
srl a srl a
add c add c
ld c,a ; c = 1.5*long ld c,a ; c = 1.5*short
; Find two consecutive marker-width intervals ld e,0 ; consecutive short counter
5$: 5$:
ld e,0
6$:
call measure_pulse call measure_pulse
jr c,cal_err_marker_measure jr c,cal_err_marker_measure
ld a,b ld a,b
cp c cp c
jr c,5$ ; not a marker jr c,6$ ; short interval found
inc e ; marker interval found ld e,0 ; long interval, restart marker
jr 5$
6$:
inc e
ld a,e ld a,e
cp 2 ; we need two of them cp 8
jr c,6$ ; not 2 yet, measure next jr c,5$
; both marker halves found ; marker found
; we are at the beginning of the first data pulse ; 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 call read_byte
jr c,cal_err_training_read jr c,cal_err_training_read
cp a,055h cp a,055h
@@ -348,22 +351,23 @@ read_byte:
; ------------------------------------------------------- ; -------------------------------------------------------
; Read and check sync bytes ; Read and check sync bytes
; carry is error ; carry is error, error value in a
; ------------------------------------------------------- ; -------------------------------------------------------
read_sync: read_sync:
call read_byte call read_byte
ret c jr c,sync_err_read_1
cp 05ah cp 05ah
jr nz,9$ jr nz,sync_err_value_1
call read_byte call read_byte
ret c jr c,sync_err_read_2
cp 0c3h cp 0c3h
jr nz,9$ jr nz,sync_err_value_2
or a or a
ret ret
9$: create_error_return sync_err_read_1, 001h
scf create_error_return sync_err_read_2, 002h
ret create_error_return sync_err_value_1, 003h
create_error_return sync_err_value_2, 004h
; ------------------------------------------------------- ; -------------------------------------------------------
; Read a block into BLOCK_BUFFER ; Read a block into BLOCK_BUFFER
@@ -379,6 +383,8 @@ read_block:
push de push de
push hl push hl
exx exx
call BIOS_FLUSH
di
; calibrate reader ; calibrate reader
call calibrate call calibrate
jr nc,1$ ; if no error jump jr nc,1$ ; if no error jump
@@ -390,6 +396,7 @@ read_block:
; calibrated, read sync bytes ; calibrated, read sync bytes
call read_sync call read_sync
jr nc,2$ ; if no error jump jr nc,2$ ; if no error jump
ld b,a
ld a,BLOCK_ERROR_SYNC ld a,BLOCK_ERROR_SYNC
scf scf
jr 101$ jr 101$
@@ -400,10 +407,13 @@ read_block:
ld hl,BLOCK_BUFFER ld hl,BLOCK_BUFFER
exx exx
4$: 4$:
call read_byte call read_byte ; byte in a
jr nc,5$ jr nc,5$
; read error ; read error
exx exx
ld a,e
exx
ld b,a ; store count in block in specific error
ld a,BLOCK_ERROR_READ ld a,BLOCK_ERROR_READ
scf scf
jr 101$ jr 101$
@@ -440,6 +450,7 @@ read_block:
pop hl pop hl
pop de pop de
exx exx
ei
ret ret
; ------------------------------------------------------- ; -------------------------------------------------------
@@ -455,6 +466,7 @@ read_block_dry:
push de push de
push hl push hl
exx exx
di
; calibrate reader ; calibrate reader
call calibrate call calibrate
jr nc,1$ ; if no error jump jr nc,1$ ; if no error jump
@@ -496,6 +508,7 @@ read_block_dry:
pop hl pop hl
pop de pop de
exx exx
ei
ret ret
; ------------------------------------------------------- ; -------------------------------------------------------
; CRC16 ; CRC16
Binary file not shown.
BIN
View File
Binary file not shown.