Skip to content

Commit

Permalink
Add support to tap2sna.py for loading 128K games
Browse files Browse the repository at this point in the history
  • Loading branch information
skoolkid committed Jul 7, 2023
1 parent b5b7fb3 commit 094d492
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 28 deletions.
10 changes: 4 additions & 6 deletions skoolkit/kbtracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# SkoolKit. If not, see <http://www.gnu.org/licenses/>.

from skoolkit import SkoolKitError
from skoolkit.pagingtracer import PagingTracer

KEYS = {
'1': (0xF7FE, 0b11111110),
Expand Down Expand Up @@ -206,7 +207,7 @@ def get_keys(keyspecs): # pragma: no cover
keys = [NO_KEY] * BOOT_DELAY # Allow time for the boot to finish

specs = []
for spec in keyspecs.split():
for spec in keyspecs:
specs.extend(TOKENS.get(spec, spec).split())

for spec in specs:
Expand All @@ -225,11 +226,12 @@ def get_keys(keyspecs): # pragma: no cover

return keys

class KeyboardTracer: # pragma: no cover
class KeyboardTracer(PagingTracer): # pragma: no cover
def __init__(self, simulator, keys):
self.simulator = simulator
self.keys = get_keys(keys)
self.border = 7
self.out7ffd = 0

def run(self, stop):
simulator = self.simulator
Expand Down Expand Up @@ -265,7 +267,3 @@ def read_port(self, registers, port):
if port in kb:
return kb.pop(port)
return 255

def write_port(self, registers, port, value):
if port % 2 == 0:
self.border = value % 8
11 changes: 5 additions & 6 deletions skoolkit/loadtracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@

from skoolkit import SkoolKitError, open_file, write, write_line
from skoolkit.basic import TextReader
from skoolkit.pagingtracer import PagingTracer
from skoolkit.simulator import (A, F, B, C, D, E, H, L, IXh, IXl, IYh, IYl, SP, I, R,
xA, xF, xB, xC, xD, xE, xH, xL, PC, T, R1)
from skoolkit.snapshot import BANKS_128K
from skoolkit.traceutils import disassemble

DEC = tuple(tuple((
Expand Down Expand Up @@ -147,9 +149,9 @@ def get_edges(blocks, first_edge, analyse=False):

return edges, indexes, data_blocks

class LoadTracer:
class LoadTracer(PagingTracer):
def __init__(self, simulator, blocks, accelerators, pause, first_edge, finish_tape,
in_min_addr, accel_dec_a, list_accelerators, border):
in_min_addr, accel_dec_a, list_accelerators, border, out7ffd):
self.accelerators = defaultdict(int)
self.inc_b_misses = 0
self.dec_b_misses = 0
Expand Down Expand Up @@ -200,6 +202,7 @@ def __init__(self, simulator, blocks, accelerators, pause, first_edge, finish_ta
self.tape_end_time = 0
self.custom_loader = False
self.border = border
self.out7ffd = out7ffd
self.text = TextReader()

def run(self, stop, fast_load, timeout, trace, trace_line, prefix, byte_fmt, word_fmt):
Expand Down Expand Up @@ -494,10 +497,6 @@ def read_port(self, registers, port):
return 191
return 255

def write_port(self, registers, port, value):
if port % 2 == 0:
self.border = value % 8

def next_block(self, tstates):
self.block_index += 1
if self.block_index >= len(self.blocks):
Expand Down
36 changes: 36 additions & 0 deletions skoolkit/pagingtracer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright 2023 Richard Dymond (rjdymond@gmail.com)
#
# This file is part of SkoolKit.
#
# SkoolKit is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# SkoolKit is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# SkoolKit. If not, see <http://www.gnu.org/licenses/>.

from skoolkit.snapshot import BANKS_128K

class PagingTracer: # pragma: no cover
def write_port(self, registers, port, value):
if port % 2 == 0:
self.border = value % 8
elif port & 0x8002 == 0:
mem = self.simulator.memory
if len(mem) == 0x28000:
cur_bank = self.out7ffd & 7
new_bank = value & 7
if cur_bank != new_bank:
c = BANKS_128K[cur_bank]
mem[c:c + 0x4000], mem[0xC000:0x10000] = mem[0xC000:0x10000], mem[c:c + 0x4000]
if new_bank:
n = BANKS_128K[new_bank]
mem[n:n + 0x4000], mem[0xC000:0x10000] = mem[0xC000:0x10000], mem[n:n + 0x4000]
if (self.out7ffd ^ value) & 16:
mem[0x0000:0x4000], mem[0x24000:] = mem[0x24000:], mem[0x0000:0x4000]
self.out7ffd = value
54 changes: 41 additions & 13 deletions skoolkit/tap2sna.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@

from skoolkit import (SkoolKitError, get_dword, get_int_param, get_object,
get_word, get_word3, integer, open_file, parse_int,
read_bin_file, warn, write_line, ROM48, VERSION)
read_bin_file, warn, write_line, ROM48, ROM128, VERSION)
from skoolkit.config import get_config, show_config, update_options
from skoolkit.kbtracer import KeyboardTracer
from skoolkit.loadsample import ACCELERATORS
from skoolkit.loadtracer import LoadTracer, get_edges
from skoolkit.simulator import (Simulator, A, F, B, C, D, E, H, L, IXh, IXl, IYh, IYl,
SP, I, R, xA, xF, xB, xC, xD, xE, xH, xL, PC)
from skoolkit.snapshot import move, poke, print_reg_help, print_state_help, write_z80v3
from skoolkit.snapshot import FRAME_DURATIONS, move, poke, print_reg_help, print_state_help, write_z80v3

SYSVARS = (
255, 0, 0, 0, # 23552 - KSTATE0
Expand Down Expand Up @@ -286,6 +286,7 @@ def _set_sim_load_config(options):
options.finish_tape = False
options.first_edge = -2168
options.load = None
options.machine = '48'
options.pause = True
options.timeout = 900
options.trace = None
Expand All @@ -306,6 +307,8 @@ def _set_sim_load_config(options):
options.first_edge = parse_int(value, options.first_edge)
elif name == 'load': # pragma: no cover
options.load = value
elif name == 'machine': # pragma: no cover
options.machine = value
elif name == 'pause': # pragma: no cover
options.pause = parse_int(value, options.pause)
elif name == 'timeout': # pragma: no cover
Expand All @@ -332,20 +335,32 @@ def sim_load(blocks, options, config):
else:
raise SkoolKitError(f'Unrecognised accelerator: {name}')

snapshot = [0] * 65536
rom = read_bin_file(ROM48, 16384)
snapshot[:len(rom)] = rom
interrupted = False
if options.machine == '128': # pragma: no cover
if not options.load:
options.load = 'ENTER'
sim_cfg = {'frame_duration': FRAME_DURATIONS[1]}
snapshot = [0] * 163840
snapshot[:0x4000] = read_bin_file(ROM128[0], 16384)
snapshot[0x24000:] = read_bin_file(ROM128[1], 16384)
stop = 0x13BE
else:
sim_cfg = {}
snapshot = [0] * 65536
snapshot[:0x4000] = read_bin_file(ROM48, 16384)
stop = 0x0605 # SAVE-ETC

if options.load: # pragma: no cover
if not options.load.endswith(' ENTER'):
options.load += ' ENTER'
simulator = Simulator(snapshot)
tracer = KeyboardTracer(simulator, options.load)
load = options.load.split()
if load[-1] != 'ENTER':
load.append('ENTER')
simulator = Simulator(snapshot, config=sim_cfg)
tracer = KeyboardTracer(simulator, load)
simulator.set_tracer(tracer)
try:
tracer.run(0x0605)
tracer.run(stop)
border = tracer.border
out7ffd = tracer.out7ffd
except KeyboardInterrupt:
write_line(f'Simulation stopped (interrupted): PC={simulator.registers[PC]}')
interrupted = True
Expand All @@ -365,14 +380,16 @@ def sim_load(blocks, options, config):
snapshot[0xFF58:] = snapshot[0x3E08:0x3EB0] # UDGs
simulator = Simulator(snapshot, {'PC': 0x0605, 'SP': 0xFF50})
border = 7
out7ffd = 0

if not interrupted:
if options.contended_in: # pragma: no cover
in_min_addr = 0x4000
else:
in_min_addr = 0x8000
tracer = LoadTracer(simulator, blocks, accelerators, options.pause, options.first_edge,
options.finish_tape, in_min_addr, options.accelerate_dec_a, list_accelerators, border)
options.finish_tape, in_min_addr, options.accelerate_dec_a, list_accelerators,
border, out7ffd)
simulator.set_tracer(tracer, False, False)
op_fmt = config['TraceOperand']
prefix, byte_fmt, word_fmt = (op_fmt + ',' * (2 - op_fmt.count(','))).split(',')[:3]
Expand Down Expand Up @@ -412,9 +429,14 @@ def sim_load(blocks, options, config):
'PC': sim_registers[PC]
}
options.reg = [f'{r}={v}' for r, v in registers.items()] + options.reg
state = [f'im={simulator.imode}', f'iff={simulator.iff}', f'border={tracer.border}']
state = [
f'im={simulator.imode}',
f'iff={simulator.iff}',
f'border={tracer.border}',
f'7ffd={tracer.out7ffd}'
]
options.state = state + options.state
return simulator.memory[0x4000:]
return simulator.memory[0x4000:0x24000]

def _get_load_params(param_str):
params = []
Expand Down Expand Up @@ -810,6 +832,7 @@ def _print_sim_load_config_help():
--sim-load-config finish-tape=0/1
--sim-load-config first-edge=N
--sim-load-config load=KEYS
--sim-load-config machine=48/128
--sim-load-config pause=0/1
--sim-load-config timeout=N
--sim-load-config trace=FILE
Expand Down Expand Up @@ -880,6 +903,11 @@ def _print_sim_load_config_help():
OPEN# - OPEN # (CS+SS SS+4)
CLOSE# - CLOSE # (CS+SS SS+5)
--sim-load-config machine=48/128
By default, tap2sna.py simulates a 48K Spectrum. Set machine=128 to simulate
a 128K Spectrum.
--sim-load-config pause=0/1
By default, the tape is paused between blocks, and resumed whenever port 254
Expand Down
3 changes: 3 additions & 0 deletions sphinx/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Changelog
* Dropped support for Python 3.7
* :ref:`tap2sna.py` now performs a :ref:`simulated LOAD <tap2sna-sim-load>` by
default, and will also overwrite an existing snapshot by default
* Added the ``machine`` simulated LOAD configuration parameter to
:ref:`tap2sna.py <tap2sna-sim-load>` (to specify whether to simulate a 48K or
a 128K Spectrum)
* Added the ``load`` simulated LOAD configuration parameter to
:ref:`tap2sna.py <tap2sna-sim-load>` (to specify an alternative command line
to use to load the tape)
Expand Down
6 changes: 4 additions & 2 deletions sphinx/source/commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1404,6 +1404,8 @@ parameters are:
loaders (e.g. polarity-sensitive loaders) require ``first-edge=0``
* ``load`` - a space-separated list of keys to press to build an alternative
command line to load the tape (see below)
* ``machine`` - the type of machine to simulate: a 48K Spectrum (``48``, the
default), or a 128K Spectrum (``128``)
* ``pause`` - pause the tape between blocks and resume playback when port 254
is read (``1``, the default), or run the tape continuously (``0``); pausing
can help with tapes that require (but do not actually contain) long pauses
Expand Down Expand Up @@ -1594,8 +1596,8 @@ Configuration parameters may also be set on the command line by using the
| Version | Changes |
+=========+===================================================================+
| 9.0 | A simulated LOAD is performed by default; an existing snapshot |
| | will be overwritten by default; added the ``load`` simulated LOAD |
| | configuration parameter |
| | will be overwritten by default; added the ``load`` and |
| | ``machine`` simulated LOAD configuration parameters |
+---------+-------------------------------------------------------------------+
| 8.10 | Configuration is read from `skoolkit.ini` if present; added the |
| | ``--ini``, ``--show-config`` and ``--tape-analysis`` options; |
Expand Down
2 changes: 2 additions & 0 deletions sphinx/source/man/tap2sna.py.rst
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ parameters are:
loaders (e.g. polarity-sensitive loaders) require ``first-edge=0``
* ``load`` - a space-separated list of keys to press to build an alternative
command line to load the tape (see below)
* ``machine`` - the type of machine to simulate: a 48K Spectrum (``48``, the
default), or a 128K Spectrum (``128``)
* ``pause`` - pause the tape between blocks and resume playback when port 254
is read (``1``, the default), or run the tape continuously (``0``); pausing
can help with tapes that require (but do not actually contain) long pauses
Expand Down
2 changes: 1 addition & 1 deletion tools/mksktarball
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ rm -rf ${ABSDIR}

rsync -aR \
{bin2{sna,tap},skool2{asm,bin,ctl,html},sna2{ctl,img,skool},snap{info,mod},tap{2sna,info},trace}.py \
skoolkit/{__init__,audio,basic,bin2{sna,tap},components,config,ctlparser,defaults,disassembler,graphics,image,kbtracer,loadsample,loadtracer,opcodes,pngwriter,refparser,simtables,simulator,skool{{,2}{asm,ctl,html},2bin,macro,parser},sna2img,sna{,2}{ctl,skool},snap{info,mod,shot},tap{2sna,info},textutils,trace,traceutils,z80}.py \
skoolkit/{__init__,audio,basic,bin2{sna,tap},components,config,ctlparser,defaults,disassembler,graphics,image,kbtracer,loadsample,loadtracer,opcodes,pagingtracer,pngwriter,refparser,simtables,simulator,skool{{,2}{asm,ctl,html},2bin,macro,parser},sna2img,sna{,2}{ctl,skool},snap{info,mod,shot},tap{2sna,info},textutils,trace,traceutils,z80}.py \
examples/hungry_horace.{ctl,ref,t2s} \
skoolkit/resources/{{128-{0,1},48}.rom,skoolkit{,-dark,-green,-plum,-wide}.css} \
tests/{{macro,skoolkit}test,test_{audio,basic,bin2{sna,tap},ctlparser,disassembler,graphics,image,refparser,simulator,skool{{,2}{asm,ctl,html},2bin,macro,parser},skoolkit,sna2{ctl,img},sna{2,}skool,snap{info,mod,shot},tap{2sna,info},textutils,trace,traceutils,z80}}.py \
Expand Down

0 comments on commit 094d492

Please sign in to comment.