Skip to content

Instantly share code, notes, and snippets.

@mistificator
Last active August 18, 2021 18:31
Show Gist options
  • Save mistificator/2692b23c17166ce16e2000f942645aca to your computer and use it in GitHub Desktop.
Save mistificator/2692b23c17166ce16e2000f942645aca to your computer and use it in GitHub Desktop.
ZX Spectrum demo tape loader
;
; Demo tape loader by Mist Poryvaev.
;
; Loads two-section tape: e.g. BASIC loader and one code block.
; Look the .TAP file image at location 0x9000.
;
; start in non-contended memory, or tape input latency will be unpredictable
org 0x8000
di
restart:
ld sp, stack
ld bc, msg_start_tape
call print_str
ld ix, values ; start loading address
call wait_pilot ; wait pilot and load header
ld bc, 19
ld (ix + 0), c
inc ix
ld (ix + 0), b
inc ix
call read_block
jp nz, checksum_error
call wait_pilot ; wait pilot and load data block
ld c, (ix - 7)
ld b, (ix - 6) ; get code block size from header
inc bc
inc bc
ld (ix + 0), c
inc ix
ld (ix + 0), b
inc ix
call read_block
jp nz, checksum_error
call wait_pilot ; wait pilot and load another header
ld bc, 19
ld (ix + 0), c
inc ix
ld (ix + 0), b
inc ix
call read_block
jp nz, checksum_error
call wait_pilot ; wait pilot and load data another block
ld c, (ix - 7)
ld b, (ix - 6) ; get code block size from header
inc bc
inc bc
ld (ix + 0), c
inc ix
ld (ix + 0), b
inc ix
call read_block
jp nz, checksum_error
ld bc, msg_all_loaded
call print_str
halt ; check memory in debugger at 0x9000 now, it contains well-formed .TAP file
jp restart
checksum_error: ; rotate border colors forever
inc a
and 0x07
out (0xFE), a
jr checksum_error
read_block: ; in: bc - count, ix - start address, l - PWM threshold
ld h, 0 ; checksum
read_block_loop:
push bc
call read_byte
ld a, (ix + 0)
xor h
ld h, a ; update checksum
inc ix
pop bc
dec bc
ld a, b
or c
jr nz, read_block_loop
ld a, h
or a ; check checksum
ret
read_byte: ; in: l - PWM threshold, ix - address
xor a
ld (ix + 0), a ; reset byte value
ld b, 8
loop_8_bit:
ld a, e
and 0x05
out (0xFE), a ; set crazy border colors
call get_bit_length
ld c, e
call get_bit_length
ld a, e
add a, c
srl a ; a = (e + c) / 2 - kind of average bit length
cp l ; compare average length with PWM threshold
jr c, set_0
set_1:
set 7, (ix + 0)
set_0:
rlc (ix + 0)
djnz loop_8_bit
ret
wait_pilot: ; out: l - detected PWM threshold
push bc
call wait_for_start
ld l, e
ld bc, 1000 ; detect leader minimum 1000 times (I think this will be enough)
wait_pilot_loop:
push bc
call get_bit_length
ld a, l
add a, e
srl a ; (length + prev_length) / 2
ld l, a ; store leader PWM threshold
pop bc
dec bc
ld a, b
or c
jr nz, wait_pilot_loop
wait_leader_tail:
call get_bit_length ; look for sync-1
ld a, e
sll a
cp l ; (2 x sync1) will be under PWM threshold
jr nc, wait_leader_tail
ld l, a ; update PWM threshold to (2 x sync1)
call get_bit_length ; get sync2
pop bc
ret
wait_for_start: ; out: d - initial value
ld a, 0x7F
in a, (0xFE) ; initial read
and 0x41
ld d, a
get_bit_length: ; in: d - initial value, out: e - bit length, d - new initial value
ld e, 0
loop_bit:
inc e
ld a, 0x7F
in a, (0xFE)
and 0x41
cp d
jr z, loop_bit
bit 0, a
jr z, space_key_pressed
ld d, a
ret
space_key_pressed:
ld bc, msg_loading_is_stopped
call print_str
halt
jp restart
print_str: ; in: bc - string address
ld a, (bc)
or a
ret z
push bc
call 0x09F4 ; "print-out"
pop bc
inc bc
jr print_str
defs 32
stack:
msg_start_tape defb "Start the tape", 0x0D, 0
msg_all_loaded defb "Tape is loaded, check memory at 0x9000", 0x0D, 0
msg_loading_is_stopped defb "Loading is stopped", 0x0D, 0
org 0x9000
values:
END 0x8000
@mistificator
Copy link
Author

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment