Added old tape routines and started with cload

This commit is contained in:
Joppe Blondel
2026-10-08 16:50:35 +02:00
parent 463c4bddc3
commit 67892cf81e
10 changed files with 885 additions and 13 deletions
+1
View File
@@ -1 +1,2 @@
build build
*__pycache__**
+2 -7
View File
@@ -5,11 +5,6 @@ ASM := zasm
ASMFLAGS := -w -b ASMFLAGS := -w -b
BUILDDIR := build BUILDDIR := build
FILES := bios
SOURCES := $(addprefix ./, $(addsuffix .asm, $(FILES)))
TARGETS := $(addprefix $(BUILDDIR)/, $(addsuffix .bin, $(FILES)))
TOPTARGETS := all clean TOPTARGETS := all clean
$(TOPTARGETS): $(SUBDIRS) $(TOPTARGETS): $(SUBDIRS)
@@ -21,9 +16,9 @@ clean:
$(BUILDDIR): $(BUILDDIR):
mkdir -p $(BUILDDIR) mkdir -p $(BUILDDIR)
$(BUILDDIR)/%.bin: ./%.asm $(BUILDDIR)/memory.inc $(BUILDDIR)/bios.bin: bios.asm tape.asm $(BUILDDIR)/memory.inc
echo '>$<' echo '>$<'
$(ASM) $(ASMFLAGS) $< $@ $(ASM) $(ASMFLAGS) bios.bin $(BUILDDIR)/bios.bin
basic: basic:
$(MAKE) -C apps/basic $(or $(MAKECMDGOALS),all) --no-print-directory $(MAKE) -C apps/basic $(or $(MAKECMDGOALS),all) --no-print-directory
+132 -2
View File
@@ -15,6 +15,7 @@
; - Unsupported functions cause FCERR, not a reset or REM ; - Unsupported functions cause FCERR, not a reset or REM
; - modern backspace handling ; - modern backspace handling
; - Added DROM/EROM/BANK0/BANK1 commands ; - Added DROM/EROM/BANK0/BANK1 commands
; - CLOAD
; ;
;================================================================================== ;==================================================================================
; The updates to the original BASIC within this file are copyright Grant Searle ; The updates to the original BASIC within this file are copyright Grant Searle
@@ -423,8 +424,8 @@ WORDTB: .WORD PEND
.WORD CONT .WORD CONT
.WORD LIST .WORD LIST
.WORD CLEAR .WORD CLEAR
.WORD FCERR .WORD CLOAD
.WORD FCERR .WORD FCERR ; csave
.WORD NEW .WORD NEW
; RESERVED WORD TOKEN VALUES ; RESERVED WORD TOKEN VALUES
@@ -4543,4 +4544,133 @@ FULLSETTOP:
FULLMSG: FULLMSG:
.BYTE "[FULL MEMORY]",CR,LF,0,0 .BYTE "[FULL MEMORY]",CR,LF,0,0
CLOAD: ld a,(hl) ; get byte after "CLOAD"
cp ZTIMES ; "*" token? ("CLOAD*")
jp z,FCERR ; yes - array load (ARRLD1)
cp ZPRINT ; "?" ("PRINT token") verify
jp z,FCERR ; yes - flag "verify"
cp '#' ; 'cload#'?
jp z,CLOADB ; yes - load binary
; just cload - load basic
jp FCERR
CLOADB: inc hl ; skip #
call GETNUM ; evaluate numeric expression
call DEINT ; convert FPREG to de
; de contains load address
; TODO do safety check on address
push hl
push bc
push de
ld hl,CLSTARTMSG
call PRS
; Load block
call CLREADBLOCK
; hl contains block, check block type
ld a,(hl)
cp 1 ; first block needs to be a header
jr nz,CLNOHDR ; First block is not a header
; first block is a header, get nr of blocks
inc hl
ld de,(hl)
; print number of blocks to load
push de
push hl
ex de,hl
call PRNTHL
ld hl,CLBLOCKSMSG
call PRS
pop hl
inc hl
inc hl ; get filename
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
; TODO check block type
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
ld bc,64 ; block size
ldir ; copy data to memory
push de ; store destination address
exx
dec de
ld a,e
cp 0
jr nz,1$ ; repeat if not done yet
ld a,d
cp 0
jr nz,1$ ; repeat if not done yet
pop de
pop bc
pop hl
ret
CLREADBLOCK:
call BIOS_CAS_READ_BLOCK
jr nc,1$
call BIOS_CAS_WAIT_SILENCE
call BIOS_CAS_READ_BLOCK
push hl
jr nc,2$
; error, make sure it is silent again
push af
call BIOS_CAS_WAIT_SILENCE
pop af
jr CLERR
1$: push hl ; save hl
call BIOS_CAS_READ_BLOCK_DRY
; Ignore errors on this block
2$: ; but make sure it is silent again
call BIOS_CAS_WAIT_SILENCE
pop hl
ret
; Second block failed as well, error
CLERR: cp 1
jr z,CLECAL
cp 2
jr z,CLESYN
cp 3
jr z,CLERED
cp 4
jr z,CLECHS
jp FCERR ; Unknown error
CLECAL: ld hl, CLECALMSG
jr CLEPRINT
CLESYN: ld hl, CLESYNMSG
jr CLEPRINT
CLERED: ld hl, CLEREDMSG
jr CLEPRINT
CLECHS: ld hl, CLECHSMSG
jr CLEPRINT
CLNOHDR:
ld hl,CLNOHDRMSG
CLEPRINT:
call PRS
pop de
pop bc
pop hl
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
CLCLRF: .db 0ah,0dh,00h
.end .end
+33
View File
@@ -26,6 +26,7 @@ TX_BUSY .equ WORKSPACE+006h
TX_BUF .equ WORKSPACE+007h TX_BUF .equ WORKSPACE+007h
RX_BUF .equ TX_BUF+BUF_SIZE RX_BUF .equ TX_BUF+BUF_SIZE
SYSREG_VALUE .equ RX_BUF+BUF_SIZE SYSREG_VALUE .equ RX_BUF+BUF_SIZE
END_OF_BIOS_WS .equ SYSREG_VALUE+001h
; ------------------------------------------------------- ; -------------------------------------------------------
; entry ; entry
@@ -99,6 +100,12 @@ boot_copy:
jp setrom jp setrom
.ORG BIOS_BANK .ORG BIOS_BANK
jp bank jp bank
.ORG BIOS_CAS_READ_BLOCK
jp read_block
.ORG BIOS_CAS_WAIT_SILENCE
jp wait_silence
.ORG BIOS_CAS_READ_BLOCK_DRY
jp read_block_dry
; ------------------------------------------------------- ; -------------------------------------------------------
; BIOS ENTRY ; BIOS ENTRY
@@ -137,9 +144,30 @@ init:
; Enter bios ; Enter bios
jp BASIC_RAM_START jp BASIC_RAM_START
; 100$
; ld hl,msg_start
; call BIOS_PUTS
; call BIOS_FLUSH
; call read_block
; jr c,101$ ; error
; ; block received correctly
; ld hl,BLOCK_BUFFER
; call BIOS_PUTS
; call BIOS_FLUSH
; jp 100$
; 101$:
; add '0' ; get error number
; call BIOS_PUTC
; ld a, 0ah
; call BIOS_PUTC
; ld a, 0dh
; call BIOS_PUTC
; call BIOS_FLUSH
; jp 100$
; Shouldnt come here ; Shouldnt come here
; just to be safe an endless loop ; just to be safe an endless loop
ei
0$: 0$:
call getc call getc
call putc call putc
@@ -524,3 +552,8 @@ bank:
; Strings ; Strings
; ------------------------------------------------------- ; -------------------------------------------------------
msg_header: .db "BIOS v1.0 - Joppe Blondel",00dh,00ah,000h msg_header: .db "BIOS v1.0 - Joppe Blondel",00dh,00ah,000h
; -------------------------------------------------------
; Other files
; -------------------------------------------------------
#include "tape.asm"
+3
View File
@@ -5,3 +5,6 @@ BIOS_PUTS .equ 0c059h
BIOS_FLUSH .equ 0c05ch BIOS_FLUSH .equ 0c05ch
BIOS_ROM .equ 0c05fh BIOS_ROM .equ 0c05fh
BIOS_BANK .equ 0c062h BIOS_BANK .equ 0c062h
BIOS_CAS_READ_BLOCK .equ 0c065h
BIOS_CAS_WAIT_SILENCE .equ 0c068h
BIOS_CAS_READ_BLOCK_DRY .equ 0c06bh
+2
View File
@@ -4,3 +4,5 @@ ROM_DISABLE_BIT .equ 001h
BANK_SELECT_BIT .equ 002h BANK_SELECT_BIT .equ 002h
BANK_ENABLE_BIT .equ 004h BANK_ENABLE_BIT .equ 004h
BANK_MODE_CLEAR_MASK .equ 0f9h BANK_MODE_CLEAR_MASK .equ 0f9h
TAPE_PORT .equ 018h
+4 -4
View File
@@ -11,11 +11,11 @@ SHARED_MEM_END := 0xffff
BIOS_STACK_TOP := 0xffff BIOS_STACK_TOP := 0xffff
BIOS_ROM_START := 0x0080 BIOS_ROM_START := 0x0080
BIOS_ROM_SIZE := 0x0580 BIOS_ROM_SIZE := 0x0780
BASIC_ROM_START := 0x0600 BASIC_ROM_START := 0x0800
BASIC_ROM_SIZE := 0x2000 BASIC_ROM_SIZE := 0x2000
BIOS_RAM_START := 0xc000 BIOS_RAM_START := 0xc000
BIOS_WP_RAM_START := 0xc600 BIOS_WP_RAM_START := 0xc800
BASIC_RAM_START := 0xca00 BASIC_RAM_START := 0xcc00
+178
View File
@@ -0,0 +1,178 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import struct
import wave
import os
import math
from pathlib import Path
BLOCK_SIZE = 64
TYPE_RAW = 0
TYPE_HEADER = 1
def crc16(data: bytes) -> int:
"""Calculate CRC-16/CCITT-FALSE."""
crc = 0xffff
for value in data:
crc ^= value << 8
for _ in range(8):
if crc & 0x8000:
crc = (crc << 1) ^ 0x1021
else:
crc <<= 1
crc &= 0xffff
return crc
class PulseWriter:
def __init__(
self,
sample_rate: int,
t_short_us: float,
t_long_us: float,
amplitude: float,
) -> None:
self.sample_rate = sample_rate
self.short_samples = self._duration_samples(t_short_us)
self.long_samples = self._duration_samples(t_long_us)
self.high = round(32767 * amplitude)
self.low = -self.high
self.samples = bytearray()
self.space(0.1)
def space(self, time: float) -> None:
self._level(0, int(time*self.sample_rate))
def _duration_samples(self, duration_us: float) -> int:
sample_count = round(duration_us * self.sample_rate / 1_000_000)
if sample_count < 1:
raise ValueError(
f"pulse duration {duration_us:g} us is shorter than one sample"
)
return sample_count
def _level(self, value: int, sample_count: int) -> None:
self.samples.extend(struct.pack("<h", value) * sample_count)
def short_pulse(self) -> None:
self._level(self.high, self.short_samples)
self._level(self.low, self.long_samples)
def long_pulse(self) -> None:
self._level(self.high, self.long_samples)
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)
def bit(self, value: int) -> None:
if value:
self.long_pulse()
else:
self.short_pulse()
def byte(self, value: int) -> None:
# Transmit bytes most-significant bit first.
for bit_number in range(7, -1, -1):
self.bit((value >> bit_number) & 1)
def bytes(self, value: bytes) -> None:
for byte in value:
self.byte(byte)
def leader(self) -> None:
self.space(0.2)
for _ in range(50):
self._level(self.high, self.short_samples)
self._level(self.low, self.short_samples)
for _ in range(50):
self._level(self.high, self.long_samples)
self._level(self.low, self.long_samples)
self.marker()
for value in (0x55, 0x5A, 0xC3):
self.byte(value)
def block(self, type:int, data:bytes) -> None:
self.leader()
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)))
def header(pulses: PulseWriter, path:Path) -> None:
fname = path.name
fsize = os.path.getsize(path)
data = struct.pack("<H", math.ceil(fsize/BLOCK_SIZE)) + fname.encode()[:32].ljust(32, b' ')
pulses.block(TYPE_HEADER, data)
pulses.block(TYPE_HEADER, data)
def raw(pulses: PulseWriter, data:bytes) -> None:
pulses.block(TYPE_RAW, data)
pulses.block(TYPE_RAW, data)
def main() -> None:
parser = argparse.ArgumentParser(
description="Generate the pulse-width encoded tape leader"
)
parser.add_argument("input", type=Path, help="input file to encode")
parser.add_argument("output", type=Path, help="output WAV file")
parser.add_argument("--sample-rate", type=int, default=48_000)
parser.add_argument(
"--t-short",
type=float,
default=250.0,
metavar="MICROSECONDS",
help="short pulse interval in microseconds (default: 250)",
)
parser.add_argument(
"--t-long",
type=float,
default=500.0,
metavar="MICROSECONDS",
help="long pulse interval in microseconds (default: 500)",
)
parser.add_argument("--amplitude", type=float, default=0.75)
args = parser.parse_args()
if args.sample_rate <= 0:
parser.error("--sample-rate must be positive")
if args.t_short <= 0 or args.t_long <= 0:
parser.error("pulse intervals must be positive")
if args.t_short >= args.t_long:
parser.error("--t-short must be less than --t-long")
if not 0.0 <= args.amplitude <= 1.0:
parser.error("--amplitude must be between 0 and 1")
try:
pulses = PulseWriter(
args.sample_rate,
args.t_short,
args.t_long,
args.amplitude,
)
except ValueError as error:
parser.error(str(error))
# create header block
header(pulses, args.input)
pulses.space(1.0)
with open(args.input, "rb") as f:
while True:
data = f.read(BLOCK_SIZE)
if not len(data):
break
raw(pulses, data)
pulses.space(1.0)
with wave.open(str(args.output), "wb") as wav:
wav.setnchannels(1)
wav.setsampwidth(2)
wav.setframerate(args.sample_rate)
wav.writeframes(pulses.samples)
if __name__ == "__main__":
main()
+530
View File
@@ -0,0 +1,530 @@
BLOCK_SIZE .equ 1+64+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
; -------------------------------------------------------
; 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
; -------------------------------------------------------
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 ; timeout
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
call sample_pulse
jr c,cal_err
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 ; timeout
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
; 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 ; timeout
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
cp a,055h
jr nz,cal_err ; training byte not 0x55
; calibration complete
or a ; clear carry
ret
cal_err:
scf
ret
; -------------------------------------------------------
; 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<SHORT_MIN
ld a,(SHORT_MAX)
ld c,a
ld a,b
cp c
jr c,bit_one
jr z,bit_one ; pulse<=SHORT_MAX
; test long window (second half is long pulse)
ld a,(LONG_MIN)
ld c,a
ld a,b
cp c
jr c,bit_bad ; pulse<LONG_MAX
ld a,(LONG_MAX)
ld c,a
ld a,b
cp c
jr c,bit_zero
jr z,bit_zero ; pulse<=LONG_MAX
bit_bad:
ld a,1
or a
ret
bit_zero:
xor a
ret
bit_one:
xor a
scf
ret
; -------------------------------------------------------
; Read byte, MSB first
; returns a (byte)
; carry means error
; needs d (comparator state)
; uses e,h,b,c
; -------------------------------------------------------
read_byte:
ld e,0
ld h,8
1$:
call read_bit
jr nz,9$
; shift in the carry bit
rl e
; repeat 8 times
dec h
jr nz,1$
; byte done,
ld a,e
or a
ret
9$:
scf
ret
; -------------------------------------------------------
; Read and check sync bytes
; carry is error
; -------------------------------------------------------
read_sync:
call read_byte
ret c
cp 05ah
jr nz,9$
call read_byte
ret c
cp 0c3h
jr nz,9$
or a
ret
9$:
scf
ret
; -------------------------------------------------------
; Read a block into BLOCK_BUFFER
; needs d (comparator state)
; uses b,c,d,e,h,l
; returns error code in a
; returns start of block in hl
; carry is fail
; -------------------------------------------------------
read_block:
; Block reading uses de' and hl' so preserve them
exx
push de
push hl
exx
; calibrate reader
call calibrate
jr nc,1$ ; if no error jump
ld a,BLOCK_ERROR_CALIBRATION
scf
jr 101$
1$:
; calibrated, read sync bytes
call read_sync
jr nc,2$ ; if no error jump
ld a,BLOCK_ERROR_SYNC
scf
jr 101$
2$:
; reader initialized, start reading bytes
exx
ld e,0
ld hl,BLOCK_BUFFER
exx
4$:
call read_byte
jr nc,5$
; read error
exx
ld a,BLOCK_ERROR_READ
scf
jr 101$
5$:
exx
ld (hl),a
inc hl
inc e
ld a,e
cp BLOCK_SIZE
exx
jr nz,4$
; Block is in BLOCK_BUFFER
; calculate crc16
ld hl, BLOCK_BUFFER
ld a, BLOCK_SIZE-2 ; dont use received crc value
call crc16
; todo compare received crc with hl
ld bc, (BLOCK_BUFFER+BLOCK_SIZE-2)
or a ; clear carry
sbc hl,bc ; do hl-bc-c
jr z,6$ ; if the same jump
; crc error
ld a,BLOCK_ERROR_CHSUM
scf
jr 101$
6$:
; Block received correctly
ld hl,BLOCK_BUFFER
or a ; clear carry
101$:
exx
pop hl
pop de
exx
ret
; -------------------------------------------------------
; Read a block but do not store
; needs d (comparator state)
; uses b,c,d,e,h,l
; returns error code in a
; carry is fail
; -------------------------------------------------------
read_block_dry:
; Block reading uses de' and hl' so preserve them
exx
push de
push hl
exx
; calibrate reader
call calibrate
jr nc,1$ ; if no error jump
ld a,BLOCK_ERROR_CALIBRATION
scf
jr 101$
1$:
; calibrated, read sync bytes
call read_sync
jr nc,2$ ; if no error jump
ld a,BLOCK_ERROR_SYNC
scf
jr 101$
2$:
; reader initialized, start reading bytes
exx
ld e,0
exx
4$:
call read_byte
jr nc,5$
; read error
exx
ld a,BLOCK_ERROR_READ
scf
jr 101$
5$:
exx
inc hl
inc e
ld a,e
cp BLOCK_SIZE
exx
jr nz,4$
; Block fully received
or a ; clear carry
101$:
exx
pop hl
pop de
exx
ret
; -------------------------------------------------------
; CRC16
; CRC-16/CCITT-FALSE (polynomial 1021h, initial value 0ffffh)
; needs hl (pointer to memory buffer)
; needs a (length of buffer)
; returns hl (crc16)
; uses a,b,c,d,e
; -------------------------------------------------------
crc16:
ld b,a
ld de,0ffffh
or a
jr z,4$
1$:
ld a,(hl)
inc hl
xor d
ld d,a
ld c,8
2$:
sla e
rl d
jr nc,3$
ld a,e
xor 021h
ld e,a
ld a,d
xor 010h
ld d,a
3$:
dec c
jr nz,2$
djnz 1$
4$:
ex de,hl
ret
; -------------------------------------------------------
; Wait for silence
; uses a,b,d
; -------------------------------------------------------
wait_silence:
call sample_pulse
jr nc,wait_silence ; no timeout -> no silence
ret
; -------------------------------------------------------
; Strings
; -------------------------------------------------------
msg_start: .db "PLAY TAPE", 0ah,0dh,00h
BIN
View File
Binary file not shown.