From 05ba313afb80e17923ebb1c35299809447a710cf Mon Sep 17 00:00:00 2001 From: Richard Dymond Date: Thu, 13 Jul 2023 17:27:37 -0300 Subject: [PATCH] Add support to tap2sna.py's phantom typist for 128 BASIC mode Also implement the special tokens 'DOWN' and 'PC=address', and add the missing '>' token. --- skoolkit/kbtracer.py | 306 +++++++++++++++++-------------- skoolkit/tap2sna.py | 10 +- sphinx/source/commands.rst | 20 +- sphinx/source/man/tap2sna.py.rst | 20 +- 4 files changed, 200 insertions(+), 156 deletions(-) diff --git a/skoolkit/kbtracer.py b/skoolkit/kbtracer.py index 57a0c10a..7e2e52f4 100644 --- a/skoolkit/kbtracer.py +++ b/skoolkit/kbtracer.py @@ -30,70 +30,99 @@ '9': (0xEFFE, 0b11111101), '0': (0xEFFE, 0b11111110), - 'Q': (0xFBFE, 0b11111110), - 'W': (0xFBFE, 0b11111101), - 'E': (0xFBFE, 0b11111011), - 'R': (0xFBFE, 0b11110111), - 'T': (0xFBFE, 0b11101111), + 'q': (0xFBFE, 0b11111110), + 'w': (0xFBFE, 0b11111101), + 'e': (0xFBFE, 0b11111011), + 'r': (0xFBFE, 0b11110111), + 't': (0xFBFE, 0b11101111), - 'Y': (0xDFFE, 0b11101111), - 'U': (0xDFFE, 0b11110111), - 'I': (0xDFFE, 0b11111011), - 'O': (0xDFFE, 0b11111101), - 'P': (0xDFFE, 0b11111110), + 'y': (0xDFFE, 0b11101111), + 'u': (0xDFFE, 0b11110111), + 'i': (0xDFFE, 0b11111011), + 'o': (0xDFFE, 0b11111101), + 'p': (0xDFFE, 0b11111110), - 'A': (0xFDFE, 0b11111110), - 'S': (0xFDFE, 0b11111101), - 'D': (0xFDFE, 0b11111011), - 'F': (0xFDFE, 0b11110111), - 'G': (0xFDFE, 0b11101111), + 'a': (0xFDFE, 0b11111110), + 's': (0xFDFE, 0b11111101), + 'd': (0xFDFE, 0b11111011), + 'f': (0xFDFE, 0b11110111), + 'g': (0xFDFE, 0b11101111), - 'H': (0xBFFE, 0b11101111), - 'J': (0xBFFE, 0b11110111), - 'K': (0xBFFE, 0b11111011), - 'L': (0xBFFE, 0b11111101), + 'h': (0xBFFE, 0b11101111), + 'j': (0xBFFE, 0b11110111), + 'k': (0xBFFE, 0b11111011), + 'l': (0xBFFE, 0b11111101), 'ENTER': (0xBFFE, 0b11111110), # ENTER 'CS': (0xFEFE, 0b11111110), # CAPS SHIFT - 'Z': (0xFEFE, 0b11111101), - 'X': (0xFEFE, 0b11111011), - 'C': (0xFEFE, 0b11110111), - 'V': (0xFEFE, 0b11101111), + 'z': (0xFEFE, 0b11111101), + 'x': (0xFEFE, 0b11111011), + 'c': (0xFEFE, 0b11110111), + 'v': (0xFEFE, 0b11101111), - 'B': (0x7FFE, 0b11101111), - 'N': (0x7FFE, 0b11110111), - 'M': (0x7FFE, 0b11111011), + 'b': (0x7FFE, 0b11101111), + 'n': (0x7FFE, 0b11110111), + 'm': (0x7FFE, 0b11111011), 'SS': (0x7FFE, 0b11111101), # SYMBOL SHIFT 'SPACE': (0x7FFE, 0b11111110), # SPACE } TOKENS = { - 'NEW': 'A', - 'BORDER': 'B', - 'CONTINUE': 'C', - 'DIM': 'D', - 'REM': 'E', - 'FOR': 'F', - 'GOTO': 'G', # GO TO - 'GOSUB': 'H', # GO SUB - 'INPUT': 'I', - 'LOAD': 'J', - 'LIST': 'K', - 'LET': 'L', - 'PAUSE': 'M', - 'NEXT': 'N', - 'POKE': 'O', - 'PRINT': 'P', - 'PLOT': 'Q', - 'RUN': 'R', - 'SAVE': 'S', - 'RANDOMIZE': 'T', - 'IF': 'U', - 'CLS': 'V', - 'DRAW': 'W', - 'CLEAR': 'X', - 'RETURN': 'Y', - 'COPY': 'Z', + 'DOWN': 'CS+6', # Cursor down + + 'A': 'CS+a', + 'B': 'CS+b', + 'C': 'CS+c', + 'D': 'CS+d', + 'E': 'CS+e', + 'F': 'CS+f', + 'G': 'CS+g', + 'H': 'CS+h', + 'I': 'CS+i', + 'J': 'CS+j', + 'K': 'CS+k', + 'L': 'CS+l', + 'M': 'CS+m', + 'N': 'CS+n', + 'O': 'CS+o', + 'P': 'CS+p', + 'Q': 'CS+q', + 'R': 'CS+r', + 'S': 'CS+s', + 'T': 'CS+t', + 'U': 'CS+u', + 'V': 'CS+v', + 'W': 'CS+w', + 'X': 'CS+x', + 'Y': 'CS+y', + 'Z': 'CS+z', + + 'NEW': 'a', + 'BORDER': 'b', + 'CONTINUE': 'c', + 'DIM': 'd', + 'REM': 'e', + 'FOR': 'f', + 'GOTO': 'g', # GO TO + 'GOSUB': 'h', # GO SUB + 'INPUT': 'i', + 'LOAD': 'j', + 'LIST': 'k', + 'LET': 'l', + 'PAUSE': 'm', + 'NEXT': 'n', + 'POKE': 'o', + 'PRINT': 'p', + 'PLOT': 'q', + 'RUN': 'r', + 'SAVE': 's', + 'RANDOMIZE': 't', + 'IF': 'u', + 'CLS': 'v', + 'DRAW': 'w', + 'CLEAR': 'x', + 'RETURN': 'y', + 'COPY': 'z', '!': 'SS+1', '@': 'SS+2', @@ -105,59 +134,59 @@ '(': 'SS+8', ')': 'SS+9', '_': 'SS+0', - 'STOP': 'SS+A', - '*': 'SS+B', - '?': 'SS+C', - 'STEP': 'SS+D', - '>=': 'SS+E', - 'TO': 'SS+F', - 'THEN': 'SS+G', - '^': 'SS+H', - 'AT': 'SS+I', - '-': 'SS+J', - '+': 'SS+K', - '=': 'SS+L', - '.': 'SS+M', - ',': 'SS+N', - ';': 'SS+O', - '"': 'SS+P', - '<=': 'SS+Q', - '<': 'SS+R', - 'NOT': 'SS+S', - 'T': 'SS+T', - 'OR': 'SS+U', - '/': 'SS+V', - '<>': 'SS+W', - '£': 'SS+X', - 'AND': 'SS+Y', - ':': 'SS+Z', + 'STOP': 'SS+a', + '*': 'SS+b', + '?': 'SS+c', + 'STEP': 'SS+d', + '>=': 'SS+e', + 'TO': 'SS+f', + 'THEN': 'SS+g', + '^': 'SS+h', + 'AT': 'SS+i', + '-': 'SS+j', + '+': 'SS+k', + '=': 'SS+l', + '.': 'SS+m', + ',': 'SS+n', + ';': 'SS+o', + '"': 'SS+p', + '<=': 'SS+q', + '<': 'SS+r', + 'NOT': 'SS+s', + '>': 'SS+t', + 'OR': 'SS+u', + '/': 'SS+v', + '<>': 'SS+w', + '£': 'SS+x', + 'AND': 'SS+y', + ':': 'SS+z', - 'READ': 'CS+SS A', - 'BIN': 'CS+SS B', - 'LPRINT': 'CS+SS C', - 'DATA': 'CS+SS D', - 'TAN': 'CS+SS E', - 'SGN': 'CS+SS F', - 'ABS': 'CS+SS G', - 'SQR': 'CS+SS H', - 'CODE': 'CS+SS I', - 'VAL': 'CS+SS J', - 'LEN': 'CS+SS K', - 'USR': 'CS+SS L', - 'PI': 'CS+SS M', - 'INKEY$': 'CS+SS N', - 'PEEK': 'CS+SS O', - 'TAB': 'CS+SS P', - 'SIN': 'CS+SS Q', - 'INT': 'CS+SS R', - 'RESTORE': 'CS+SS S', - 'RND': 'CS+SS T', - 'CHR$': 'CS+SS U', - 'LLIST': 'CS+SS V', - 'COS': 'CS+SS W', - 'EXP': 'CS+SS X', - 'STR$': 'CS+SS Y', - 'LN': 'CS+SS Z', + 'READ': 'CS+SS a', + 'BIN': 'CS+SS b', + 'LPRINT': 'CS+SS c', + 'DATA': 'CS+SS d', + 'TAN': 'CS+SS e', + 'SGN': 'CS+SS f', + 'ABS': 'CS+SS g', + 'SQR': 'CS+SS h', + 'CODE': 'CS+SS i', + 'VAL': 'CS+SS j', + 'LEN': 'CS+SS k', + 'USR': 'CS+SS l', + 'PI': 'CS+SS m', + 'INKEY$': 'CS+SS n', + 'PEEK': 'CS+SS o', + 'TAB': 'CS+SS p', + 'SIN': 'CS+SS q', + 'INT': 'CS+SS r', + 'RESTORE': 'CS+SS s', + 'RND': 'CS+SS t', + 'CHR$': 'CS+SS u', + 'LLIST': 'CS+SS v', + 'COS': 'CS+SS w', + 'EXP': 'CS+SS x', + 'STR$': 'CS+SS y', + 'LN': 'CS+SS z', 'FORMAT': 'CS+SS SS+0', 'DEFFN': 'CS+SS SS+1', # DEF FN @@ -169,42 +198,38 @@ 'ERASE': 'CS+SS SS+7', 'POINT': 'CS+SS SS+8', 'CAT': 'CS+SS SS+9', - '~': 'CS+SS SS+A', - 'BRIGHT': 'CS+SS SS+B', - 'PAPER': 'CS+SS SS+C', - '\\': 'CS+SS SS+D', - 'ATN': 'CS+SS SS+E', - '{': 'CS+SS SS+F', - '}': 'CS+SS SS+G', - 'CIRCLE': 'CS+SS SS+H', - 'IN': 'CS+SS SS+I', - 'VAL$': 'CS+SS SS+J', - 'SCREEN$': 'CS+SS SS+K', - 'ATTR': 'CS+SS SS+L', - 'INVERSE': 'CS+SS SS+M', - 'OVER': 'CS+SS SS+N', - 'OUT': 'CS+SS SS+O', - '(C)': 'CS+SS SS+P', # © - 'ASN': 'CS+SS SS+Q', - 'VERIFY': 'CS+SS SS+R', - '|': 'CS+SS SS+S', - 'MERGE': 'CS+SS SS+T', - ']': 'CS+SS SS+U', - 'FLASH': 'CS+SS SS+V', - 'ACS': 'CS+SS SS+W', - 'INK': 'CS+SS SS+X', - '[': 'CS+SS SS+Y', - 'BEEP': 'CS+SS SS+Z', + '~': 'CS+SS SS+a', + 'BRIGHT': 'CS+SS SS+b', + 'PAPER': 'CS+SS SS+c', + '\\': 'CS+SS SS+d', + 'ATN': 'CS+SS SS+e', + '{': 'CS+SS SS+f', + '}': 'CS+SS SS+g', + 'CIRCLE': 'CS+SS SS+h', + 'IN': 'CS+SS SS+i', + 'VAL$': 'CS+SS SS+j', + 'SCREEN$': 'CS+SS SS+k', + 'ATTR': 'CS+SS SS+l', + 'INVERSE': 'CS+SS SS+m', + 'OVER': 'CS+SS SS+n', + 'OUT': 'CS+SS SS+o', + '(C)': 'CS+SS SS+p', # © + 'ASN': 'CS+SS SS+q', + 'VERIFY': 'CS+SS SS+r', + '|': 'CS+SS SS+s', + 'MERGE': 'CS+SS SS+t', + ']': 'CS+SS SS+u', + 'FLASH': 'CS+SS SS+v', + 'ACS': 'CS+SS SS+w', + 'INK': 'CS+SS SS+x', + '[': 'CS+SS SS+y', + 'BEEP': 'CS+SS SS+z', } NO_KEY = {} -BOOT_DELAY = 4 - -KEY_DELAY = 4 - -def get_keys(keyspecs): # pragma: no cover - keys = [NO_KEY] * BOOT_DELAY # Allow time for the boot to finish +def get_keys(keyspecs, delay): # pragma: no cover + keys = [NO_KEY] * 4 # Delay before first keypress specs = [] for spec in keyspecs: @@ -220,16 +245,15 @@ def get_keys(keyspecs): # pragma: no cover raise SkoolKitError(f'Unrecognised token: {p}') if kb: keys.append(kb) - # Allow time for the '5 call counter' (see 0x02BF) to reach 0 - # before the next keypress - keys.extend([NO_KEY] * KEY_DELAY) + # Allow enough time between keypresses for each one to register + keys.extend([NO_KEY] * delay) return keys class KeyboardTracer(PagingTracer): # pragma: no cover - def __init__(self, simulator, keys): + def __init__(self, simulator, keys, delay): self.simulator = simulator - self.keys = get_keys(keys) + self.keys = get_keys(keys, delay) self.border = 7 self.out7ffd = 0 diff --git a/skoolkit/tap2sna.py b/skoolkit/tap2sna.py index 6446cb3b..94134d2a 100644 --- a/skoolkit/tap2sna.py +++ b/skoolkit/tap2sna.py @@ -343,18 +343,26 @@ def sim_load(blocks, options, config): sim_cfg = {'frame_duration': FRAME_DURATIONS[1]} memory = Memory() stop = 0x13BE + kb_delay = 13 else: sim_cfg = {} memory = [0] * 65536 memory[:0x4000] = read_bin_file(ROM48, 16384) stop = 0x0605 # SAVE-ETC + kb_delay = 4 if options.load: # pragma: no cover load = options.load.split() + if load[-1].startswith('PC='): + pc = load.pop() + try: + stop = get_int_param(pc[3:], True) + except ValueError: + raise SkoolKitError(f"Invalid integer in 'load' parameter: {pc}") if load[-1] != 'ENTER': load.append('ENTER') simulator = Simulator(memory, config=sim_cfg) - tracer = KeyboardTracer(simulator, load) + tracer = KeyboardTracer(simulator, load, kb_delay) simulator.set_tracer(tracer) try: tracer.run(stop) diff --git a/sphinx/source/commands.rst b/sphinx/source/commands.rst index 16d0a895..986e835b 100644 --- a/sphinx/source/commands.rst +++ b/sphinx/source/commands.rst @@ -1497,21 +1497,27 @@ loop accelerators are: The ``load`` parameter may be used to specify an alternative command line to load the tape in cases where neither 'LOAD ""' nor 'LOAD ""CODE' works. Its value is a space-separated list of keys to press to build the command line. -Each alphanumeric key is denoted by its digit or upper case letter. Multiple -simultaneous keypresses are denoted by separating them with '+'. All BASIC -tokens except those that contain a space are translated into their -corresponding digit, letter, special key or combination thereof. The following -special tokens are also recognised: +Each alphanumeric key is denoted by its digit or letter. Multiple simultaneous +keypresses are denoted by separating them with '+'. All BASIC tokens except +those that contain a space are translated into their corresponding digit, +letter, special key or combination thereof. The following special tokens are +also recognised: * ``CS`` - CAPS SHIFT * ``SS`` - SYMBOL SHIFT * ``SPACE`` - SPACE * ``ENTER`` - ENTER -* ``GOTO`` - GO TO ('G') -* ``GOSUB`` - GO SUB ('H') +* ``DOWN`` - Cursor down ('CS+6') +* ``GOTO`` - GO TO ('g') +* ``GOSUB`` - GO SUB ('h') * ``DEFFN`` - DEF FN ('CS+SS SS+1') * ``OPEN#`` - OPEN # ('CS+SS SS+4') * ``CLOSE#`` - CLOSE # ('CS+SS SS+5') +* ``PC=address`` - Stop the keyboard input simulation at this address + +The ``PC=address`` token, if present, must appear last. The default address is +either 0x0605 (when a 48K Spectrum is being simulated) or 0x13BE (on a 128K +Spectrum). The simulated LOAD begins at this address. ``ENTER`` is automatically appended to the command line if not already present. diff --git a/sphinx/source/man/tap2sna.py.rst b/sphinx/source/man/tap2sna.py.rst index 2c674e1c..d3995ce4 100644 --- a/sphinx/source/man/tap2sna.py.rst +++ b/sphinx/source/man/tap2sna.py.rst @@ -229,22 +229,28 @@ loop accelerators are: The ``load`` parameter may be used to specify an alternative command line to load the tape in cases where neither 'LOAD ""' nor 'LOAD ""CODE' works. Its value is a space-separated list of keys to press to build the command line. -Each alphanumeric key is denoted by its digit or upper case letter. Multiple -simultaneous keypresses are denoted by separating them with '+'. All BASIC -tokens except those that contain a space are translated into their -corresponding digit, letter, special key or combination thereof. The following -special tokens are also recognised: +Each alphanumeric key is denoted by its digit or letter. Multiple simultaneous +keypresses are denoted by separating them with '+'. All BASIC tokens except +those that contain a space are translated into their corresponding digit, +letter, special key or combination thereof. The following special tokens are +also recognised: | | ``CS`` - CAPS SHIFT | ``SS`` - SYMBOL SHIFT | ``SPACE`` - SPACE | ``ENTER`` - ENTER -| ``GOTO`` - GO TO ('G') -| ``GOSUB`` - GO SUB ('H') +| ``DOWN`` - Cursor down ('CS+6') +| ``GOTO`` - GO TO ('g') +| ``GOSUB`` - GO SUB ('h') | ``DEFFN`` - DEF FN ('CS+SS SS+1') | ``OPEN#`` - OPEN # ('CS+SS SS+4') | ``CLOSE#`` - CLOSE # ('CS+SS SS+5') +| ``PC=address`` - Stop the keyboard input simulation at this address + +The ``PC=address`` token, if present, must appear last. The default address is +either 0x0605 (when a 48K Spectrum is being simulated) or 0x13BE (on a 128K +Spectrum). The simulated LOAD begins at this address. ``ENTER`` is automatically appended to the command line if not already present.