mirror of
https://github.com/KrisKennaway/ii-sound.git
synced 2024-06-09 18:29:32 +00:00
It works!
This commit is contained in:
parent
cea66fe916
commit
a356f6a3a7
|
@ -118,7 +118,7 @@ def audio_bytestream(data: numpy.ndarray, step: int, lookahead_steps: int,
|
||||||
|
|
||||||
clicks = 0
|
clicks = 0
|
||||||
min_lookahead_steps = lookahead_steps
|
min_lookahead_steps = lookahead_steps
|
||||||
while i < dlen // 10:
|
while i < dlen // 1:
|
||||||
if i >= next_tick:
|
if i >= next_tick:
|
||||||
eta.print_status()
|
eta.print_status()
|
||||||
next_tick = int(eta.i * dlen / 1000)
|
next_tick = int(eta.i * dlen / 1000)
|
||||||
|
@ -164,12 +164,12 @@ def audio_bytestream(data: numpy.ndarray, step: int, lookahead_steps: int,
|
||||||
numpy.mean(data[i:i + opcode_length]))
|
numpy.mean(data[i:i + opcode_length]))
|
||||||
|
|
||||||
# print(frame_offset, i / sample_rate, opcode)
|
# print(frame_offset, i / sample_rate, opcode)
|
||||||
for v in all_positions[0]:
|
# for v in all_positions[0]:
|
||||||
yield (v * sp.scale).astype(numpy.float32)
|
# yield
|
||||||
# # print(v * sp.scale)
|
# # print(v * sp.scale)
|
||||||
# if frame_offset == 2047:
|
# if frame_offset == 2047:
|
||||||
# print(opcode)
|
# print(opcode)
|
||||||
# yield opcode
|
yield opcode, (all_positions * sp.scale).astype(numpy.float32)
|
||||||
|
|
||||||
i += opcode_length
|
i += opcode_length
|
||||||
frame_offset = (frame_offset + 1) % 2048
|
frame_offset = (frame_offset + 1) % 2048
|
||||||
|
@ -244,6 +244,7 @@ def main():
|
||||||
parser.add_argument("--norm_percentile", default=100,
|
parser.add_argument("--norm_percentile", default=100,
|
||||||
help="Normalize to specified percentile value of input "
|
help="Normalize to specified percentile value of input "
|
||||||
"audio")
|
"audio")
|
||||||
|
parser.add_argument("--wav_output", type=str, help="output audio file")
|
||||||
parser.add_argument("--noise_output", type=str, help="output audio file")
|
parser.add_argument("--noise_output", type=str, help="output audio file")
|
||||||
parser.add_argument("input", type=str, help="input audio file to convert")
|
parser.add_argument("input", type=str, help="input audio file to convert")
|
||||||
parser.add_argument("output", type=str, help="output audio file")
|
parser.add_argument("output", type=str, help="output audio file")
|
||||||
|
@ -262,8 +263,13 @@ def main():
|
||||||
output_buffer = []
|
output_buffer = []
|
||||||
input_offset = 0
|
input_offset = 0
|
||||||
|
|
||||||
output_context = sf.SoundFile(
|
opcode_context = open(args.output, "wb+")
|
||||||
args.output, "w", output_rate, channels=1, format='WAV')
|
|
||||||
|
if args.wav_output:
|
||||||
|
wav_context = sf.SoundFile(
|
||||||
|
args.wav_output, "w", output_rate, channels=1, format='WAV')
|
||||||
|
else:
|
||||||
|
wav_context = contextlib.nullcontext
|
||||||
|
|
||||||
if args.noise_output:
|
if args.noise_output:
|
||||||
noise_context = sf.SoundFile(
|
noise_context = sf.SoundFile(
|
||||||
|
@ -273,12 +279,19 @@ def main():
|
||||||
# We're not creating a file but still need a context
|
# We're not creating a file but still need a context
|
||||||
noise_context = contextlib.nullcontext
|
noise_context = contextlib.nullcontext
|
||||||
|
|
||||||
with output_context as output_f, noise_context as noise_f:
|
with wav_context as wav_f, noise_context as noise_f, opcode_context\
|
||||||
for idx, sample in enumerate(audio_bytestream(
|
as opcode_f:
|
||||||
|
for idx, sample_data in enumerate(audio_bytestream(
|
||||||
input_audio, args.step_size, args.lookahead_cycles,
|
input_audio, args.step_size, args.lookahead_cycles,
|
||||||
sample_rate)):
|
sample_rate)):
|
||||||
output_buffer.append(sample)
|
opcode, samples = sample_data
|
||||||
input_offset += 1
|
# print(hex(idx), opcode, hex(opcode.byte))
|
||||||
|
opcode_f.write(bytes([opcode.byte]))
|
||||||
|
|
||||||
|
output_buffer.extend(samples)
|
||||||
|
input_offset += len(samples)
|
||||||
|
|
||||||
|
# TODO: don't bother computing if we're not writing wavs
|
||||||
|
|
||||||
# Keep accumulating as long as we have <10MB in the buffer, or are
|
# Keep accumulating as long as we have <10MB in the buffer, or are
|
||||||
# within 10MB from the end. This ensures we have enough samples to
|
# within 10MB from the end. This ensures we have enough samples to
|
||||||
|
@ -291,7 +304,8 @@ def main():
|
||||||
output_buffer, input_audio[input_offset - len(output_buffer):],
|
output_buffer, input_audio[input_offset - len(output_buffer):],
|
||||||
sample_rate, output_rate, bool(args.noise_output)
|
sample_rate, output_rate, bool(args.noise_output)
|
||||||
)
|
)
|
||||||
output_f.write(resampled_output_buffer)
|
if args.wav_output:
|
||||||
|
wav_f.write(resampled_output_buffer)
|
||||||
if args.noise_output:
|
if args.noise_output:
|
||||||
noise_f.write(resampled_noise_buffer)
|
noise_f.write(resampled_noise_buffer)
|
||||||
|
|
||||||
|
@ -302,11 +316,12 @@ def main():
|
||||||
output_buffer, input_audio[input_offset - len(output_buffer):],
|
output_buffer, input_audio[input_offset - len(output_buffer):],
|
||||||
sample_rate, output_rate, bool(args.noise_output)
|
sample_rate, output_rate, bool(args.noise_output)
|
||||||
)
|
)
|
||||||
output_f.write(resampled_output_buffer)
|
if args.wav_output:
|
||||||
|
wav_f.write(resampled_output_buffer)
|
||||||
if args.noise_output:
|
if args.noise_output:
|
||||||
noise_f.write(resampled_noise_buffer)
|
noise_f.write(resampled_noise_buffer)
|
||||||
|
|
||||||
# with open(args.output, "wb+") as f:
|
# with as f:
|
||||||
# for opcode in audio_bytestream(
|
# for opcode in audio_bytestream(
|
||||||
# preprocess(args.input, sample_rate, args.normalization,
|
# preprocess(args.input, sample_rate, args.normalization,
|
||||||
# args.norm_percentile), args.step_size,
|
# args.norm_percentile), args.step_size,
|
||||||
|
|
|
@ -83,12 +83,12 @@ def eof_trampoline_stage2(cycles) -> List[opcodes_6502.Opcode]:
|
||||||
ops = [
|
ops = [
|
||||||
opcodes_6502.Opcode(4, 3, "LDA WDATA"),
|
opcodes_6502.Opcode(4, 3, "LDA WDATA"),
|
||||||
opcodes_6502.Opcode(4, 3, "STA @0+1"),
|
opcodes_6502.Opcode(4, 3, "STA @0+1"),
|
||||||
opcodes_6502.Literal("@0:", indent=0),
|
|
||||||
opcodes_6502.Opcode(
|
opcodes_6502.Opcode(
|
||||||
6, 3, "JMP (eof_trampoline_%d_stage3_page)" % cycles)
|
6, 3, "JMP (eof_trampoline_%d_stage3_page)" % cycles)
|
||||||
]
|
]
|
||||||
if cycles < 7 or cycles == 8:
|
if cycles < 7 or cycles == 8:
|
||||||
return label + ops
|
return label + ops[:-1] + [opcodes_6502.Literal("@0:", indent=0)] + [
|
||||||
|
ops[-1]]
|
||||||
|
|
||||||
# For cycles == 7 or > 8 we need to interleave a STA $C030 into stage 2
|
# For cycles == 7 or > 8 we need to interleave a STA $C030 into stage 2
|
||||||
# because we couldn't fit it in stage 1
|
# because we couldn't fit it in stage 1
|
||||||
|
@ -97,7 +97,12 @@ def eof_trampoline_stage2(cycles) -> List[opcodes_6502.Opcode]:
|
||||||
opcodes_6502.STA_C030,
|
opcodes_6502.STA_C030,
|
||||||
opcodes_6502.padding(100)
|
opcodes_6502.padding(100)
|
||||||
]
|
]
|
||||||
return label + list(opcodes_6502.interleave_opcodes(interleave_ops, ops))
|
res = label + list(opcodes_6502.interleave_opcodes(interleave_ops, ops))
|
||||||
|
# We can't insert the label before interleaving because we might have
|
||||||
|
# NOP/STA inserted after it
|
||||||
|
# TODO: this is a bit of a hack - add support for binding the literal to
|
||||||
|
# the following opcode so it can't be split
|
||||||
|
return res[:-1] + [opcodes_6502.Literal("@0:", indent=0)] + [res[-1]]
|
||||||
|
|
||||||
|
|
||||||
EOF_TRAMPOLINE_STAGE2 = {
|
EOF_TRAMPOLINE_STAGE2 = {
|
||||||
|
|
|
@ -8,7 +8,7 @@ objects = $(patsubst %.s,%.o,$(src_files))
|
||||||
all: $(DISKIMAGE)
|
all: $(DISKIMAGE)
|
||||||
|
|
||||||
%.o : %.s
|
%.o : %.s
|
||||||
ca65 -t apple2 --cpu 6502 -o $@ $<
|
ca65 -t apple2 --cpu 6502 -o $@ -l player.lst $<
|
||||||
|
|
||||||
%.system : %.o
|
%.system : %.o
|
||||||
cl65 -t apple2 -C make/apple2-asm-system.cfg -u __EXEHDR__ --start-addr 0x2000 -o $@ $<
|
cl65 -t apple2 -C make/apple2-asm-system.cfg -u __EXEHDR__ --start-addr 0x2000 -o $@ $<
|
||||||
|
|
|
@ -18,5 +18,6 @@ SEGMENTS {
|
||||||
CODE: load = MAIN, type = rw;
|
CODE: load = MAIN, type = rw;
|
||||||
RODATA: load = MAIN, type = ro, optional = yes;
|
RODATA: load = MAIN, type = ro, optional = yes;
|
||||||
DATA: load = MAIN, type = rw, optional = yes;
|
DATA: load = MAIN, type = rw, optional = yes;
|
||||||
|
DATA256: load = MAIN, type = rw, optional = yes, align = $100;
|
||||||
BSS: load = BSS, type = bss, optional = yes, define = yes;
|
BSS: load = BSS, type = bss, optional = yes, define = yes;
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
372
player/player.s
372
player/player.s
|
@ -36,8 +36,6 @@
|
||||||
; speaker is in a known trajectory. We can compensate for this in the audio encoder.
|
; speaker is in a known trajectory. We can compensate for this in the audio encoder.
|
||||||
|
|
||||||
.proc main
|
.proc main
|
||||||
.org $2000
|
|
||||||
|
|
||||||
init:
|
init:
|
||||||
JMP bootstrap
|
JMP bootstrap
|
||||||
|
|
||||||
|
@ -53,10 +51,10 @@ MAC: .byte $00,$08,$DC,$01,$02,$03 ; W5100 MAC ADDRESS
|
||||||
; TODO: make slot I/O addresses customizable at runtime - would probably require somehow
|
; TODO: make slot I/O addresses customizable at runtime - would probably require somehow
|
||||||
; compiling a list of all of the binary offsets at which we reference $C09x and patching
|
; compiling a list of all of the binary offsets at which we reference $C09x and patching
|
||||||
; them in memory or on-disk.
|
; them in memory or on-disk.
|
||||||
WMODE = $C094
|
WMODE = $C0b4
|
||||||
WADRH = $C095
|
WADRH = $C0b5
|
||||||
WADRL = $C096
|
WADRL = $C0b6
|
||||||
WDATA = $C097
|
WDATA = $C0b7
|
||||||
|
|
||||||
; W5100 LOCATIONS
|
; W5100 LOCATIONS
|
||||||
MACADDR = $0009 ; MAC ADDRESS
|
MACADDR = $0009 ; MAC ADDRESS
|
||||||
|
@ -271,12 +269,23 @@ setup:
|
||||||
lda #$00
|
lda #$00
|
||||||
sta RXRD
|
sta RXRD
|
||||||
|
|
||||||
; to restore after checkrecv
|
|
||||||
LDY #>RXBASE
|
|
||||||
LDX #>S0RXRSR
|
LDX #>S0RXRSR
|
||||||
STX WADRH
|
STX WADRH
|
||||||
LDX #<S0RXRSR
|
LDX #<S0RXRSR
|
||||||
JMP checkrecv
|
|
||||||
|
fill_socket:
|
||||||
|
LDA #$07 ; 2
|
||||||
|
@0:
|
||||||
|
STX WADRL ; #<S0RXRSR
|
||||||
|
CMP WDATA ; High byte of received size
|
||||||
|
BCS @0 ; in common case when there is already sufficient data waiting.
|
||||||
|
; There is data to read - we don't care exactly how much because it's at least 2K
|
||||||
|
; point to start of socket buffer
|
||||||
|
LDX #>RXBASE
|
||||||
|
STX WADRH
|
||||||
|
LDX #$00
|
||||||
|
STX WADRL
|
||||||
|
JMP (WDATA) ; Start playing!
|
||||||
|
|
||||||
real_exit:
|
real_exit:
|
||||||
INC RESET_VECTOR+2 ; Invalidate power-up byte
|
INC RESET_VECTOR+2 ; Invalidate power-up byte
|
||||||
|
@ -291,10 +300,13 @@ exit_parmtable:
|
||||||
.BYTE 0 ; Byte reserved for future use
|
.BYTE 0 ; Byte reserved for future use
|
||||||
.WORD 0000 ; Pointer reserved for future use
|
.WORD 0000 ; Pointer reserved for future use
|
||||||
|
|
||||||
; The actual player code, which will be copied to $3xx for execution
|
RXRD:
|
||||||
;
|
.byte 00
|
||||||
; opcode cycle counts are for 65c02, for 6502 they are 1 less because JMP (indirect) is 5 cycles instead of 6.
|
|
||||||
|
|
||||||
|
; Stage 2 and 3 player code
|
||||||
|
.include "player_stage2_3_generated.s"
|
||||||
|
|
||||||
|
; Stage 1 player code, which will be copied to $3xx for execution
|
||||||
begin_copy_page1:
|
begin_copy_page1:
|
||||||
; generated audio playback code
|
; generated audio playback code
|
||||||
.include "player_generated.s"
|
.include "player_generated.s"
|
||||||
|
@ -302,339 +314,11 @@ begin_copy_page1:
|
||||||
; Quit to ProDOS
|
; Quit to ProDOS
|
||||||
exit:
|
exit:
|
||||||
JMP real_exit
|
JMP real_exit
|
||||||
|
|
||||||
; Manage W5100 socket buffer and ACK TCP stream.
|
|
||||||
;
|
|
||||||
; In order to simplify the buffer management we expect this ACK opcode to consume the last 4 bytes in a 2K "TCP frame".
|
|
||||||
; i.e. we can assume that we need to consume exactly 2K from the W5100 socket buffer.
|
|
||||||
;
|
|
||||||
; While during this we need to keep ticking the speaker at a regular cadence to maintain the same net position of the
|
|
||||||
; speaker cone. We choose to tick every 14 cycles, which requires adding in minimal NOP padding.
|
|
||||||
;
|
|
||||||
; We end up ticking 8 times with 10 cycles left over, assuming we don't stall waiting for the socket buffer to refill.
|
|
||||||
;
|
|
||||||
; From the point of view of speaker voltages this slowpath is equivalent to the following opcode sequence:
|
|
||||||
; TICK_6 (TICK_14 * 7) with 4 cycles left over, adding 4 to the effective n of the next TICK_n we jump to (as chosen by
|
|
||||||
; the encoder). XXX timing
|
|
||||||
;
|
|
||||||
; If we do stall waiting for data then there is no need to worry about maintaining an even cadence, because audio
|
|
||||||
; will already be disrupted (since the encoder won't have predicted it, so will be tracking wrong). The speaker will
|
|
||||||
; resynchronize within a few hundred microseconds though.
|
|
||||||
end_of_frame_10_10:
|
|
||||||
STA TICK ; 4
|
|
||||||
JMP _end_of_frame_10_10 ; 3 rest of end_of_frame doesn't fit in page 3
|
|
||||||
|
|
||||||
end_of_frame_20_4:
|
|
||||||
STA TICK ; 4
|
|
||||||
JMP _end_of_frame_10_10 ; 3 rest of end_of_frame doesn't fit in page 3
|
|
||||||
|
|
||||||
end_copy_page1:
|
end_copy_page1:
|
||||||
;
|
|
||||||
;_end_of_frame:
|
|
||||||
; STA zpdummy ; 3
|
|
||||||
; ; Save the W5100 address pointer so we can come back here later
|
|
||||||
; ; We know the low-order byte is 0 because Socket RX memory is page-aligned and so is 2K frame.
|
|
||||||
; ; IMPORTANT - from now on until we restore this below, we can't trash the Y register!
|
|
||||||
; STA TICK ; 4 [10]
|
|
||||||
; LDY WADRH ; 4
|
|
||||||
;
|
|
||||||
; ; Read Received Read pointer
|
|
||||||
; LDA #>S0RXRD ; 2
|
|
||||||
; STA TICK ; 4 [10]
|
|
||||||
; STA WADRH ; 4
|
|
||||||
;
|
|
||||||
; LDX #<S0RXRD ; 2
|
|
||||||
; STA TICK ; 4 [10]
|
|
||||||
;
|
|
||||||
; STX WADRL ; 4
|
|
||||||
; NOP ; 2
|
|
||||||
; STA TICK ; 4 [10]
|
|
||||||
; LDA WDATA ; 4 Read high byte
|
|
||||||
;
|
|
||||||
; ; No need to read low byte since it's guaranteed to be 0 since we're at the end of a 2K frame.
|
|
||||||
;
|
|
||||||
; ; Update new Received Read pointer
|
|
||||||
; ; We have received an additional 2KB
|
|
||||||
; CLC ; 2
|
|
||||||
; STA TICK ; 4 [10]
|
|
||||||
; ADC #$08 ; 2
|
|
||||||
;
|
|
||||||
; STX WADRL ; 4 Reset address pointer, X still has #<S0RXRD
|
|
||||||
; STA TICK ; 4 [10]
|
|
||||||
; ; No need to store low byte since it's unchanged at 0
|
|
||||||
; STA WDATA ; 4 Store new high byte
|
|
||||||
;
|
|
||||||
; ; Send the Receive command
|
|
||||||
; LDA #<S0CR ; 2
|
|
||||||
; STA TICK ; 4 [10]
|
|
||||||
; STA WADRL ; 4
|
|
||||||
;
|
|
||||||
; LDA #SCRECV ; 2
|
|
||||||
; STA TICK ; 4 [10]
|
|
||||||
; STA WDATA ; 4
|
|
||||||
;
|
|
||||||
;checkrecv:
|
|
||||||
; LDA #<S0RXRSR ; 2 Socket 0 Received Size register
|
|
||||||
; STA TICK ; 4 [10]
|
|
||||||
; LDX #$07 ; 2
|
|
||||||
; NOP ; 2
|
|
||||||
; NOP ; 2
|
|
||||||
; STA TICK ; 4 [10]
|
|
||||||
; ; we might loop an unknown number of times here waiting for data but the default should be to fall
|
|
||||||
; ; straight through
|
|
||||||
;@0:
|
|
||||||
; STA WADRL ; 4
|
|
||||||
; NOP ; 2
|
|
||||||
; STA TICK ; 4 [10]
|
|
||||||
; CPX WDATA ; 4 High byte of received size
|
|
||||||
; BCS @0 ; 2 in common case when there is already sufficient data waiting.
|
|
||||||
; STA TICK ; 4 [10]
|
|
||||||
;
|
|
||||||
; ; point W5100 back into the RX buffer where we left off
|
|
||||||
; ; There is data to read - we don't care exactly how much because it's at least 2K
|
|
||||||
; ;
|
|
||||||
; ; Restore W5100 address pointer where we last found it.
|
|
||||||
; ;
|
|
||||||
; ; It turns out that the W5100 automatically wraps the address pointer at the end of the 8K RX/TX buffers
|
|
||||||
; ; Since we're using an 8K socket, that means we don't have to do any work to manage the read pointer!
|
|
||||||
; STY WADRH ; 4
|
|
||||||
; LDX #$00 ; 2
|
|
||||||
; STA TICK ; 4 [10]
|
|
||||||
;
|
|
||||||
; STX WADRL ; 4
|
|
||||||
; NOP ; 2
|
|
||||||
; STA TICK ; 4 [10]
|
|
||||||
; JMP (WDATA) ; 6
|
|
||||||
;.endproc
|
|
||||||
|
|
||||||
|
.segment "DATA256"
|
||||||
|
begin_copy_page8:
|
||||||
|
.include "player_stage3_table_generated.s"
|
||||||
|
end_copy_page8:
|
||||||
|
|
||||||
; 72 cycles --> 133 with tick padding
|
|
||||||
; + 7 from dispatcher = 140 total
|
|
||||||
_end_of_frame_10_10:
|
|
||||||
; Save the W5100 address pointer so we can come back here later
|
|
||||||
; We know the low-order byte is 0 because Socket RX memory is page-aligned and so is 2K frame.
|
|
||||||
; IMPORTANT - from now on until we restore this below, we can't trash the Y register!
|
|
||||||
STA zpdummy ; 3
|
|
||||||
STA TICK ; 4
|
|
||||||
LDY WADRH ; 4
|
|
||||||
|
|
||||||
; Update new Received Read pointer
|
|
||||||
; We know we have received an additional 2KB, so we don't need to read the current value from the hardware. We can
|
|
||||||
; track it ourselves instead.
|
|
||||||
LDA #>S0RXRD ; 2
|
|
||||||
STA TICK ; [10]
|
|
||||||
STA WADRH ; 4
|
|
||||||
LDX #<S0RXRD ; 2
|
|
||||||
STA TICK ; [10]
|
|
||||||
STX WADRL ; 4
|
|
||||||
|
|
||||||
CLC ; 2
|
|
||||||
STA TICK ; [10]
|
|
||||||
LDA RXRD ; 4
|
|
||||||
ADC #$08 ; 2
|
|
||||||
STA TICK ; [10]
|
|
||||||
STA WDATA ; 4 Store new high byte
|
|
||||||
LDX #<S0CR ; 2 prepare to reset WADRL
|
|
||||||
STA TICK ; [10]
|
|
||||||
STA RXRD ; 4
|
|
||||||
|
|
||||||
; Send the Receive command
|
|
||||||
LDA #SCRECV ; 2
|
|
||||||
STA TICK ; [10]
|
|
||||||
STX WADRL ; 4
|
|
||||||
LDX #<S0RXRSR ; 2 Socket 0 Received Size register
|
|
||||||
STA TICK ; [10]
|
|
||||||
STA WDATA ; 4 #SCRECV
|
|
||||||
|
|
||||||
checkrecv:
|
|
||||||
LDA #$07 ; 2
|
|
||||||
; we might loop an unknown number of times here waiting for data but the default should be to fall
|
|
||||||
; straight through
|
|
||||||
@0:
|
|
||||||
STA TICK ; [10]
|
|
||||||
STX WADRL ; 4 #<S0RXRSR
|
|
||||||
NOP ; 2
|
|
||||||
STA TICK ; [10]
|
|
||||||
CMP WDATA ; 4 High byte of received size
|
|
||||||
BCS @0 ; 2 in common case when there is already sufficient data waiting.
|
|
||||||
STA TICK ; [10]
|
|
||||||
; point W5100 back into the RX buffer where we left off
|
|
||||||
; There is data to read - we don't care exactly how much because it's at least 2K
|
|
||||||
;
|
|
||||||
; Restore W5100 address pointer where we last found it.
|
|
||||||
;
|
|
||||||
; It turns out that the W5100 automatically wraps the address pointer at the end of the 8K RX/TX buffers
|
|
||||||
; Since we're using an 8K socket, that means we don't have to do any work to manage the read pointer!
|
|
||||||
STY WADRH ; 4
|
|
||||||
LDX #$00 ; 2
|
|
||||||
STA TICK ; [10]
|
|
||||||
STX WADRL ; 4
|
|
||||||
NOP ; 2
|
|
||||||
STA TICK ; [10]
|
|
||||||
JMP (WDATA) ; 6
|
|
||||||
|
|
||||||
; 74 cycles + 7 from dispatcher = 81 total
|
|
||||||
_end_of_frame:
|
|
||||||
; Save the W5100 address pointer so we can come back here later
|
|
||||||
; We know the low-order byte is 0 because Socket RX memory is page-aligned and so is 2K frame.
|
|
||||||
; IMPORTANT - from now on until we restore this below, we can't trash the Y register!
|
|
||||||
LDY WADRH ; 4
|
|
||||||
|
|
||||||
; Update new Received Read pointer
|
|
||||||
; We know we have received an additional 2KB, so we don't need to read the current value from the hardware. We can
|
|
||||||
; track it ourselves instead.
|
|
||||||
LDA #>S0RXRD ; 2
|
|
||||||
STA WADRH ; 4
|
|
||||||
LDA #<S0RXRD ; 2
|
|
||||||
STA WADRL ; 4
|
|
||||||
|
|
||||||
; TODO: in principle we could prepare this outside of the EOF path
|
|
||||||
LDA RXRD ; 4
|
|
||||||
CLC ; 2
|
|
||||||
ADC #$08 ; 2
|
|
||||||
STA WDATA ; 4 Store new high byte
|
|
||||||
STA RXRD ; 4 Save for next time
|
|
||||||
|
|
||||||
; Send the Receive command
|
|
||||||
LDA #<S0CR ; 2 prepare to reset WADRL
|
|
||||||
STA WADRL ; 4
|
|
||||||
LDA #SCRECV ; 2
|
|
||||||
STA WDATA ; 4 #SCRECV
|
|
||||||
|
|
||||||
LDA #$07 ; 2
|
|
||||||
; we might loop an unknown number of times here waiting for data but the default should be to fall
|
|
||||||
; straight through
|
|
||||||
LDX #<S0RXRSR ; 2 Socket 0 Received Size register
|
|
||||||
@0:
|
|
||||||
STX WADRL ; 4 #<S0RXRSR
|
|
||||||
CMP WDATA ; 4 High byte of received size
|
|
||||||
BCS @0 ; 2 in common case when there is already sufficient data waiting.
|
|
||||||
; point W5100 back into the RX buffer where we left off
|
|
||||||
; There is data to read - we don't care exactly how much because it's at least 2K
|
|
||||||
;
|
|
||||||
; Restore W5100 address pointer where we last found it.
|
|
||||||
;
|
|
||||||
; It turns out that the W5100 automatically wraps the address pointer at the end of the 8K RX/TX buffers
|
|
||||||
; Since we're using an 8K socket, that means we don't have to do any work to manage the read pointer!
|
|
||||||
STY WADRH ; 4
|
|
||||||
LDA #$00 ; 2
|
|
||||||
STA WADRL ; 4
|
|
||||||
JMP (WDATA) ; 6
|
|
||||||
|
|
||||||
; 4 20 4 20 4 20 4 20 4 20 4 6 = 130
|
|
||||||
_end_of_frame_4_20:
|
|
||||||
; Save the W5100 address pointer so we can come back here later
|
|
||||||
; We know the low-order byte is 0 because Socket RX memory is page-aligned and so is 2K frame.
|
|
||||||
; IMPORTANT - from now on until we restore this below, we can't trash the Y register!
|
|
||||||
LDY WADRH ; 4
|
|
||||||
STA zpdummy ; 3
|
|
||||||
|
|
||||||
; Update new Received Read pointer
|
|
||||||
; We know we have received an additional 2KB, so we don't need to read the current value from the hardware. We can
|
|
||||||
; track it ourselves instead.
|
|
||||||
LDA #>S0RXRD ; 2
|
|
||||||
STA WADRH ; 4
|
|
||||||
STA TICK ; 4 [20]
|
|
||||||
STA TICK ; 4 [4]
|
|
||||||
LDA #<S0RXRD ; 2
|
|
||||||
STA WADRL ; 4
|
|
||||||
|
|
||||||
; TODO: in principle we could prepare this outside of the EOF path
|
|
||||||
LDA RXRD ; 4
|
|
||||||
CLC ; 2
|
|
||||||
ADC #$08 ; 2
|
|
||||||
NOP ; 2 XXX
|
|
||||||
STA TICK ; 4 [20]
|
|
||||||
STA TICK ; 4 [4]
|
|
||||||
STA WDATA ; 4 Store new high byte
|
|
||||||
STA RXRD ; 4 Save for next time
|
|
||||||
|
|
||||||
; Send the Receive command
|
|
||||||
LDA #<S0CR ; 2 prepare to reset WADRL
|
|
||||||
STA WADRL ; 4
|
|
||||||
LDA #SCRECV ; 2
|
|
||||||
STA TICK ; 4 [20]
|
|
||||||
STA TICK ; 4 [4]
|
|
||||||
STA WDATA ; 4 #SCRECV
|
|
||||||
|
|
||||||
LDA #$07 ; 2
|
|
||||||
; we might loop an unknown number of times here waiting for data but the default should be to fall
|
|
||||||
; straight through
|
|
||||||
LDX #<S0RXRSR ; 2 Socket 0 Received Size register
|
|
||||||
@0:
|
|
||||||
STX WADRL ; 4 #<S0RXRSR
|
|
||||||
CMP WDATA ; 4 High byte of received size
|
|
||||||
STA TICK ; 4 [20]
|
|
||||||
STA TICK ; 4 [4]
|
|
||||||
BCS @0 ; 2 in common case when there is already sufficient data waiting.
|
|
||||||
; point W5100 back into the RX buffer where we left off
|
|
||||||
; There is data to read - we don't care exactly how much because it's at least 2K
|
|
||||||
;
|
|
||||||
; Restore W5100 address pointer where we last found it.
|
|
||||||
;
|
|
||||||
; It turns out that the W5100 automatically wraps the address pointer at the end of the 8K RX/TX buffers
|
|
||||||
; Since we're using an 8K socket, that means we don't have to do any work to manage the read pointer!
|
|
||||||
STY WADRH ; 4
|
|
||||||
LDA #$00 ; 2
|
|
||||||
STA WADRL ; 4
|
|
||||||
NOP ; 2
|
|
||||||
NOP ; 2
|
|
||||||
STA TICK ; 4 [20]
|
|
||||||
STA TICK ; 4 [4]
|
|
||||||
JMP (WDATA) ; 6
|
|
||||||
|
|
||||||
; 4
|
|
||||||
_end_of_frame_4_10:
|
|
||||||
; Save the W5100 address pointer so we can come back here later
|
|
||||||
; We know the low-order byte is 0 because Socket RX memory is page-aligned and so is 2K frame.
|
|
||||||
; IMPORTANT - from now on until we restore this below, we can't trash the Y register!
|
|
||||||
STA zpdummy ; 3
|
|
||||||
STA TICK ; [10]
|
|
||||||
STA TICK ; [4]
|
|
||||||
LDY WADRH ; 4
|
|
||||||
|
|
||||||
; Update new Received Read pointer
|
|
||||||
; We know we have received an additional 2KB, so we don't need to read the current value from the hardware. We can
|
|
||||||
; track it ourselves instead.
|
|
||||||
LDA #>S0RXRD ; 2
|
|
||||||
STA WADRH ; 4
|
|
||||||
LDA #<S0RXRD ; 2
|
|
||||||
STA WADRL ; 4
|
|
||||||
|
|
||||||
; TODO: in principle we could prepare this outside of the EOF path
|
|
||||||
LDA RXRD ; 4
|
|
||||||
CLC ; 2
|
|
||||||
ADC #$08 ; 2
|
|
||||||
STA WDATA ; 4 Store new high byte
|
|
||||||
STA RXRD ; 4 Save for next time
|
|
||||||
|
|
||||||
; Send the Receive command
|
|
||||||
LDA #<S0CR ; 2 prepare to reset WADRL
|
|
||||||
STA WADRL ; 4
|
|
||||||
LDA #SCRECV ; 2
|
|
||||||
STA WDATA ; 4 #SCRECV
|
|
||||||
|
|
||||||
LDA #$07 ; 2
|
|
||||||
; we might loop an unknown number of times here waiting for data but the default should be to fall
|
|
||||||
; straight through
|
|
||||||
LDX #<S0RXRSR ; 2 Socket 0 Received Size register
|
|
||||||
@0:
|
|
||||||
STX WADRL ; 4 #<S0RXRSR
|
|
||||||
CMP WDATA ; 4 High byte of received size
|
|
||||||
BCS @0 ; 2 in common case when there is already sufficient data waiting.
|
|
||||||
; point W5100 back into the RX buffer where we left off
|
|
||||||
; There is data to read - we don't care exactly how much because it's at least 2K
|
|
||||||
;
|
|
||||||
; Restore W5100 address pointer where we last found it.
|
|
||||||
;
|
|
||||||
; It turns out that the W5100 automatically wraps the address pointer at the end of the 8K RX/TX buffers
|
|
||||||
; Since we're using an 8K socket, that means we don't have to do any work to manage the read pointer!
|
|
||||||
STY WADRH ; 4
|
|
||||||
LDA #$00 ; 2
|
|
||||||
STA WADRL ; 4
|
|
||||||
JMP (WDATA) ; 6
|
|
||||||
|
|
||||||
|
|
||||||
RXRD:
|
|
||||||
.byte 00
|
|
||||||
.endproc
|
.endproc
|
||||||
|
|
|
@ -90,35 +90,35 @@ eof_trampoline_16_stage2:
|
||||||
eof_trampoline_17_stage2:
|
eof_trampoline_17_stage2:
|
||||||
LDA WDATA ; 4 cycles
|
LDA WDATA ; 4 cycles
|
||||||
STA @0+1 ; 4 cycles
|
STA @0+1 ; 4 cycles
|
||||||
@0:
|
|
||||||
NOP ; 2 cycles
|
NOP ; 2 cycles
|
||||||
STA $C030 ; 4 cycles
|
STA $C030 ; 4 cycles
|
||||||
|
@0:
|
||||||
JMP (eof_trampoline_17_stage3_page) ; 6 cycles
|
JMP (eof_trampoline_17_stage3_page) ; 6 cycles
|
||||||
|
|
||||||
eof_trampoline_18_stage2:
|
eof_trampoline_18_stage2:
|
||||||
LDA WDATA ; 4 cycles
|
LDA WDATA ; 4 cycles
|
||||||
STA @0+1 ; 4 cycles
|
STA @0+1 ; 4 cycles
|
||||||
@0:
|
|
||||||
STA zpdummy ; 3 cycles
|
STA zpdummy ; 3 cycles
|
||||||
STA $C030 ; 4 cycles
|
STA $C030 ; 4 cycles
|
||||||
|
@0:
|
||||||
JMP (eof_trampoline_18_stage3_page) ; 6 cycles
|
JMP (eof_trampoline_18_stage3_page) ; 6 cycles
|
||||||
|
|
||||||
eof_trampoline_19_stage2:
|
eof_trampoline_19_stage2:
|
||||||
LDA WDATA ; 4 cycles
|
LDA WDATA ; 4 cycles
|
||||||
STA @0+1 ; 4 cycles
|
STA @0+1 ; 4 cycles
|
||||||
@0:
|
|
||||||
NOP ; 2 cycles
|
NOP ; 2 cycles
|
||||||
NOP ; 2 cycles
|
NOP ; 2 cycles
|
||||||
STA $C030 ; 4 cycles
|
STA $C030 ; 4 cycles
|
||||||
|
@0:
|
||||||
JMP (eof_trampoline_19_stage3_page) ; 6 cycles
|
JMP (eof_trampoline_19_stage3_page) ; 6 cycles
|
||||||
|
|
||||||
eof_trampoline_20_stage2:
|
eof_trampoline_20_stage2:
|
||||||
LDA WDATA ; 4 cycles
|
LDA WDATA ; 4 cycles
|
||||||
STA @0+1 ; 4 cycles
|
STA @0+1 ; 4 cycles
|
||||||
@0:
|
|
||||||
NOP ; 2 cycles
|
NOP ; 2 cycles
|
||||||
STA zpdummy ; 3 cycles
|
STA zpdummy ; 3 cycles
|
||||||
STA $C030 ; 4 cycles
|
STA $C030 ; 4 cycles
|
||||||
|
@0:
|
||||||
JMP (eof_trampoline_20_stage3_page) ; 6 cycles
|
JMP (eof_trampoline_20_stage3_page) ; 6 cycles
|
||||||
|
|
||||||
eof_trampoline_21_stage2:
|
eof_trampoline_21_stage2:
|
||||||
|
@ -130,11 +130,11 @@ eof_trampoline_21_stage2:
|
||||||
eof_trampoline_22_stage2:
|
eof_trampoline_22_stage2:
|
||||||
LDA WDATA ; 4 cycles
|
LDA WDATA ; 4 cycles
|
||||||
STA @0+1 ; 4 cycles
|
STA @0+1 ; 4 cycles
|
||||||
@0:
|
|
||||||
NOP ; 2 cycles
|
NOP ; 2 cycles
|
||||||
NOP ; 2 cycles
|
NOP ; 2 cycles
|
||||||
STA zpdummy ; 3 cycles
|
STA zpdummy ; 3 cycles
|
||||||
STA $C030 ; 4 cycles
|
STA $C030 ; 4 cycles
|
||||||
|
@0:
|
||||||
JMP (eof_trampoline_22_stage3_page) ; 6 cycles
|
JMP (eof_trampoline_22_stage3_page) ; 6 cycles
|
||||||
|
|
||||||
eof_stage_2_10_10:
|
eof_stage_2_10_10:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user