Back to the begin
This commit is contained in:
+74
-538
@@ -4,581 +4,117 @@
|
|||||||
.org TAPE_LOADER_ROM_ADDRESS
|
.org TAPE_LOADER_ROM_ADDRESS
|
||||||
|
|
||||||
TAPE_PORT = 0x18
|
TAPE_PORT = 0x18
|
||||||
MAX_CYCLE_COUNT = 20
|
MAX_BLOCK_SIZE = 0x100
|
||||||
MAX_BLOCK_SIZE = 0x200
|
|
||||||
|
|
||||||
STACK_TOP = 0xBFFF
|
STACK_TOP = 0xBFFF
|
||||||
WORK_BASE = 0xB000
|
WORK_BASE = 0xB000
|
||||||
|
|
||||||
OLD_SP = WORK_BASE + 0x00 ; word
|
OLD_SP = WORK_BASE + 0x00 ; word
|
||||||
OLD_IFF = WORK_BASE + 0x02 ; byte
|
OLD_IFF = WORK_BASE + 0x02 ; byte
|
||||||
SHORT_LEN = WORK_BASE + 0x03 ; byte
|
|
||||||
LONG_LEN = WORK_BASE + 0x04 ; byte
|
|
||||||
TRESHOLD = WORK_BASE + 0x05 ; byte
|
|
||||||
SHORT_COUNT = WORK_BASE + 0x06 ; byte
|
|
||||||
LONG_COUNT = WORK_BASE + 0x07 ; byte
|
|
||||||
BLOCK_LENGTH = WORK_BASE + 0x08 ; word
|
|
||||||
BLOCK_NUMBER = WORK_BASE + 0x0a ; byte
|
|
||||||
NR_OF_BLOCKS = WORK_BASE + 0x0b ; word
|
|
||||||
|
|
||||||
BLOCK_BUFFER = WORK_BASE + 0x100 ; MAX_BLOCK_SIZE
|
BLOCK_BUFFER = WORK_BASE + 0x100 ; MAX_BLOCK_SIZE
|
||||||
|
|
||||||
; #################################################3
|
; #################################################3
|
||||||
; Entry
|
; Entry
|
||||||
; #################################################3
|
; #################################################3
|
||||||
tape_loader:
|
tape_loader:
|
||||||
ld (OLD_SP),sp
|
ld (OLD_SP),sp
|
||||||
; Save interrupt state.
|
; Save interrupt state.
|
||||||
ld a,i
|
ld a,i
|
||||||
jp po,iff_was_off
|
jp po,iff_was_off
|
||||||
ld a,#1
|
ld a,#1
|
||||||
jr save_iff
|
jr save_iff
|
||||||
iff_was_off:
|
iff_was_off:
|
||||||
xor a
|
xor a
|
||||||
save_iff:
|
save_iff:
|
||||||
ld (OLD_IFF),a
|
ld (OLD_IFF),a
|
||||||
di
|
di
|
||||||
ld sp,#STACK_TOP
|
ld sp,#STACK_TOP
|
||||||
|
|
||||||
|
main:
|
||||||
; Initial message.
|
; Initial message.
|
||||||
ld hl,#msg_start
|
ld hl,#msg_start
|
||||||
call print_with_irqs
|
call print_string_ei
|
||||||
|
|
||||||
; Read the first block
|
|
||||||
call read_block
|
|
||||||
jr z,1$
|
|
||||||
ld hl,#msg_error_checksum
|
|
||||||
ei
|
|
||||||
call print_string
|
|
||||||
call print_crlf
|
|
||||||
di
|
|
||||||
jp exit
|
|
||||||
1$:
|
|
||||||
; nr of blocks and filename stored in buffer
|
|
||||||
ld hl,(BLOCK_BUFFER)
|
|
||||||
ld (NR_OF_BLOCKS),hl
|
|
||||||
|
|
||||||
; Print info of blocks
|
|
||||||
ei
|
|
||||||
ld hl,#msg_filename
|
|
||||||
call print_string
|
|
||||||
ld hl,#BLOCK_BUFFER
|
|
||||||
inc hl
|
|
||||||
inc hl
|
|
||||||
call print_string
|
|
||||||
call print_crlf
|
|
||||||
ld hl,#msg_nr_of_blocks
|
|
||||||
call print_string
|
|
||||||
ld hl,(NR_OF_BLOCKS)
|
|
||||||
call print_hex16
|
|
||||||
call print_crlf
|
|
||||||
|
|
||||||
call BIOS_FLUSH
|
|
||||||
di
|
|
||||||
|
|
||||||
; #################################################3
|
|
||||||
; Read a consecutive list of blocks
|
|
||||||
; #################################################3
|
|
||||||
read_consecutive_blocks:
|
|
||||||
ld bc,(NR_OF_BLOCKS)
|
|
||||||
push bc
|
|
||||||
1$:
|
|
||||||
call read_block
|
|
||||||
jr z,2$
|
|
||||||
ei
|
|
||||||
ld a,#'x'
|
|
||||||
call BIOS_PUTC
|
|
||||||
call BIOS_FLUSH
|
|
||||||
di
|
|
||||||
jr 3$
|
|
||||||
2$:
|
|
||||||
ei
|
|
||||||
ld a,#'*'
|
|
||||||
call BIOS_PUTC
|
|
||||||
call BIOS_FLUSH
|
|
||||||
di
|
|
||||||
|
|
||||||
3$:
|
|
||||||
pop bc
|
|
||||||
dec bc
|
|
||||||
push bc
|
|
||||||
; Check for zero
|
|
||||||
ld a,b
|
|
||||||
or a,c
|
|
||||||
jr nz,1$
|
|
||||||
pop bc
|
|
||||||
|
|
||||||
; All blocks are read
|
|
||||||
ei
|
|
||||||
ld a,#'V'
|
|
||||||
call BIOS_PUTC
|
|
||||||
call print_crlf
|
|
||||||
call BIOS_FLUSH
|
|
||||||
di
|
|
||||||
|
|
||||||
; #################################################3
|
; #################################################3
|
||||||
; Exit
|
; Exit
|
||||||
; #################################################3
|
; #################################################3
|
||||||
exit:
|
exit:
|
||||||
di
|
ei
|
||||||
ld sp,(OLD_SP)
|
call BIOS_FLUSH
|
||||||
; Return code 0
|
di
|
||||||
ld a,#0
|
ld sp,(OLD_SP)
|
||||||
ld c,a
|
; Return code 0
|
||||||
; Restore caller's interrupt state.
|
ld a,#0
|
||||||
ld a,(OLD_IFF)
|
ld c,a
|
||||||
or a
|
; Restore caller's interrupt state.
|
||||||
ld a,c
|
ld a,(OLD_IFF)
|
||||||
jr z,return_di
|
or a
|
||||||
ei
|
ld a,c
|
||||||
ret
|
jr z,return_di
|
||||||
|
ei
|
||||||
|
ret
|
||||||
return_di:
|
return_di:
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
|
||||||
; #################################################3
|
|
||||||
; Wait for next edge
|
|
||||||
; D holds current comparator state
|
|
||||||
; Returns B = loop count
|
|
||||||
; #################################################3
|
|
||||||
wait_edge:
|
|
||||||
ld b,#0
|
|
||||||
1$:
|
|
||||||
inc b
|
|
||||||
in a,(TAPE_PORT)
|
|
||||||
cp a,d
|
|
||||||
jr z,1$
|
|
||||||
ld d,a
|
|
||||||
ret
|
|
||||||
|
|
||||||
; #################################################3
|
|
||||||
; Wait for a full cycle
|
|
||||||
; D holds the current comparator state
|
|
||||||
; Returns B = loop count
|
|
||||||
; #################################################3
|
|
||||||
wait_cycle:
|
|
||||||
ld b,#0
|
|
||||||
1$:
|
|
||||||
inc b
|
|
||||||
in a,(TAPE_PORT)
|
|
||||||
cp a,d
|
|
||||||
jp z,1$
|
|
||||||
ld d,a
|
|
||||||
2$:
|
|
||||||
inc b
|
|
||||||
in a,(TAPE_PORT)
|
|
||||||
cp a,d
|
|
||||||
jp z,2$
|
|
||||||
ld d,a
|
|
||||||
ret
|
|
||||||
|
|
||||||
; #################################################3
|
|
||||||
; Eat A cycles
|
|
||||||
; #################################################3
|
|
||||||
eat_cycles:
|
|
||||||
or a,a
|
|
||||||
ret z
|
|
||||||
ld c,a
|
|
||||||
1$:
|
|
||||||
call wait_cycle
|
|
||||||
dec c
|
|
||||||
jr nz,1$
|
|
||||||
ret
|
|
||||||
|
|
||||||
; #################################################3
|
|
||||||
; Average 16 cycle intervals
|
|
||||||
; Returns A = average loop count
|
|
||||||
; preserves bc
|
|
||||||
; #################################################3
|
|
||||||
average16:
|
|
||||||
push bc
|
|
||||||
ld c,#16
|
|
||||||
ld e,#0
|
|
||||||
|
|
||||||
1$:
|
|
||||||
call wait_cycle
|
|
||||||
; Add loop count to e and take moving average
|
|
||||||
ld a,e
|
|
||||||
add a,b
|
|
||||||
srl a
|
|
||||||
ld e,a
|
|
||||||
dec c
|
|
||||||
jr nz,1$
|
|
||||||
|
|
||||||
ld a,e
|
|
||||||
pop bc
|
|
||||||
ret
|
|
||||||
|
|
||||||
; #################################################3
|
|
||||||
; Calibrate
|
|
||||||
; Gets short and long pulse lenghts and bit pulse counts
|
|
||||||
; from training sequence
|
|
||||||
; carry = succeeded
|
|
||||||
; #################################################3
|
|
||||||
calibrate:
|
|
||||||
; establish comparator state
|
|
||||||
in a,(TAPE_PORT)
|
|
||||||
ld d,a
|
|
||||||
; Wait for the first edge
|
|
||||||
call wait_edge
|
|
||||||
; Wait for the next edge to be back in phase with the
|
|
||||||
; audio stream
|
|
||||||
call wait_edge
|
|
||||||
|
|
||||||
; Measure shoft pulses
|
|
||||||
call average16
|
|
||||||
ld (SHORT_LEN), a
|
|
||||||
|
|
||||||
; Threshold for finding long pulse -> short*1.5
|
|
||||||
; stored in c
|
|
||||||
ld c,a
|
|
||||||
srl a
|
|
||||||
add a,c
|
|
||||||
ld c,a
|
|
||||||
|
|
||||||
; Wait for the first long pulse
|
|
||||||
1$:
|
|
||||||
call average16
|
|
||||||
cp a,c
|
|
||||||
jr c,1$
|
|
||||||
|
|
||||||
; Measure long pulses
|
|
||||||
call average16
|
|
||||||
ld (LONG_LEN), a
|
|
||||||
|
|
||||||
; Calculate period treshold
|
|
||||||
ld b,a
|
|
||||||
ld a,(SHORT_LEN)
|
|
||||||
; Average between the two lengths
|
|
||||||
add a,b
|
|
||||||
srl a
|
|
||||||
ld (TRESHOLD),a
|
|
||||||
|
|
||||||
; Wait for first short for training
|
|
||||||
ld c,a
|
|
||||||
3$:
|
|
||||||
call wait_cycle
|
|
||||||
ld a,b
|
|
||||||
cp a,c
|
|
||||||
jr nc,3$
|
|
||||||
|
|
||||||
; Count short cycles
|
|
||||||
ld e,#1
|
|
||||||
4$:
|
|
||||||
call wait_cycle
|
|
||||||
ld a,b
|
|
||||||
cp a,c
|
|
||||||
jr nc,5$
|
|
||||||
inc e
|
|
||||||
ld a,#MAX_CYCLE_COUNT
|
|
||||||
cp a,e
|
|
||||||
jp c,error_cycle_count
|
|
||||||
jr 4$
|
|
||||||
5$:
|
|
||||||
ld a,e
|
|
||||||
ld (SHORT_COUNT),a
|
|
||||||
|
|
||||||
; Count long cycles
|
|
||||||
ld e,#1
|
|
||||||
6$:
|
|
||||||
call wait_cycle
|
|
||||||
ld a,b
|
|
||||||
cp a,c
|
|
||||||
jr c,7$
|
|
||||||
inc e
|
|
||||||
ld a,#MAX_CYCLE_COUNT
|
|
||||||
cp a,e
|
|
||||||
jp c,error_cycle_count
|
|
||||||
jr 6$
|
|
||||||
7$:
|
|
||||||
ld a,e
|
|
||||||
ld (LONG_COUNT),a
|
|
||||||
|
|
||||||
; First cycle of short was already consymed
|
|
||||||
ld a,(SHORT_COUNT)
|
|
||||||
dec a
|
|
||||||
call eat_cycles
|
|
||||||
|
|
||||||
; Consume rest of training bits
|
|
||||||
; 6 L/S pairs then last L
|
|
||||||
ld h,#6
|
|
||||||
8$:
|
|
||||||
ld a,(LONG_COUNT)
|
|
||||||
call eat_cycles
|
|
||||||
ld a,(SHORT_COUNT)
|
|
||||||
call eat_cycles
|
|
||||||
dec h
|
|
||||||
jr nz,8$
|
|
||||||
ld a,(LONG_COUNT)
|
|
||||||
call eat_cycles
|
|
||||||
|
|
||||||
; Test calibration with sync byte
|
|
||||||
call check_sync_byte
|
|
||||||
ret
|
|
||||||
|
|
||||||
error_cycle_count:
|
|
||||||
ei
|
|
||||||
ld hl,#msg_error_cycle_count
|
|
||||||
call print_string
|
|
||||||
call print_crlf
|
|
||||||
di
|
|
||||||
jp exit
|
|
||||||
|
|
||||||
|
|
||||||
; #################################################3
|
|
||||||
; Read one bit
|
|
||||||
; carry = value
|
|
||||||
; #################################################3
|
|
||||||
read_bit:
|
|
||||||
call wait_cycle
|
|
||||||
ld a,(TRESHOLD)
|
|
||||||
ld c,a
|
|
||||||
ld a,b
|
|
||||||
cp a,c
|
|
||||||
jr c,2$
|
|
||||||
|
|
||||||
; zero bit
|
|
||||||
1$:
|
|
||||||
ld a,(LONG_COUNT)
|
|
||||||
dec a
|
|
||||||
call eat_cycles
|
|
||||||
or a,a ; clear carry
|
|
||||||
ret
|
|
||||||
; one bit
|
|
||||||
2$:
|
|
||||||
ld a,(SHORT_COUNT)
|
|
||||||
dec a
|
|
||||||
call eat_cycles
|
|
||||||
scf ; set carry
|
|
||||||
ret
|
|
||||||
|
|
||||||
; #################################################3
|
|
||||||
; Read byte
|
|
||||||
; A = byte
|
|
||||||
; #################################################3
|
|
||||||
read_byte:
|
|
||||||
ld e,#0
|
|
||||||
ld h,#8
|
|
||||||
1$:
|
|
||||||
call read_bit
|
|
||||||
rl e
|
|
||||||
dec h
|
|
||||||
jr nz,1$
|
|
||||||
ld a,e
|
|
||||||
ret
|
|
||||||
|
|
||||||
; #################################################3
|
|
||||||
; Check sync byte
|
|
||||||
; carry = succeeded
|
|
||||||
; #################################################3
|
|
||||||
check_sync_byte:
|
|
||||||
call read_byte
|
|
||||||
ld e,a
|
|
||||||
ld a,#0xf0
|
|
||||||
cp a,e
|
|
||||||
jr z,1$
|
|
||||||
or a,a
|
|
||||||
ret
|
|
||||||
1$:
|
|
||||||
scf
|
|
||||||
ret
|
|
||||||
|
|
||||||
error_wrong_sync_byte:
|
|
||||||
ei
|
|
||||||
ld hl,#msg_error_sync_not_found
|
|
||||||
call print_string
|
|
||||||
call print_crlf
|
|
||||||
di
|
|
||||||
jp exit
|
|
||||||
|
|
||||||
; #################################################3
|
|
||||||
; Read a block
|
|
||||||
; checksum in a
|
|
||||||
; #################################################3
|
|
||||||
read_block:
|
|
||||||
call calibrate
|
|
||||||
jp nc,error_wrong_sync_byte
|
|
||||||
|
|
||||||
; Get block number
|
|
||||||
call read_byte
|
|
||||||
ld (BLOCK_NUMBER),a
|
|
||||||
|
|
||||||
; Get block length
|
|
||||||
call read_byte
|
|
||||||
ld b,a
|
|
||||||
push bc
|
|
||||||
call read_byte
|
|
||||||
pop bc
|
|
||||||
ld c,a
|
|
||||||
ld (BLOCK_LENGTH),bc
|
|
||||||
|
|
||||||
; Check block length against max length
|
|
||||||
;ld hl,#MAX_BLOCK_SIZE
|
|
||||||
;and a,a
|
|
||||||
;sbc hl, bc
|
|
||||||
;jp c,error_block_too_big
|
|
||||||
|
|
||||||
exx
|
|
||||||
|
|
||||||
ld bc,(BLOCK_LENGTH)
|
|
||||||
ld hl,#BLOCK_BUFFER
|
|
||||||
|
|
||||||
1$:
|
|
||||||
exx
|
|
||||||
call read_byte
|
|
||||||
exx
|
|
||||||
|
|
||||||
ld (hl),a
|
|
||||||
inc hl
|
|
||||||
|
|
||||||
dec bc
|
|
||||||
; Check for zero
|
|
||||||
ld a,b
|
|
||||||
or a,c
|
|
||||||
jr nz,1$
|
|
||||||
|
|
||||||
; Read checksum
|
|
||||||
exx
|
|
||||||
call read_byte
|
|
||||||
|
|
||||||
; Calculate checksum of block
|
|
||||||
ld hl,#BLOCK_BUFFER
|
|
||||||
ld bc,(BLOCK_LENGTH)
|
|
||||||
push af
|
|
||||||
xor a,a
|
|
||||||
ld e,a
|
|
||||||
2$:
|
|
||||||
ld a,e
|
|
||||||
add a,(hl)
|
|
||||||
ld e,a
|
|
||||||
inc hl
|
|
||||||
dec bc
|
|
||||||
; Check for zero
|
|
||||||
ld a,b
|
|
||||||
or a,c
|
|
||||||
jr nz,2$
|
|
||||||
|
|
||||||
pop af
|
|
||||||
; e is calculated checksum
|
|
||||||
; a is received checksum
|
|
||||||
ld h,a
|
|
||||||
ld l,e
|
|
||||||
cp a,e
|
|
||||||
ret
|
|
||||||
|
|
||||||
error_block_too_big:
|
|
||||||
ei
|
|
||||||
ld hl,#msg_error_block_too_big
|
|
||||||
call print_string
|
|
||||||
call print_crlf
|
|
||||||
di
|
|
||||||
jp exit
|
|
||||||
|
|
||||||
|
|
||||||
; #################################################3
|
; #################################################3
|
||||||
; IO Helper functions
|
; IO Helper functions
|
||||||
; #################################################3
|
; #################################################3
|
||||||
print_with_irqs:
|
print_string_ei:
|
||||||
ei
|
ei
|
||||||
call print_string
|
call print_string
|
||||||
call BIOS_FLUSH
|
call BIOS_FLUSH
|
||||||
di
|
di
|
||||||
ret
|
ret
|
||||||
|
|
||||||
print_string:
|
print_string:
|
||||||
ld a,(hl)
|
ld a,(hl)
|
||||||
or a
|
or a
|
||||||
ret z
|
ret z
|
||||||
call BIOS_PUTC
|
call BIOS_PUTC
|
||||||
inc hl
|
inc hl
|
||||||
jr print_string
|
jr print_string
|
||||||
|
|
||||||
print_crlf:
|
print_crlf:
|
||||||
ld a,#13
|
ld a,#13
|
||||||
call BIOS_PUTC
|
call BIOS_PUTC
|
||||||
ld a,#10
|
ld a,#10
|
||||||
jp BIOS_PUTC
|
jp BIOS_PUTC
|
||||||
|
|
||||||
print_hex16:
|
print_hex16:
|
||||||
push hl
|
push hl
|
||||||
ld a,h
|
ld a,h
|
||||||
call print_hex8
|
call print_hex8
|
||||||
pop hl
|
pop hl
|
||||||
ld a,l
|
ld a,l
|
||||||
call print_hex8
|
call print_hex8
|
||||||
ret
|
ret
|
||||||
|
|
||||||
print_hex8:
|
print_hex8:
|
||||||
push af
|
push af
|
||||||
rrca
|
rrca
|
||||||
rrca
|
rrca
|
||||||
rrca
|
rrca
|
||||||
rrca
|
rrca
|
||||||
and #0x0F
|
and #0x0F
|
||||||
call print_hex_nibble
|
call print_hex_nibble
|
||||||
pop af
|
pop af
|
||||||
and #0x0F
|
and #0x0F
|
||||||
|
|
||||||
; fall through
|
; fall through
|
||||||
print_hex_nibble:
|
print_hex_nibble:
|
||||||
cp #10
|
cp #10
|
||||||
jr c,print_hex_digit
|
jr c,print_hex_digit
|
||||||
add a,#('A' - 10)
|
add a,#('A' - 10)
|
||||||
jp BIOS_PUTC
|
jp BIOS_PUTC
|
||||||
|
|
||||||
print_hex_digit:
|
print_hex_digit:
|
||||||
add a,#'0'
|
add a,#'0'
|
||||||
jp BIOS_PUTC
|
jp BIOS_PUTC
|
||||||
|
|
||||||
; #################################################3
|
; #################################################3
|
||||||
; Strings
|
; Strings
|
||||||
; #################################################3
|
; #################################################3
|
||||||
msg_start:
|
msg_start:
|
||||||
.ascii "TAPE DECODE TEST - PLAY TAPE"
|
.ascii "TAPE DECODE TEST - PLAY TAPE"
|
||||||
.db 13,10,0
|
.db 13,10,0
|
||||||
|
|
||||||
msg_short_len:
|
|
||||||
.ascii "Short len: "
|
|
||||||
.db 0
|
|
||||||
|
|
||||||
msg_long_len:
|
|
||||||
.ascii "Long len: "
|
|
||||||
.db 0
|
|
||||||
|
|
||||||
msg_treshold:
|
|
||||||
.ascii "Treshold: "
|
|
||||||
.db 0
|
|
||||||
|
|
||||||
msg_short_count:
|
|
||||||
.ascii "Short count: "
|
|
||||||
.db 0
|
|
||||||
|
|
||||||
msg_long_count:
|
|
||||||
.ascii "Long count: "
|
|
||||||
.db 0
|
|
||||||
|
|
||||||
msg_error_cycle_count:
|
|
||||||
.ascii "Cycle count out of bounds"
|
|
||||||
.db 0
|
|
||||||
|
|
||||||
msg_error_sync_not_found:
|
|
||||||
.ascii "Sync not found"
|
|
||||||
.db 0
|
|
||||||
|
|
||||||
msg_error_checksum:
|
|
||||||
.ascii "Checksum error"
|
|
||||||
.db 0
|
|
||||||
|
|
||||||
msg_filename:
|
|
||||||
.ascii "Filename: "
|
|
||||||
.db 0
|
|
||||||
|
|
||||||
msg_nr_of_blocks:
|
|
||||||
.ascii "Blocks: "
|
|
||||||
.db 0
|
|
||||||
|
|
||||||
msg_error_block_too_big:
|
|
||||||
.ascii "Block too big"
|
|
||||||
.db 0
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user