working irl decoding
This commit is contained in:
+30
-16
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -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
@@ -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("<B",type) + data[:BLOCK_SIZE].ljust(BLOCK_SIZE, b'\0')
|
||||
self.bytes(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:
|
||||
fname = path.name
|
||||
|
||||
@@ -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
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user