Space Taxi is finally starting to look viable on the GS!
This commit is contained in:
parent
2acfce011d
commit
477d39f7e9
98 changed files with 13506 additions and 1340 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -64,3 +64,9 @@ __pycache__/
|
||||||
|
|
||||||
# AGI game data (Sierra/fan-made AGI games used for testing; never committed)
|
# AGI game data (Sierra/fan-made AGI games used for testing; never committed)
|
||||||
examples/agi/gamedata/
|
examples/agi/gamedata/
|
||||||
|
|
||||||
|
# Space Taxi behaviour-gate captures (tools/stsim/gate.sh capture). Large
|
||||||
|
# and regenerable from the tree; gateHashes.txt is the committed reference.
|
||||||
|
tools/stsim/golden/
|
||||||
|
tools/stsim/stsim
|
||||||
|
tools/stsim/recorder
|
||||||
|
|
|
||||||
BIN
examples/spacetaxi/21_!.wav
Normal file
BIN
examples/spacetaxi/21_!.wav
Normal file
Binary file not shown.
BIN
examples/spacetaxi/31_1.wav
Normal file
BIN
examples/spacetaxi/31_1.wav
Normal file
Binary file not shown.
BIN
examples/spacetaxi/32_2.wav
Normal file
BIN
examples/spacetaxi/32_2.wav
Normal file
Binary file not shown.
BIN
examples/spacetaxi/33_3.wav
Normal file
BIN
examples/spacetaxi/33_3.wav
Normal file
Binary file not shown.
BIN
examples/spacetaxi/34_4.wav
Normal file
BIN
examples/spacetaxi/34_4.wav
Normal file
Binary file not shown.
BIN
examples/spacetaxi/35_5.wav
Normal file
BIN
examples/spacetaxi/35_5.wav
Normal file
Binary file not shown.
BIN
examples/spacetaxi/36_6.wav
Normal file
BIN
examples/spacetaxi/36_6.wav
Normal file
Binary file not shown.
BIN
examples/spacetaxi/37_7.wav
Normal file
BIN
examples/spacetaxi/37_7.wav
Normal file
Binary file not shown.
BIN
examples/spacetaxi/38_8.wav
Normal file
BIN
examples/spacetaxi/38_8.wav
Normal file
Binary file not shown.
BIN
examples/spacetaxi/39_9.wav
Normal file
BIN
examples/spacetaxi/39_9.wav
Normal file
Binary file not shown.
BIN
examples/spacetaxi/3F_x.wav
Normal file
BIN
examples/spacetaxi/3F_x.wav
Normal file
Binary file not shown.
BIN
examples/spacetaxi/48_H.wav
Normal file
BIN
examples/spacetaxi/48_H.wav
Normal file
Binary file not shown.
BIN
examples/spacetaxi/50_P.wav
Normal file
BIN
examples/spacetaxi/50_P.wav
Normal file
Binary file not shown.
BIN
examples/spacetaxi/54_T.wav
Normal file
BIN
examples/spacetaxi/54_T.wav
Normal file
Binary file not shown.
BIN
examples/spacetaxi/55_U.wav
Normal file
BIN
examples/spacetaxi/55_U.wav
Normal file
Binary file not shown.
BIN
examples/spacetaxi/assets/RawTitle.png
(Stored with Git LFS)
Normal file
BIN
examples/spacetaxi/assets/RawTitle.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
examples/spacetaxi/assets/TitleShr.png
(Stored with Git LFS)
Normal file
BIN
examples/spacetaxi/assets/TitleShr.png
(Stored with Git LFS)
Normal file
Binary file not shown.
81
examples/spacetaxi/assets/depackSpeechRam.py
Executable file
81
examples/spacetaxi/assets/depackSpeechRam.py
Executable file
|
|
@ -0,0 +1,81 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Run Space Taxi's depacker to produce a FLAT 64K RAM image.
|
||||||
|
|
||||||
|
Why this exists: the disk's "SPACE TAXI+" is a crunched single file (it
|
||||||
|
matches the running game's memory in 0.6% of bytes), so the speech
|
||||||
|
samples cannot be read out of it statically. And a VICE memory dump is
|
||||||
|
no good either -- it captures the banked-IN view, so $A000-$BFFF comes
|
||||||
|
back as BASIC ROM and 13 of the 15 utterances are missing. Every
|
||||||
|
mem0000*.bin in this tree has that problem.
|
||||||
|
|
||||||
|
The way out needs no emulator install: stuff/spacetaxi/cpu6502.py is a
|
||||||
|
plain 6502 core over a flat 64K bytearray with no banking and no I/O,
|
||||||
|
which is all a depacker needs. Load the .prg, run from the BASIC SYS
|
||||||
|
entry, and the depacked image IS the RAM -- $A000-$BFFF included.
|
||||||
|
|
||||||
|
Feed the result to extractSpeech.py.
|
||||||
|
|
||||||
|
Usage: depackSpeechRam.py [spaceTaxiPlus.prg] [out.bin]
|
||||||
|
Defaults resolve inside stuff/spacetaxi/ (gitignored research
|
||||||
|
area, same as genC64Data.py's inputs).
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
STUFF = os.path.join(HERE, "..", "..", "..", "stuff", "spacetaxi")
|
||||||
|
|
||||||
|
# The BASIC stub is `0B08 D307 9E "2059" 00` -- SYS 2059.
|
||||||
|
ENTRY = 0x080B
|
||||||
|
MAX_STEPS = 60000000
|
||||||
|
CHECK_MASK = 0xFFFF # how often to test for completion
|
||||||
|
|
||||||
|
# Completion test: the $9B00 index table is populated and the first
|
||||||
|
# utterance's $FE lead-in is in place.
|
||||||
|
TABLE_ADDR = 0x9B00
|
||||||
|
FIRST_UTT = 0x9B40
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
prgPath = sys.argv[1] if len(sys.argv) > 1 else os.path.join(STUFF, "extract", "SPACE TAXI+.prg")
|
||||||
|
outPath = sys.argv[2] if len(sys.argv) > 2 else os.path.join(STUFF, "staxi-ram.bin")
|
||||||
|
|
||||||
|
sys.path.insert(0, STUFF)
|
||||||
|
try:
|
||||||
|
from cpu6502 import Cpu6502
|
||||||
|
except ImportError:
|
||||||
|
sys.exit("depackSpeechRam: need cpu6502.py in %s" % STUFF)
|
||||||
|
|
||||||
|
prg = open(prgPath, "rb").read()
|
||||||
|
load = prg[0] | (prg[1] << 8)
|
||||||
|
ram = bytearray(65536)
|
||||||
|
ram[load:load + len(prg) - 2] = prg[2:]
|
||||||
|
print("loaded %s at $%04X..$%04X" % (os.path.basename(prgPath), load, load + len(prg) - 3))
|
||||||
|
|
||||||
|
cpu = Cpu6502(ram)
|
||||||
|
cpu.pc = ENTRY
|
||||||
|
steps = 0
|
||||||
|
done = False
|
||||||
|
try:
|
||||||
|
while steps < MAX_STEPS:
|
||||||
|
cpu.step()
|
||||||
|
steps += 1
|
||||||
|
if (steps & CHECK_MASK) == 0:
|
||||||
|
if ram[TABLE_ADDR] != 0 and ram[FIRST_UTT] == 0xFE:
|
||||||
|
done = True
|
||||||
|
break
|
||||||
|
except Exception as exc:
|
||||||
|
# A depacker that runs off into I/O or an undocumented opcode
|
||||||
|
# still usually gets the data down first, so report and carry on
|
||||||
|
# to the write rather than throwing the work away.
|
||||||
|
print("6502 stopped after %d steps at PC=$%04X: %s" % (steps, cpu.pc, exc))
|
||||||
|
|
||||||
|
open(outPath, "wb").write(bytes(ram))
|
||||||
|
print("%d steps, depack %s -> %s" % (steps, "detected" if done else "NOT detected", outPath))
|
||||||
|
if not done:
|
||||||
|
print("WARNING: completion check never fired; the image may be partial.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
225
examples/spacetaxi/assets/extractSpeech.py
Executable file
225
examples/spacetaxi/assets/extractSpeech.py
Executable file
|
|
@ -0,0 +1,225 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Extract Space Taxi's digitised speech from a C64 memory dump.
|
||||||
|
|
||||||
|
FORMAT (reverse-engineered from bankedSfxLoad at $9802, see
|
||||||
|
stuff/spacetaxi/live-level1.lst). It is NOT 4-bit $D418 digi and it is
|
||||||
|
NOT the SID speech synth. It is 1-BIT RUN-LENGTH audio:
|
||||||
|
|
||||||
|
* An index table at $9B00 holds [code, ptrLo, ptrHi] triples,
|
||||||
|
terminated by a zero code. `code` is the PETSCII character the game
|
||||||
|
asks for -- 'H' 'T' '!' spell "Hey Taxi!", 'P' + digit + '?' spell
|
||||||
|
"Pad two, please", 'U' is "up".
|
||||||
|
* Each utterance is a byte stream. For every byte the player busy-waits
|
||||||
|
that many passes of a fixed delay loop and then TOGGLES bit 0 of
|
||||||
|
$D417, which flips voice 1 in and out of the filter -- a 1-bit
|
||||||
|
speaker. So each byte is the length of one half-cycle.
|
||||||
|
* $FE is a raster sync (the player waits on $D011), $FF ends the
|
||||||
|
utterance.
|
||||||
|
|
||||||
|
The delay loop at $98A1..$98AA costs 27 cycles per pass and the
|
||||||
|
toggle/advance/fetch tail ($98AC..$987F) another 33, hence CYCLES().
|
||||||
|
|
||||||
|
LIMITATION: a plain VICE memory dump captures the BANKED-IN view, so
|
||||||
|
$A000-$BFFF comes back as BASIC ROM ("CBMBASIC" at $A004) and most of the
|
||||||
|
speech is missing -- the player banks BASIC out ($01 = $06) precisely
|
||||||
|
because the samples live in the RAM underneath. Dump it with the ROMs
|
||||||
|
banked out, e.g. in the VICE monitor:
|
||||||
|
|
||||||
|
bank ram
|
||||||
|
save "staxi-ram.bin" 0 0000 ffff
|
||||||
|
|
||||||
|
Usage: extractSpeech.py <dump.bin> [outDir]
|
||||||
|
The dump may have a 2-byte load address or not; both are handled.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
import wave
|
||||||
|
|
||||||
|
NTSC_HZ = 1022727.0 # the port already uses this (ST_SID_HZ_NUM)
|
||||||
|
OUT_RATE = 22050
|
||||||
|
TABLE_ADDR = 0x9B00
|
||||||
|
END_MARK = 0xFF
|
||||||
|
SYNC_MARK = 0xFE
|
||||||
|
|
||||||
|
|
||||||
|
def cyclesFor(count):
|
||||||
|
"""Half-cycle length in CPU cycles for one stream byte."""
|
||||||
|
return 27 * count + 33
|
||||||
|
|
||||||
|
|
||||||
|
def loadDump(path):
|
||||||
|
"""Return a 64K-addressable bytes object from a dump with or without
|
||||||
|
a load-address header."""
|
||||||
|
raw = open(path, "rb").read()
|
||||||
|
if len(raw) in (65538, 65536 + 2):
|
||||||
|
return raw[2:]
|
||||||
|
if len(raw) == 65536:
|
||||||
|
return raw
|
||||||
|
# Headerless dump starting at $0801 (what mem0801.prg turned out to be).
|
||||||
|
pad = bytearray(65536)
|
||||||
|
pad[0x0801:0x0801 + len(raw)] = raw[:65536 - 0x0801]
|
||||||
|
return bytes(pad)
|
||||||
|
|
||||||
|
|
||||||
|
def readTable(mem):
|
||||||
|
"""[(code, pointer)] from the $9B00 index."""
|
||||||
|
out = []
|
||||||
|
y = 0
|
||||||
|
while y < 0x300:
|
||||||
|
code = mem[TABLE_ADDR + y]
|
||||||
|
if code == 0 or code == 0xFF:
|
||||||
|
break
|
||||||
|
out.append((code, mem[TABLE_ADDR + y + 1] | (mem[TABLE_ADDR + y + 2] << 8)))
|
||||||
|
y += 3
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def decode(mem, ptr, limit=0x4000):
|
||||||
|
"""Return (runs, byteCount, sawEnd). runs is [(level, cycles)]."""
|
||||||
|
runs = []
|
||||||
|
level = 0
|
||||||
|
addr = ptr
|
||||||
|
while addr - ptr < limit:
|
||||||
|
b = mem[addr]
|
||||||
|
addr += 1
|
||||||
|
if b == END_MARK:
|
||||||
|
return runs, addr - ptr, True
|
||||||
|
if b == SYNC_MARK:
|
||||||
|
runs.append((level, int(NTSC_HZ / 60.0)))
|
||||||
|
continue
|
||||||
|
runs.append((level, cyclesFor(b)))
|
||||||
|
level ^= 1
|
||||||
|
return runs, addr - ptr, False
|
||||||
|
|
||||||
|
|
||||||
|
def writeWav(runs, path):
|
||||||
|
samples = bytearray()
|
||||||
|
carry = 0.0
|
||||||
|
perOut = NTSC_HZ / OUT_RATE
|
||||||
|
for level, cyc in runs:
|
||||||
|
carry += cyc
|
||||||
|
n = int(carry / perOut)
|
||||||
|
carry -= n * perOut
|
||||||
|
samples += struct.pack("<h", 12000 if level else -12000) * n
|
||||||
|
w = wave.open(path, "wb")
|
||||||
|
w.setnchannels(1)
|
||||||
|
w.setsampwidth(2)
|
||||||
|
w.setframerate(OUT_RATE)
|
||||||
|
w.writeframes(bytes(samples))
|
||||||
|
w.close()
|
||||||
|
return len(samples) // 2 / float(OUT_RATE)
|
||||||
|
|
||||||
|
|
||||||
|
def emitC(mem, table, romLo, outDir):
|
||||||
|
"""Write stSpeechData.c/.h: the raw run-length streams plus an index.
|
||||||
|
|
||||||
|
The streams ship as-is (about 7.4 KB for all 15) and the game expands
|
||||||
|
them to PCM on the fly -- rendering them at bake time would be ~38 KB
|
||||||
|
at 5 kHz, which will not fit an IIgs bank alongside everything else.
|
||||||
|
"""
|
||||||
|
blobs = []
|
||||||
|
index = []
|
||||||
|
for code, ptr in table:
|
||||||
|
if ptr >= romLo:
|
||||||
|
continue
|
||||||
|
runs, nbytes, sawEnd = decode(mem, ptr)
|
||||||
|
nxt = next((q for _, q in sorted(table, key=lambda e: e[1]) if q > ptr), None)
|
||||||
|
if not sawEnd or (nxt is not None and nbytes > nxt - ptr):
|
||||||
|
continue
|
||||||
|
index.append((code, sum(len(b) for b in blobs), nbytes))
|
||||||
|
blobs.append(bytes(mem[ptr:ptr + nbytes]))
|
||||||
|
data = b"".join(blobs)
|
||||||
|
|
||||||
|
h = os.path.join(outDir, "stSpeechData.h")
|
||||||
|
c = os.path.join(outDir, "stSpeechData.c")
|
||||||
|
with open(h, "w") as fp:
|
||||||
|
fp.write("// Generated by assets/extractSpeech.py. Do not hand-edit.\n"
|
||||||
|
"//\n"
|
||||||
|
"// Space Taxi's digitised speech as the C64 stores it: 1-bit\n"
|
||||||
|
"// run-length streams. Each byte is one half-cycle length; the\n"
|
||||||
|
"// player toggles its output after each. $FE is a raster sync\n"
|
||||||
|
"// and $FF ends an utterance. stAudio.c expands these to PCM\n"
|
||||||
|
"// through the jlAudioPlaySfxStream fill callback.\n\n"
|
||||||
|
"#ifndef ST_SPEECH_DATA_H\n#define ST_SPEECH_DATA_H\n\n"
|
||||||
|
"#include <stdint.h>\n\n"
|
||||||
|
"typedef struct {\n"
|
||||||
|
" uint8_t code; // PETSCII: 'H' 'T' '!' 'P' '1'..'9' '?' 'U'\n"
|
||||||
|
" uint16_t offset; // into kStSpeechRle\n"
|
||||||
|
" uint16_t length;\n"
|
||||||
|
"} StSpeechEntryT;\n\n"
|
||||||
|
"#define ST_SPEECH_COUNT %du\n"
|
||||||
|
"#define ST_SPEECH_RLE_BYTES %du\n\n"
|
||||||
|
"extern const StSpeechEntryT kStSpeechIndex[ST_SPEECH_COUNT];\n"
|
||||||
|
"extern const uint8_t kStSpeechRle[ST_SPEECH_RLE_BYTES];\n\n"
|
||||||
|
"#endif\n" % (len(index), len(data)))
|
||||||
|
with open(c, "w") as fp:
|
||||||
|
fp.write('// Generated by assets/extractSpeech.py. Do not hand-edit.\n\n'
|
||||||
|
'#include "stSpeechData.h"\n\n'
|
||||||
|
'const StSpeechEntryT kStSpeechIndex[ST_SPEECH_COUNT] = {\n')
|
||||||
|
for code, off, ln in index:
|
||||||
|
fp.write(" { 0x%02Xu, %5du, %5du }, // '%s'\n"
|
||||||
|
% (code, off, ln, chr(code) if 33 <= code < 127 else "?"))
|
||||||
|
fp.write("};\n\nconst uint8_t kStSpeechRle[ST_SPEECH_RLE_BYTES] = {\n")
|
||||||
|
for i in range(0, len(data), 16):
|
||||||
|
fp.write(" " + " ".join("0x%02X," % b for b in data[i:i + 16]) + "\n")
|
||||||
|
fp.write("};\n")
|
||||||
|
print("\nemitted %s (%d entries) and %s (%d bytes)" % (h, len(index), c, len(data)))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
sys.exit(__doc__)
|
||||||
|
emit = "--c" in sys.argv
|
||||||
|
args = [a for a in sys.argv[1:] if a != "--c"]
|
||||||
|
mem = loadDump(args[0])
|
||||||
|
outDir = args[1] if len(args) > 1 else "speech"
|
||||||
|
os.makedirs(outDir, exist_ok=True)
|
||||||
|
|
||||||
|
if mem[0xA004:0xA00C] == b"CBMBASIC":
|
||||||
|
sys.stderr.write("WARNING: $A000 holds BASIC ROM, not the RAM under it.\n"
|
||||||
|
" Utterances at $A000 and above will not decode.\n"
|
||||||
|
" Re-dump with `bank ram` (see the module docstring).\n\n")
|
||||||
|
|
||||||
|
table = readTable(mem)
|
||||||
|
romLo = 0xA000 if mem[0xA004:0xA00C] == b"CBMBASIC" else 0x10000
|
||||||
|
ordered = sorted(p for _, p in table)
|
||||||
|
good = 0
|
||||||
|
|
||||||
|
print("%-4s %-6s %7s %7s %s" % ("code", "ptr", "bytes", "dur(s)", "file"))
|
||||||
|
for code, ptr in table:
|
||||||
|
label = chr(code) if 33 <= code < 127 else "?"
|
||||||
|
# Utterances are stored back to back, so the next pointer is the
|
||||||
|
# expected end. Both checks matter: BASIC ROM is full of $FF, so
|
||||||
|
# "found a terminator" alone happily reports success on garbage --
|
||||||
|
# it claimed all 15 decoded from a ROM-shadowed dump.
|
||||||
|
nxt = next((q for q in ordered if q > ptr), None)
|
||||||
|
expected = (nxt - ptr) if nxt else None
|
||||||
|
if ptr >= romLo:
|
||||||
|
print("%-4s $%04X %7s %7s -- SKIPPED (shadowed by BASIC ROM in this dump)"
|
||||||
|
% (label, ptr, "-", "-"))
|
||||||
|
continue
|
||||||
|
runs, nbytes, sawEnd = decode(mem, ptr)
|
||||||
|
if not sawEnd:
|
||||||
|
print("%-4s $%04X %7d %7s -- SKIPPED (no $FF terminator)" % (label, ptr, nbytes, "-"))
|
||||||
|
continue
|
||||||
|
# The terminator must land BEFORE the next utterance starts.
|
||||||
|
# (Entries are back to back but may carry a pad byte, so this is
|
||||||
|
# <= rather than ==.) Overrunning means the $FF we found was not
|
||||||
|
# ours -- which is exactly what happens against a ROM shadow.
|
||||||
|
if expected is not None and nbytes > expected:
|
||||||
|
print("%-4s $%04X %7d %7s -- SKIPPED (ran %d bytes, next entry is %d away)"
|
||||||
|
% (label, ptr, nbytes, "-", nbytes, expected))
|
||||||
|
continue
|
||||||
|
path = os.path.join(outDir, "%02X_%s.wav" % (code, label if label != "?" else "x"))
|
||||||
|
dur = writeWav(runs, path)
|
||||||
|
good += 1
|
||||||
|
print("%-4s $%04X %7d %7.2f %s" % (label, ptr, nbytes, dur, os.path.basename(path)))
|
||||||
|
print("\n%d of %d utterances decoded into %s" % (good, len(table), outDir))
|
||||||
|
if emit:
|
||||||
|
emitC(mem, table, romLo, outDir)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
@ -55,6 +55,21 @@ def c_bytes(data, per_line=16, indent=" "):
|
||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
# The cross-segment accessors the IIgs needs (see the block this emits
|
||||||
|
# into stC64Data.c). These used to be hand-added to the GENERATED file,
|
||||||
|
# which meant re-running this script silently deleted them and broke the
|
||||||
|
# IIgs build -- the header kept declaring them and nothing defined them.
|
||||||
|
# Prototype and return expression per accessor; the header prints the
|
||||||
|
# prototypes aligned, so keep the spacing.
|
||||||
|
ACCESSORS = [
|
||||||
|
("const uint8_t *stC64Charset(void);", "&kStCharset[0][0]"),
|
||||||
|
("const uint8_t *stC64SpriteBitmap(uint8_t idx);", "kStSpriteBitmaps[idx]"),
|
||||||
|
("uint8_t stC64RngByte(uint8_t idx);", "kStRngTable[idx]"),
|
||||||
|
("const uint8_t *stC64SfxProgram(uint8_t idx);", "kStSfxPrograms[idx]"),
|
||||||
|
("uint8_t stC64FlameCel(uint8_t dirMask);", "kStFlameCelByDirMask[dirMask]"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
raw = load_raw()
|
raw = load_raw()
|
||||||
sprite_count = SPRITE_PTR_LAST - SPRITE_PTR_FIRST + 1
|
sprite_count = SPRITE_PTR_LAST - SPRITE_PTR_FIRST + 1
|
||||||
|
|
@ -100,9 +115,11 @@ def main():
|
||||||
h.append("// 8=RIGHT); 0 = no cel for that combination.")
|
h.append("// 8=RIGHT); 0 = no cel for that combination.")
|
||||||
h.append("extern const uint8_t kStFlameCelByDirMask[16];")
|
h.append("extern const uint8_t kStFlameCelByDirMask[16];")
|
||||||
h.append("")
|
h.append("")
|
||||||
h.append("// Level-intro star velocities per sprite 0..6 ($450C / $4513).")
|
h.append("// Cross-segment-safe accessors (see stC64Data.c): reach the tables above")
|
||||||
h.append("extern const int8_t kStIntroStarDx[7];")
|
h.append("// by an inter-segment call on the IIgs, where a direct 2-byte data")
|
||||||
h.append("extern const int8_t kStIntroStarDy[7];")
|
h.append("// reference from another bank is not relocatable.")
|
||||||
|
for decl in ACCESSORS:
|
||||||
|
h.append(decl[0])
|
||||||
h.append("")
|
h.append("")
|
||||||
h.append("#endif")
|
h.append("#endif")
|
||||||
|
|
||||||
|
|
@ -143,11 +160,25 @@ def main():
|
||||||
c.append(c_bytes(flame))
|
c.append(c_bytes(flame))
|
||||||
c.append("};")
|
c.append("};")
|
||||||
c.append("")
|
c.append("")
|
||||||
dx = [b - 256 if b >= 128 else b for b in raw[0x450C:0x450C + 7]]
|
|
||||||
dy = [b - 256 if b >= 128 else b for b in raw[0x4513:0x4513 + 7]]
|
|
||||||
c.append("const int8_t kStIntroStarDx[7] = { " + ", ".join(str(v) for v in dx) + " };")
|
|
||||||
c.append("const int8_t kStIntroStarDy[7] = { " + ", ".join(str(v) for v in dy) + " };")
|
|
||||||
c.append("")
|
c.append("")
|
||||||
|
c.append("// ---------------------------------------------------------------------------")
|
||||||
|
c.append("// Accessors. On the IIgs the program is split across banks and a 2-byte")
|
||||||
|
c.append("// data reference cannot cross a segment (only a 3-byte JSL/JML can). These")
|
||||||
|
c.append("// tiny functions live in the same segment as the tables, so their reads")
|
||||||
|
c.append("// are intra-segment; callers in other segments reach them by a supported")
|
||||||
|
c.append("// inter-segment call. On the other ports they inline away to nothing.")
|
||||||
|
c.append("// ---------------------------------------------------------------------------")
|
||||||
|
first = True
|
||||||
|
for decl, body in ACCESSORS:
|
||||||
|
c.append("")
|
||||||
|
if not first:
|
||||||
|
c.append("")
|
||||||
|
first = False
|
||||||
|
# The header pads return types to align its prototypes; the
|
||||||
|
# definitions here must not inherit that padding.
|
||||||
|
c.append(" ".join(decl[:-1].split()) + " {")
|
||||||
|
c.append(" return " + body + ";")
|
||||||
|
c.append("}")
|
||||||
|
|
||||||
with open(OUT_H, "w", encoding="ascii") as fp:
|
with open(OUT_H, "w", encoding="ascii") as fp:
|
||||||
fp.write("\n".join(h) + "\n")
|
fp.write("\n".join(h) + "\n")
|
||||||
|
|
|
||||||
76
examples/spacetaxi/assets/genSongData.py
Normal file
76
examples/spacetaxi/assets/genSongData.py
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# Bake examples/spacetaxi/assets/songs/song*.song into stSongData.h.
|
||||||
|
#
|
||||||
|
# python3 examples/spacetaxi/assets/genSongData.py
|
||||||
|
#
|
||||||
|
# The .song sources come from the game's own player run under a 6502
|
||||||
|
# emulator: assets/sidCapture.py logs every SID write per PAL frame and
|
||||||
|
# assets/sidToSong.py turns that into songbake text.
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
here = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
repo = os.path.abspath(os.path.join(here, "..", "..", ".."))
|
||||||
|
songbake = os.path.join(repo, "tools", "songbake", "songbake.py")
|
||||||
|
out = os.path.join(here, "..", "stSongData.h")
|
||||||
|
count = 9
|
||||||
|
|
||||||
|
header = """// Space Taxi (C64) tunes as JoeyLib JYM1 streams. GENERATED by
|
||||||
|
// assets/genSongData.py -- do not hand-edit. The songs were extracted by
|
||||||
|
// running the game's own music player under a 6502 emulator
|
||||||
|
// (assets/sidCapture.py logs every SID write per PAL frame;
|
||||||
|
// assets/sidToSong.py turns that into songbake text in assets/songs/).
|
||||||
|
//
|
||||||
|
// Where the C64 plays them:
|
||||||
|
// BLOCKING via $44CF (the game spins until the tune ends; the port
|
||||||
|
// parks in ST_STATE_JINGLE with the sim frozen):
|
||||||
|
// 0..3 start of every screen, rotating on $5E9B ($6906)
|
||||||
|
// 4 level 25 start ($6935) and the bonus cab ($70B1)
|
||||||
|
// 5 game over ($5C1F), after the two-second GAME OVER! wait
|
||||||
|
// NON-BLOCKING via $CB02 (started and left to the IRQ player):
|
||||||
|
// 6 the high-score name entry screen ($4E35) -- no port screen yet
|
||||||
|
// 7 the high-score table ($4C1F, joystick UP on the title)
|
||||||
|
// 8 the level intro while the cab flies in ($459C); endless, so
|
||||||
|
// the source is trimmed to 30 s and looped
|
||||||
|
#ifndef ST_SONG_DATA_H
|
||||||
|
#define ST_SONG_DATA_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
footer = """typedef struct {
|
||||||
|
const uint8_t *data;
|
||||||
|
uint16_t length;
|
||||||
|
} StSongT;
|
||||||
|
|
||||||
|
#define ST_SONG_COUNT %du
|
||||||
|
#define ST_SONG_BONUS 4u
|
||||||
|
#define ST_SONG_GAME_OVER 5u
|
||||||
|
#define ST_SONG_NAME_ENTRY 6u
|
||||||
|
#define ST_SONG_SCORES 7u
|
||||||
|
#define ST_SONG_INTRO 8u
|
||||||
|
|
||||||
|
static const StSongT kStSongs[ST_SONG_COUNT] = {
|
||||||
|
%s};
|
||||||
|
|
||||||
|
#endif // ST_SONG_DATA_H
|
||||||
|
"""
|
||||||
|
|
||||||
|
parts = []
|
||||||
|
for n in range(count):
|
||||||
|
src = os.path.join(here, "songs", "song%d.song" % n)
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=".h", delete=False) as tmp:
|
||||||
|
path = tmp.name
|
||||||
|
subprocess.run([sys.executable, songbake, "-c", "gStSong%d" % n, src, path], check=True, stderr=subprocess.DEVNULL)
|
||||||
|
body = open(path).read()
|
||||||
|
os.unlink(path)
|
||||||
|
body = "\n".join(l for l in body.split("\n") if l.startswith("static") or l.startswith(" ") or l.startswith("}"))
|
||||||
|
parts.append(body.rstrip() + "\n\n")
|
||||||
|
|
||||||
|
table = "".join(" { gStSong%d, (uint16_t)sizeof(gStSong%d) },\n" % (n, n) for n in range(count))
|
||||||
|
with open(out, "w") as fp:
|
||||||
|
fp.write(header + "".join(parts) + footer % (count, table))
|
||||||
|
print("wrote", os.path.normpath(out))
|
||||||
106
examples/spacetaxi/assets/genSpeechPcm.py
Executable file
106
examples/spacetaxi/assets/genSpeechPcm.py
Executable file
|
|
@ -0,0 +1,106 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""genSpeechPcm.py - bake Space Taxi's speech to 8-bit PCM.
|
||||||
|
Reads the C64 run-length speech streams from stSpeechData.c (the output
|
||||||
|
of extractSpeech.py) and writes speech.bin: every utterance decoded to
|
||||||
|
5 kHz sample bytes for each passenger pitch the game can select, in BOTH
|
||||||
|
polarities. The decode is the exact algorithm stAudio.c used to run per
|
||||||
|
sample at play time; on the 65816 that took 2.4 s of CPU per second of
|
||||||
|
speech (three run bytes per sample, each a multiply) and stalled the ride
|
||||||
|
for half a second on every "HEY TAXI". A 1-bit intermediate format came
|
||||||
|
next, but expanding bits still needed a per-byte loop (and an asm copy of
|
||||||
|
it on the IIgs); with sample bytes the player is a block copy.
|
||||||
|
Polarity: an utterance is decoded from a +100 start. The C64 keeps the
|
||||||
|
output level running from one utterance into the next, so a phrase whose
|
||||||
|
first word ends on the low level plays its second word inverted. Both
|
||||||
|
versions are stored and the player picks by the running parity.
|
||||||
|
speech.bin (little-endian):
|
||||||
|
"STSP" u8 version(2) u8 pitchBase u8 pitchCount u8 count
|
||||||
|
u8 format (0 = signed +/-100, 1 = 0x80-biased for the IIgs raw stream)
|
||||||
|
u8 pad[3] u32 dataBytes
|
||||||
|
entries[pitchCount * count * 2]: u8 code, u8 parity, u16 samples,
|
||||||
|
u32 offset -- polarity is the innermost index (0 = as decoded,
|
||||||
|
1 = inverted). parity = 1 when the utterance ends on the
|
||||||
|
opposite level from the one it started on.
|
||||||
|
data: sample bytes
|
||||||
|
usage: genSpeechPcm.py stSpeechData.c speech.bin [--biased]
|
||||||
|
"""
|
||||||
|
import re
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
|
||||||
|
SID_HZ = 1022727 # ST_SID_HZ_NUM
|
||||||
|
RATE = 5000 # ST_SPEECH_RATE
|
||||||
|
CYC_PER_SAMPLE = SID_HZ // RATE
|
||||||
|
SYNC_CYCLES = SID_HZ // 60 // 8
|
||||||
|
CYCLE_TAIL = 33
|
||||||
|
SYNC = 0xFE
|
||||||
|
END = 0xFF
|
||||||
|
PITCH_BASE = 2 # stFare.c: stAudioSpeech(rng(3) + 2)
|
||||||
|
PITCH_COUNT = 3
|
||||||
|
|
||||||
|
|
||||||
|
def parseSource(path):
|
||||||
|
text = open(path).read()
|
||||||
|
idx = re.search(r"kStSpeechIndex\[[^\]]*\]\s*=\s*\{(.*?)\};", text, re.S).group(1)
|
||||||
|
entries = [(int(c, 16), int(o), int(n)) for c, o, n in
|
||||||
|
re.findall(r"\{\s*0x([0-9A-Fa-f]+)u,\s*(\d+)u,\s*(\d+)u\s*\}", idx)]
|
||||||
|
rle = re.search(r"kStSpeechRle\[[^\]]*\]\s*=\s*\{(.*?)\};", text, re.S).group(1)
|
||||||
|
data = bytes(int(x, 16) for x in re.findall(r"0x([0-9A-Fa-f]{2})", rle))
|
||||||
|
return entries, data
|
||||||
|
|
||||||
|
|
||||||
|
def decode(rle, perOn):
|
||||||
|
"""speechDecode() for one utterance from a +100 start, remain 0."""
|
||||||
|
remain = 0
|
||||||
|
level = 100
|
||||||
|
out = []
|
||||||
|
i = 0
|
||||||
|
n = len(rle)
|
||||||
|
while True:
|
||||||
|
while remain <= 0:
|
||||||
|
if i >= n:
|
||||||
|
return out, level
|
||||||
|
b = rle[i]
|
||||||
|
i += 1
|
||||||
|
if b == END:
|
||||||
|
i = n
|
||||||
|
continue
|
||||||
|
if b == SYNC:
|
||||||
|
remain += SYNC_CYCLES
|
||||||
|
continue
|
||||||
|
remain += perOn * b + CYCLE_TAIL
|
||||||
|
level = -100 if level > 0 else 100
|
||||||
|
out.append(level)
|
||||||
|
remain -= CYC_PER_SAMPLE
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = [a for a in sys.argv[1:] if not a.startswith("--")]
|
||||||
|
biased = "--biased" in sys.argv[1:]
|
||||||
|
src, dst = args[0], args[1]
|
||||||
|
entries, data = parseSource(src)
|
||||||
|
records = []
|
||||||
|
blob = bytearray()
|
||||||
|
for p in range(PITCH_COUNT):
|
||||||
|
perOn = 5 * (PITCH_BASE + p) + 12
|
||||||
|
for code, off, length in entries:
|
||||||
|
samples, level = decode(data[off:off + length], perOn)
|
||||||
|
parity = 1 if level < 0 else 0
|
||||||
|
for pol in range(2):
|
||||||
|
pcm = bytearray()
|
||||||
|
for s in samples:
|
||||||
|
v = -s if pol else s
|
||||||
|
pcm.append((v + 0x80) & 0xFF if biased else v & 0xFF)
|
||||||
|
records.append((code, parity, len(samples), len(blob)))
|
||||||
|
blob += pcm
|
||||||
|
with open(dst, "wb") as f:
|
||||||
|
f.write(b"STSP" + struct.pack("<BBBBBBBBI", 2, PITCH_BASE, PITCH_COUNT, len(entries), 1 if biased else 0, 0, 0, 0, len(blob)))
|
||||||
|
for code, parity, n, off in records:
|
||||||
|
f.write(struct.pack("<BBHI", code, parity, n, off))
|
||||||
|
f.write(blob)
|
||||||
|
total = sum(n for _, _, n, _ in records)
|
||||||
|
print(f"{dst}: {len(entries)} utterances x {PITCH_COUNT} pitches x 2 polarities, {total} samples, {len(blob)} data bytes, {'biased' if biased else 'signed'}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
57
examples/spacetaxi/assets/sidCapture.py
Normal file
57
examples/spacetaxi/assets/sidCapture.py
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Run the C64 Space Taxi music player and log every SID write per frame.
|
||||||
|
|
||||||
|
The player lives in the TITLE memory image at $CB00-$CFFF (its own relocated
|
||||||
|
copy; the $7500 copy is gameplay-side). Rather than hand-decode its byte
|
||||||
|
code, drive the real routines under cpu6502.py -- the same trick that
|
||||||
|
extracted the speech:
|
||||||
|
$CB00 select song ($CB01 = song number) and init (hooks the IRQ vector)
|
||||||
|
$CF52 the IRQ body: first frame calls start ($CC28), then ticks the three
|
||||||
|
voices via $CC6C, then JMPs to the KERNAL IRQ ($EA31) which is what
|
||||||
|
advances the jiffy clock $A2. We poke an RTS at $EA31 and bump $A2
|
||||||
|
ourselves, once per frame.
|
||||||
|
A song is over when all three voice stream pointers' high bytes are zero
|
||||||
|
(that is exactly what the game's play-and-wait loop at $44D5 spins on).
|
||||||
|
"""
|
||||||
|
import json, sys
|
||||||
|
sys.path.insert(0, "/home/scott/claude/joeylib/stuff/spacetaxi")
|
||||||
|
from cpu6502 import Cpu6502
|
||||||
|
|
||||||
|
RAM = bytearray(open(sys.argv[1], "rb").read()[:65536])
|
||||||
|
OUT = sys.argv[2]
|
||||||
|
MAX_FRAMES = 4000
|
||||||
|
|
||||||
|
|
||||||
|
class SidTrap(Cpu6502):
|
||||||
|
def __init__(self, ram):
|
||||||
|
super().__init__(ram)
|
||||||
|
self.log = []
|
||||||
|
self.frame = 0
|
||||||
|
|
||||||
|
def write8(self, addr, val):
|
||||||
|
a = addr & 0xFFFF
|
||||||
|
if 0xD400 <= a <= 0xD418:
|
||||||
|
self.log.append((self.frame, a - 0xD400, val & 0xFF))
|
||||||
|
super().write8(addr, val)
|
||||||
|
|
||||||
|
|
||||||
|
songs = {}
|
||||||
|
for song in range(0, 9):
|
||||||
|
ram = bytearray(RAM)
|
||||||
|
ram[0xEA31] = 0x60 # KERNAL IRQ tail -> RTS
|
||||||
|
cpu = SidTrap(ram)
|
||||||
|
cpu.call(0xCC00) # full reset first
|
||||||
|
ram[0xCB01] = song
|
||||||
|
cpu.call(0xCB00) # select + arm
|
||||||
|
for frame in range(MAX_FRAMES):
|
||||||
|
cpu.frame = frame
|
||||||
|
ram[0xA2] = (ram[0xA2] + 1) & 0xFF
|
||||||
|
cpu.call(0xCF52)
|
||||||
|
if ram[0xCB81] == 0 and ram[0xCB88] == 0 and ram[0xCB8F] == 0:
|
||||||
|
break
|
||||||
|
songs[song] = {"frames": frame + 1, "writes": cpu.log,
|
||||||
|
"ptr": ram[0xCB40 + song * 2] | (ram[0xCB41 + song * 2] << 8)}
|
||||||
|
print(f"song {song}: ptr=${songs[song]['ptr']:04X} frames={frame+1} "
|
||||||
|
f"({(frame+1)/50:.1f}s PAL) sidWrites={len(cpu.log)}")
|
||||||
|
|
||||||
|
json.dump(songs, open(OUT, "w"))
|
||||||
111
examples/spacetaxi/assets/sidToSong.py
Normal file
111
examples/spacetaxi/assets/sidToSong.py
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""SID register log (sidCapture.py) -> JoeyLib .song text for songbake.
|
||||||
|
|
||||||
|
One row per PAL frame (tickms 20). Per voice, a note-on is a gate rising
|
||||||
|
edge or a pitch change while gated; gate falling is 'off'. SID frequency ->
|
||||||
|
Hz exactly (@Hz cells), so no equal-temperament rounding. Attenuation comes
|
||||||
|
from the sustain nibble. A voice whose waveform is NOISE goes to the noise
|
||||||
|
column instead of a tone column, with a pitch scaled from its frequency.
|
||||||
|
Loop detection: the player is deterministic, so once the (frame-relative)
|
||||||
|
event rows repeat with a fixed period, that period is the loop.
|
||||||
|
"""
|
||||||
|
import json, sys
|
||||||
|
|
||||||
|
PAL_CLOCK = 985248.0
|
||||||
|
songs = json.load(open(sys.argv[1]))
|
||||||
|
outDir = sys.argv[2]
|
||||||
|
|
||||||
|
|
||||||
|
def sidHz(lo, hi):
|
||||||
|
return ((hi << 8) | lo) * PAL_CLOCK / 16777216.0
|
||||||
|
|
||||||
|
|
||||||
|
def rowsFor(writes, nframes):
|
||||||
|
# replay register writes frame by frame, producing per-frame voice state
|
||||||
|
regs = [0] * 0x19
|
||||||
|
state = []
|
||||||
|
byFrame = {}
|
||||||
|
for f, r, v in writes:
|
||||||
|
byFrame.setdefault(f, []).append((r, v))
|
||||||
|
for f in range(nframes):
|
||||||
|
for r, v in byFrame.get(f, []):
|
||||||
|
regs[r] = v
|
||||||
|
vs = []
|
||||||
|
for vc in range(3):
|
||||||
|
b = vc * 7
|
||||||
|
vs.append(dict(hz=sidHz(regs[b], regs[b + 1]), gate=regs[b + 4] & 1,
|
||||||
|
wave=regs[b + 4] & 0xF0, sus=regs[b + 6] >> 4))
|
||||||
|
state.append(vs)
|
||||||
|
return state
|
||||||
|
|
||||||
|
|
||||||
|
def atten(sus):
|
||||||
|
# SID sustain 0..15 -> AGI attenuation 14..0 (0 loudest)
|
||||||
|
return max(0, min(14, round(14 - sus * 14 / 15)))
|
||||||
|
|
||||||
|
|
||||||
|
def noisePitch(hz):
|
||||||
|
# brighter hiss for higher SID "frequency"; 0..31, 0 = brightest
|
||||||
|
p = int(31 - min(31, hz / 400.0))
|
||||||
|
return max(0, min(31, p))
|
||||||
|
|
||||||
|
|
||||||
|
def toRows(state):
|
||||||
|
rows = []
|
||||||
|
prev = [dict(hz=None, gate=0, wave=0, sus=0) for _ in range(3)]
|
||||||
|
for vs in state:
|
||||||
|
tone = ["---", "---", "---"]
|
||||||
|
noise = "---"
|
||||||
|
for vc in range(3):
|
||||||
|
cur, was = vs[vc], prev[vc]
|
||||||
|
isNoise = (cur["wave"] & 0x80) != 0
|
||||||
|
on = cur["gate"] and (not was["gate"] or abs(cur["hz"] - was["hz"]) > 0.5
|
||||||
|
or cur["wave"] != was["wave"])
|
||||||
|
off = was["gate"] and not cur["gate"]
|
||||||
|
if on:
|
||||||
|
if isNoise:
|
||||||
|
noise = f"N{noisePitch(cur['hz'])}:{atten(cur['sus'])}"
|
||||||
|
else:
|
||||||
|
hz = max(1, min(65535, int(round(cur["hz"]))))
|
||||||
|
tone[vc] = f"@{hz}:{atten(cur['sus'])}"
|
||||||
|
elif off:
|
||||||
|
if (was["wave"] & 0x80):
|
||||||
|
noise = "off"
|
||||||
|
else:
|
||||||
|
tone[vc] = "off"
|
||||||
|
prev[vc] = dict(cur)
|
||||||
|
rows.append((tone, noise))
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
|
def findLoop(rows):
|
||||||
|
# smallest (offset, period) with rows[o+k] == rows[o+p+k] for the tail
|
||||||
|
n = len(rows)
|
||||||
|
for p in range(8, n // 2):
|
||||||
|
for o in range(0, n - 2 * p):
|
||||||
|
if rows[o:o + p] == rows[o + p:o + 2 * p] and rows[o:n - p] == rows[o + p:n]:
|
||||||
|
return o, p
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
for num, s in sorted(songs.items(), key=lambda kv: int(kv[0])):
|
||||||
|
num = int(num)
|
||||||
|
st = rowsFor(s["writes"], s["frames"])
|
||||||
|
rows = toRows(st)
|
||||||
|
loop = findLoop(rows) if s["frames"] >= 4000 else None
|
||||||
|
if loop:
|
||||||
|
o, p = loop
|
||||||
|
rows = rows[:o + p]
|
||||||
|
lines = ["; Space Taxi (C64) song %d, extracted by running the game's own" % num,
|
||||||
|
"; player ($CB00/$CF52) under cpu6502.py and logging SID writes.",
|
||||||
|
"; One row = one PAL frame.", "tickms 20"]
|
||||||
|
for i, (tone, noise) in enumerate(rows):
|
||||||
|
if loop and i == loop[0]:
|
||||||
|
lines.append("loop")
|
||||||
|
cells = tone + ([noise] if noise != "---" or True else [])
|
||||||
|
lines.append(" ".join(cells))
|
||||||
|
open(f"{outDir}/song{num}.song", "w").write("\n".join(lines) + "\n")
|
||||||
|
events = sum(1 for t, n in rows for c in t + [n] if c != "---")
|
||||||
|
waves = sorted({hex(v["wave"]) for vs in st for v in vs if v["gate"]})
|
||||||
|
print(f"song {num}: {len(rows)} rows, {events} events, waveforms {waves}"
|
||||||
|
+ (f", LOOP at row {loop[0]} period {loop[1]}" if loop else ""))
|
||||||
94
examples/spacetaxi/assets/songs/song0.song
Normal file
94
examples/spacetaxi/assets/songs/song0.song
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
; Space Taxi (C64) song 0, extracted by running the game's own
|
||||||
|
; player ($CB00/$CF52) under cpu6502.py and logging SID writes.
|
||||||
|
; One row = one PAL frame.
|
||||||
|
tickms 20
|
||||||
|
--- --- --- ---
|
||||||
|
@336:2 @424:2 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@378:2 @424:2 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@336:2 @424:2 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@378:2 @476:2 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@318:2 @252:2 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @476:2 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@283:2 @252:2 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @476:2 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@252:2 @252:2 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
94
examples/spacetaxi/assets/songs/song1.song
Normal file
94
examples/spacetaxi/assets/songs/song1.song
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
; Space Taxi (C64) song 1, extracted by running the game's own
|
||||||
|
; player ($CB00/$CF52) under cpu6502.py and logging SID writes.
|
||||||
|
; One row = one PAL frame.
|
||||||
|
tickms 20
|
||||||
|
--- --- --- ---
|
||||||
|
@252:7 @159:7 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
@283:7 @168:7 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
@252:7 @189:7 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
@283:7 @212:7 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
@318:7 @189:7 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
@336:7 @168:7 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
@318:7 @159:7 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
@336:7 @168:7 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
@378:7 @189:7 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
86
examples/spacetaxi/assets/songs/song2.song
Normal file
86
examples/spacetaxi/assets/songs/song2.song
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
; Space Taxi (C64) song 2, extracted by running the game's own
|
||||||
|
; player ($CB00/$CF52) under cpu6502.py and logging SID writes.
|
||||||
|
; One row = one PAL frame.
|
||||||
|
tickms 20
|
||||||
|
--- --- --- ---
|
||||||
|
@126:0 @159:0 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
@126:0 @168:0 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
@141:0 @189:0 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
@159:0 @159:0 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
@159:0 @168:0 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
@168:0 @189:0 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
@168:0 @159:0 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
@189:0 @168:0 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- @159:0 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @126:0 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
86
examples/spacetaxi/assets/songs/song3.song
Normal file
86
examples/spacetaxi/assets/songs/song3.song
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
; Space Taxi (C64) song 3, extracted by running the game's own
|
||||||
|
; player ($CB00/$CF52) under cpu6502.py and logging SID writes.
|
||||||
|
; One row = one PAL frame.
|
||||||
|
tickms 20
|
||||||
|
--- --- --- ---
|
||||||
|
@252:0 @336:2 @336:2 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
@283:0 --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@318:0 @336:2 @336:2 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
@252:0 --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@283:0 @336:2 @336:2 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
@252:0 --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@283:0 @336:2 @336:2 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
@476:0 --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @318:2 @378:2 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @336:2 @378:2 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @283:2 @252:2 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @252:2 @476:2 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
126
examples/spacetaxi/assets/songs/song4.song
Normal file
126
examples/spacetaxi/assets/songs/song4.song
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
; Space Taxi (C64) song 4, extracted by running the game's own
|
||||||
|
; player ($CB00/$CF52) under cpu6502.py and logging SID writes.
|
||||||
|
; One row = one PAL frame.
|
||||||
|
tickms 20
|
||||||
|
--- --- --- ---
|
||||||
|
@504:5 @449:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@504:5 @449:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
@673:5 @504:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@504:5 @424:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@673:5 @504:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
@848:5 @673:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @635:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @673:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- --- ---
|
||||||
736
examples/spacetaxi/assets/songs/song5.song
Normal file
736
examples/spacetaxi/assets/songs/song5.song
Normal file
|
|
@ -0,0 +1,736 @@
|
||||||
|
; Space Taxi (C64) song 5, extracted by running the game's own
|
||||||
|
; player ($CB00/$CF52) under cpu6502.py and logging SID writes.
|
||||||
|
; One row = one PAL frame.
|
||||||
|
tickms 20
|
||||||
|
--- --- --- ---
|
||||||
|
@504:2 @159:2 @106:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @212:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @159:2 @106:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @212:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @159:2 @106:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @212:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @159:2 @106:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- @212:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@504:2 @159:2 @106:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @212:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @159:2 @106:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @212:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @159:2 @106:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @212:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @159:2 @106:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- @212:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@504:2 @159:2 @106:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @212:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @159:2 @106:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @212:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @159:2 @106:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- @212:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
@566:2 @141:2 @100:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- @200:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@566:2 @141:2 @100:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @200:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @141:2 @100:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @200:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @141:2 @100:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @200:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @141:2 @100:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- @200:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@635:2 @504:2 @94:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- @189:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @94:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @189:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@673:2 @566:2 @84:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- @168:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @84:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @168:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@755:2 @635:2 @79:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- @159:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @79:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @159:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@800:2 @635:2 @71:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- @141:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @71:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @141:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@848:2 @635:2 @67:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @133:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @67:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @133:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @67:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @133:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @67:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @133:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@424:2 @318:2 @67:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- @133:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@848:2 @336:2 @252:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
@848:2 @336:2 @252:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
@848:2 @336:2 @252:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
@755:2 @318:2 @225:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
@755:2 @318:2 @225:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
@673:2 @283:2 @212:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
@635:2 @252:2 @189:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@566:2 @283:2 @168:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@755:2 @318:2 @126:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @189:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @126:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @189:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @126:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @189:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @126:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @189:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @126:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- @189:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@635:2 --- @126:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- @189:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@504:2 --- @126:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- @189:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@635:2 --- @126:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- @189:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
@755:2 @283:2 @119:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @189:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @119:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- @189:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@848:2 --- @119:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @189:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @119:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- @189:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@755:2 --- @119:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- @189:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@673:2 --- @119:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- @189:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@635:2 --- @119:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- @189:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@566:2 --- @119:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- @189:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
@504:2 @318:2 @126:10 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
840
examples/spacetaxi/assets/songs/song6.song
Normal file
840
examples/spacetaxi/assets/songs/song6.song
Normal file
|
|
@ -0,0 +1,840 @@
|
||||||
|
; Space Taxi (C64) song 6, extracted by running the game's own
|
||||||
|
; player ($CB00/$CF52) under cpu6502.py and logging SID writes.
|
||||||
|
; One row = one PAL frame.
|
||||||
|
tickms 20
|
||||||
|
--- --- --- ---
|
||||||
|
--- @378:1 @504:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @378:1 @504:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @378:1 @504:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @378:1 @504:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@94:0 --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@126:0 --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@159:0 --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@189:0 @336:1 @449:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @300:1 @400:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @336:1 @449:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @378:1 @504:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @378:1 @504:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off @378:1 @504:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @378:1 @504:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@212:0 --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@189:0 --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@159:0 --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @378:1 @504:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @378:1 @504:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@141:0 @424:1 @566:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @378:1 @504:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @424:1 @566:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@119:0 @476:1 @635:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @476:1 @635:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @476:1 @635:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @476:1 @635:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@59:0 --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @476:1 @635:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @476:1 @635:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @476:1 @635:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @424:1 @566:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @476:1 @635:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@126:0 @504:1 @673:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @504:1 @673:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @504:1 @673:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @504:1 @673:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@63:0 --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @504:1 @673:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off @504:1 @673:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@126:0 @566:1 @755:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @504:1 @673:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @566:1 @755:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@141:0 @635:1 @848:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@71:0 --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @635:1 @848:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off @635:1 @848:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@141:0 @566:1 @848:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @566:1 @848:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @566:1 @848:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@159:0 @534:1 @848:1 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
852
examples/spacetaxi/assets/songs/song7.song
Normal file
852
examples/spacetaxi/assets/songs/song7.song
Normal file
|
|
@ -0,0 +1,852 @@
|
||||||
|
; Space Taxi (C64) song 7, extracted by running the game's own
|
||||||
|
; player ($CB00/$CF52) under cpu6502.py and logging SID writes.
|
||||||
|
; One row = one PAL frame.
|
||||||
|
tickms 20
|
||||||
|
--- --- --- ---
|
||||||
|
@378:0 @1270:5 @126:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1346:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1270:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- @1132:5 @119:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1270:5 off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1346:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@252:0 @1270:5 @106:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1346:5 off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1510:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@283:0 @1008:5 @94:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@318:0 --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@336:0 --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1008:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@378:0 --- @126:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @952:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @848:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @755:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1132:5 @119:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1270:5 off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1346:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1510:5 @126:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1510:5 off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1510:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@424:0 @1132:5 @94:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
@378:0 --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@336:0 @1132:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@378:0 @1270:5 @126:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@336:0 @952:5 @119:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
@318:0 --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@283:0 @952:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@318:0 @1008:5 @106:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@283:0 @755:5 @94:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
@252:0 --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@238:0 @755:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
@252:0 @1008:5 @126:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @952:5 off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@318:0 @1008:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
@189:0 @1132:5 @119:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1008:5 off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1132:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1270:5 @106:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1132:5 off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1270:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@238:0 @1346:5 @94:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@252:0 @1270:5 off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@283:0 @1346:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
@252:0 @1270:5 @126:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1270:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@238:0 @1510:5 @119:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@252:0 --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@283:0 --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@318:0 --- @106:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@283:0 @1510:5 @94:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
@283:0 --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@378:0 @1132:5 @141:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1008:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @952:5 @119:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1008:5 off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1132:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @755:5 @106:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@378:0 @1132:5 --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@378:0 --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@378:0 @1199:5 @94:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@378:0 --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
@378:0 --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
@378:0 @1270:5 @126:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1510:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1270:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1510:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1270:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @94:7 ---
|
||||||
|
--- @1510:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1270:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1510:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1270:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1510:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @126:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1270:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1510:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1270:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1510:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1270:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @94:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1510:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1270:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1510:5 --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- @1270:5 --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- @126:7 ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- off --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
off --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- off ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
|
--- --- --- ---
|
||||||
1508
examples/spacetaxi/assets/songs/song8.song
Normal file
1508
examples/spacetaxi/assets/songs/song8.song
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -19,27 +19,20 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "spacetaxi.h"
|
#include <joey/file.h>
|
||||||
#include "stDemoStreams.h"
|
|
||||||
|
|
||||||
// Diagnostics. The logger drags in vsnprintf (with newlib that pulls the
|
#include "spacetaxi.h"
|
||||||
// whole formatted-output and float-conversion family, ~45 KB) plus a
|
#include "stSongData.h"
|
||||||
// multi-KB ring buffer, and the game has no need of either. That is
|
#include "stDemoStreams.h"
|
||||||
// unaffordable on the IIgs, whose bank-0 BSS budget cannot spare the
|
|
||||||
// ring, and on the X68000, which ships on a Human68k floppy with about
|
|
||||||
// 330 KB of usable space for the binary AND its levels. Both compile it
|
|
||||||
// out; the ports with room keep it.
|
|
||||||
#if defined(__W65816__) || defined(JOEYLIB_PLATFORM_X68000)
|
|
||||||
#define ST_LOG_RESET() ((void)0)
|
|
||||||
#define ST_LOG(...) ((void)0)
|
|
||||||
#else
|
|
||||||
#define ST_LOG_RESET() jlLogReset()
|
|
||||||
#define ST_LOG(...) jlLogF(__VA_ARGS__)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define ST_TICK_HZ 30u
|
#define ST_TICK_HZ 30u
|
||||||
#define ST_MAX_CATCHUP_TICKS 4u
|
#define ST_MAX_CATCHUP_TICKS 4u
|
||||||
#define ST_LEVEL_COUNT 24u
|
// Screens A..X, then screen Y -- the 25th, " MYSTERY SCREEN !!! " -- which
|
||||||
|
// $5167 loads once the 24-hour shifts have cleared all of A..X. Files are
|
||||||
|
// levels/level01.dat .. level25.dat, so Y is index 24.
|
||||||
|
#define ST_LEVEL_COUNT 24u // A..X, the screens a shift sweeps
|
||||||
|
#define ST_SCREEN_Y 24u // the 25th screen ($5171 LDA #$59)
|
||||||
|
#define ST_LEVEL_FILES 25u // level01.dat .. level25.dat
|
||||||
// "GET READY" shows over the old screen while the C64 cycles the
|
// "GET READY" shows over the old screen while the C64 cycles the
|
||||||
// leaving-passenger sprites (three busy waits, about half a second).
|
// leaving-passenger sprites (three busy waits, about half a second).
|
||||||
#define ST_GET_READY_TICKS 15u
|
#define ST_GET_READY_TICKS 15u
|
||||||
|
|
@ -48,6 +41,18 @@
|
||||||
// Menu joystick repeat delay ($4101 with X = $82).
|
// Menu joystick repeat delay ($4101 with X = $82).
|
||||||
#define ST_MENU_REPEAT_TICKS 8u
|
#define ST_MENU_REPEAT_TICKS 8u
|
||||||
|
|
||||||
|
// The immortal cabbies ($4A89 image, $4E35 screen).
|
||||||
|
#define ST_HS_ENTRIES 10u
|
||||||
|
#define ST_HS_NAME_CHARS 15u
|
||||||
|
#define ST_HS_ENTRY_BYTES 24u // score[7] 0 name[15] shift
|
||||||
|
#define ST_HS_POINT_AT 4u // "dddd.dd"
|
||||||
|
#define ST_HS_GREET_DIGIT_AT 23u // "CONGRATULATIONS CABBIE n,"
|
||||||
|
#define ST_HS_NAME_COL 12u // $0664: row 15, column 12
|
||||||
|
#define ST_HS_NAME_ROW 15u
|
||||||
|
#define ST_HS_CURSOR '%' // $4F4D flips it against blank
|
||||||
|
#define ST_HS_BLINK_TICKS 3u // $4101 x $1E between flips, ~55 ms
|
||||||
|
#define ST_HS_SAVE_NAME "IMMORTAL"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
ST_STATE_TITLE = 0,
|
ST_STATE_TITLE = 0,
|
||||||
ST_STATE_HIGHSCORES,
|
ST_STATE_HIGHSCORES,
|
||||||
|
|
@ -57,7 +62,9 @@ typedef enum {
|
||||||
ST_STATE_GET_READY,
|
ST_STATE_GET_READY,
|
||||||
ST_STATE_INTRO,
|
ST_STATE_INTRO,
|
||||||
ST_STATE_PLAYING,
|
ST_STATE_PLAYING,
|
||||||
ST_STATE_GAME_OVER
|
ST_STATE_GAME_OVER,
|
||||||
|
ST_STATE_JINGLE, // a $44CF tune sounding; sim frozen until it ends
|
||||||
|
ST_STATE_NAME_ENTRY // $4E35: a top-ten cabbie types a name
|
||||||
} StStateE;
|
} StStateE;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -73,8 +80,39 @@ typedef struct {
|
||||||
bool fireArmed; // $445D: FIRE must be released first
|
bool fireArmed; // $445D: FIRE must be released first
|
||||||
uint8_t lastFrame;
|
uint8_t lastFrame;
|
||||||
uint16_t paceAcc;
|
uint16_t paceAcc;
|
||||||
|
StStateE afterJingle; // where ST_STATE_JINGLE returns to
|
||||||
|
bool gameOverTune; // $5C24: song 5 started after the wait
|
||||||
|
uint8_t hsSlot; // $4D51 table slot being named
|
||||||
|
uint8_t hsLen; // $4D2C characters typed
|
||||||
|
uint8_t hsCursor; // $4F4D the blinking cursor's current glyph
|
||||||
|
uint8_t hsBlinkTicks; // ticks toward the next cursor flip
|
||||||
|
bool paused; // $4FCB: SHIFT is holding the game
|
||||||
} StGameT;
|
} StGameT;
|
||||||
|
|
||||||
|
// The immortal cabbies ($4A89): ten entries of a right-aligned "dddd.dd"
|
||||||
|
// score, a name of up to fifteen letters and the shift it was earned on.
|
||||||
|
// The C64 keeps this table on its own disk (the $9948 IEC routines load
|
||||||
|
// it at boot and save it after every entry); the port keeps the same
|
||||||
|
// 240-byte image in a save file. Shipped contents as the dump has them.
|
||||||
|
typedef struct {
|
||||||
|
uint8_t score[ST_NUMBER_CHARS + 1u]; // ASCII, NUL-terminated
|
||||||
|
uint8_t name[ST_HS_NAME_CHARS + 1u]; // ASCII, space-padded, NUL-terminated
|
||||||
|
uint8_t shift; // 1..5
|
||||||
|
} StHighScoreT;
|
||||||
|
|
||||||
|
static StHighScoreT gHighScores[ST_HS_ENTRIES] = {
|
||||||
|
{ "3877.56", "MICHAEL PLATE ", 4u },
|
||||||
|
{ "1809.51", "MICHAEL PLATE ", 2u },
|
||||||
|
{ "1179.87", "ANDREAS PLATE ", 4u },
|
||||||
|
{ " 892.65", "MICHAEL PLATE ", 1u },
|
||||||
|
{ " 685.37", "ANDREAS PLATE ", 1u },
|
||||||
|
{ " 629.45", "MICHAEL PLATE ", 4u },
|
||||||
|
{ " 569.50", "MICHAEL PLATE ", 1u },
|
||||||
|
{ " 504.97", "ANDREAS PLATE ", 2u },
|
||||||
|
{ " 498.77", "ANDREAS PLATE ", 5u },
|
||||||
|
{ " 490.85", "ANDREAS PLATE ", 2u },
|
||||||
|
};
|
||||||
|
|
||||||
// Menu texts ($5347..$545D) in screen codes.
|
// Menu texts ($5347..$545D) in screen codes.
|
||||||
static const uint8_t kTextGameVariations[] = "GAME VARIATIONS";
|
static const uint8_t kTextGameVariations[] = "GAME VARIATIONS";
|
||||||
static const uint8_t kTextCabbies[] = "SELECT NUMBER OF CABBIES:";
|
static const uint8_t kTextCabbies[] = "SELECT NUMBER OF CABBIES:";
|
||||||
|
|
@ -94,18 +132,14 @@ static const uint8_t kTextGetReady[] = " GET READY FOR A RIDE ";
|
||||||
static const uint8_t kTextOnTheTaxi[] = " ON THE SPACE TAXI! ";
|
static const uint8_t kTextOnTheTaxi[] = " ON THE SPACE TAXI! ";
|
||||||
static const uint8_t kTextGameOver[] = " GAME OVER! ";
|
static const uint8_t kTextGameOver[] = " GAME OVER! ";
|
||||||
static const uint8_t kTextBlank12[] = " ";
|
static const uint8_t kTextBlank12[] = " ";
|
||||||
// $4C1F / $556A screens.
|
// $4C1F (the table) and $4E35 (the name entry) screens.
|
||||||
static const uint8_t kTextImmortal[] = "THE IMMORTAL CABBIES";
|
static const uint8_t kTextImmortal[] = "THE IMMORTAL CABBIES";
|
||||||
static const uint8_t *kHighScores[8] = {
|
static const uint8_t kTextShift[] = "SHIFT";
|
||||||
(const uint8_t *)"3877.56 MICHAEL PLATE",
|
static const uint8_t kTextReturnTitle[] = "PRESS FIRE TO RETURN TO TITLE PAGE";
|
||||||
(const uint8_t *)"1809.51 MICHAEL PLATE",
|
static const uint8_t kTextCongrats[] = "CONGRATULATIONS CABBIE ,";
|
||||||
(const uint8_t *)"1179.87 ANDREAS PLATE",
|
static const uint8_t kTextRanks[] = "YOUR SCORE RANKS YOU AS ONE OF";
|
||||||
(const uint8_t *)" 892.65 MICHAEL PLATE",
|
static const uint8_t kTextTopTen[] = "THE TOP TEN CABBIES OF ALL TIME";
|
||||||
(const uint8_t *)" 685.37 ANDREAS PLATE",
|
static const uint8_t kTextEnterName[] = "ENTER YOUR NAME BELOW";
|
||||||
(const uint8_t *)" 629.45 MICHAEL PLATE",
|
|
||||||
(const uint8_t *)" 569.50 MICHAEL PLATE",
|
|
||||||
(const uint8_t *)" 504.97 ANDREAS PLATE",
|
|
||||||
};
|
|
||||||
static const uint8_t *kAboutLines[7] = {
|
static const uint8_t *kAboutLines[7] = {
|
||||||
(const uint8_t *)"THIS PROGRAM DEVELOPED AND WRITTEN BY",
|
(const uint8_t *)"THIS PROGRAM DEVELOPED AND WRITTEN BY",
|
||||||
(const uint8_t *)"JOHN F. KUTCHER",
|
(const uint8_t *)"JOHN F. KUTCHER",
|
||||||
|
|
@ -117,8 +151,9 @@ static const uint8_t *kAboutLines[7] = {
|
||||||
};
|
};
|
||||||
static const uint8_t kTextReturn[] = "PRESS FIRE TO RETURN";
|
static const uint8_t kTextReturn[] = "PRESS FIRE TO RETURN";
|
||||||
|
|
||||||
static StGameT gGame;
|
static StGameT gGame;
|
||||||
static StSimT gSim;
|
static StSimT gSim;
|
||||||
|
static jlSurfaceT *gStage;
|
||||||
// One level buffer serves both gameplay and the title screen: the title
|
// One level buffer serves both gameplay and the title screen: the title
|
||||||
// is another scene, and gameplay never needs it at the same time, so it
|
// is another scene, and gameplay never needs it at the same time, so it
|
||||||
// is loaded into gLevel on each return to the title. (Keeping a second
|
// is loaded into gLevel on each return to the title. (Keeping a second
|
||||||
|
|
@ -126,17 +161,25 @@ static StSimT gSim;
|
||||||
static StLevelT gLevel;
|
static StLevelT gLevel;
|
||||||
|
|
||||||
|
|
||||||
static void blankScreen(StSimT *sim);
|
static void blankScreen(StSimT *sim, uint8_t border, uint8_t bg);
|
||||||
static void enterTitle(void);
|
static void enterTitle(void);
|
||||||
static void gameOverEnter(void);
|
static void gameOverEnter(void);
|
||||||
|
static void gameOverFinish(void);
|
||||||
|
static void highScoreDraw(void);
|
||||||
|
static uint8_t highScoreInsert(const uint8_t *score);
|
||||||
|
static void highScoreLoad(void);
|
||||||
|
static void highScoreSave(void);
|
||||||
static bool joyFire(void);
|
static bool joyFire(void);
|
||||||
static uint8_t joyMask(void);
|
static uint8_t joyMask(void);
|
||||||
static bool loadLevel(uint8_t index);
|
static bool loadLevel(uint8_t index);
|
||||||
static uint8_t nextLevelForShift(void);
|
static uint8_t nextLevelForShift(void);
|
||||||
static uint8_t paceTicks(void);
|
static uint8_t paceTicks(void);
|
||||||
|
static bool serviceJingle(void);
|
||||||
static void runCabbiesMenu(uint8_t ticks);
|
static void runCabbiesMenu(uint8_t ticks);
|
||||||
static void runDemoEnd(void);
|
static void runDemoEnd(void);
|
||||||
static void runIntro(uint8_t ticks);
|
static void runIntro(uint8_t ticks);
|
||||||
|
static void runNameEntry(uint8_t ticks);
|
||||||
|
static bool nameEntryStart(void);
|
||||||
static void runPlaying(uint8_t ticks);
|
static void runPlaying(uint8_t ticks);
|
||||||
static void runShiftMenu(uint8_t ticks);
|
static void runShiftMenu(uint8_t ticks);
|
||||||
static void runTitle(uint8_t ticks);
|
static void runTitle(uint8_t ticks);
|
||||||
|
|
@ -149,12 +192,12 @@ static void startNewScreen(void);
|
||||||
|
|
||||||
|
|
||||||
// $40CA -- spaces everywhere, black.
|
// $40CA -- spaces everywhere, black.
|
||||||
static void blankScreen(StSimT *sim) {
|
static void blankScreen(StSimT *sim, uint8_t border, uint8_t bg) {
|
||||||
memset(sim->screen, ST_CHAR_SPACE, ST_SCREEN_CELLS);
|
memset(sim->screen, ST_CHAR_SPACE, ST_SCREEN_CELLS);
|
||||||
memset(sim->color, 0, ST_SCREEN_CELLS);
|
memset(sim->color, 0, ST_SCREEN_CELLS);
|
||||||
stSimDirtyAll(sim);
|
stSimDirtyAll(sim);
|
||||||
sim->bgColor = 0u;
|
sim->bgColor = bg;
|
||||||
sim->borderColor = 0u;
|
sim->borderColor = border;
|
||||||
sim->frame.enableMask = 0u;
|
sim->frame.enableMask = 0u;
|
||||||
stRenderSceneChanged(sim);
|
stRenderSceneChanged(sim);
|
||||||
}
|
}
|
||||||
|
|
@ -166,16 +209,239 @@ static void enterTitle(void) {
|
||||||
if (!stLevelLoad(&gLevel, "levels/title.dat")) {
|
if (!stLevelLoad(&gLevel, "levels/title.dat")) {
|
||||||
ST_LOG("spacetaxi: ! title load FAILED");
|
ST_LOG("spacetaxi: ! title load FAILED");
|
||||||
}
|
}
|
||||||
|
stRenderLevelCels(gStage, 0); // the last level's cels go
|
||||||
stTitleEnter(&gSim, &gLevel);
|
stTitleEnter(&gSim, &gLevel);
|
||||||
stRenderSceneChanged(&gSim);
|
stRenderSceneChanged(&gSim);
|
||||||
stRenderTitleLogo(&gSim);
|
stRenderTitleLogo(&gSim);
|
||||||
stAudioNoise(false);
|
stAudioNoise(false);
|
||||||
stAudioSilence();
|
stAudioSilence();
|
||||||
|
jlMusicStop();
|
||||||
gGame.state = ST_STATE_TITLE;
|
gGame.state = ST_STATE_TITLE;
|
||||||
gGame.fireArmed = false;
|
gGame.fireArmed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// $6BD0 -- one more player done; everybody done -> title.
|
||||||
|
static void gameOverFinish(void) {
|
||||||
|
gSim.playersDone++;
|
||||||
|
if (gSim.playersDone == gSim.playerCount) {
|
||||||
|
enterTitle();
|
||||||
|
} else {
|
||||||
|
startNewScreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// $4C1F -- the immortal cabbies: ten rows of shift, "$", score and name,
|
||||||
|
// each entry drawn only while its last score digit is a digit (a blank
|
||||||
|
// row is an unused slot). Song 7 plays under it until FIRE ($4D04).
|
||||||
|
static void highScoreDraw(void) {
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
blankScreen(&gSim, 3u, 14u);
|
||||||
|
stSimDrawText(&gSim, 9u, 1u, kTextImmortal, 6u);
|
||||||
|
stSimDrawText(&gSim, 32u, 3u, kTextShift, 15u);
|
||||||
|
for (i = 0u; i < ST_HS_ENTRIES; i++) {
|
||||||
|
const StHighScoreT *e = &gHighScores[i];
|
||||||
|
uint8_t row = (uint8_t)(4u + i * 2u);
|
||||||
|
if (e->score[ST_NUMBER_CHARS - 1u] < '0') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
stSimPutChar(&gSim, 34u, row, (uint8_t)('0' + e->shift));
|
||||||
|
stSimPutColor(&gSim, 34u, row, 15u);
|
||||||
|
stSimPutChar(&gSim, 4u, row, '$');
|
||||||
|
stSimPutColor(&gSim, 4u, row, 1u);
|
||||||
|
stSimDrawText(&gSim, 5u, row, e->score, 1u);
|
||||||
|
stSimDrawText(&gSim, 15u, row, e->name, 1u);
|
||||||
|
}
|
||||||
|
stSimDrawText(&gSim, 3u, 24u, kTextReturnTitle, 0u);
|
||||||
|
stAudioMusic(ST_SONG_SCORES, false); // $4C29
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// $4D92 -- where a score belongs in the table, or ST_HS_ENTRIES when it
|
||||||
|
// does not make it. The table is only opened to scores of $50.00 and up
|
||||||
|
// (second character a digit, or third character 5 or more). The scores
|
||||||
|
// are right-aligned text, so a character compare orders them; a tie goes
|
||||||
|
// above the older entry. The new entry is opened with a blank name.
|
||||||
|
static uint8_t highScoreInsert(const uint8_t *score) {
|
||||||
|
uint8_t slot;
|
||||||
|
uint8_t k;
|
||||||
|
|
||||||
|
if (score[1] < '0' && score[2] < '5') {
|
||||||
|
return ST_HS_ENTRIES;
|
||||||
|
}
|
||||||
|
for (slot = 0u; slot < ST_HS_ENTRIES; slot++) {
|
||||||
|
const uint8_t *old = gHighScores[slot].score;
|
||||||
|
for (k = 0u; k < ST_NUMBER_CHARS; k++) {
|
||||||
|
if (score[k] != old[k]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (k == ST_NUMBER_CHARS || score[k] > old[k]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (slot == ST_HS_ENTRIES) {
|
||||||
|
return ST_HS_ENTRIES;
|
||||||
|
}
|
||||||
|
for (k = (uint8_t)(ST_HS_ENTRIES - 1u); k > slot; k--) {
|
||||||
|
gHighScores[k] = gHighScores[k - 1u];
|
||||||
|
}
|
||||||
|
memcpy(gHighScores[slot].score, score, ST_NUMBER_CHARS + 1u);
|
||||||
|
memset(gHighScores[slot].name, ' ', ST_HS_NAME_CHARS);
|
||||||
|
gHighScores[slot].name[ST_HS_NAME_CHARS] = 0u;
|
||||||
|
gHighScores[slot].shift = 0u;
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// $23E5 -> $9948 at boot: the table from disk, when there is one. The
|
||||||
|
// image is the C64's own: per entry score[7], 0, name[15], shift.
|
||||||
|
static void highScoreLoad(void) {
|
||||||
|
uint8_t image[ST_HS_ENTRIES * ST_HS_ENTRY_BYTES];
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
if (jlSaveRead(ST_HS_SAVE_NAME, image, sizeof(image)) != sizeof(image)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (i = 0u; i < ST_HS_ENTRIES; i++) {
|
||||||
|
const uint8_t *e = &image[i * ST_HS_ENTRY_BYTES];
|
||||||
|
if (e[ST_NUMBER_CHARS] != 0u) {
|
||||||
|
ST_LOG("spacetaxi: ! %s is not a score table", ST_HS_SAVE_NAME);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0u; i < ST_HS_ENTRIES; i++) {
|
||||||
|
const uint8_t *e = &image[i * ST_HS_ENTRY_BYTES];
|
||||||
|
memcpy(gHighScores[i].score, e, ST_NUMBER_CHARS);
|
||||||
|
gHighScores[i].score[ST_NUMBER_CHARS] = 0u;
|
||||||
|
memcpy(gHighScores[i].name, &e[ST_NUMBER_CHARS + 1u], ST_HS_NAME_CHARS);
|
||||||
|
gHighScores[i].name[ST_HS_NAME_CHARS] = 0u;
|
||||||
|
gHighScores[i].shift = e[ST_HS_ENTRY_BYTES - 1u];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// $4E11 -> $9948 after an entry: the table back to disk.
|
||||||
|
static void highScoreSave(void) {
|
||||||
|
uint8_t image[ST_HS_ENTRIES * ST_HS_ENTRY_BYTES];
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
for (i = 0u; i < ST_HS_ENTRIES; i++) {
|
||||||
|
uint8_t *e = &image[i * ST_HS_ENTRY_BYTES];
|
||||||
|
memcpy(e, gHighScores[i].score, ST_NUMBER_CHARS);
|
||||||
|
e[ST_NUMBER_CHARS] = 0u;
|
||||||
|
memcpy(&e[ST_NUMBER_CHARS + 1u], gHighScores[i].name, ST_HS_NAME_CHARS);
|
||||||
|
e[ST_HS_ENTRY_BYTES - 1u] = gHighScores[i].shift;
|
||||||
|
}
|
||||||
|
if (!jlSaveWrite(ST_HS_SAVE_NAME, image, sizeof(image))) {
|
||||||
|
ST_LOG("spacetaxi: ! could not save %s", ST_HS_SAVE_NAME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// $4D52 -- after a player's GAME OVER: their score as "dddd.dd" text, and
|
||||||
|
// if it ranks, the $4E35 screen: red border, grey field, the cabbie's
|
||||||
|
// number in the greeting, the score at the top, song 6, and a blinking
|
||||||
|
// cursor where the name goes. Returns false when the score did not rank
|
||||||
|
// and the game moves straight on.
|
||||||
|
static bool nameEntryStart(void) {
|
||||||
|
uint8_t score[ST_NUMBER_CHARS + 1u];
|
||||||
|
uint8_t greet[sizeof(kTextCongrats)];
|
||||||
|
uint8_t k;
|
||||||
|
|
||||||
|
for (k = 0u; k < ST_NUMBER_CHARS; k++) {
|
||||||
|
uint8_t ch = gSim.scoreBackup[gSim.player][k];
|
||||||
|
if (ch == ST_CHAR_BLANK) {
|
||||||
|
score[k] = ' ';
|
||||||
|
} else {
|
||||||
|
uint8_t d = (uint8_t)(ch - ST_CHAR_DIGIT_BASE); // $4345
|
||||||
|
score[k] = (uint8_t)('0' + ((d == 10u) ? 0u : d));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
score[ST_HS_POINT_AT] = '.';
|
||||||
|
score[ST_NUMBER_CHARS] = 0u;
|
||||||
|
gGame.hsSlot = highScoreInsert(score);
|
||||||
|
if (gGame.hsSlot == ST_HS_ENTRIES) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
blankScreen(&gSim, 2u, 15u);
|
||||||
|
while (jlInputGetChar() >= 0) { // $0277 = 0: nothing typed earlier counts
|
||||||
|
}
|
||||||
|
memcpy(greet, kTextCongrats, sizeof(greet));
|
||||||
|
greet[ST_HS_GREET_DIGIT_AT] = (uint8_t)('1' + gSim.player);
|
||||||
|
stSimDrawText(&gSim, 7u, 3u, greet, 0u);
|
||||||
|
stSimDrawText(&gSim, 5u, 5u, kTextRanks, 0u);
|
||||||
|
stSimDrawText(&gSim, 4u, 7u, kTextTopTen, 0u);
|
||||||
|
stSimDrawText(&gSim, 9u, 9u, kTextEnterName, 0u);
|
||||||
|
stSimDrawText(&gSim, 16u, 0u, score, 6u);
|
||||||
|
stSimPutChar(&gSim, 15u, 0u, '$');
|
||||||
|
stSimPutColor(&gSim, 15u, 0u, 6u);
|
||||||
|
stAudioMusic(ST_SONG_NAME_ENTRY, false); // $4EB6
|
||||||
|
gGame.hsLen = 0u;
|
||||||
|
gGame.hsCursor = ST_HS_CURSOR;
|
||||||
|
gGame.hsBlinkTicks = 0u;
|
||||||
|
gGame.state = ST_STATE_NAME_ENTRY;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// $4ED8 -- the entry loop: the cursor flips between "%" and blank, then
|
||||||
|
// the keyboard: letters, space, ".", "!" and "," go in (fifteen at most),
|
||||||
|
// DEL takes one back, RETURN keeps the name (a "*" when none was typed),
|
||||||
|
// stamps the shift, stops the tune and saves the table.
|
||||||
|
static void runNameEntry(uint8_t ticks) {
|
||||||
|
StHighScoreT *e = &gHighScores[gGame.hsSlot];
|
||||||
|
uint8_t col = (uint8_t)(ST_HS_NAME_COL + gGame.hsLen);
|
||||||
|
int c;
|
||||||
|
|
||||||
|
gGame.hsBlinkTicks = (uint8_t)(gGame.hsBlinkTicks + ticks);
|
||||||
|
if (gGame.hsBlinkTicks >= ST_HS_BLINK_TICKS) {
|
||||||
|
gGame.hsBlinkTicks = 0u;
|
||||||
|
gGame.hsCursor ^= ST_HS_CURSOR ^ ' ';
|
||||||
|
stSimPutChar(&gSim, col, ST_HS_NAME_ROW, gGame.hsCursor);
|
||||||
|
stSimPutColor(&gSim, col, ST_HS_NAME_ROW, 6u);
|
||||||
|
}
|
||||||
|
while ((c = jlInputGetChar()) >= 0) {
|
||||||
|
if (c >= 'a' && c <= 'z') {
|
||||||
|
c = c - 'a' + 'A';
|
||||||
|
}
|
||||||
|
if (c == JL_CHAR_RETURN) {
|
||||||
|
stSimPutChar(&gSim, col, ST_HS_NAME_ROW, ' ');
|
||||||
|
if (gGame.hsLen == 0u) {
|
||||||
|
e->name[0] = '*';
|
||||||
|
}
|
||||||
|
e->shift = gGame.variation;
|
||||||
|
jlMusicStop(); // $44E1
|
||||||
|
highScoreSave(); // $4E11
|
||||||
|
gameOverFinish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (c == JL_CHAR_BACKSPACE || c == JL_CHAR_DELETE) {
|
||||||
|
if (gGame.hsLen != 0u) {
|
||||||
|
stSimPutChar(&gSim, col, ST_HS_NAME_ROW, ' ');
|
||||||
|
gGame.hsLen--;
|
||||||
|
col--;
|
||||||
|
e->name[gGame.hsLen] = ' ';
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (c != ' ' && c != '.' && c != '!' && c != ',' && (c < 'A' || c > 'Z')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (gGame.hsLen == ST_HS_NAME_CHARS) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
e->name[gGame.hsLen] = (uint8_t)c;
|
||||||
|
stSimPutChar(&gSim, col, ST_HS_NAME_ROW, (uint8_t)c);
|
||||||
|
stSimPutColor(&gSim, col, ST_HS_NAME_ROW, 6u);
|
||||||
|
gGame.hsLen++;
|
||||||
|
col++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// $5FBB -- "GAME OVER!" over the play field, sprites hidden.
|
// $5FBB -- "GAME OVER!" over the play field, sprites hidden.
|
||||||
static void gameOverEnter(void) {
|
static void gameOverEnter(void) {
|
||||||
stSimDrawText(&gSim, 14u, 10u, kTextBlank12, 1u);
|
stSimDrawText(&gSim, 14u, 10u, kTextBlank12, 1u);
|
||||||
|
|
@ -184,8 +450,9 @@ static void gameOverEnter(void) {
|
||||||
gSim.frame.enableMask = 0u;
|
gSim.frame.enableMask = 0u;
|
||||||
stAudioNoise(false);
|
stAudioNoise(false);
|
||||||
stAudioSilence();
|
stAudioSilence();
|
||||||
gGame.waitTicks = ST_GAME_OVER_TICKS;
|
gGame.waitTicks = ST_GAME_OVER_TICKS;
|
||||||
gGame.state = ST_STATE_GAME_OVER;
|
gGame.gameOverTune = false;
|
||||||
|
gGame.state = ST_STATE_GAME_OVER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -222,7 +489,7 @@ static uint8_t joyMask(void) {
|
||||||
static bool loadLevel(uint8_t index) {
|
static bool loadLevel(uint8_t index) {
|
||||||
char path[32];
|
char path[32];
|
||||||
|
|
||||||
if (index >= ST_LEVEL_COUNT) {
|
if (index >= ST_LEVEL_FILES) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Hand-rolled "levels/levelNN.dat" (NN = index+1, 01..24) so the
|
// Hand-rolled "levels/levelNN.dat" (NN = index+1, 01..24) so the
|
||||||
|
|
@ -238,6 +505,7 @@ static bool loadLevel(uint8_t index) {
|
||||||
ST_LOG("spacetaxi: ! cannot load %s", path);
|
ST_LOG("spacetaxi: ! cannot load %s", path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
stRenderLevelCels(gStage, &gLevel);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -254,7 +522,13 @@ static uint8_t nextLevelForShift(void) {
|
||||||
}
|
}
|
||||||
return (uint8_t)((gGame.variation - 1u) * 8u + sim->levelState);
|
return (uint8_t)((gGame.variation - 1u) * 8u + sim->levelState);
|
||||||
}
|
}
|
||||||
if (sim->levelState >= ST_LEVEL_COUNT) {
|
// $5022/$5167: the 24-hour shifts play screen Y after A..X, and only
|
||||||
|
// then is the shift over -- which is also what lights the "finished"
|
||||||
|
// marker in the screens-completed field (levelState reaches 25).
|
||||||
|
if (sim->levelState == ST_LEVEL_COUNT) {
|
||||||
|
return ST_SCREEN_Y;
|
||||||
|
}
|
||||||
|
if (sim->levelState > ST_LEVEL_COUNT) {
|
||||||
return 0xFFu;
|
return 0xFFu;
|
||||||
}
|
}
|
||||||
if (gGame.variation == 4u) {
|
if (gGame.variation == 4u) {
|
||||||
|
|
@ -355,6 +629,7 @@ static void runIntro(uint8_t ticks) {
|
||||||
}
|
}
|
||||||
for (k = 0u; k < (uint8_t)(ticks * 2u); k++) {
|
for (k = 0u; k < (uint8_t)(ticks * 2u); k++) {
|
||||||
if (stIntroStep(&gSim)) {
|
if (stIntroStep(&gSim)) {
|
||||||
|
jlMusicStop(); // $4709
|
||||||
stSimEnterLevel(&gSim, &gLevel);
|
stSimEnterLevel(&gSim, &gLevel);
|
||||||
stRenderSceneChanged(&gSim);
|
stRenderSceneChanged(&gSim);
|
||||||
if (gGame.demo) {
|
if (gGame.demo) {
|
||||||
|
|
@ -362,6 +637,7 @@ static void runIntro(uint8_t ticks) {
|
||||||
stSimSetDemoStream(&gSim, e->stream, e->length);
|
stSimSetDemoStream(&gSim, e->stream, e->length);
|
||||||
}
|
}
|
||||||
gGame.state = ST_STATE_PLAYING;
|
gGame.state = ST_STATE_PLAYING;
|
||||||
|
serviceJingle();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -377,6 +653,9 @@ static void runPlaying(uint8_t ticks) {
|
||||||
StTickResultE r = stSimTick(&gSim);
|
StTickResultE r = stSimTick(&gSim);
|
||||||
stAudioTick();
|
stAudioTick();
|
||||||
if (r == ST_TICK_CONTINUE) {
|
if (r == ST_TICK_CONTINUE) {
|
||||||
|
if (serviceJingle()) {
|
||||||
|
return; // $70D4 bonus cab: the tune blocks
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (gGame.demo) {
|
if (gGame.demo) {
|
||||||
|
|
@ -390,6 +669,9 @@ static void runPlaying(uint8_t ticks) {
|
||||||
case ST_TICK_LIFE_LOST:
|
case ST_TICK_LIFE_LOST:
|
||||||
stSimRespawn(&gSim);
|
stSimRespawn(&gSim);
|
||||||
stRenderSceneChanged(&gSim);
|
stRenderSceneChanged(&gSim);
|
||||||
|
if (serviceJingle()) {
|
||||||
|
return; // $6906 on the new cab
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ST_TICK_PLAYER_OUT:
|
case ST_TICK_PLAYER_OUT:
|
||||||
gameOverEnter();
|
gameOverEnter();
|
||||||
|
|
@ -401,6 +683,27 @@ static void runPlaying(uint8_t ticks) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Start a tune the sim asked for and park the loop in ST_STATE_JINGLE
|
||||||
|
// until it ends. Returns true when a tune was started, so callers can
|
||||||
|
// stop ticking the sim for this frame -- the C64's $44CF blocks there.
|
||||||
|
static bool serviceJingle(void) {
|
||||||
|
uint8_t song;
|
||||||
|
|
||||||
|
if (gSim.jingleRequest == 0u) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
song = (uint8_t)(gSim.jingleRequest - 1u);
|
||||||
|
gSim.jingleRequest = 0u;
|
||||||
|
stAudioJingle(song);
|
||||||
|
if (!stAudioJingleActive()) {
|
||||||
|
return false; // no audio: nothing to wait for
|
||||||
|
}
|
||||||
|
gGame.afterJingle = gGame.state;
|
||||||
|
gGame.state = ST_STATE_JINGLE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// $54F8 -- UP/DOWN pick the shift, FIRE confirms and starts the game.
|
// $54F8 -- UP/DOWN pick the shift, FIRE confirms and starts the game.
|
||||||
static void runShiftMenu(uint8_t ticks) {
|
static void runShiftMenu(uint8_t ticks) {
|
||||||
uint8_t mask = joyMask();
|
uint8_t mask = joyMask();
|
||||||
|
|
@ -462,13 +765,7 @@ static void runTitle(uint8_t ticks) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ((mask & 0x01u) != 0u) {
|
if ((mask & 0x01u) != 0u) {
|
||||||
uint8_t i;
|
highScoreDraw();
|
||||||
blankScreen(&gSim);
|
|
||||||
stSimDrawText(&gSim, 10u, 1u, kTextImmortal, 1u);
|
|
||||||
for (i = 0u; i < 8u; i++) {
|
|
||||||
stSimDrawText(&gSim, 9u, (uint8_t)(4u + i * 2u), kHighScores[i], 1u);
|
|
||||||
}
|
|
||||||
stSimDrawText(&gSim, 10u, 22u, kTextReturn, 1u);
|
|
||||||
gGame.state = ST_STATE_HIGHSCORES;
|
gGame.state = ST_STATE_HIGHSCORES;
|
||||||
gGame.fireArmed = false;
|
gGame.fireArmed = false;
|
||||||
return;
|
return;
|
||||||
|
|
@ -477,7 +774,7 @@ static void runTitle(uint8_t ticks) {
|
||||||
static const uint8_t kAboutCols[7] = { 1u, 12u, 15u, 4u, 5u, 6u, 1u };
|
static const uint8_t kAboutCols[7] = { 1u, 12u, 15u, 4u, 5u, 6u, 1u };
|
||||||
static const uint8_t kAboutRows[7] = { 3u, 5u, 7u, 10u, 12u, 14u, 18u };
|
static const uint8_t kAboutRows[7] = { 3u, 5u, 7u, 10u, 12u, 14u, 18u };
|
||||||
uint8_t i;
|
uint8_t i;
|
||||||
blankScreen(&gSim);
|
blankScreen(&gSim, 12u, 15u); // $558E: grey border, light-grey field
|
||||||
for (i = 0u; i < 7u; i++) {
|
for (i = 0u; i < 7u; i++) {
|
||||||
stSimDrawText(&gSim, kAboutCols[i], kAboutRows[i], kAboutLines[i], 1u);
|
stSimDrawText(&gSim, kAboutCols[i], kAboutRows[i], kAboutLines[i], 1u);
|
||||||
}
|
}
|
||||||
|
|
@ -500,7 +797,7 @@ static void shiftMenuColor(uint8_t sel, uint8_t color) {
|
||||||
|
|
||||||
// $5295 -- GAME VARIATIONS page 1.
|
// $5295 -- GAME VARIATIONS page 1.
|
||||||
static void showCabbiesMenu(void) {
|
static void showCabbiesMenu(void) {
|
||||||
blankScreen(&gSim);
|
blankScreen(&gSim, 0u, 0u);
|
||||||
stSimDrawText(&gSim, 13u, 0u, kTextGameVariations, 5u);
|
stSimDrawText(&gSim, 13u, 0u, kTextGameVariations, 5u);
|
||||||
stSimDrawText(&gSim, 1u, 2u, kTextCabbies, 7u);
|
stSimDrawText(&gSim, 1u, 2u, kTextCabbies, 7u);
|
||||||
stSimDrawText(&gSim, 5u, 4u, kTextLeftRight, 11u);
|
stSimDrawText(&gSim, 5u, 4u, kTextLeftRight, 11u);
|
||||||
|
|
@ -518,7 +815,7 @@ static void showCabbiesMenu(void) {
|
||||||
static void showShiftMenu(void) {
|
static void showShiftMenu(void) {
|
||||||
uint8_t i;
|
uint8_t i;
|
||||||
|
|
||||||
blankScreen(&gSim);
|
blankScreen(&gSim, 0u, 0u);
|
||||||
stSimDrawText(&gSim, 1u, 8u, kTextSelectShift, 7u);
|
stSimDrawText(&gSim, 1u, 8u, kTextSelectShift, 7u);
|
||||||
for (i = 0u; i < 5u; i++) {
|
for (i = 0u; i < 5u; i++) {
|
||||||
stSimDrawText(&gSim, 2u, (uint8_t)(10u + i * 2u), kTextShifts[i], 6u);
|
stSimDrawText(&gSim, 2u, (uint8_t)(10u + i * 2u), kTextShifts[i], 6u);
|
||||||
|
|
@ -560,6 +857,11 @@ static void startDemoScreen(void) {
|
||||||
static void startIntro(void) {
|
static void startIntro(void) {
|
||||||
stIntroEnter(&gSim, &gLevel);
|
stIntroEnter(&gSim, &gLevel);
|
||||||
stRenderSceneChanged(&gSim);
|
stRenderSceneChanged(&gSim);
|
||||||
|
// After the scene change, which turns it back off.
|
||||||
|
stRenderIntroStars();
|
||||||
|
// $459C: the intro's tune plays under the fly-in, not blocking; the
|
||||||
|
// C64 leaves it to the IRQ player and stops it at $4709.
|
||||||
|
stAudioMusic(ST_SONG_INTRO, true);
|
||||||
gGame.state = ST_STATE_INTRO;
|
gGame.state = ST_STATE_INTRO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -601,9 +903,15 @@ int main(void) {
|
||||||
jlConfigT config;
|
jlConfigT config;
|
||||||
jlSurfaceT *stage;
|
jlSurfaceT *stage;
|
||||||
|
|
||||||
#if defined(JOEYLIB_PLATFORM_IIGS)
|
// The codegen arena holds every compiled cel, the baked ones too: the
|
||||||
// One 64 KB bank: enough for the cab, exhaust and passenger cels.
|
// 37 cab/exhaust/passenger cels plus a level's hook cels (level I has
|
||||||
config.codegenBytes = 60UL * 1024;
|
// 30). On the IIgs that is ~230 KB of 65816 code in 64 KB banks, and
|
||||||
|
// the arena grants as many banks as the machine has (a 1 MB machine
|
||||||
|
// gets one; whatever does not fit draws interpreted). The 68k ports'
|
||||||
|
// cels are far larger (9-13 KB each) and their JIT is fast, so they
|
||||||
|
// keep a modest arena and compile at level entry.
|
||||||
|
#if defined(JOEYLIB_PLATFORM_IIGS) || defined(JOEYLIB_PLATFORM_DOS)
|
||||||
|
config.codegenBytes = 256UL * 1024;
|
||||||
#else
|
#else
|
||||||
config.codegenBytes = 160UL * 1024;
|
config.codegenBytes = 160UL * 1024;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -618,6 +926,7 @@ int main(void) {
|
||||||
jlShutdown();
|
jlShutdown();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
gStage = stage;
|
||||||
memset(&gGame, 0, sizeof(gGame));
|
memset(&gGame, 0, sizeof(gGame));
|
||||||
memset(&gSim, 0, sizeof(gSim));
|
memset(&gSim, 0, sizeof(gSim));
|
||||||
stRenderInit(stage);
|
stRenderInit(stage);
|
||||||
|
|
@ -630,6 +939,7 @@ int main(void) {
|
||||||
stSimSeedDemoBuffer(&gSim, kDemoBufferInit, ST_DEMO_BUFFER_BYTES);
|
stSimSeedDemoBuffer(&gSim, kDemoBufferInit, ST_DEMO_BUFFER_BYTES);
|
||||||
gSim.waveIdx = 3u;
|
gSim.waveIdx = 3u;
|
||||||
stRenderPrewarm(stage, &gSim);
|
stRenderPrewarm(stage, &gSim);
|
||||||
|
highScoreLoad(); // $23E5
|
||||||
enterTitle();
|
enterTitle();
|
||||||
gGame.lastFrame = (uint8_t)jlFrameCount();
|
gGame.lastFrame = (uint8_t)jlFrameCount();
|
||||||
|
|
||||||
|
|
@ -637,8 +947,39 @@ int main(void) {
|
||||||
uint8_t ticks;
|
uint8_t ticks;
|
||||||
|
|
||||||
jlInputPoll();
|
jlInputPoll();
|
||||||
if (jlKeyPressed(KEY_ESCAPE)) {
|
// $4FCB runStopWatcher, on the game tick, the intro and the menus:
|
||||||
break;
|
// SHIFT held freezes everything with the sound gated off and any
|
||||||
|
// tune stopped (the intro's restarts on release); RUN/STOP -- ESC
|
||||||
|
// here -- abandons the game for the title, and on the title it
|
||||||
|
// leaves the program (the C64 had nowhere else to go).
|
||||||
|
if (gGame.state != ST_STATE_JINGLE && gGame.state != ST_STATE_GAME_OVER && gGame.state != ST_STATE_NAME_ENTRY) {
|
||||||
|
if (jlKeyDown(KEY_LSHIFT) || jlKeyDown(KEY_RSHIFT)) {
|
||||||
|
if (!gGame.paused) {
|
||||||
|
gGame.paused = true;
|
||||||
|
stAudioSilence();
|
||||||
|
stAudioNoise(false);
|
||||||
|
jlMusicStop();
|
||||||
|
}
|
||||||
|
stRenderFrame(stage, &gSim);
|
||||||
|
jlAudioFrameTick();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (gGame.paused) {
|
||||||
|
gGame.paused = false;
|
||||||
|
gGame.lastFrame = (uint8_t)jlFrameCount(); // the held time is not owed
|
||||||
|
gGame.paceAcc = 0u;
|
||||||
|
if (gGame.state == ST_STATE_INTRO) {
|
||||||
|
stAudioMusic(ST_SONG_INTRO, true); // $46B5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (jlKeyPressed(KEY_ESCAPE)) {
|
||||||
|
if (gGame.state == ST_STATE_TITLE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
jlMusicStop(); // $5000
|
||||||
|
gGame.demo = false;
|
||||||
|
enterTitle();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ticks = paceTicks();
|
ticks = paceTicks();
|
||||||
switch (gGame.state) {
|
switch (gGame.state) {
|
||||||
|
|
@ -677,19 +1018,33 @@ int main(void) {
|
||||||
case ST_STATE_PLAYING:
|
case ST_STATE_PLAYING:
|
||||||
runPlaying(ticks);
|
runPlaying(ticks);
|
||||||
break;
|
break;
|
||||||
|
case ST_STATE_JINGLE:
|
||||||
|
// The C64 spins in $44D5 until the tune ends. A joystick
|
||||||
|
// press ends the attract demo here too ($4FE2 stops the
|
||||||
|
// music on input).
|
||||||
|
if (gGame.demo && (joyMask() & 0x1Fu) != 0u) {
|
||||||
|
jlMusicStop();
|
||||||
|
runDemoEnd();
|
||||||
|
} else if (!stAudioJingleActive()) {
|
||||||
|
gGame.state = gGame.afterJingle;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case ST_STATE_GAME_OVER:
|
case ST_STATE_GAME_OVER:
|
||||||
if (gGame.waitTicks > ticks) {
|
if (gGame.waitTicks > ticks) {
|
||||||
gGame.waitTicks = (uint16_t)(gGame.waitTicks - ticks);
|
gGame.waitTicks = (uint16_t)(gGame.waitTicks - ticks);
|
||||||
} else {
|
} else if (!gGame.gameOverTune) {
|
||||||
// $6BD0: one more player done; everybody done -> title.
|
// $5C24: after the GAME OVER! wait, song 5 plays out.
|
||||||
gSim.playersDone++;
|
gGame.gameOverTune = true;
|
||||||
if (gSim.playersDone == gSim.playerCount) {
|
stAudioJingle(ST_SONG_GAME_OVER);
|
||||||
enterTitle();
|
} else if (stAudioJingleActive()) {
|
||||||
} else {
|
// still sounding
|
||||||
startNewScreen();
|
} else if (!nameEntryStart()) {
|
||||||
}
|
gameOverFinish();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case ST_STATE_NAME_ENTRY:
|
||||||
|
runNameEntry(ticks);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
enterTitle();
|
enterTitle();
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,21 @@
|
||||||
|
|
||||||
#include "stSim.h"
|
#include "stSim.h"
|
||||||
|
|
||||||
|
// Diagnostics. The logger drags in vsnprintf (with newlib that pulls the
|
||||||
|
// whole formatted-output and float-conversion family, ~45 KB) plus a
|
||||||
|
// multi-KB ring buffer, and the game has no need of either. That is
|
||||||
|
// unaffordable on the IIgs, whose bank-0 BSS budget cannot spare the
|
||||||
|
// ring, and on the X68000, which ships on a Human68k floppy with about
|
||||||
|
// 330 KB of usable space for the binary AND its levels. Both compile it
|
||||||
|
// out; the ports with room keep it.
|
||||||
|
#if defined(__W65816__) || defined(JOEYLIB_PLATFORM_X68000)
|
||||||
|
#define ST_LOG_RESET() ((void)0)
|
||||||
|
#define ST_LOG(...) ((void)0)
|
||||||
|
#else
|
||||||
|
#define ST_LOG_RESET() jlLogReset()
|
||||||
|
#define ST_LOG(...) jlLogF(__VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
|
||||||
// Level loading through the JoeyLib data path (DATA/levels/...).
|
// Level loading through the JoeyLib data path (DATA/levels/...).
|
||||||
bool stLevelLoad(StLevelT *out, const char *path);
|
bool stLevelLoad(StLevelT *out, const char *path);
|
||||||
|
|
||||||
|
|
@ -35,7 +50,13 @@ void stRenderSceneChanged(StSimT *sim);
|
||||||
// cycle are palette writes instead of repainting 103 (and 407) cells.
|
// cycle are palette writes instead of repainting 103 (and 407) cells.
|
||||||
// Call after stRenderSceneChanged, which turns it back off.
|
// Call after stRenderSceneChanged, which turns it back off.
|
||||||
void stRenderTitleLogo(StSimT *sim);
|
void stRenderTitleLogo(StSimT *sim);
|
||||||
|
// Level intro only: cycle the starfield's ring palette slots so the warp
|
||||||
|
// animates without a single pixel moving. Call after
|
||||||
|
// stRenderSceneChanged, which turns it back off.
|
||||||
|
void stRenderIntroStars(void);
|
||||||
void stRenderPrewarm(jlSurfaceT *stage, StSimT *sim);
|
void stRenderPrewarm(jlSurfaceT *stage, StSimT *sim);
|
||||||
|
// Ready a level's hook cels before its ride (evicts the previous level's).
|
||||||
|
void stRenderLevelCels(jlSurfaceT *stage, const StLevelT *level);
|
||||||
|
|
||||||
// Audio sink setup and the per-tick release timers ($4320).
|
// Audio sink setup and the per-tick release timers ($4320).
|
||||||
void stAudioInit(void);
|
void stAudioInit(void);
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,18 @@
|
||||||
// time; envelopes and waveforms are approximated with an attenuation
|
// time; envelopes and waveforms are approximated with an attenuation
|
||||||
// ramp. The speech samples of the original are not extracted; each
|
// ramp. The speech samples of the original are not extracted; each
|
||||||
// spoken character becomes a short chirp so the cues still land.
|
// spoken character becomes a short chirp so the cues still land.
|
||||||
|
//
|
||||||
|
// UPDATE: the speech IS extracted now (assets/extractSpeech.py), so the
|
||||||
|
// chirp is gone. stSpeechData.c carries the C64's own 1-bit run-length
|
||||||
|
// streams; assets/genSpeechPcm.py bakes them to sample bytes
|
||||||
|
// (DATA/speech.bin) and speechFill below streams those through
|
||||||
|
// jlAudioPlaySfxStream.
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "spacetaxi.h"
|
#include "spacetaxi.h"
|
||||||
|
#include <joey/file.h>
|
||||||
|
#include "stSongData.h"
|
||||||
|
|
||||||
|
|
||||||
// SID voice index in a program's byte 8 -> JoeyLib voice slot.
|
// SID voice index in a program's byte 8 -> JoeyLib voice slot.
|
||||||
|
|
@ -25,10 +35,44 @@
|
||||||
#define ST_SID_HZ_NUM 1022727UL
|
#define ST_SID_HZ_NUM 1022727UL
|
||||||
#define ST_SID_HZ_DEN 16777216UL
|
#define ST_SID_HZ_DEN 16777216UL
|
||||||
|
|
||||||
// Speech chirp: one short tone per character, pitched by the character.
|
// ---- speech ----
|
||||||
#define ST_SPEECH_TICKS 3u
|
//
|
||||||
#define ST_SPEECH_BASE_HZ 300u
|
// The C64 player ($9802) busy-waits a delay loop per stream byte and then
|
||||||
#define ST_SPEECH_STEP_HZ 9u
|
// toggles its 1-bit output, so a byte is one half-cycle. The loop costs
|
||||||
|
// (5k + 12) cycles per pass plus a 33-cycle tail, where k is the inner
|
||||||
|
// count at $98A2 -- normally 3, giving 27. A code below $09 does not
|
||||||
|
// speak: it REWRITES k, which is how the original gives each fare a
|
||||||
|
// slightly different voice pitch. stAudioSpeech honours that.
|
||||||
|
#define ST_SPEECH_SLOT 0u // slots 1..3 are the SID voices
|
||||||
|
#define ST_SPEECH_RATE 5000u // enough for a 1-bit source
|
||||||
|
#define ST_SPEECH_PITCH_BASE 3u // $98A2's power-on value
|
||||||
|
#define ST_SPEECH_PITCH_MAX 15u // the player's AND #$0F
|
||||||
|
// Longest phrase is "PX?" -- pad, digit, please.
|
||||||
|
#define ST_SPEECH_QUEUE 4u
|
||||||
|
// The speech is baked to sample bytes per pitch, in both polarities, by
|
||||||
|
// assets/genSpeechPcm.py (DATA/speech.bin, format in that script), so
|
||||||
|
// playing it is a block copy. Decoding the C64's run-length stream at
|
||||||
|
// play time cost the 65816 2.4 s of CPU per second of speech and stalled
|
||||||
|
// the ride half a second on every "HEY TAXI"; the 1-bit intermediate
|
||||||
|
// format that followed still needed a per-byte expansion loop.
|
||||||
|
#define ST_SPEECH_FILE "speech.bin"
|
||||||
|
#define ST_SPEECH_VERSION 2u
|
||||||
|
#define ST_SPEECH_HEADER_BYTES 16u
|
||||||
|
#define ST_SPEECH_ENTRY_BYTES 8u
|
||||||
|
#define ST_SPEECH_POLARITIES 2u
|
||||||
|
#define ST_SPEECH_FORMAT_SIGNED 0u
|
||||||
|
#define ST_SPEECH_FORMAT_BIASED 1u
|
||||||
|
#define ST_SPEECH_LOAD_CHUNK 16384u
|
||||||
|
#define ST_SPEECH_STAGE_MASK (ST_SPEECH_STAGE_BYTES - 1u)
|
||||||
|
// Decode-ahead staging: a whole engine chunk (7168 at 5 kHz, 1.4 s) plus
|
||||||
|
// slack. The per-tick slice covers playback (5000/s is ~330 a tick)
|
||||||
|
// with room to spare and is cheap enough to never need a burst; a
|
||||||
|
// starved pull still copies ST_SPEECH_STARVED_SLICE on the spot rather
|
||||||
|
// than ending the stream.
|
||||||
|
#define ST_SPEECH_STAGE_BYTES 8192u
|
||||||
|
#define ST_SPEECH_TICK_SLICE 384u
|
||||||
|
#define ST_SPEECH_STARVED_SLICE 1024u
|
||||||
|
#define ST_SPEECH_ARM_SLICE 1024u
|
||||||
|
|
||||||
// Thrust sweep register value -> Hz: the C64 writes the same byte to
|
// Thrust sweep register value -> Hz: the C64 writes the same byte to
|
||||||
// both frequency bytes, so the word is value * 257.
|
// both frequency bytes, so the word is value * 257.
|
||||||
|
|
@ -44,10 +88,46 @@ typedef struct {
|
||||||
static StSfxSlotT gSfx; // SID voice 1 (programs + sweep)
|
static StSfxSlotT gSfx; // SID voice 1 (programs + sweep)
|
||||||
static StSfxSlotT gTone; // SID voice 2
|
static StSfxSlotT gTone; // SID voice 2
|
||||||
static bool gNoiseOn;
|
static bool gNoiseOn;
|
||||||
static uint16_t gSpeechTicks;
|
|
||||||
|
// Speech playback state. The fill callback walks a queue of utterances so
|
||||||
|
// a phrase like "HT" ("Hey" + "Taxi") runs as one continuous stream
|
||||||
|
// instead of each character cutting off the last.
|
||||||
|
static uint8_t gSpeechQueue[ST_SPEECH_QUEUE];
|
||||||
|
static uint8_t gSpeechHead;
|
||||||
|
static uint8_t gSpeechTail;
|
||||||
|
static bool gSpeechActive;
|
||||||
|
static uint8_t gSpeechPitch = ST_SPEECH_PITCH_BASE;
|
||||||
|
// DATA/speech.bin, whole: header, entry table, sample bytes. The bytes
|
||||||
|
// are already in the port's raw stream format (jlAudioSfxRawFormat):
|
||||||
|
// signed +/-100, or 0x80-biased on the IIgs so a chunk needs no
|
||||||
|
// conversion; speechLoad refuses a file baked for the other one.
|
||||||
|
static uint8_t *gSpeechBlob;
|
||||||
|
static uint8_t gSpeechPitchBase;
|
||||||
|
static uint8_t gSpeechPitchCount;
|
||||||
|
static uint8_t gSpeechCount; // utterances per pitch
|
||||||
|
static const uint8_t *gSpeechPcm; // current utterance's samples
|
||||||
|
static uint16_t gSpeechSample; // next sample index in it
|
||||||
|
static uint16_t gSpeechSamples; // its length
|
||||||
|
static uint8_t gSpeechParity; // it ends on the opposite level
|
||||||
|
static uint8_t gSpeechPol; // 0 / 1: which baked polarity the next utterance plays
|
||||||
|
// Copied-ahead PCM ring for the stream (see speechFill). Allocated by
|
||||||
|
// stAudioInit rather than static: 8 KB of BSS is a fifth of the IIgs
|
||||||
|
// entry bank, which text, rodata, BSS and the C heap all share.
|
||||||
|
static uint8_t *gSpeechStage;
|
||||||
|
static uint16_t gSpeechStageRead; // byte index of the next unread sample
|
||||||
|
static uint16_t gSpeechStageWrite; // byte index of the next free byte
|
||||||
|
static uint16_t gSpeechStageCount; // bytes staged and unread
|
||||||
|
static bool gSpeechDecoding; // the decoder still has stream left
|
||||||
|
|
||||||
|
|
||||||
static uint16_t sidHz(uint16_t word);
|
static uint16_t sidHz(uint16_t word);
|
||||||
|
static void speechLoad(void);
|
||||||
|
static bool speechNext(void);
|
||||||
|
static void speechArm(void);
|
||||||
|
static uint16_t speechDecode(uint8_t *dst, uint16_t count);
|
||||||
|
static uint32_t speechFill(void *ctx, int8_t *dst, uint32_t count);
|
||||||
|
static void speechPumpSlice(uint16_t samples);
|
||||||
|
static bool speechQueue(uint8_t ch);
|
||||||
static void voiceOff(StSfxSlotT *slot);
|
static void voiceOff(StSfxSlotT *slot);
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -61,6 +141,208 @@ static uint16_t sidHz(uint16_t word) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Point the decoder at the next queued utterance. False when the queue
|
||||||
|
// is empty, which ends the stream.
|
||||||
|
// DATA/speech.bin into one jlAlloc block; without it the game is mute
|
||||||
|
// but otherwise fine.
|
||||||
|
static void speechLoad(void) {
|
||||||
|
FILE *fp = jlDataOpen(ST_SPEECH_FILE, "rb");
|
||||||
|
uint8_t head[ST_SPEECH_HEADER_BYTES];
|
||||||
|
uint8_t format;
|
||||||
|
uint32_t dataBytes;
|
||||||
|
uint32_t total;
|
||||||
|
uint32_t done;
|
||||||
|
|
||||||
|
gSpeechBlob = 0;
|
||||||
|
if (fp == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
format = (jlAudioSfxRawFormat() == JL_SFX_RAW_UNSIGNED8_NOZERO) ? ST_SPEECH_FORMAT_BIASED : ST_SPEECH_FORMAT_SIGNED;
|
||||||
|
if (fread(head, 1u, sizeof(head), fp) != sizeof(head) || head[0] != 'S' || head[1] != 'T' || head[2] != 'S' || head[3] != 'P' || head[4] != ST_SPEECH_VERSION || head[8] != format) {
|
||||||
|
fclose(fp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gSpeechPitchBase = head[5];
|
||||||
|
gSpeechPitchCount = head[6];
|
||||||
|
gSpeechCount = head[7];
|
||||||
|
dataBytes = (uint32_t)head[12] | ((uint32_t)head[13] << 8) | ((uint32_t)head[14] << 16) | ((uint32_t)head[15] << 24);
|
||||||
|
total = ST_SPEECH_HEADER_BYTES + (uint32_t)gSpeechPitchCount * gSpeechCount * ST_SPEECH_POLARITIES * ST_SPEECH_ENTRY_BYTES + dataBytes;
|
||||||
|
gSpeechBlob = (uint8_t *)jlAlloc(total);
|
||||||
|
if (gSpeechBlob == 0) {
|
||||||
|
fclose(fp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(gSpeechBlob, head, sizeof(head));
|
||||||
|
// The block spans banks on the IIgs; read it in bank-safe pieces.
|
||||||
|
done = ST_SPEECH_HEADER_BYTES;
|
||||||
|
while (done < total) {
|
||||||
|
uint32_t want = total - done;
|
||||||
|
|
||||||
|
if (want > ST_SPEECH_LOAD_CHUNK) {
|
||||||
|
want = ST_SPEECH_LOAD_CHUNK;
|
||||||
|
}
|
||||||
|
if (fread(gSpeechBlob + done, 1u, (size_t)want, fp) != (size_t)want) {
|
||||||
|
jlFree(gSpeechBlob);
|
||||||
|
gSpeechBlob = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
done += want;
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool speechNext(void) {
|
||||||
|
uint8_t row = 0u;
|
||||||
|
|
||||||
|
if (gSpeechBlob == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (gSpeechPitch > gSpeechPitchBase) {
|
||||||
|
row = (uint8_t)(gSpeechPitch - gSpeechPitchBase);
|
||||||
|
if (row >= gSpeechPitchCount) {
|
||||||
|
row = (uint8_t)(gSpeechPitchCount - 1u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (gSpeechHead != gSpeechTail) {
|
||||||
|
uint8_t code = gSpeechQueue[gSpeechHead];
|
||||||
|
const uint8_t *e = gSpeechBlob + ST_SPEECH_HEADER_BYTES + (uint16_t)row * gSpeechCount * ST_SPEECH_POLARITIES * ST_SPEECH_ENTRY_BYTES;
|
||||||
|
uint8_t k;
|
||||||
|
|
||||||
|
gSpeechHead = (uint8_t)((gSpeechHead + 1u) % ST_SPEECH_QUEUE);
|
||||||
|
for (k = 0u; k < gSpeechCount; k++, e += ST_SPEECH_POLARITIES * ST_SPEECH_ENTRY_BYTES) {
|
||||||
|
if (e[0] == code) {
|
||||||
|
const uint8_t *v = e + (uint16_t)gSpeechPol * ST_SPEECH_ENTRY_BYTES;
|
||||||
|
uint32_t offset = (uint32_t)v[4] | ((uint32_t)v[5] << 8) | ((uint32_t)v[6] << 16) | ((uint32_t)v[7] << 24);
|
||||||
|
|
||||||
|
gSpeechParity = v[1];
|
||||||
|
gSpeechSamples = (uint16_t)(v[2] | ((uint16_t)v[3] << 8));
|
||||||
|
gSpeechSample = 0u;
|
||||||
|
gSpeechPcm = gSpeechBlob + ST_SPEECH_HEADER_BYTES + (uint32_t)gSpeechPitchCount * gSpeechCount * ST_SPEECH_POLARITIES * ST_SPEECH_ENTRY_BYTES + offset;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Unknown code: skip it rather than dropping the whole phrase.
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Copy up to `count` samples of the queued utterances into `dst`, fewer
|
||||||
|
// when they run out (0 = none left). jlMemCopy is an MVN on the IIgs;
|
||||||
|
// the utterance bytes are already in the stream's format, so this is
|
||||||
|
// the whole of the play-time speech work.
|
||||||
|
static uint16_t speechDecode(uint8_t *dst, uint16_t count) {
|
||||||
|
uint16_t n = 0u;
|
||||||
|
|
||||||
|
while (n < count) {
|
||||||
|
uint16_t left = (uint16_t)(gSpeechSamples - gSpeechSample);
|
||||||
|
uint16_t take = (uint16_t)(count - n);
|
||||||
|
|
||||||
|
if (left == 0u) {
|
||||||
|
// The next utterance carries on from the level this one left.
|
||||||
|
if (gSpeechParity != 0u) {
|
||||||
|
gSpeechPol ^= 1u;
|
||||||
|
}
|
||||||
|
if (!speechNext()) {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (take > left) {
|
||||||
|
take = left;
|
||||||
|
}
|
||||||
|
jlMemCopy(dst + n, gSpeechPcm + gSpeechSample, take);
|
||||||
|
gSpeechSample = (uint16_t)(gSpeechSample + take);
|
||||||
|
n = (uint16_t)(n + take);
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The audio engine's chunk pull. It used to decode the whole chunk right
|
||||||
|
// here -- 7168 samples, ~1.5 s of speech, in ONE frame: every "HEY
|
||||||
|
// TAXI" froze the ride for over a second on the IIgs. Now the chunk is
|
||||||
|
// copied out of the staging buffer that speechPumpSlice fills a slice at
|
||||||
|
// a time across the ticks before it is due, and only a stream that has
|
||||||
|
// outrun the pump decodes here, a bounded slice, so it never starves.
|
||||||
|
static uint32_t speechFill(void *ctx, int8_t *dst, uint32_t count) {
|
||||||
|
uint16_t want = (count > ST_SPEECH_STAGE_BYTES) ? (uint16_t)ST_SPEECH_STAGE_BYTES : (uint16_t)count;
|
||||||
|
uint16_t n;
|
||||||
|
|
||||||
|
(void)ctx;
|
||||||
|
if (gSpeechStageCount == 0u && gSpeechDecoding) {
|
||||||
|
speechPumpSlice(ST_SPEECH_STARVED_SLICE);
|
||||||
|
}
|
||||||
|
n = (gSpeechStageCount < want) ? gSpeechStageCount : want;
|
||||||
|
if (n != 0u) {
|
||||||
|
// jlMemCopy (MVN on the IIgs; libc memcpy is a byte loop there),
|
||||||
|
// in two runs when the unread bytes wrap the ring's end.
|
||||||
|
uint8_t *to = (uint8_t *)dst;
|
||||||
|
uint16_t left = n;
|
||||||
|
|
||||||
|
while (left != 0u) {
|
||||||
|
uint16_t run = (uint16_t)(ST_SPEECH_STAGE_BYTES - gSpeechStageRead);
|
||||||
|
|
||||||
|
if (run > left) {
|
||||||
|
run = left;
|
||||||
|
}
|
||||||
|
jlMemCopy(to, gSpeechStage + gSpeechStageRead, run);
|
||||||
|
to += run;
|
||||||
|
gSpeechStageRead = (uint16_t)((gSpeechStageRead + run) & ST_SPEECH_STAGE_MASK);
|
||||||
|
left = (uint16_t)(left - run);
|
||||||
|
}
|
||||||
|
gSpeechStageCount = (uint16_t)(gSpeechStageCount - n);
|
||||||
|
}
|
||||||
|
if (n == 0u) {
|
||||||
|
// Out of queued utterances. Only END the stream if this chunk
|
||||||
|
// produced nothing at all -- a phrase is fed one character at a
|
||||||
|
// time ("HT" is "Hey" then "Taxi"), and the first arm decodes
|
||||||
|
// all of 'H' before 'T' has even been queued. Ending here would
|
||||||
|
// let the 'T' arm a NEW stream that cut 'H' off, so the player
|
||||||
|
// only ever heard the last character. Returning a short chunk
|
||||||
|
// keeps the slot armed and the next pull picks up whatever
|
||||||
|
// arrived since.
|
||||||
|
gSpeechActive = false;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Decode up to `samples` more into the staging buffer. Once the decoder
|
||||||
|
// reports nothing left it stays quiet until the next speechArm. Reads
|
||||||
|
// and writes stay even so the chunk copy can move words.
|
||||||
|
static void speechPumpSlice(uint16_t samples) {
|
||||||
|
uint16_t room = (uint16_t)(ST_SPEECH_STAGE_BYTES - gSpeechStageCount);
|
||||||
|
|
||||||
|
if (!gSpeechDecoding || room == 0u) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (samples > room) {
|
||||||
|
samples = room;
|
||||||
|
}
|
||||||
|
// Fill towards the ring's end, then from its start.
|
||||||
|
while (samples != 0u) {
|
||||||
|
uint16_t run = (uint16_t)(ST_SPEECH_STAGE_BYTES - gSpeechStageWrite);
|
||||||
|
uint16_t got;
|
||||||
|
|
||||||
|
if (run > samples) {
|
||||||
|
run = samples;
|
||||||
|
}
|
||||||
|
got = speechDecode(gSpeechStage + gSpeechStageWrite, run);
|
||||||
|
if (got < run) {
|
||||||
|
gSpeechDecoding = false;
|
||||||
|
}
|
||||||
|
gSpeechStageWrite = (uint16_t)((gSpeechStageWrite + got) & ST_SPEECH_STAGE_MASK);
|
||||||
|
gSpeechStageCount = (uint16_t)(gSpeechStageCount + got);
|
||||||
|
samples = (uint16_t)(samples - got);
|
||||||
|
if (!gSpeechDecoding) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void voiceOff(StSfxSlotT *slot) {
|
static void voiceOff(StSfxSlotT *slot) {
|
||||||
if (slot->ticksLeft != 0u) {
|
if (slot->ticksLeft != 0u) {
|
||||||
jlAudioVoice(slot->voice, 0u, ST_ATTEN_OFF);
|
jlAudioVoice(slot->voice, 0u, ST_ATTEN_OFF);
|
||||||
|
|
@ -71,6 +353,9 @@ static void voiceOff(StSfxSlotT *slot) {
|
||||||
|
|
||||||
// Once per game tick: run the release timers ($4320 sfxEnvelopeTick).
|
// Once per game tick: run the release timers ($4320 sfxEnvelopeTick).
|
||||||
void stAudioTick(void) {
|
void stAudioTick(void) {
|
||||||
|
if (gSpeechActive) {
|
||||||
|
speechPumpSlice(ST_SPEECH_TICK_SLICE);
|
||||||
|
}
|
||||||
if (gSfx.ticksLeft != 0u) {
|
if (gSfx.ticksLeft != 0u) {
|
||||||
gSfx.ticksLeft--;
|
gSfx.ticksLeft--;
|
||||||
if (gSfx.ticksLeft == 0u) {
|
if (gSfx.ticksLeft == 0u) {
|
||||||
|
|
@ -83,23 +368,23 @@ void stAudioTick(void) {
|
||||||
jlAudioVoice(gTone.voice, 0u, ST_ATTEN_OFF);
|
jlAudioVoice(gTone.voice, 0u, ST_ATTEN_OFF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (gSpeechTicks != 0u) {
|
|
||||||
gSpeechTicks--;
|
|
||||||
if (gSpeechTicks == 0u) {
|
|
||||||
jlAudioVoice(ST_VOICE_TONE, 0u, ST_ATTEN_OFF);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void stAudioInit(void) {
|
void stAudioInit(void) {
|
||||||
(void)jlAudioInit();
|
(void)jlAudioInit();
|
||||||
|
gSpeechStage = (uint8_t *)jlAlloc(ST_SPEECH_STAGE_BYTES);
|
||||||
|
speechLoad();
|
||||||
gSfx.voice = ST_VOICE_SFX;
|
gSfx.voice = ST_VOICE_SFX;
|
||||||
gSfx.ticksLeft = 0u;
|
gSfx.ticksLeft = 0u;
|
||||||
gTone.voice = ST_VOICE_TONE;
|
gTone.voice = ST_VOICE_TONE;
|
||||||
gTone.ticksLeft = 0u;
|
gTone.ticksLeft = 0u;
|
||||||
gNoiseOn = false;
|
gNoiseOn = false;
|
||||||
gSpeechTicks = 0u;
|
gSpeechHead = 0u;
|
||||||
|
gSpeechTail = 0u;
|
||||||
|
gSpeechActive = false;
|
||||||
|
gSpeechPitch = ST_SPEECH_PITCH_BASE;
|
||||||
|
gSpeechPol = 0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -108,6 +393,10 @@ void stAudioShutdown(void) {
|
||||||
voiceOff(&gTone);
|
voiceOff(&gTone);
|
||||||
jlAudioNoise(0u, ST_ATTEN_OFF);
|
jlAudioNoise(0u, ST_ATTEN_OFF);
|
||||||
jlAudioShutdown();
|
jlAudioShutdown();
|
||||||
|
jlFree(gSpeechStage);
|
||||||
|
gSpeechStage = 0;
|
||||||
|
jlFree(gSpeechBlob);
|
||||||
|
gSpeechBlob = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -151,10 +440,112 @@ void stAudioSilence(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// $9802 -- one spoken character (a chirp here).
|
// $44CF -- play one of the game's tunes and block. The C64 silences the
|
||||||
|
// SID first ($5C1A / $6906) and then spins until the song ends; the host
|
||||||
|
// does the spinning (ST_STATE_JINGLE), so here it is just the start.
|
||||||
|
void stAudioJingle(uint8_t song) {
|
||||||
|
stAudioMusic(song, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// $CB02 -- start a tune and leave it to the player: the level intro's
|
||||||
|
// song and the high-score table's. Speech is cancelled too: it streams
|
||||||
|
// on the slot the tracker's voice 0 rides.
|
||||||
|
void stAudioMusic(uint8_t song, bool loop) {
|
||||||
|
if (song >= ST_SONG_COUNT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stAudioSilence();
|
||||||
|
stAudioNoise(false);
|
||||||
|
gSpeechActive = false;
|
||||||
|
gSpeechDecoding = false;
|
||||||
|
gSpeechStageRead = 0u;
|
||||||
|
gSpeechStageWrite = 0u;
|
||||||
|
gSpeechStageCount = 0u;
|
||||||
|
gSpeechHead = gSpeechTail;
|
||||||
|
jlAudioStopSfx(ST_SPEECH_SLOT);
|
||||||
|
jlMusicPlay(kStSongs[song].data, kStSongs[song].length, loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool stAudioJingleActive(void) {
|
||||||
|
return jlMusicIsPlaying();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Queue one utterance code. Returns false when the code is not speech (a
|
||||||
|
// pitch change) or the queue is full.
|
||||||
|
static bool speechQueue(uint8_t ch) {
|
||||||
|
uint8_t next;
|
||||||
|
|
||||||
|
if (ch < 9u) {
|
||||||
|
// Below $09 the original does not speak: it rewrites the delay
|
||||||
|
// loop's inner count, re-pitching every later utterance. That is
|
||||||
|
// where the passenger's voice variation comes from, so keep it.
|
||||||
|
gSpeechPitch = (uint8_t)(ch & ST_SPEECH_PITCH_MAX);
|
||||||
|
if (gSpeechPitch == 0u) {
|
||||||
|
gSpeechPitch = ST_SPEECH_PITCH_BASE;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
next = (uint8_t)((gSpeechTail + 1u) % ST_SPEECH_QUEUE);
|
||||||
|
if (next == gSpeechHead) {
|
||||||
|
return false; // queue full; drop rather than stall
|
||||||
|
}
|
||||||
|
gSpeechQueue[gSpeechTail] = ch;
|
||||||
|
gSpeechTail = next;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Start the stream if it is idle. Call this ONCE, after the whole phrase is
|
||||||
|
// queued: arming decodes everything queued so far into a single chunk, and
|
||||||
|
// each extra chunk costs a refill deadline of dead air, because the refill
|
||||||
|
// only happens when the game next calls jlAudioFrameTick -- once per rendered
|
||||||
|
// frame. Arming per character is what broke phrases into words with silence
|
||||||
|
// between them.
|
||||||
|
static void speechArm(void) {
|
||||||
|
if (gSpeechActive || gSpeechStage == 0 || gSpeechBlob == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!speechNext()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gSpeechPol = 0u; // a phrase starts on +100
|
||||||
|
gSpeechActive = true;
|
||||||
|
gSpeechDecoding = true;
|
||||||
|
gSpeechStageRead = 0u;
|
||||||
|
gSpeechStageWrite = 0u;
|
||||||
|
gSpeechStageCount = 0u;
|
||||||
|
// A first slice now, so the opening chunk sounds this tick.
|
||||||
|
speechPumpSlice(ST_SPEECH_ARM_SLICE);
|
||||||
|
jlAudioPlaySfxStreamRaw(ST_SPEECH_SLOT, speechFill, (void *)0, ST_SPEECH_RATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// $9802 -- one spoken character. Speaks immediately; prefer
|
||||||
|
// stAudioSpeechPhrase for anything longer than a single code.
|
||||||
void stAudioSpeech(uint8_t ch) {
|
void stAudioSpeech(uint8_t ch) {
|
||||||
jlAudioVoice(ST_VOICE_TONE, (uint16_t)(ST_SPEECH_BASE_HZ + (uint16_t)(ch & 0x1Fu) * ST_SPEECH_STEP_HZ), ST_ATTEN_LOUD);
|
if (speechQueue(ch)) {
|
||||||
gSpeechTicks = ST_SPEECH_TICKS;
|
speechArm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// A whole phrase: queue every code FIRST, then arm once, so the phrase
|
||||||
|
// decodes into as few chunks as it fits into.
|
||||||
|
void stAudioSpeechPhrase(const uint8_t *speech) {
|
||||||
|
bool any = false;
|
||||||
|
|
||||||
|
while (*speech != 0u) {
|
||||||
|
if (speechQueue(*speech)) {
|
||||||
|
any = true;
|
||||||
|
}
|
||||||
|
speech++;
|
||||||
|
}
|
||||||
|
if (any) {
|
||||||
|
speechArm();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1322,10 +1322,6 @@ const uint8_t kStFlameCelByDirMask[16] = {
|
||||||
0x00, 0xD8, 0xD4, 0x00, 0xD5, 0xD2, 0xD1, 0x00, 0xD7, 0xD3, 0xD6, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0xD8, 0xD4, 0x00, 0xD5, 0xD2, 0xD1, 0x00, 0xD7, 0xD3, 0xD6, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
};
|
};
|
||||||
|
|
||||||
const int8_t kStIntroStarDx[7] = { 0, 6, 6, 6, -6, -6, -6 };
|
|
||||||
const int8_t kStIntroStarDy[7] = { -4, -4, 0, 4, 4, 0, -4 };
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Accessors. On the IIgs the program is split across banks and a 2-byte
|
// Accessors. On the IIgs the program is split across banks and a 2-byte
|
||||||
|
|
@ -1358,13 +1354,3 @@ const uint8_t *stC64SfxProgram(uint8_t idx) {
|
||||||
uint8_t stC64FlameCel(uint8_t dirMask) {
|
uint8_t stC64FlameCel(uint8_t dirMask) {
|
||||||
return kStFlameCelByDirMask[dirMask];
|
return kStFlameCelByDirMask[dirMask];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int8_t stC64IntroStarDx(uint8_t idx) {
|
|
||||||
return kStIntroStarDx[idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int8_t stC64IntroStarDy(uint8_t idx) {
|
|
||||||
return kStIntroStarDy[idx];
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -44,10 +44,6 @@ extern const uint8_t kStSfxPrograms[ST_SFX_COUNT][ST_SFX_PROGRAM_BYTES];
|
||||||
// 8=RIGHT); 0 = no cel for that combination.
|
// 8=RIGHT); 0 = no cel for that combination.
|
||||||
extern const uint8_t kStFlameCelByDirMask[16];
|
extern const uint8_t kStFlameCelByDirMask[16];
|
||||||
|
|
||||||
// Level-intro star velocities per sprite 0..6 ($450C / $4513).
|
|
||||||
extern const int8_t kStIntroStarDx[7];
|
|
||||||
extern const int8_t kStIntroStarDy[7];
|
|
||||||
|
|
||||||
// Cross-segment-safe accessors (see stC64Data.c): reach the tables above
|
// Cross-segment-safe accessors (see stC64Data.c): reach the tables above
|
||||||
// by an inter-segment call on the IIgs, where a direct 2-byte data
|
// by an inter-segment call on the IIgs, where a direct 2-byte data
|
||||||
// reference from another bank is not relocatable.
|
// reference from another bank is not relocatable.
|
||||||
|
|
@ -56,7 +52,5 @@ const uint8_t *stC64SpriteBitmap(uint8_t idx);
|
||||||
uint8_t stC64RngByte(uint8_t idx);
|
uint8_t stC64RngByte(uint8_t idx);
|
||||||
const uint8_t *stC64SfxProgram(uint8_t idx);
|
const uint8_t *stC64SfxProgram(uint8_t idx);
|
||||||
uint8_t stC64FlameCel(uint8_t dirMask);
|
uint8_t stC64FlameCel(uint8_t dirMask);
|
||||||
int8_t stC64IntroStarDx(uint8_t idx);
|
|
||||||
int8_t stC64IntroStarDy(uint8_t idx);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "stSim.h"
|
||||||
|
|
||||||
// 3x3 tiles = 24x24 px; 32 bytes per 8x8 tile, tile-major (matches
|
// 3x3 tiles = 24x24 px; 32 bytes per 8x8 tile, tile-major (matches
|
||||||
// jlSpriteCreateFromSurface / the .spr cel layout).
|
// jlSpriteCreateFromSurface / the .spr cel layout).
|
||||||
#define ST_CEL_TILES 3u
|
#define ST_CEL_TILES 3u
|
||||||
|
|
@ -29,6 +31,11 @@ typedef struct {
|
||||||
extern const StCelDefT kStCels[];
|
extern const StCelDefT kStCels[];
|
||||||
extern const uint16_t kStCelCount;
|
extern const uint16_t kStCelCount;
|
||||||
|
|
||||||
|
// The cels a level's hook program can show (stLevelCels.c): at most `max`
|
||||||
|
// into `out`, no duplicates. 0 for a level without hook sprites.
|
||||||
|
#define ST_LEVEL_CELS_MAX 40u
|
||||||
|
uint8_t stLevelCelList(const StLevelT *level, StCelDefT *out, uint8_t max);
|
||||||
|
|
||||||
// Expand a VIC bitmap (63 bytes, 21 rows x 3, or NULL) into the 288-byte
|
// Expand a VIC bitmap (63 bytes, 21 rows x 3, or NULL) into the 288-byte
|
||||||
// tile-major 4bpp-packed cel blob. Colours as in the C64 multicolour
|
// tile-major 4bpp-packed cel blob. Colours as in the C64 multicolour
|
||||||
// rules; hires uses `color` for a set bit.
|
// rules; hires uses `color` for a set bit.
|
||||||
|
|
|
||||||
|
|
@ -11,17 +11,16 @@
|
||||||
#include "stSim.h"
|
#include "stSim.h"
|
||||||
|
|
||||||
// Screen-code strings ($6C1E..$6C7D). All ASCII-compatible glyphs.
|
// Screen-code strings ($6C1E..$6C7D). All ASCII-compatible glyphs.
|
||||||
static const uint8_t kTextPadPlease[] = { 0x50, 0x41, 0x44, 0x20, 0x20, 0x20, 0x50, 0x4C, 0x45, 0x41, 0x53, 0x45, 0 }; // $6C1E "PAD PLEASE"
|
static const uint8_t kTextPadPlease[] = "PAD PLEASE"; // $6C1E
|
||||||
static const uint8_t kTextUpPlease[] = { 0x20, 0x55, 0x50, 0x20, 0x50, 0x4C, 0x45, 0x41, 0x53, 0x45, 0x21, 0x20, 0 }; // $6C2B " UP PLEASE! "
|
static const uint8_t kTextUpPlease[] = " UP PLEASE! "; // $6C2B
|
||||||
static const uint8_t kTextBlank[] = { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0 }; // $6C38
|
static const uint8_t kTextHeyTaxi[] = " HEY, TAXI! "; // $6C45
|
||||||
static const uint8_t kTextHeyTaxi[] = { 0x20, 0x48, 0x45, 0x59, 0x2C, 0x20, 0x54, 0x41, 0x58, 0x49, 0x21, 0x20, 0 }; // $6C45 " HEY, TAXI! "
|
static const uint8_t kTextThanks[] = " THANKS "; // $6C5F
|
||||||
static const uint8_t kTextThanks[] = { 0x20, 0x20, 0x20, 0x54, 0x48, 0x41, 0x4E, 0x4B, 0x53, 0x20, 0x20, 0x20, 0 }; // $6C5F " THANKS "
|
|
||||||
// Speech cue strings fed to the sample player one character at a time.
|
// Speech cue strings fed to the sample player one character at a time.
|
||||||
static const uint8_t kSpeechHeyTaxi[] = { 0x48, 0x54, 0 }; // $6C6C "HT"
|
static const uint8_t kSpeechHeyTaxi[] = "HT"; // $6C6C
|
||||||
static const uint8_t kSpeechUp[] = { 0x55, 0x3F, 0 }; // $6C6F "U?"
|
static const uint8_t kSpeechUp[] = "U?"; // $6C6F
|
||||||
static const uint8_t kSpeechPad[] = { 0x50, 0x58, 0x3F, 0 }; // $6C72 "PX?" (X = digit)
|
static const uint8_t kSpeechPad[] = "PX?"; // $6C72 (X = digit)
|
||||||
static const uint8_t kSpeechThanks[] = { 0x21, 0 }; // $6C76 "!"
|
static const uint8_t kSpeechThanks[] = "!"; // $6C76
|
||||||
static const uint8_t kSpeechHey[] = { 0x48, 0 }; // $6C78 "H"
|
static const uint8_t kSpeechHey[] = "H"; // $6C78
|
||||||
// Waiting wave cycle ($66D6) and the base-fare blob ($43B9 = +5.00).
|
// Waiting wave cycle ($66D6) and the base-fare blob ($43B9 = +5.00).
|
||||||
static const uint8_t kWaveCels[4] = { 0xC6, 0xC7, 0xD9, 0xC7 };
|
static const uint8_t kWaveCels[4] = { 0xC6, 0xC7, 0xD9, 0xC7 };
|
||||||
static const uint8_t kFareBlob[7] = { 0x66, 0x66, 0x66, 0x6F, 0x77, 0x74, 0x74 };
|
static const uint8_t kFareBlob[7] = { 0x66, 0x66, 0x66, 0x6F, 0x77, 0x74, 0x74 };
|
||||||
|
|
@ -45,12 +44,12 @@ static void beamIn(StSimT *sim) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sim->spr[1].ptr--;
|
sim->spr[1].ptr--;
|
||||||
if (sim->spr[1].ptr != 0xC7u) {
|
if (sim->spr[1].ptr != ST_SPRITE_PASS_STAND) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (sim->collisionPhase != 0u) {
|
if (sim->collisionPhase != 0u) {
|
||||||
sim->stage = ST_STAGE_LEAVE;
|
sim->stage = ST_STAGE_LEAVE;
|
||||||
sim->spr[1].ptr = 0xC7u;
|
sim->spr[1].ptr = ST_SPRITE_PASS_STAND;
|
||||||
sim->spriteSlots[sim->activeSpriteIdx] = 0u;
|
sim->spriteSlots[sim->activeSpriteIdx] = 0u;
|
||||||
sim->activeSpriteIdx = 0u;
|
sim->activeSpriteIdx = 0u;
|
||||||
sim->fareSlotCount--;
|
sim->fareSlotCount--;
|
||||||
|
|
@ -60,15 +59,9 @@ static void beamIn(StSimT *sim) {
|
||||||
stSimDrawText(sim, 14u, 24u, kTextHeyTaxi, 1u);
|
stSimDrawText(sim, 14u, 24u, kTextHeyTaxi, 1u);
|
||||||
stFareBoardingSound(sim, kSpeechHeyTaxi);
|
stFareBoardingSound(sim, kSpeechHeyTaxi);
|
||||||
// $43D1: the meter starts at $10.00..$19.00.
|
// $43D1: the meter starts at $10.00..$19.00.
|
||||||
{
|
stSimHudInit(sim);
|
||||||
uint8_t k;
|
stSimPutChar(sim, 35u, 24u, (uint8_t)(ST_CHAR_DIGIT_BASE + stSimRng(sim, 10u)));
|
||||||
static const uint8_t kTemplate[7] = { 0x66, 0x66, 0x66, 0x74, 0x77, 0x74, 0x74 };
|
stSimPutChar(sim, 34u, 24u, (uint8_t)(ST_CHAR_DIGIT_BASE + 1u));
|
||||||
for (k = 0u; k < ST_NUMBER_CHARS; k++) {
|
|
||||||
stSimPutChar(sim, (uint8_t)(32u + k), 24u, kTemplate[k]);
|
|
||||||
}
|
|
||||||
stSimPutChar(sim, 35u, 24u, (uint8_t)(ST_CHAR_DIGIT_BASE + stSimRng(sim, 10u)));
|
|
||||||
stSimPutChar(sim, 34u, 24u, (uint8_t)(ST_CHAR_DIGIT_BASE + 1u));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -125,7 +118,7 @@ static void newFare(StSimT *sim) {
|
||||||
sim->spr[1].enable = 1u;
|
sim->spr[1].enable = 1u;
|
||||||
sim->stage = ST_STAGE_BEAM_IN;
|
sim->stage = ST_STAGE_BEAM_IN;
|
||||||
sim->decayTimer = sim->decayReload;
|
sim->decayTimer = sim->decayReload;
|
||||||
sim->spr[1].ptr = 0xCBu;
|
sim->spr[1].ptr = ST_SPRITE_PASS_BEAM;
|
||||||
stAudioSpeech((uint8_t)(stSimRng(sim, 3u) + 2u));
|
stAudioSpeech((uint8_t)(stSimRng(sim, 3u) + 2u));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,14 +129,14 @@ static void stageBoard(StSimT *sim) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sim->spr[1].ptr++;
|
sim->spr[1].ptr++;
|
||||||
if (sim->spr[1].ptr != 0xCCu) {
|
if (sim->spr[1].ptr != ST_SPRITE_PASS_GONE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sim->spr[1].enable = 0u;
|
sim->spr[1].enable = 0u;
|
||||||
sim->stage++;
|
sim->stage++;
|
||||||
sim->activeDyingSlot = sim->activePad;
|
sim->activeDyingSlot = sim->activePad;
|
||||||
(void)stFareChooseDestination(sim);
|
(void)stFareChooseDestination(sim);
|
||||||
if (sim->activeSpriteIdx == 0x0Bu) {
|
if (sim->activeSpriteIdx == ST_FARE_DEST_UP) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stSimDrawText(sim, 14u, 24u, kTextPadPlease, 1u);
|
stSimDrawText(sim, 14u, 24u, kTextPadPlease, 1u);
|
||||||
|
|
@ -164,7 +157,7 @@ static void stageCleanup(StSimT *sim) {
|
||||||
sim->deathInProgress = 0u;
|
sim->deathInProgress = 0u;
|
||||||
sim->stage = ST_STAGE_IDLE;
|
sim->stage = ST_STAGE_IDLE;
|
||||||
stSimPadLight(sim, false);
|
stSimPadLight(sim, false);
|
||||||
stSimDrawText(sim, 14u, 24u, kTextBlank, 1u);
|
stSimClearMessage(sim);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -176,7 +169,7 @@ static void stageDropIn(StSimT *sim) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sim->spr[1].ptr--;
|
sim->spr[1].ptr--;
|
||||||
if (sim->spr[1].ptr != 0xC7u) {
|
if (sim->spr[1].ptr != ST_SPRITE_PASS_STAND) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sim->stage++;
|
sim->stage++;
|
||||||
|
|
@ -184,13 +177,7 @@ static void stageDropIn(StSimT *sim) {
|
||||||
stFareBoardingSound(sim, kSpeechThanks);
|
stFareBoardingSound(sim, kSpeechThanks);
|
||||||
stSimBcdAdd(sim, ST_CELL_SCORE, kFareBlob);
|
stSimBcdAdd(sim, ST_CELL_SCORE, kFareBlob);
|
||||||
stSimBcdAdd(sim, ST_CELL_SCORE, &sim->screen[ST_CELL_FARE]);
|
stSimBcdAdd(sim, ST_CELL_SCORE, &sim->screen[ST_CELL_FARE]);
|
||||||
{
|
stSimHudInit(sim);
|
||||||
static const uint8_t kTemplate[7] = { 0x66, 0x66, 0x66, 0x74, 0x77, 0x74, 0x74 };
|
|
||||||
uint8_t k;
|
|
||||||
for (k = 0u; k < ST_NUMBER_CHARS; k++) {
|
|
||||||
stSimPutChar(sim, (uint8_t)(32u + k), 24u, kTemplate[k]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sim->fareSlotCount != 0u) {
|
if (sim->fareSlotCount != 0u) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -204,7 +191,7 @@ void stFareLeaveStep(StSimT *sim) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sim->spr[1].ptr++;
|
sim->spr[1].ptr++;
|
||||||
if (sim->spr[1].ptr != 0xCCu) {
|
if (sim->spr[1].ptr != ST_SPRITE_PASS_GONE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sim->spr[1].enable = 0u;
|
sim->spr[1].enable = 0u;
|
||||||
|
|
@ -227,24 +214,19 @@ void stFareWaveStep(StSimT *sim) {
|
||||||
void stFareWalkStep(StSimT *sim) {
|
void stFareWalkStep(StSimT *sim) {
|
||||||
int16_t passX;
|
int16_t passX;
|
||||||
int16_t targetX;
|
int16_t targetX;
|
||||||
uint16_t sum;
|
|
||||||
|
|
||||||
sim->walkParity ^= 1u;
|
sim->walkParity ^= 1u;
|
||||||
passX = (int16_t)(((int16_t)sim->spr[1].msb << 8) | sim->spr[1].col);
|
passX = (int16_t)stSimSpriteX(sim, 1u);
|
||||||
targetX = (int16_t)(((int16_t)sim->hoverXFrac << 8) | sim->hoverXCol);
|
targetX = (int16_t)(((int16_t)sim->hoverXFrac << 8) | sim->hoverXCol);
|
||||||
if ((int16_t)(passX - targetX) < 0) {
|
if ((int16_t)(passX - targetX) < 0) {
|
||||||
// $6D30: walk right, cels $C2/$C3.
|
// $6D30: walk right, cels $C2/$C3.
|
||||||
sim->spr[1].ptr = (uint8_t)(0xC2u + sim->walkParity);
|
sim->spr[1].ptr = (uint8_t)(ST_SPRITE_PASS_WALK_R + sim->walkParity);
|
||||||
sum = (uint16_t)((uint16_t)sim->spr[1].col + 2u);
|
stSimSpriteAddX(sim, 1u, 2u);
|
||||||
sim->spr[1].col = (uint8_t)sum;
|
|
||||||
sim->spr[1].msb = (uint8_t)(sim->spr[1].msb + (uint8_t)(sum >> 8));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// $6D4B: walk left, cels $C4/$C5.
|
// $6D4B: walk left, cels $C4/$C5.
|
||||||
sim->spr[1].ptr = (uint8_t)(0xC4u + sim->walkParity);
|
sim->spr[1].ptr = (uint8_t)(ST_SPRITE_PASS_WALK_L + sim->walkParity);
|
||||||
sum = (uint16_t)((uint16_t)sim->spr[1].col + 0xFEu);
|
stSimSpriteAddX(sim, 1u, 0xFEu);
|
||||||
sim->spr[1].col = (uint8_t)sum;
|
|
||||||
sim->spr[1].msb = (uint8_t)(sim->spr[1].msb + 0xFFu + (uint8_t)(sum >> 8));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -255,7 +237,7 @@ static void walkToTarget(StSimT *sim) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ((sim->spr[1].col & 0xFEu) == (sim->hoverXCol & 0xFEu)) {
|
if ((sim->spr[1].col & 0xFEu) == (sim->hoverXCol & 0xFEu)) {
|
||||||
sim->spr[1].ptr = 0xC7u;
|
sim->spr[1].ptr = ST_SPRITE_PASS_STAND;
|
||||||
sim->stage++;
|
sim->stage++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -272,17 +254,14 @@ void stFareBoardingSound(StSimT *sim, const uint8_t *speech) {
|
||||||
(void)sim;
|
(void)sim;
|
||||||
stAudioSilence();
|
stAudioSilence();
|
||||||
stAudioNoise(false);
|
stAudioNoise(false);
|
||||||
while (*speech != 0u) {
|
stAudioSpeechPhrase(speech);
|
||||||
stAudioSpeech(*speech);
|
|
||||||
speech++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// $6537 -- reserve a free fare pad by RNG (linear probe on collision),
|
// $6537 -- reserve a free fare pad by RNG (linear probe on collision),
|
||||||
// or "UP PLEASE" once every fare pad is taken. Returns the pad number.
|
// or "UP PLEASE" once every fare pad is taken. Returns the pad number.
|
||||||
uint8_t stFareChooseDestination(StSimT *sim) {
|
uint8_t stFareChooseDestination(StSimT *sim) {
|
||||||
uint8_t special = sim->level->specialPad;
|
uint8_t special = sim->specialPad;
|
||||||
uint8_t x;
|
uint8_t x;
|
||||||
|
|
||||||
if (sim->fareSlotCount == special) {
|
if (sim->fareSlotCount == special) {
|
||||||
|
|
@ -349,7 +328,7 @@ void stFarePostTickGate(StSimT *sim) {
|
||||||
sim->spr[1].msb = sim->spr[0].msb;
|
sim->spr[1].msb = sim->spr[0].msb;
|
||||||
sim->spr[1].col = sim->spr[0].col;
|
sim->spr[1].col = sim->spr[0].col;
|
||||||
sim->spr[1].row = sim->spr[0].row;
|
sim->spr[1].row = sim->spr[0].row;
|
||||||
sim->spr[1].ptr = 0xCBu;
|
sim->spr[1].ptr = ST_SPRITE_PASS_BEAM;
|
||||||
sim->decayTimer = sim->decayReload;
|
sim->decayTimer = sim->decayReload;
|
||||||
pad = &sim->pads[sim->activeSpriteIdx - 1u];
|
pad = &sim->pads[sim->activeSpriteIdx - 1u];
|
||||||
sim->hoverXFrac = pad->passMsb;
|
sim->hoverXFrac = pad->passMsb;
|
||||||
|
|
@ -374,7 +353,7 @@ void stFareSquashed(StSimT *sim) {
|
||||||
sim->spriteSlots[sim->activeSpriteIdx] = 0u;
|
sim->spriteSlots[sim->activeSpriteIdx] = 0u;
|
||||||
sim->activeSpriteIdx = 0u;
|
sim->activeSpriteIdx = 0u;
|
||||||
sim->fareSlotCount--;
|
sim->fareSlotCount--;
|
||||||
stSimDrawText(sim, 14u, 24u, kTextBlank, 1u);
|
stSimClearMessage(sim);
|
||||||
}
|
}
|
||||||
sim->stage = ST_STAGE_LEAVE;
|
sim->stage = ST_STAGE_LEAVE;
|
||||||
stFareBoardingSound(sim, kSpeechHey);
|
stFareBoardingSound(sim, kSpeechHey);
|
||||||
|
|
@ -387,14 +366,8 @@ void stFareSquashed(StSimT *sim) {
|
||||||
// $43F3 counts down in the input-mask byte and leaves it zero.
|
// $43F3 counts down in the input-mask byte and leaves it zero.
|
||||||
sim->inputMask = 0u;
|
sim->inputMask = 0u;
|
||||||
}
|
}
|
||||||
sim->spr[1].ptr = 0xC7u;
|
sim->spr[1].ptr = ST_SPRITE_PASS_STAND;
|
||||||
{
|
stSimHudInit(sim);
|
||||||
static const uint8_t kTemplate[7] = { 0x66, 0x66, 0x66, 0x74, 0x77, 0x74, 0x74 };
|
|
||||||
uint8_t k;
|
|
||||||
for (k = 0u; k < ST_NUMBER_CHARS; k++) {
|
|
||||||
stSimPutChar(sim, (uint8_t)(32u + k), 24u, kTemplate[k]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -442,7 +415,7 @@ void stFareStageDispatch(StSimT *sim) {
|
||||||
void stFareUpPlease(StSimT *sim) {
|
void stFareUpPlease(StSimT *sim) {
|
||||||
uint8_t k;
|
uint8_t k;
|
||||||
|
|
||||||
sim->activeSpriteIdx = 0x0Bu;
|
sim->activeSpriteIdx = ST_FARE_DEST_UP;
|
||||||
stSimDrawText(sim, 14u, 24u, kTextUpPlease, 1u);
|
stSimDrawText(sim, 14u, 24u, kTextUpPlease, 1u);
|
||||||
for (k = 0u; k < 4u; k++) {
|
for (k = 0u; k < 4u; k++) {
|
||||||
stSimPutChar(sim, (uint8_t)(18u + k), 0u, ST_CHAR_SPACE);
|
stSimPutChar(sim, (uint8_t)(18u + k), 0u, ST_CHAR_SPACE);
|
||||||
|
|
|
||||||
85
examples/spacetaxi/stHookTables.h
Normal file
85
examples/spacetaxi/stHookTables.h
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
// Space Taxi -- where each level's hook program keeps its tables.
|
||||||
|
//
|
||||||
|
// Every address is a byte in the level's shipped hook blob ($7D98..$7FFF,
|
||||||
|
// StLevelT.hookData), so the same numbers serve the hook programs
|
||||||
|
// (stHooks.c, reading through the live simulation) and the cel manifest
|
||||||
|
// (stLevelCels.c, reading the level file alone -- the offline cel baker
|
||||||
|
// has no simulation). One list, two readers.
|
||||||
|
#ifndef ST_HOOK_TABLES_H
|
||||||
|
#define ST_HOOK_TABLES_H
|
||||||
|
|
||||||
|
// Level indices (0 = A).
|
||||||
|
#define ST_LEVEL_E 4u
|
||||||
|
#define ST_LEVEL_G 6u
|
||||||
|
#define ST_LEVEL_H 7u
|
||||||
|
#define ST_LEVEL_I 8u
|
||||||
|
#define ST_LEVEL_J 9u
|
||||||
|
#define ST_LEVEL_K 10u
|
||||||
|
#define ST_LEVEL_L 11u
|
||||||
|
#define ST_LEVEL_O 14u
|
||||||
|
#define ST_LEVEL_P 15u
|
||||||
|
#define ST_LEVEL_Q 16u
|
||||||
|
#define ST_LEVEL_R 17u
|
||||||
|
#define ST_LEVEL_S 18u
|
||||||
|
#define ST_LEVEL_T 19u
|
||||||
|
#define ST_LEVEL_U 20u
|
||||||
|
#define ST_LEVEL_V 21u
|
||||||
|
#define ST_LEVEL_W 22u
|
||||||
|
#define ST_LEVEL_X 23u
|
||||||
|
|
||||||
|
// Hardware sprites 3..7 are the hook sprites on every level that has any.
|
||||||
|
#define ST_HOOK_FIRST_SPRITE 3u
|
||||||
|
|
||||||
|
// ---- level G "TELEPORTS" ----
|
||||||
|
#define ST_HK_G_CEL 0x7DD4u // 6 cels the orbs cycle through
|
||||||
|
#define ST_HK_G_CEL_PHASES 6u
|
||||||
|
#define ST_HK_G_ORB_COL 0x7DDAu // per sprite: start column, msb, row
|
||||||
|
#define ST_HK_G_ORB_MSB 0x7DE2u
|
||||||
|
#define ST_HK_G_ORB_ROW 0x7DEAu
|
||||||
|
#define ST_HK_G_SPOT_COL 0x7EF3u // 5 landing spots: column, msb, row
|
||||||
|
#define ST_HK_G_SPOT_MSB 0x7EF8u
|
||||||
|
#define ST_HK_G_SPOT_ROW 0x7EFDu
|
||||||
|
#define ST_HK_G_SFX_HOP 0x7F03u
|
||||||
|
#define ST_HK_G_ORB_COLOR 4u
|
||||||
|
|
||||||
|
// ---- level H "PUZZLER" ----
|
||||||
|
#define ST_HK_H_CEL 0x80u // the one sprite cel
|
||||||
|
#define ST_HK_H_COLOR_IDLE 0x01u
|
||||||
|
#define ST_HK_H_COLOR_LIT 0x0Au
|
||||||
|
#define ST_HK_H_COLOR_ACTIVE 0x05u
|
||||||
|
|
||||||
|
// ---- level I "CROSSFIRE" ----
|
||||||
|
#define ST_HK_I_CEL_RISE 0x7DD8u // 5 cels
|
||||||
|
#define ST_HK_I_RISE_PHASES 5u
|
||||||
|
#define ST_HK_I_CEL_BURST 0x7DE5u // 8 cels
|
||||||
|
#define ST_HK_I_BURST_PHASES 8u
|
||||||
|
#define ST_HK_I_COLOR 0x7E7Fu // 3 colours
|
||||||
|
#define ST_HK_I_COLORS 3u
|
||||||
|
#define ST_HK_I_COLOR_IDLE 0x02u
|
||||||
|
|
||||||
|
// ---- levels J "SHOOTING STARS" and P "BLIZZARD" (the falling hazards) ----
|
||||||
|
#define ST_HK_FALL_CEL 0x80u // falling: $80/$81
|
||||||
|
#define ST_HK_FALL_CEL_BURST 0x82u // then the burst run from the table
|
||||||
|
#define ST_HK_J_CEL_BURST 0x7EC9u // phases 1..9
|
||||||
|
#define ST_HK_J_BURST_PHASES 10u
|
||||||
|
#define ST_HK_J_COLOR 7u
|
||||||
|
#define ST_HK_P_CEL_BURST 0x7F09u // phases 1..4
|
||||||
|
#define ST_HK_P_BURST_PHASES 5u
|
||||||
|
#define ST_HK_P_COLOR 3u
|
||||||
|
|
||||||
|
// ---- level L "BLACK HOLE" ----
|
||||||
|
#define ST_HK_L_CEL_FIRST 0x80u // $80..$82 spin
|
||||||
|
#define ST_HK_L_CEL_END 0x83u
|
||||||
|
#define ST_HK_L_COLOR 0u
|
||||||
|
|
||||||
|
// ---- level Q "INTERFERENCE" ----
|
||||||
|
#define ST_HK_Q_CEL 0x7DF2u // 12 cels, multicolour
|
||||||
|
#define ST_HK_Q_CEL_PHASES 12u
|
||||||
|
#define ST_HK_Q_COLOR 6u
|
||||||
|
|
||||||
|
// ---- level U "REBOUND" ----
|
||||||
|
#define ST_HK_U_CEL 0x7F4Bu // 4-entry walk over $80..$82
|
||||||
|
#define ST_HK_U_CEL_PHASES 4u
|
||||||
|
#define ST_HK_U_COLOR 7u
|
||||||
|
|
||||||
|
#endif // ST_HOOK_TABLES_H
|
||||||
File diff suppressed because it is too large
Load diff
114
examples/spacetaxi/stLevelCels.c
Normal file
114
examples/spacetaxi/stLevelCels.c
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
// Space Taxi -- the cels a level's hook program can put on screen.
|
||||||
|
//
|
||||||
|
// Hook sprites are built from the level's own VIC bitmaps in whatever
|
||||||
|
// colour the hook gives them, and compiling one on the 65816 costs about
|
||||||
|
// half a second -- far too long to do the first time a cel appears mid-
|
||||||
|
// ride. So every level lists its cels up front, from the same hook
|
||||||
|
// tables the hook program reads (stHookTables.h): the offline baker turns
|
||||||
|
// the list into a pre-compiled bank (sprites/levelNN.spc) and the game
|
||||||
|
// compiles whatever the bank does not supply BEFORE the ride starts.
|
||||||
|
//
|
||||||
|
// Only the level file is needed, no simulation, so the baker can link
|
||||||
|
// this on the host without the rest of the game.
|
||||||
|
|
||||||
|
#include "stCels.h"
|
||||||
|
#include "stHookTables.h"
|
||||||
|
|
||||||
|
#define HKL(level, addr) ((level)->hookData[(addr) - ST_HOOK_BASE])
|
||||||
|
|
||||||
|
static void celAdd(StCelDefT *out, uint8_t *count, uint8_t max, uint8_t ptr, uint8_t color, uint8_t multi, uint8_t mc0, uint8_t mc1);
|
||||||
|
|
||||||
|
|
||||||
|
// Append one cel unless the list already has it.
|
||||||
|
static void celAdd(StCelDefT *out, uint8_t *count, uint8_t max, uint8_t ptr, uint8_t color, uint8_t multi, uint8_t mc0, uint8_t mc1) {
|
||||||
|
uint8_t k;
|
||||||
|
|
||||||
|
for (k = 0u; k < *count; k++) {
|
||||||
|
if (out[k].ptr == ptr && out[k].color == color && out[k].multi == multi && out[k].mc0 == mc0 && out[k].mc1 == mc1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (*count == max) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
out[*count].ptr = ptr;
|
||||||
|
out[*count].color = color;
|
||||||
|
out[*count].multi = multi;
|
||||||
|
out[*count].mc0 = mc0;
|
||||||
|
out[*count].mc1 = mc1;
|
||||||
|
(*count)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t stLevelCelList(const StLevelT *level, StCelDefT *out, uint8_t max) {
|
||||||
|
uint8_t n = 0u;
|
||||||
|
uint8_t k;
|
||||||
|
uint8_t c;
|
||||||
|
|
||||||
|
switch (level->levelIndex) {
|
||||||
|
case ST_LEVEL_G:
|
||||||
|
for (k = 0u; k < ST_HK_G_CEL_PHASES; k++) {
|
||||||
|
celAdd(out, &n, max, HKL(level, ST_HK_G_CEL + k), ST_HK_G_ORB_COLOR, 0u, 0u, 0u);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ST_LEVEL_H:
|
||||||
|
celAdd(out, &n, max, ST_HK_H_CEL, ST_HK_H_COLOR_IDLE, 0u, 0u, 0u);
|
||||||
|
celAdd(out, &n, max, ST_HK_H_CEL, ST_HK_H_COLOR_LIT, 0u, 0u, 0u);
|
||||||
|
celAdd(out, &n, max, ST_HK_H_CEL, ST_HK_H_COLOR_ACTIVE, 0u, 0u, 0u);
|
||||||
|
break;
|
||||||
|
case ST_LEVEL_I:
|
||||||
|
// Every cel in the idle colour and in each of the three the
|
||||||
|
// launcher picks at random.
|
||||||
|
for (c = 0u; c <= ST_HK_I_COLORS; c++) {
|
||||||
|
uint8_t color = (c == 0u) ? ST_HK_I_COLOR_IDLE : HKL(level, ST_HK_I_COLOR + c - 1u);
|
||||||
|
for (k = 0u; k < ST_HK_I_RISE_PHASES; k++) {
|
||||||
|
celAdd(out, &n, max, HKL(level, ST_HK_I_CEL_RISE + k), color, 0u, 0u, 0u);
|
||||||
|
}
|
||||||
|
for (k = 0u; k < ST_HK_I_BURST_PHASES; k++) {
|
||||||
|
celAdd(out, &n, max, HKL(level, ST_HK_I_CEL_BURST + k), color, 0u, 0u, 0u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ST_LEVEL_J:
|
||||||
|
celAdd(out, &n, max, ST_HK_FALL_CEL, ST_HK_J_COLOR, 0u, 0u, 0u);
|
||||||
|
celAdd(out, &n, max, (uint8_t)(ST_HK_FALL_CEL + 1u), ST_HK_J_COLOR, 0u, 0u, 0u);
|
||||||
|
celAdd(out, &n, max, ST_HK_FALL_CEL_BURST, ST_HK_J_COLOR, 0u, 0u, 0u);
|
||||||
|
for (k = 1u; k < ST_HK_J_BURST_PHASES; k++) {
|
||||||
|
celAdd(out, &n, max, HKL(level, ST_HK_J_CEL_BURST + k), ST_HK_J_COLOR, 0u, 0u, 0u);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ST_LEVEL_L:
|
||||||
|
for (k = ST_HK_L_CEL_FIRST; k < ST_HK_L_CEL_END; k++) {
|
||||||
|
celAdd(out, &n, max, k, ST_HK_L_COLOR, 0u, 0u, 0u);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ST_LEVEL_P:
|
||||||
|
celAdd(out, &n, max, ST_HK_FALL_CEL, ST_HK_P_COLOR, 0u, 0u, 0u);
|
||||||
|
celAdd(out, &n, max, (uint8_t)(ST_HK_FALL_CEL + 1u), ST_HK_P_COLOR, 0u, 0u, 0u);
|
||||||
|
celAdd(out, &n, max, ST_HK_FALL_CEL_BURST, ST_HK_P_COLOR, 0u, 0u, 0u);
|
||||||
|
for (k = 1u; k < ST_HK_P_BURST_PHASES; k++) {
|
||||||
|
celAdd(out, &n, max, HKL(level, ST_HK_P_CEL_BURST + k), ST_HK_P_COLOR, 0u, 0u, 0u);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ST_LEVEL_Q:
|
||||||
|
// The hook's cels show HIRES in colour 6 for the whole recorded
|
||||||
|
// ride (tools/stsim STSIM_CELS census, 2026-09-22); the
|
||||||
|
// multicolour variant (the level's own $D025/$D026) stays
|
||||||
|
// listed for the phases the census did not reach.
|
||||||
|
for (k = 0u; k < ST_HK_Q_CEL_PHASES; k++) {
|
||||||
|
celAdd(out, &n, max, HKL(level, ST_HK_Q_CEL + k), ST_HK_Q_COLOR, 0u, 0u, 0u);
|
||||||
|
}
|
||||||
|
for (k = 0u; k < ST_HK_Q_CEL_PHASES; k++) {
|
||||||
|
celAdd(out, &n, max, HKL(level, ST_HK_Q_CEL + k), ST_HK_Q_COLOR, 1u, level->header[5], level->header[6]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ST_LEVEL_U:
|
||||||
|
for (k = 0u; k < ST_HK_U_CEL_PHASES; k++) {
|
||||||
|
celAdd(out, &n, max, HKL(level, ST_HK_U_CEL + k), ST_HK_U_COLOR, 0u, 0u, 0u);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
354
examples/spacetaxi/stMarshalIigs.s
Normal file
354
examples/spacetaxi/stMarshalIigs.s
Normal file
|
|
@ -0,0 +1,354 @@
|
||||||
|
; stMarshalIigs.s - Space Taxi's stSimMarshal for the Apple IIgs.
|
||||||
|
;
|
||||||
|
; GAS syntax for the llvm-mos w65816 toolchain (see src/iigs/peislam.s
|
||||||
|
; for the conventions). The C version copies eight sprites' shadow
|
||||||
|
; registers into the displayed frame through far pointers, which the
|
||||||
|
; w65816 backend turns into ~5000 cycles of pointer re-materialisation
|
||||||
|
; per tick (5% of a ride frame). This is the same copy as straight-line
|
||||||
|
; [dp],y moves: ~700 cycles.
|
||||||
|
;
|
||||||
|
; void stSimMarshalIigs(const StMarshalArgsT *args)
|
||||||
|
; args (A = low word, X = bank) points at { spr, frame, multi }:
|
||||||
|
; +0 const StSpriteT *spr (32-bit: low word, bank word)
|
||||||
|
; +4 StFrameT *frame
|
||||||
|
; +8 uint8_t multi
|
||||||
|
; StSpriteT (6 bytes): col, msb, row, enable, ptr, color.
|
||||||
|
; StFrameT: x[8] (u16, +0), y[8] (+16), ptr[8] (+24), color[8] (+32),
|
||||||
|
; enableMask (+40), multiMask (+41).
|
||||||
|
; stSim.c checks these layouts at compile time.
|
||||||
|
;
|
||||||
|
; Every store runs with M=16 and writes two bytes: the byte after each
|
||||||
|
; single-byte field gets a junk high byte that the NEXT store repairs,
|
||||||
|
; so the blocks run in order x, y, ptr, color and finish with the
|
||||||
|
; enable/multi word. The last colour store's junk lands on enableMask,
|
||||||
|
; which the final word overwrites.
|
||||||
|
;
|
||||||
|
; Direct-page $E0-$EF are the compiler's own per-function scratch
|
||||||
|
; (re-set before every use, never live across a call); $C0 is left alone.
|
||||||
|
.section .text.stSimMarshalIigs,"ax"
|
||||||
|
.globl stSimMarshalIigs
|
||||||
|
stSimMarshalIigs:
|
||||||
|
php
|
||||||
|
rep #0x30
|
||||||
|
.a16
|
||||||
|
.i16
|
||||||
|
sta 0xe0 ; args low
|
||||||
|
txa
|
||||||
|
sta 0xe2 ; args bank
|
||||||
|
ldy #0
|
||||||
|
lda [0xe0],y
|
||||||
|
sta 0xe4 ; spr low
|
||||||
|
ldy #2
|
||||||
|
lda [0xe0],y
|
||||||
|
sta 0xe6 ; spr bank
|
||||||
|
ldy #4
|
||||||
|
lda [0xe0],y
|
||||||
|
sta 0xe8 ; frame low
|
||||||
|
ldy #6
|
||||||
|
lda [0xe0],y
|
||||||
|
sta 0xea ; frame bank
|
||||||
|
ldy #8
|
||||||
|
lda [0xe0],y
|
||||||
|
and #0xff
|
||||||
|
sta 0xec ; multi
|
||||||
|
stz 0xee ; enable mask accumulator
|
||||||
|
; sprite 0: x = (msb ? 0x100 : 0) | col; enable bit
|
||||||
|
ldy #0
|
||||||
|
lda [0xe4],y ; col | msb<<8
|
||||||
|
sta 0xf0
|
||||||
|
and #0xff00
|
||||||
|
beq mX0clr
|
||||||
|
lda 0xf0
|
||||||
|
and #0x00ff
|
||||||
|
ora #0x0100
|
||||||
|
bra mX0st
|
||||||
|
mX0clr:
|
||||||
|
lda 0xf0
|
||||||
|
and #0x00ff
|
||||||
|
mX0st:
|
||||||
|
ldy #0
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #3
|
||||||
|
lda [0xe4],y ; enable | ptr<<8
|
||||||
|
and #0xff
|
||||||
|
beq mE0off
|
||||||
|
lda 0xee
|
||||||
|
ora #1
|
||||||
|
sta 0xee
|
||||||
|
mE0off:
|
||||||
|
; sprite 1: x = (msb ? 0x100 : 0) | col; enable bit
|
||||||
|
ldy #6
|
||||||
|
lda [0xe4],y ; col | msb<<8
|
||||||
|
sta 0xf0
|
||||||
|
and #0xff00
|
||||||
|
beq mX1clr
|
||||||
|
lda 0xf0
|
||||||
|
and #0x00ff
|
||||||
|
ora #0x0100
|
||||||
|
bra mX1st
|
||||||
|
mX1clr:
|
||||||
|
lda 0xf0
|
||||||
|
and #0x00ff
|
||||||
|
mX1st:
|
||||||
|
ldy #2
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #9
|
||||||
|
lda [0xe4],y ; enable | ptr<<8
|
||||||
|
and #0xff
|
||||||
|
beq mE1off
|
||||||
|
lda 0xee
|
||||||
|
ora #2
|
||||||
|
sta 0xee
|
||||||
|
mE1off:
|
||||||
|
; sprite 2: x = (msb ? 0x100 : 0) | col; enable bit
|
||||||
|
ldy #12
|
||||||
|
lda [0xe4],y ; col | msb<<8
|
||||||
|
sta 0xf0
|
||||||
|
and #0xff00
|
||||||
|
beq mX2clr
|
||||||
|
lda 0xf0
|
||||||
|
and #0x00ff
|
||||||
|
ora #0x0100
|
||||||
|
bra mX2st
|
||||||
|
mX2clr:
|
||||||
|
lda 0xf0
|
||||||
|
and #0x00ff
|
||||||
|
mX2st:
|
||||||
|
ldy #4
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #15
|
||||||
|
lda [0xe4],y ; enable | ptr<<8
|
||||||
|
and #0xff
|
||||||
|
beq mE2off
|
||||||
|
lda 0xee
|
||||||
|
ora #4
|
||||||
|
sta 0xee
|
||||||
|
mE2off:
|
||||||
|
; sprite 3: x = (msb ? 0x100 : 0) | col; enable bit
|
||||||
|
ldy #18
|
||||||
|
lda [0xe4],y ; col | msb<<8
|
||||||
|
sta 0xf0
|
||||||
|
and #0xff00
|
||||||
|
beq mX3clr
|
||||||
|
lda 0xf0
|
||||||
|
and #0x00ff
|
||||||
|
ora #0x0100
|
||||||
|
bra mX3st
|
||||||
|
mX3clr:
|
||||||
|
lda 0xf0
|
||||||
|
and #0x00ff
|
||||||
|
mX3st:
|
||||||
|
ldy #6
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #21
|
||||||
|
lda [0xe4],y ; enable | ptr<<8
|
||||||
|
and #0xff
|
||||||
|
beq mE3off
|
||||||
|
lda 0xee
|
||||||
|
ora #8
|
||||||
|
sta 0xee
|
||||||
|
mE3off:
|
||||||
|
; sprite 4: x = (msb ? 0x100 : 0) | col; enable bit
|
||||||
|
ldy #24
|
||||||
|
lda [0xe4],y ; col | msb<<8
|
||||||
|
sta 0xf0
|
||||||
|
and #0xff00
|
||||||
|
beq mX4clr
|
||||||
|
lda 0xf0
|
||||||
|
and #0x00ff
|
||||||
|
ora #0x0100
|
||||||
|
bra mX4st
|
||||||
|
mX4clr:
|
||||||
|
lda 0xf0
|
||||||
|
and #0x00ff
|
||||||
|
mX4st:
|
||||||
|
ldy #8
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #27
|
||||||
|
lda [0xe4],y ; enable | ptr<<8
|
||||||
|
and #0xff
|
||||||
|
beq mE4off
|
||||||
|
lda 0xee
|
||||||
|
ora #16
|
||||||
|
sta 0xee
|
||||||
|
mE4off:
|
||||||
|
; sprite 5: x = (msb ? 0x100 : 0) | col; enable bit
|
||||||
|
ldy #30
|
||||||
|
lda [0xe4],y ; col | msb<<8
|
||||||
|
sta 0xf0
|
||||||
|
and #0xff00
|
||||||
|
beq mX5clr
|
||||||
|
lda 0xf0
|
||||||
|
and #0x00ff
|
||||||
|
ora #0x0100
|
||||||
|
bra mX5st
|
||||||
|
mX5clr:
|
||||||
|
lda 0xf0
|
||||||
|
and #0x00ff
|
||||||
|
mX5st:
|
||||||
|
ldy #10
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #33
|
||||||
|
lda [0xe4],y ; enable | ptr<<8
|
||||||
|
and #0xff
|
||||||
|
beq mE5off
|
||||||
|
lda 0xee
|
||||||
|
ora #32
|
||||||
|
sta 0xee
|
||||||
|
mE5off:
|
||||||
|
; sprite 6: x = (msb ? 0x100 : 0) | col; enable bit
|
||||||
|
ldy #36
|
||||||
|
lda [0xe4],y ; col | msb<<8
|
||||||
|
sta 0xf0
|
||||||
|
and #0xff00
|
||||||
|
beq mX6clr
|
||||||
|
lda 0xf0
|
||||||
|
and #0x00ff
|
||||||
|
ora #0x0100
|
||||||
|
bra mX6st
|
||||||
|
mX6clr:
|
||||||
|
lda 0xf0
|
||||||
|
and #0x00ff
|
||||||
|
mX6st:
|
||||||
|
ldy #12
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #39
|
||||||
|
lda [0xe4],y ; enable | ptr<<8
|
||||||
|
and #0xff
|
||||||
|
beq mE6off
|
||||||
|
lda 0xee
|
||||||
|
ora #64
|
||||||
|
sta 0xee
|
||||||
|
mE6off:
|
||||||
|
; sprite 7: x = (msb ? 0x100 : 0) | col; enable bit
|
||||||
|
ldy #42
|
||||||
|
lda [0xe4],y ; col | msb<<8
|
||||||
|
sta 0xf0
|
||||||
|
and #0xff00
|
||||||
|
beq mX7clr
|
||||||
|
lda 0xf0
|
||||||
|
and #0x00ff
|
||||||
|
ora #0x0100
|
||||||
|
bra mX7st
|
||||||
|
mX7clr:
|
||||||
|
lda 0xf0
|
||||||
|
and #0x00ff
|
||||||
|
mX7st:
|
||||||
|
ldy #14
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #45
|
||||||
|
lda [0xe4],y ; enable | ptr<<8
|
||||||
|
and #0xff
|
||||||
|
beq mE7off
|
||||||
|
lda 0xee
|
||||||
|
ora #128
|
||||||
|
sta 0xee
|
||||||
|
mE7off:
|
||||||
|
; y[] block (16-bit stores; the junk high byte is repaired by the next store)
|
||||||
|
ldy #2
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #16
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #8
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #17
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #14
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #18
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #20
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #19
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #26
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #20
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #32
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #21
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #38
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #22
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #44
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #23
|
||||||
|
sta [0xe8],y
|
||||||
|
; ptr[] block (16-bit stores; the junk high byte is repaired by the next store)
|
||||||
|
ldy #4
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #24
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #10
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #25
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #16
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #26
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #22
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #27
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #28
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #28
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #34
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #29
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #40
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #30
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #46
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #31
|
||||||
|
sta [0xe8],y
|
||||||
|
; color[] block (16-bit stores; the junk high byte is repaired by the next store)
|
||||||
|
ldy #5
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #32
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #11
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #33
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #17
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #34
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #23
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #35
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #29
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #36
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #35
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #37
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #41
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #38
|
||||||
|
sta [0xe8],y
|
||||||
|
ldy #47
|
||||||
|
lda [0xe4],y
|
||||||
|
ldy #39
|
||||||
|
sta [0xe8],y
|
||||||
|
; enableMask | multiMask<<8
|
||||||
|
lda 0xec
|
||||||
|
xba
|
||||||
|
and #0xff00
|
||||||
|
ora 0xee
|
||||||
|
ldy #40
|
||||||
|
sta [0xe8],y
|
||||||
|
plp
|
||||||
|
rtl
|
||||||
|
|
@ -40,11 +40,9 @@
|
||||||
// cab, exhaust and passenger sets plus the intro star) or a cel change
|
// cab, exhaust and passenger sets plus the intro star) or a cel change
|
||||||
// mid-play rebuilds and recompiles a sprite. Glyphs are direct-mapped
|
// mid-play rebuilds and recompiles a sprite. Glyphs are direct-mapped
|
||||||
// by character, so the count is a power of two.
|
// by character, so the count is a power of two.
|
||||||
#if defined(__W65816__)
|
// Room for the baked cab/passenger/exhaust set (37) plus the largest
|
||||||
#define ST_SPRITE_CACHE 48u
|
// level's hook cels (I: 30) at once.
|
||||||
#else
|
#define ST_SPRITE_CACHE 80u
|
||||||
#define ST_SPRITE_CACHE 72u
|
|
||||||
#endif
|
|
||||||
#define ST_SPRITE_BACKUP_BYTES JOEY_SPRITE_BACKUP_BYTES(ST_SPRITE_TILES, ST_SPRITE_TILES)
|
#define ST_SPRITE_BACKUP_BYTES JOEY_SPRITE_BACKUP_BYTES(ST_SPRITE_TILES, ST_SPRITE_TILES)
|
||||||
#define ST_SPRITE_PX (8 * ST_SPRITE_TILES)
|
#define ST_SPRITE_PX (8 * ST_SPRITE_TILES)
|
||||||
// Palette slots the title logo animation owns. The title screen draws
|
// Palette slots the title logo animation owns. The title screen draws
|
||||||
|
|
@ -71,7 +69,9 @@ typedef struct {
|
||||||
bool used;
|
bool used;
|
||||||
bool pinned; // prewarmed moving cel: never evicted (its
|
bool pinned; // prewarmed moving cel: never evicted (its
|
||||||
// compiled code is costly to re-emit on the 65816)
|
// compiled code is costly to re-emit on the 65816)
|
||||||
|
bool compiled; // jlSpriteCompile succeeded for this cel
|
||||||
uint32_t lastUse; // for least-recently-used eviction
|
uint32_t lastUse; // for least-recently-used eviction
|
||||||
|
uint32_t retryAt; // cacheStamp at which a failed compile may retry
|
||||||
} StSpriteCacheT;
|
} StSpriteCacheT;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -91,6 +91,9 @@ typedef struct {
|
||||||
// Title logo (stRenderTitleLogo): all four flip-book frames in one
|
// Title logo (stRenderTitleLogo): all four flip-book frames in one
|
||||||
// tile, animated by rewriting logoSlotMask[]'s palette slots.
|
// tile, animated by rewriting logoSlotMask[]'s palette slots.
|
||||||
bool logoMode;
|
bool logoMode;
|
||||||
|
bool starMode; // level intro: cycling the starfield
|
||||||
|
uint8_t starPhase; // which ring the bright band is on
|
||||||
|
uint8_t starTick; // frames until the band steps outward
|
||||||
bool logoTileOk; // the live glyph is one of the frames
|
bool logoTileOk; // the live glyph is one of the frames
|
||||||
uint8_t logoSlotCount;
|
uint8_t logoSlotCount;
|
||||||
uint8_t logoSlotMask[ST_LOGO_PAL_SLOTS]; // frames each slot is lit in
|
uint8_t logoSlotMask[ST_LOGO_PAL_SLOTS]; // frames each slot is lit in
|
||||||
|
|
@ -100,8 +103,6 @@ typedef struct {
|
||||||
jlTileT logoTile;
|
jlTileT logoTile;
|
||||||
// Cell range (inclusive) each character has been painted in since
|
// Cell range (inclusive) each character has been painted in since
|
||||||
// the last full repaint: where a changed glyph's cells can be.
|
// the last full repaint: where a changed glyph's cells can be.
|
||||||
uint16_t glyphFirst[ST_CHARSET_CHARS];
|
|
||||||
uint16_t glyphLast[ST_CHARSET_CHARS];
|
|
||||||
// Column band of the cells about to be repainted, per character row
|
// Column band of the cells about to be repainted, per character row
|
||||||
// (min > max = the row is clean), the same shape as the library's
|
// (min > max = the row is clean), the same shape as the library's
|
||||||
// own per-row dirty bands.
|
// own per-row dirty bands.
|
||||||
|
|
@ -111,7 +112,10 @@ typedef struct {
|
||||||
StSpriteCacheT cache[ST_SPRITE_CACHE];
|
StSpriteCacheT cache[ST_SPRITE_CACHE];
|
||||||
StSpriteCacheT *lastHit[ST_HW_SPRITES]; // the entry each hardware sprite used last
|
StSpriteCacheT *lastHit[ST_HW_SPRITES]; // the entry each hardware sprite used last
|
||||||
jlSpriteBackupT backup[ST_HW_SPRITES];
|
jlSpriteBackupT backup[ST_HW_SPRITES];
|
||||||
uint8_t backupMem[ST_HW_SPRITES][ST_SPRITE_BACKUP_BYTES] __attribute__((aligned(2)));
|
// ST_HW_SPRITES backups of ST_SPRITE_BACKUP_BYTES each, from jlAlloc:
|
||||||
|
// 4.5 KB that as BSS would not fit the IIgs entry bank (text, rodata,
|
||||||
|
// BSS and the C heap share its 64 KB).
|
||||||
|
uint8_t *backupMem;
|
||||||
StDrawnT slot[ST_HW_SPRITES];
|
StDrawnT slot[ST_HW_SPRITES];
|
||||||
uint8_t lastPresentFrame;
|
uint8_t lastPresentFrame;
|
||||||
} StRenderStateT;
|
} StRenderStateT;
|
||||||
|
|
@ -132,9 +136,26 @@ static const uint16_t kC64Palette[16] = {
|
||||||
// four of these are used; the rest are headroom if the frames change.
|
// four of these are used; the rest are headroom if the frames change.
|
||||||
static const uint8_t kLogoPalSlot[ST_LOGO_PAL_SLOTS] = { 4u, 8u, 9u, 10u, 13u, 14u, 15u };
|
static const uint8_t kLogoPalSlot[ST_LOGO_PAL_SLOTS] = { 4u, 8u, 9u, 10u, 13u, 14u, 15u };
|
||||||
|
|
||||||
|
// Level-intro warp: how a star ring looks at each distance BEHIND the
|
||||||
|
// bright band, as C64 colour indices (white, light grey, grey, dark
|
||||||
|
// grey, then black). A ring this far behind the band or further is
|
||||||
|
// unlit, so a lit head with a short fading tail sweeps outward and the
|
||||||
|
// rest of the field stays dark.
|
||||||
|
static const uint8_t kStarRamp[] = { 1u, 15u, 12u, 11u };
|
||||||
|
#define ST_STAR_RAMP_LEN (sizeof(kStarRamp) / sizeof(kStarRamp[0]))
|
||||||
|
// Frames per ring step. The intro runs at the game's 30 Hz tick, so a
|
||||||
|
// step every other frame sweeps the eight rings in about half a second.
|
||||||
|
#define ST_STAR_STEP_FRAMES 2u
|
||||||
|
|
||||||
|
|
||||||
static void buildSpriteCel(const uint8_t *bm, uint8_t multi, uint8_t color, uint8_t mc0, uint8_t mc1);
|
static void buildSpriteCel(const uint8_t *bm, uint8_t multi, uint8_t color, uint8_t mc0, uint8_t mc1);
|
||||||
static jlSpriteT *cachedSprite(StSimT *sim, uint8_t idx);
|
static jlSpriteT *cachedSprite(StSimT *sim, uint8_t idx);
|
||||||
|
static void cacheCompile(StSpriteCacheT *e);
|
||||||
|
static StSpriteCacheT *cacheFreeSlot(void);
|
||||||
|
static void levelCelsEvict(void);
|
||||||
|
static const uint8_t *levelSpriteBitmap(const StLevelT *level, uint8_t ptr);
|
||||||
|
static void loadingBar(jlSurfaceT *stage, uint8_t done, uint8_t total);
|
||||||
|
static uint8_t cellCol(uint16_t cell, uint8_t row);
|
||||||
static uint8_t cellRow(uint16_t cell);
|
static uint8_t cellRow(uint16_t cell);
|
||||||
static void collectGlyphs(StSimT *sim);
|
static void collectGlyphs(StSimT *sim);
|
||||||
static void dirtyBands(const StSimT *sim);
|
static void dirtyBands(const StSimT *sim);
|
||||||
|
|
@ -145,6 +166,7 @@ static bool loadPrecompiledCels(void);
|
||||||
static bool logoBuildTile(const StSimT *sim);
|
static bool logoBuildTile(const StSimT *sim);
|
||||||
static uint8_t logoFrameIndex(const StSimT *sim);
|
static uint8_t logoFrameIndex(const StSimT *sim);
|
||||||
static void logoPaletteApply(const StSimT *sim, uint8_t frame);
|
static void logoPaletteApply(const StSimT *sim, uint8_t frame);
|
||||||
|
static void starPaletteApply(void);
|
||||||
static void paintCells(jlSurfaceT *stage, StSimT *sim);
|
static void paintCells(jlSurfaceT *stage, StSimT *sim);
|
||||||
static void pasteCell(jlSurfaceT *stage, const StSimT *sim, uint16_t cell, uint8_t bx, uint8_t by);
|
static void pasteCell(jlSurfaceT *stage, const StSimT *sim, uint16_t cell, uint8_t bx, uint8_t by);
|
||||||
static bool spriteOnScreen(int16_t px, int16_t py);
|
static bool spriteOnScreen(int16_t px, int16_t py);
|
||||||
|
|
@ -171,12 +193,27 @@ static void buildSpriteCel(const uint8_t *bm, uint8_t multi, uint8_t color, uint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Compile a cached cel if it is not already, backing off after a failure
|
||||||
|
// (arena full) so a cel that cannot fit is not re-staged every frame --
|
||||||
|
// jlSpriteCompile's failure path runs the whole emitter twice.
|
||||||
|
#define ST_COMPILE_RETRY_STAMPS 4096u // ~8 frames' worth of cachedSprite calls per frame -> ~25 s
|
||||||
|
static void cacheCompile(StSpriteCacheT *e) {
|
||||||
|
if (e->compiled || gRender.cacheStamp < e->retryAt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e->compiled = jlSpriteCompile(e->sprite);
|
||||||
|
if (!e->compiled) {
|
||||||
|
e->retryAt = gRender.cacheStamp + ST_COMPILE_RETRY_STAMPS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// The JoeyLib sprite for hardware sprite `idx` of the current frame,
|
// The JoeyLib sprite for hardware sprite `idx` of the current frame,
|
||||||
// built on first use from its VIC bitmap and colours.
|
// built on first use from its VIC bitmap and colours.
|
||||||
static jlSpriteT *cachedSprite(StSimT *sim, uint8_t idx) {
|
static jlSpriteT *cachedSprite(StSimT *sim, uint8_t idx) {
|
||||||
uint8_t ptr = sim->frame.ptr[idx];
|
uint8_t ptr = sim->frame.ptr[idx];
|
||||||
uint8_t color = drawColor(sim, idx);
|
uint8_t color = drawColor(sim, idx);
|
||||||
uint8_t multi = (uint8_t)((sim->frame.multiMask >> idx) & 1u);
|
uint8_t multi = (uint8_t)((sim->frame.multiMask & kStBit[idx]) != 0u ? 1u : 0u);
|
||||||
uint8_t mc0 = multi ? sim->spriteMc0 : 0u;
|
uint8_t mc0 = multi ? sim->spriteMc0 : 0u;
|
||||||
uint8_t mc1 = multi ? sim->spriteMc1 : 0u;
|
uint8_t mc1 = multi ? sim->spriteMc1 : 0u;
|
||||||
uint8_t level = (ptr < ST_SPRITE_PTR_FIRST && sim->level != 0) ? sim->level->levelIndex : 0xFFu;
|
uint8_t level = (ptr < ST_SPRITE_PTR_FIRST && sim->level != 0) ? sim->level->levelIndex : 0xFFu;
|
||||||
|
|
@ -213,20 +250,36 @@ static jlSpriteT *cachedSprite(StSimT *sim, uint8_t idx) {
|
||||||
// so fail into the interpreter path.
|
// so fail into the interpreter path.
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
// No bitmap for this pointer (hooks enable a sprite a tick before
|
||||||
|
// they aim it, leaving it at block 0): nothing to draw, and no cel
|
||||||
|
// to build and evict for.
|
||||||
|
if (stSimSpriteBitmap(sim, ptr) == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (slot->used) {
|
if (slot->used) {
|
||||||
jlSpriteDestroy(slot->sprite);
|
jlSpriteDestroy(slot->sprite);
|
||||||
slot->sprite = 0;
|
slot->sprite = 0;
|
||||||
slot->used = false;
|
slot->used = false;
|
||||||
|
slot->compiled = false;
|
||||||
}
|
}
|
||||||
buildSpriteCel(stSimSpriteBitmap(sim, ptr), multi, color, mc0, mc1);
|
buildSpriteCel(stSimSpriteBitmap(sim, ptr), multi, color, mc0, mc1);
|
||||||
slot->sprite = jlSpriteCreateFromSurface(gRender.scratch, 0, 0, ST_SPRITE_TILES, ST_SPRITE_TILES);
|
slot->sprite = jlSpriteCreateFromSurface(gRender.scratch, 0, 0, ST_SPRITE_TILES, ST_SPRITE_TILES);
|
||||||
if (slot->sprite == 0) {
|
if (slot->sprite == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// Only the cab, exhaust and passenger move every tick; the codegen
|
// Nothing compiles mid-ride: a compile is ~0.5 s on the 65816, a
|
||||||
// arena is theirs. Everything else stays interpreted (drawn seldom).
|
// visible hitch. The cab, exhaust and passenger cels come baked
|
||||||
if (idx <= 2u) {
|
// (stRenderPrewarm), a level's hook cels come baked or are compiled
|
||||||
(void)jlSpriteCompile(slot->sprite);
|
// at level entry (stRenderLevelCels), and a cel that still gets
|
||||||
|
// built here draws interpreted -- and is logged, because it means
|
||||||
|
// the level's cel list (stLevelCels.c) missed one.
|
||||||
|
slot->compiled = false;
|
||||||
|
slot->retryAt = 0u;
|
||||||
|
if (idx <= 2u && gPrewarming) {
|
||||||
|
cacheCompile(slot);
|
||||||
|
}
|
||||||
|
if (!gPrewarming) {
|
||||||
|
ST_LOG("spacetaxi: ! cel $%02X colour %u (level %u) built mid-ride -- add it to stLevelCelList", ptr, color, level);
|
||||||
}
|
}
|
||||||
slot->lastUse = gRender.cacheStamp;
|
slot->lastUse = gRender.cacheStamp;
|
||||||
slot->ptr = ptr;
|
slot->ptr = ptr;
|
||||||
|
|
@ -257,6 +310,19 @@ static const uint8_t kCellRow[125] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Column of a cell whose row is known: cell - row * 40 through a table,
|
||||||
|
// because the two-shift form of the multiply came back as a __mulhi3
|
||||||
|
// library call on the 65816.
|
||||||
|
static uint8_t cellCol(uint16_t cell, uint8_t row) {
|
||||||
|
static const uint16_t kRowBase[ST_SCREEN_ROWS] = {
|
||||||
|
0, 40, 80, 120, 160, 200, 240, 280, 320, 360, 400, 440, 480,
|
||||||
|
520, 560, 600, 640, 680, 720, 760, 800, 840, 880, 920, 960
|
||||||
|
};
|
||||||
|
return (uint8_t)(cell - kRowBase[row]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static uint8_t cellRow(uint16_t cell) {
|
static uint8_t cellRow(uint16_t cell) {
|
||||||
return kCellRow[cell >> 3];
|
return kCellRow[cell >> 3];
|
||||||
}
|
}
|
||||||
|
|
@ -271,7 +337,7 @@ static uint8_t cellRow(uint16_t cell) {
|
||||||
static void collectGlyphs(StSimT *sim) {
|
static void collectGlyphs(StSimT *sim) {
|
||||||
uint8_t k;
|
uint8_t k;
|
||||||
uint16_t cell;
|
uint16_t cell;
|
||||||
uint16_t last;
|
uint16_t next;
|
||||||
|
|
||||||
if (sim->charDirtyAll) {
|
if (sim->charDirtyAll) {
|
||||||
sim->charDirtyAll = false;
|
sim->charDirtyAll = false;
|
||||||
|
|
@ -283,18 +349,33 @@ static void collectGlyphs(StSimT *sim) {
|
||||||
if (sim->dirtyAll) {
|
if (sim->dirtyAll) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
last = gRender.glyphLast[ch];
|
// Exactly the cells showing `ch`, straight off the reverse index
|
||||||
for (cell = gRender.glyphFirst[ch]; cell <= last; cell++) {
|
// the sim maintains (StSimT in stSim.h). No scan, no screen[]
|
||||||
if (sim->screen[cell] != ch || sim->cellDirty[cell] != 0u) {
|
// test: a cell is in this list if and only if it shows `ch`.
|
||||||
continue;
|
//
|
||||||
}
|
// This replaced a walk of a per-glyph [first, last] span. That
|
||||||
sim->cellDirty[cell] = 1u;
|
// span only ever widened -- pasteCell pushed it out and nothing
|
||||||
if (sim->dirtyCount < (uint16_t)(sizeof(sim->dirtyList) / sizeof(sim->dirtyList[0]))) {
|
// pulled it back -- so a glyph that once appeared in transient
|
||||||
sim->dirtyList[sim->dirtyCount++] = cell;
|
// text kept a screen-wide span for the rest of the level and was
|
||||||
} else {
|
// rescanned on every animation frame, at 24-bit far-pointer cost
|
||||||
sim->dirtyAll = true;
|
// per cell. It measured 22% of the whole program on level H.
|
||||||
break;
|
//
|
||||||
|
// `next` is read before the body because the overflow arm below
|
||||||
|
// stops the walk, and because keeping the read up here makes the
|
||||||
|
// loop independent of anything the body does to the cell.
|
||||||
|
cell = sim->glyphHead[ch];
|
||||||
|
while (cell != ST_GLYPH_CELL_NONE) {
|
||||||
|
next = sim->cellNext[cell];
|
||||||
|
if (sim->cellDirty[cell] == 0u) {
|
||||||
|
sim->cellDirty[cell] = 1u;
|
||||||
|
if (sim->dirtyCount < (uint16_t)(sizeof(sim->dirtyList) / sizeof(sim->dirtyList[0]))) {
|
||||||
|
sim->dirtyList[sim->dirtyCount++] = cell;
|
||||||
|
} else {
|
||||||
|
sim->dirtyAll = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
cell = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sim->charDirtyCount = 0u;
|
sim->charDirtyCount = 0u;
|
||||||
|
|
@ -331,7 +412,7 @@ static void dirtyBands(const StSimT *sim) {
|
||||||
for (k = 0u; k < sim->dirtyCount; k++) {
|
for (k = 0u; k < sim->dirtyCount; k++) {
|
||||||
uint16_t cell = sim->dirtyList[k];
|
uint16_t cell = sim->dirtyList[k];
|
||||||
uint8_t r = cellRow(cell);
|
uint8_t r = cellRow(cell);
|
||||||
uint8_t col = (uint8_t)(cell - ((uint16_t)r << 5) - ((uint16_t)r << 3));
|
uint8_t col = cellCol(cell, r);
|
||||||
if (col < gRender.dirtyColMin[r]) {
|
if (col < gRender.dirtyColMin[r]) {
|
||||||
gRender.dirtyColMin[r] = col;
|
gRender.dirtyColMin[r] = col;
|
||||||
}
|
}
|
||||||
|
|
@ -422,7 +503,7 @@ static bool logoBuildTile(const StSimT *sim) {
|
||||||
uint8_t f;
|
uint8_t f;
|
||||||
uint8_t slot;
|
uint8_t slot;
|
||||||
for (f = 0u; f < ST_LOGO_FRAMES; f++) {
|
for (f = 0u; f < ST_LOGO_FRAMES; f++) {
|
||||||
if (((sim->charset[ST_LOGO_FRAME_FIRST + f][row] >> (7u - col)) & 1u) != 0u) {
|
if (((stSimGlyphRow(sim, (uint8_t)(ST_LOGO_FRAME_FIRST + f), row) >> (7u - col)) & 1u) != 0u) {
|
||||||
mask |= (uint8_t)(1u << f);
|
mask |= (uint8_t)(1u << f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -454,7 +535,8 @@ static uint8_t logoFrameIndex(const StSimT *sim) {
|
||||||
uint8_t f;
|
uint8_t f;
|
||||||
|
|
||||||
for (f = 0u; f < ST_LOGO_FRAMES; f++) {
|
for (f = 0u; f < ST_LOGO_FRAMES; f++) {
|
||||||
if (memcmp(sim->charset[ST_LOGO_CHAR], sim->charset[ST_LOGO_FRAME_FIRST + f], 8u) == 0) {
|
if (memcmp(stSimGlyph(sim, ST_LOGO_CHAR),
|
||||||
|
stSimGlyph(sim, (uint8_t)(ST_LOGO_FRAME_FIRST + f)), 8u) == 0) {
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -494,8 +576,6 @@ static void paintCells(jlSurfaceT *stage, StSimT *sim) {
|
||||||
uint8_t by;
|
uint8_t by;
|
||||||
|
|
||||||
if (sim->dirtyAll) {
|
if (sim->dirtyAll) {
|
||||||
memset(gRender.glyphFirst, 0xFF, sizeof(gRender.glyphFirst));
|
|
||||||
memset(gRender.glyphLast, 0, sizeof(gRender.glyphLast));
|
|
||||||
cell = 0u;
|
cell = 0u;
|
||||||
for (by = 0u; by < ST_SCREEN_ROWS; by++) {
|
for (by = 0u; by < ST_SCREEN_ROWS; by++) {
|
||||||
for (bx = 0u; bx < ST_SCREEN_COLS; bx++) {
|
for (bx = 0u; bx < ST_SCREEN_COLS; bx++) {
|
||||||
|
|
@ -511,7 +591,7 @@ static void paintCells(jlSurfaceT *stage, StSimT *sim) {
|
||||||
for (k = 0u; k < sim->dirtyCount; k++) {
|
for (k = 0u; k < sim->dirtyCount; k++) {
|
||||||
cell = sim->dirtyList[k];
|
cell = sim->dirtyList[k];
|
||||||
by = cellRow(cell);
|
by = cellRow(cell);
|
||||||
bx = (uint8_t)(cell - ((uint16_t)by << 5) - ((uint16_t)by << 3));
|
bx = cellCol(cell, by);
|
||||||
pasteCell(stage, sim, cell, bx, by);
|
pasteCell(stage, sim, cell, bx, by);
|
||||||
sim->cellDirty[cell] = 0u;
|
sim->cellDirty[cell] = 0u;
|
||||||
}
|
}
|
||||||
|
|
@ -525,12 +605,6 @@ static void paintCells(jlSurfaceT *stage, StSimT *sim) {
|
||||||
static void pasteCell(jlSurfaceT *stage, const StSimT *sim, uint16_t cell, uint8_t bx, uint8_t by) {
|
static void pasteCell(jlSurfaceT *stage, const StSimT *sim, uint16_t cell, uint8_t bx, uint8_t by) {
|
||||||
uint8_t chr = sim->screen[cell];
|
uint8_t chr = sim->screen[cell];
|
||||||
|
|
||||||
if (cell < gRender.glyphFirst[chr]) {
|
|
||||||
gRender.glyphFirst[chr] = cell;
|
|
||||||
}
|
|
||||||
if (cell > gRender.glyphLast[chr]) {
|
|
||||||
gRender.glyphLast[chr] = cell;
|
|
||||||
}
|
|
||||||
// On the title the logo's cells carry all four flip-book frames at
|
// On the title the logo's cells carry all four flip-book frames at
|
||||||
// once; the palette decides which one shows, so they are pasted as
|
// once; the palette decides which one shows, so they are pasted as
|
||||||
// they are and never touched again.
|
// they are and never touched again.
|
||||||
|
|
@ -538,7 +612,30 @@ static void pasteCell(jlSurfaceT *stage, const StSimT *sim, uint16_t cell, uint8
|
||||||
jlTilePaste(stage, bx, by, &gRender.logoTile);
|
jlTilePaste(stage, bx, by, &gRender.logoTile);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
jlTilePasteGlyph(stage, bx, by, sim->charset[chr], sim->color[cell], sim->bgColor);
|
jlTilePasteGlyph(stage, bx, by, stSimGlyph(sim, chr), sim->color[cell], sim->bgColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Light the starfield's ring colours for the current phase: the ring
|
||||||
|
// the band is on takes kStarRamp[0], the ones just inside it the rest
|
||||||
|
// of the ramp, and everything else the background. One palette write
|
||||||
|
// per step and not a single pixel moves -- that is the whole point of
|
||||||
|
// ST_STAR_RING_FIRST (stSim.h).
|
||||||
|
static void starPaletteApply(void) {
|
||||||
|
uint16_t pal[SURFACE_COLORS_PER_PALETTE];
|
||||||
|
uint16_t color;
|
||||||
|
uint8_t behind;
|
||||||
|
uint8_t ring;
|
||||||
|
|
||||||
|
jlPaletteGet(gRender.stage, 0u, pal);
|
||||||
|
for (ring = 0u; ring < ST_STAR_RING_COUNT; ring++) {
|
||||||
|
// How far this ring sits behind the band, wrapping so the band
|
||||||
|
// re-enters at the centre as it leaves the edge.
|
||||||
|
behind = (uint8_t)((gRender.starPhase - ring) & (ST_STAR_RING_COUNT - 1u));
|
||||||
|
color = (behind < ST_STAR_RAMP_LEN) ? kC64Palette[kStarRamp[behind]] : kC64Palette[0];
|
||||||
|
pal[ST_STAR_RING_FIRST + ring] = color;
|
||||||
|
}
|
||||||
|
jlPaletteSet(gRender.stage, 0u, pal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -598,15 +695,26 @@ void stRenderFrame(jlSurfaceT *stage, StSimT *sim) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (gRender.starMode) {
|
||||||
|
// The warp is the only thing animating here, and it animates in
|
||||||
|
// the palette: no cells change, no sprite moves for it, so the
|
||||||
|
// present that ends this frame copies no pixels at all.
|
||||||
|
gRender.starTick++;
|
||||||
|
if (gRender.starTick >= ST_STAR_STEP_FRAMES) {
|
||||||
|
gRender.starTick = 0u;
|
||||||
|
gRender.starPhase = (uint8_t)((gRender.starPhase + 1u) & (ST_STAR_RING_COUNT - 1u));
|
||||||
|
starPaletteApply();
|
||||||
|
}
|
||||||
|
}
|
||||||
collectGlyphs(sim);
|
collectGlyphs(sim);
|
||||||
dirtyBands(sim);
|
dirtyBands(sim);
|
||||||
for (k = 0u; k < ST_HW_SPRITES; k++) {
|
for (k = 0u; k < ST_HW_SPRITES; k++) {
|
||||||
uint8_t idx = (uint8_t)(ST_HW_SPRITES - 1u - k);
|
uint8_t idx = (uint8_t)(ST_HW_SPRITES - 1u - k);
|
||||||
StDrawnT *d = &gRender.slot[k];
|
StDrawnT *d = &gRender.slot[k];
|
||||||
uint8_t multi = (uint8_t)((sim->frame.multiMask >> idx) & 1u);
|
uint8_t multi = (uint8_t)((sim->frame.multiMask & kStBit[idx]) != 0u ? 1u : 0u);
|
||||||
int16_t px = (int16_t)((int16_t)sim->frame.x[idx] - ST_SPRITE_X_ORIGIN);
|
int16_t px = (int16_t)((int16_t)sim->frame.x[idx] - ST_SPRITE_X_ORIGIN);
|
||||||
int16_t py = (int16_t)((int16_t)sim->frame.y[idx] - ST_SPRITE_Y_ORIGIN);
|
int16_t py = (int16_t)((int16_t)sim->frame.y[idx] - ST_SPRITE_Y_ORIGIN);
|
||||||
bool want = (sim->frame.enableMask & (uint8_t)(1u << idx)) != 0u && spriteOnScreen(px, py);
|
bool want = (sim->frame.enableMask & kStBit[idx]) != 0u && spriteOnScreen(px, py);
|
||||||
bool same = (want == d->drawn);
|
bool same = (want == d->drawn);
|
||||||
if (same && want) {
|
if (same && want) {
|
||||||
same = (d->x == sim->frame.x[idx] && d->y == sim->frame.y[idx] && d->ptr == sim->frame.ptr[idx] && d->color == drawColor(sim, idx) && d->multi == multi);
|
same = (d->x == sim->frame.x[idx] && d->y == sim->frame.y[idx] && d->ptr == sim->frame.ptr[idx] && d->color == drawColor(sim, idx) && d->multi == multi);
|
||||||
|
|
@ -633,7 +741,7 @@ void stRenderFrame(jlSurfaceT *stage, StSimT *sim) {
|
||||||
jlSpriteT *sp;
|
jlSpriteT *sp;
|
||||||
int16_t px;
|
int16_t px;
|
||||||
int16_t py;
|
int16_t py;
|
||||||
if ((sim->frame.enableMask & (uint8_t)(1u << idx)) == 0u) {
|
if ((sim->frame.enableMask & kStBit[idx]) == 0u) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
px = (int16_t)((int16_t)sim->frame.x[idx] - ST_SPRITE_X_ORIGIN);
|
px = (int16_t)((int16_t)sim->frame.x[idx] - ST_SPRITE_X_ORIGIN);
|
||||||
|
|
@ -651,7 +759,7 @@ void stRenderFrame(jlSurfaceT *stage, StSimT *sim) {
|
||||||
d->y = sim->frame.y[idx];
|
d->y = sim->frame.y[idx];
|
||||||
d->ptr = sim->frame.ptr[idx];
|
d->ptr = sim->frame.ptr[idx];
|
||||||
d->color = drawColor(sim, idx);
|
d->color = drawColor(sim, idx);
|
||||||
d->multi = (uint8_t)((sim->frame.multiMask >> idx) & 1u);
|
d->multi = (uint8_t)((sim->frame.multiMask & kStBit[idx]) != 0u ? 1u : 0u);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
// Sync to the retrace unless the frame already spans more than
|
// Sync to the retrace unless the frame already spans more than
|
||||||
|
|
@ -667,13 +775,20 @@ void stRenderFrame(jlSurfaceT *stage, StSimT *sim) {
|
||||||
|
|
||||||
|
|
||||||
void stRenderInit(jlSurfaceT *stage) {
|
void stRenderInit(jlSurfaceT *stage) {
|
||||||
uint8_t k;
|
uint8_t *backupMem = gRender.backupMem;
|
||||||
|
uint8_t k;
|
||||||
|
|
||||||
memset(&gRender, 0, sizeof(gRender));
|
memset(&gRender, 0, sizeof(gRender));
|
||||||
memset(gRender.glyphFirst, 0xFF, sizeof(gRender.glyphFirst));
|
if (backupMem == 0) {
|
||||||
gRender.stage = stage;
|
backupMem = (uint8_t *)jlAlloc((uint32_t)ST_HW_SPRITES * ST_SPRITE_BACKUP_BYTES);
|
||||||
|
if (backupMem == 0) {
|
||||||
|
ST_LOG("stRenderInit: no memory for sprite backups");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gRender.backupMem = backupMem;
|
||||||
|
gRender.stage = stage;
|
||||||
for (k = 0u; k < ST_HW_SPRITES; k++) {
|
for (k = 0u; k < ST_HW_SPRITES; k++) {
|
||||||
gRender.backup[k].bytes = gRender.backupMem[k];
|
gRender.backup[k].bytes = (backupMem == 0) ? 0 : backupMem + (uint16_t)k * ST_SPRITE_BACKUP_BYTES;
|
||||||
}
|
}
|
||||||
jlPaletteSet(stage, 0u, kC64Palette);
|
jlPaletteSet(stage, 0u, kC64Palette);
|
||||||
jlScbSetRange(stage, 0u, (uint16_t)(SURFACE_HEIGHT - 1u), 0u);
|
jlScbSetRange(stage, 0u, (uint16_t)(SURFACE_HEIGHT - 1u), 0u);
|
||||||
|
|
@ -695,10 +810,145 @@ void stRenderInit(jlSurfaceT *stage) {
|
||||||
// cache so no cel is JIT-compiled at boot (each 65816 compile costs
|
// cache so no cel is JIT-compiled at boot (each 65816 compile costs
|
||||||
// ~0.4 s). Every kStCels entry becomes a pinned cache slot keyed exactly
|
// ~0.4 s). Every kStCels entry becomes a pinned cache slot keyed exactly
|
||||||
// as the play lookups: system ptr (level 0xFF), the level-wide colours.
|
// as the play lookups: system ptr (level 0xFF), the level-wide colours.
|
||||||
|
// The first unused cache slot, or NULL when the cache is full.
|
||||||
|
static StSpriteCacheT *cacheFreeSlot(void) {
|
||||||
|
uint8_t k;
|
||||||
|
|
||||||
|
for (k = 0u; k < ST_SPRITE_CACHE; k++) {
|
||||||
|
if (!gRender.cache[k].used) {
|
||||||
|
return &gRender.cache[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Drop every cel that belongs to a level (the baked set is level 0xFF and
|
||||||
|
// stays), freeing its arena code, and forget any hardware sprite's last
|
||||||
|
// hit on it.
|
||||||
|
static void levelCelsEvict(void) {
|
||||||
|
uint8_t k;
|
||||||
|
|
||||||
|
for (k = 0u; k < ST_SPRITE_CACHE; k++) {
|
||||||
|
StSpriteCacheT *e = &gRender.cache[k];
|
||||||
|
uint8_t h;
|
||||||
|
if (!e->used || e->level == 0xFFu) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (h = 0u; h < ST_HW_SPRITES; h++) {
|
||||||
|
if (gRender.lastHit[h] == e) {
|
||||||
|
gRender.lastHit[h] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jlSpriteDestroy(e->sprite);
|
||||||
|
e->sprite = 0;
|
||||||
|
e->used = false;
|
||||||
|
e->pinned = false;
|
||||||
|
e->compiled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// A level's own sprite block, or NULL when the level does not ship it.
|
||||||
|
static const uint8_t *levelSpriteBitmap(const StLevelT *level, uint8_t ptr) {
|
||||||
|
uint8_t k;
|
||||||
|
|
||||||
|
for (k = 0u; k < level->spriteCount; k++) {
|
||||||
|
if (level->sprites[k].ptr == ptr) {
|
||||||
|
return level->sprites[k].bitmap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void loadingBar(jlSurfaceT *stage, uint8_t done, uint8_t total) {
|
||||||
|
jlFillRect(stage, 60, 110, (uint16_t)((uint16_t)done * 200u / total), 6u, 1u);
|
||||||
|
jlStagePresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Ready a level's hook cels BEFORE the ride: the previous level's cels
|
||||||
|
// go, then the level's list (stLevelCelList) is filled from its baked
|
||||||
|
// bank (sprites/levelNN.spc) or, when there is none, built and compiled
|
||||||
|
// here behind the LOADING bar. Either way nothing compiles once the
|
||||||
|
// level is running. Keyed exactly as cachedSprite keys them, so every
|
||||||
|
// lookup during the ride is a hit.
|
||||||
|
void stRenderLevelCels(jlSurfaceT *stage, const StLevelT *level) {
|
||||||
|
static StCelDefT list[ST_LEVEL_CELS_MAX];
|
||||||
|
jlSpriteT *cels[ST_LEVEL_CELS_MAX];
|
||||||
|
char path[24];
|
||||||
|
uint8_t n;
|
||||||
|
uint8_t got;
|
||||||
|
uint8_t k;
|
||||||
|
|
||||||
|
levelCelsEvict();
|
||||||
|
if (level == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
n = stLevelCelList(level, list, ST_LEVEL_CELS_MAX);
|
||||||
|
if (n == 0u) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(path, "sprites/level", 13u);
|
||||||
|
path[13] = (char)('0' + (level->levelIndex + 1u) / 10u);
|
||||||
|
path[14] = (char)('0' + (level->levelIndex + 1u) % 10u);
|
||||||
|
memcpy(path + 15, ".spc", 5u);
|
||||||
|
got = (uint8_t)jlSpriteBankLoadPrecompiled(path, cels, n, 0);
|
||||||
|
if (got == n) {
|
||||||
|
ST_LOG("spacetaxi: level %u cels from %s (%u)", level->levelIndex, path, n);
|
||||||
|
} else {
|
||||||
|
for (k = 0u; k < got; k++) {
|
||||||
|
jlSpriteDestroy(cels[k]);
|
||||||
|
}
|
||||||
|
ST_LOG("spacetaxi: level %u: no usable %s -- compiling %u cels at entry", level->levelIndex, path, n);
|
||||||
|
stRenderLoading(stage);
|
||||||
|
}
|
||||||
|
gPrewarming = true;
|
||||||
|
for (k = 0u; k < n; k++) {
|
||||||
|
StSpriteCacheT *slot = cacheFreeSlot();
|
||||||
|
if (slot == 0) {
|
||||||
|
ST_LOG("spacetaxi: ! sprite cache full at level cel %u", k);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (got == n) {
|
||||||
|
slot->sprite = cels[k];
|
||||||
|
slot->compiled = true;
|
||||||
|
} else {
|
||||||
|
const uint8_t *bm = levelSpriteBitmap(level, list[k].ptr);
|
||||||
|
if (bm == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
buildSpriteCel(bm, list[k].multi, list[k].color, list[k].mc0, list[k].mc1);
|
||||||
|
slot->sprite = jlSpriteCreateFromSurface(gRender.scratch, 0, 0, ST_SPRITE_TILES, ST_SPRITE_TILES);
|
||||||
|
if (slot->sprite == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
slot->compiled = false;
|
||||||
|
slot->retryAt = 0u;
|
||||||
|
cacheCompile(slot);
|
||||||
|
loadingBar(stage, (uint8_t)(k + 1u), n);
|
||||||
|
}
|
||||||
|
slot->ptr = list[k].ptr;
|
||||||
|
slot->color = list[k].color;
|
||||||
|
slot->multi = list[k].multi;
|
||||||
|
slot->mc0 = list[k].mc0;
|
||||||
|
slot->mc1 = list[k].mc1;
|
||||||
|
slot->level = level->levelIndex;
|
||||||
|
slot->used = true;
|
||||||
|
slot->pinned = true;
|
||||||
|
slot->lastUse = gRender.cacheStamp;
|
||||||
|
}
|
||||||
|
gPrewarming = false;
|
||||||
|
if (got != n) {
|
||||||
|
jlFillRect(stage, 60, 110, 200u, 6u, 0u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Returns false if the .spc is missing or built for another target/shift
|
// Returns false if the .spc is missing or built for another target/shift
|
||||||
// count (other ports, or a stale bake), so the caller falls back to the
|
// count (a stale bake), so the caller falls back to the runtime JIT
|
||||||
// runtime JIT prewarm below. Its cross-platform contract makes this a
|
// prewarm below. Every port stages its own bake (make/<port>.mk).
|
||||||
// no-op everywhere but the IIgs, where the bake is wired.
|
|
||||||
static bool loadPrecompiledCels(void) {
|
static bool loadPrecompiledCels(void) {
|
||||||
jlSpriteT *cels[64];
|
jlSpriteT *cels[64];
|
||||||
uint16_t n;
|
uint16_t n;
|
||||||
|
|
@ -761,8 +1011,10 @@ void stRenderPrewarm(jlSurfaceT *stage, StSimT *sim) {
|
||||||
// Precompiled bank: instant, no per-cel JIT. Falls through to the
|
// Precompiled bank: instant, no per-cel JIT. Falls through to the
|
||||||
// runtime build below on any port without a matching .spc.
|
// runtime build below on any port without a matching .spc.
|
||||||
if (loadPrecompiledCels()) {
|
if (loadPrecompiledCels()) {
|
||||||
|
ST_LOG("spacetaxi: cels from sprites/staxicels.spc");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
ST_LOG("spacetaxi: ! no usable sprites/staxicels.spc -- JIT prewarm");
|
||||||
saved = sim->frame;
|
saved = sim->frame;
|
||||||
gPrewarming = true;
|
gPrewarming = true;
|
||||||
sim->frame.multiMask = 0x07u;
|
sim->frame.multiMask = 0x07u;
|
||||||
|
|
@ -773,24 +1025,21 @@ void stRenderPrewarm(jlSurfaceT *stage, StSimT *sim) {
|
||||||
sim->frame.color[0] = 0x06u;
|
sim->frame.color[0] = 0x06u;
|
||||||
(void)cachedSprite(sim, 0u);
|
(void)cachedSprite(sim, 0u);
|
||||||
done++;
|
done++;
|
||||||
jlFillRect(stage, 60, 110, (uint16_t)((uint16_t)done * 200u / total), 6u, 1u);
|
loadingBar(stage, done, total);
|
||||||
jlStagePresent();
|
|
||||||
}
|
}
|
||||||
for (k = 0u; k < sizeof(kPassPtrs); k++) {
|
for (k = 0u; k < sizeof(kPassPtrs); k++) {
|
||||||
sim->frame.ptr[1] = kPassPtrs[k];
|
sim->frame.ptr[1] = kPassPtrs[k];
|
||||||
sim->frame.color[1] = 0x06u;
|
sim->frame.color[1] = 0x06u;
|
||||||
(void)cachedSprite(sim, 1u);
|
(void)cachedSprite(sim, 1u);
|
||||||
done++;
|
done++;
|
||||||
jlFillRect(stage, 60, 110, (uint16_t)((uint16_t)done * 200u / total), 6u, 1u);
|
loadingBar(stage, done, total);
|
||||||
jlStagePresent();
|
|
||||||
}
|
}
|
||||||
for (k = 0u; k < sizeof(kFlamePtrs); k++) {
|
for (k = 0u; k < sizeof(kFlamePtrs); k++) {
|
||||||
sim->frame.ptr[2] = kFlamePtrs[k];
|
sim->frame.ptr[2] = kFlamePtrs[k];
|
||||||
sim->frame.color[2] = 0x07u;
|
sim->frame.color[2] = 0x07u;
|
||||||
(void)cachedSprite(sim, 2u);
|
(void)cachedSprite(sim, 2u);
|
||||||
done++;
|
done++;
|
||||||
jlFillRect(stage, 60, 110, (uint16_t)((uint16_t)done * 200u / total), 6u, 1u);
|
loadingBar(stage, done, total);
|
||||||
jlStagePresent();
|
|
||||||
}
|
}
|
||||||
// The intro star is a hires sprite in colour 7. Build it as a moving
|
// The intro star is a hires sprite in colour 7. Build it as a moving
|
||||||
// (idx <= 2) cel so it compiles: the level-name intro draws seven of
|
// (idx <= 2) cel so it compiles: the level-name intro draws seven of
|
||||||
|
|
@ -809,16 +1058,17 @@ void stRenderPrewarm(jlSurfaceT *stage, StSimT *sim) {
|
||||||
void stRenderSceneChanged(StSimT *sim) {
|
void stRenderSceneChanged(StSimT *sim) {
|
||||||
uint8_t k;
|
uint8_t k;
|
||||||
|
|
||||||
if (gRender.logoMode) {
|
if (gRender.logoMode || gRender.starMode) {
|
||||||
// The logo's reserved slots go back to being C64 colours.
|
// The borrowed slots go back to being C64 colours.
|
||||||
gRender.logoMode = false;
|
gRender.logoMode = false;
|
||||||
gRender.logoTileOk = false;
|
gRender.logoTileOk = false;
|
||||||
|
gRender.starMode = false;
|
||||||
jlPaletteSet(gRender.stage, 0u, kC64Palette);
|
jlPaletteSet(gRender.stage, 0u, kC64Palette);
|
||||||
}
|
}
|
||||||
for (k = 0u; k < ST_HW_SPRITES; k++) {
|
for (k = 0u; k < ST_HW_SPRITES; k++) {
|
||||||
gRender.slot[k].drawn = false;
|
gRender.slot[k].drawn = false;
|
||||||
}
|
}
|
||||||
stSimDirtyAll(sim);
|
stSimDirtyCells(sim);
|
||||||
sim->charDirtyAll = true;
|
sim->charDirtyAll = true;
|
||||||
sim->charDirtyCount = 0u;
|
sim->charDirtyCount = 0u;
|
||||||
}
|
}
|
||||||
|
|
@ -838,7 +1088,19 @@ void stRenderTitleLogo(StSimT *sim) {
|
||||||
gRender.logoMode = true;
|
gRender.logoMode = true;
|
||||||
gRender.logoTileOk = (frame < ST_LOGO_FRAMES);
|
gRender.logoTileOk = (frame < ST_LOGO_FRAMES);
|
||||||
logoPaletteApply(sim, gRender.logoTileOk ? frame : 0u);
|
logoPaletteApply(sim, gRender.logoTileOk ? frame : 0u);
|
||||||
stSimDirtyAll(sim);
|
stSimDirtyCells(sim);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Turn on the level intro's palette-cycled starfield. Call it after
|
||||||
|
// stRenderSceneChanged (which turns it back off) on entry to the level
|
||||||
|
// intro. The cells were painted by stIntroEnter; from here the warp is
|
||||||
|
// one palette write every ST_STAR_STEP_FRAMES frames.
|
||||||
|
void stRenderIntroStars(void) {
|
||||||
|
gRender.starMode = true;
|
||||||
|
gRender.starPhase = 0u;
|
||||||
|
gRender.starTick = 0u;
|
||||||
|
starPaletteApply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -848,4 +1110,6 @@ void stRenderShutdown(void) {
|
||||||
jlSurfaceDestroy(gRender.scratch);
|
jlSurfaceDestroy(gRender.scratch);
|
||||||
gRender.scratch = 0;
|
gRender.scratch = 0;
|
||||||
}
|
}
|
||||||
|
jlFree(gRender.backupMem);
|
||||||
|
gRender.backupMem = 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -31,6 +31,9 @@
|
||||||
#define ST_SCREEN_COLS 40u
|
#define ST_SCREEN_COLS 40u
|
||||||
#define ST_SCREEN_ROWS 25u
|
#define ST_SCREEN_ROWS 25u
|
||||||
#define ST_SCREEN_CELLS (ST_SCREEN_COLS * ST_SCREEN_ROWS)
|
#define ST_SCREEN_CELLS (ST_SCREEN_COLS * ST_SCREEN_ROWS)
|
||||||
|
// End-of-list / not-linked sentinel for the glyph reverse index below.
|
||||||
|
// ST_SCREEN_CELLS is 1000, so no real cell index can collide with it.
|
||||||
|
#define ST_GLYPH_CELL_NONE 0xFFFFu
|
||||||
// row * 40 + col. Written as shifts (40 == 32 + 8) because the 65816 has
|
// row * 40 + col. Written as shifts (40 == 32 + 8) because the 65816 has
|
||||||
// no multiply: with a variable row the * form called the compiler's
|
// no multiply: with a variable row the * form called the compiler's
|
||||||
// software multiply helper, and the collision pre-check does that a
|
// software multiply helper, and the collision pre-check does that a
|
||||||
|
|
@ -43,6 +46,9 @@
|
||||||
#define ST_SPRITE_Y_ORIGIN 50
|
#define ST_SPRITE_Y_ORIGIN 50
|
||||||
#define ST_SPRITE_W 24
|
#define ST_SPRITE_W 24
|
||||||
#define ST_SPRITE_H 21
|
#define ST_SPRITE_H 21
|
||||||
|
// A sprite row shifted onto the 8-pixel character grid has one of 8
|
||||||
|
// phases (its left edge 0..7 pixels into a cell) -- see rowWindow.
|
||||||
|
#define ST_ROW_PHASES 8
|
||||||
#define ST_HW_SPRITES 8u
|
#define ST_HW_SPRITES 8u
|
||||||
|
|
||||||
// Fixed screen cells the game writes directly (C64 screen addresses).
|
// Fixed screen cells the game writes directly (C64 screen addresses).
|
||||||
|
|
@ -53,6 +59,8 @@
|
||||||
#define ST_CELL_SCREENS ST_CELL(23, 32) // $07B8..$07BC
|
#define ST_CELL_SCREENS ST_CELL(23, 32) // $07B8..$07BC
|
||||||
#define ST_CELL_SCORE ST_CELL(24, 2) // $07C2..$07C8, 7 chars
|
#define ST_CELL_SCORE ST_CELL(24, 2) // $07C2..$07C8, 7 chars
|
||||||
#define ST_CELL_MESSAGE ST_CELL(24, 14) // $07CE, 11 chars
|
#define ST_CELL_MESSAGE ST_CELL(24, 14) // $07CE, 11 chars
|
||||||
|
#define ST_MESSAGE_COL 14u
|
||||||
|
#define ST_MESSAGE_ROW 24u
|
||||||
#define ST_CELL_PAD_DIGIT ST_CELL(24, 18) // $07D2
|
#define ST_CELL_PAD_DIGIT ST_CELL(24, 18) // $07D2
|
||||||
#define ST_CELL_FARE ST_CELL(24, 32) // $07E0..$07E6, 7 chars
|
#define ST_CELL_FARE ST_CELL(24, 32) // $07E0..$07E6, 7 chars
|
||||||
#define ST_NUMBER_CHARS 7u
|
#define ST_NUMBER_CHARS 7u
|
||||||
|
|
@ -71,7 +79,57 @@
|
||||||
#define ST_CHAR_FUEL_EMPTY 0x7Au
|
#define ST_CHAR_FUEL_EMPTY 0x7Au
|
||||||
#define ST_CHAR_TRANSPORTER 0x67u // animated top-wall hatch
|
#define ST_CHAR_TRANSPORTER 0x67u // animated top-wall hatch
|
||||||
#define ST_CHAR_CAB_ICON 0xC8u
|
#define ST_CHAR_CAB_ICON 0xC8u
|
||||||
#define ST_CHAR_MENU_SENTINEL 0x7Bu // $07A6 == $7B: first fuel cell half = out of fuel
|
#define ST_CHAR_ELECTROID 0x6Fu // level O's scrolling barrier glyph
|
||||||
|
#define ST_FARE_DEST_UP 0x0Bu // $7D8E: the fare aboard wants UP
|
||||||
|
|
||||||
|
// ---- level-intro starfield --------------------------------------------
|
||||||
|
//
|
||||||
|
// The C64's level intro streams seven star SPRITES out of the centre of
|
||||||
|
// the screen. That is the most expensive thing this port asks a present
|
||||||
|
// for: eight sprites are save-under drawn and restored every frame, and
|
||||||
|
// because a row's dirty band is one min..max interval, two stars far
|
||||||
|
// apart on the same row widen that row to nearly full width. Radially
|
||||||
|
// spreading stars maximise exactly that, so the warp ends up presenting
|
||||||
|
// close to the whole framebuffer every frame -- which is what makes it
|
||||||
|
// the slowest thing the IIgs does, and wasted work everywhere else.
|
||||||
|
//
|
||||||
|
// So the warp is not animated in pixels at all. The starfield is painted
|
||||||
|
// ONCE into screen RAM and animated by cycling the colours its cells are
|
||||||
|
// drawn in -- the same trick the title logo already uses
|
||||||
|
// (stRenderTitleLogo). A frame costs one palette upload and NO dirty
|
||||||
|
// pixel rows.
|
||||||
|
//
|
||||||
|
// The contract between the two halves is just this colour range:
|
||||||
|
// stTitle.c colours a star's cell ST_STAR_RING_FIRST + ring (ring 0 is
|
||||||
|
// the centre), and the renderer cycles that range. These are C64 colour
|
||||||
|
// indices the intro screen never uses -- it draws in 0, 3 and, in demo
|
||||||
|
// mode, 4 and 5; the cab sprite adds 2, 6 and 7.
|
||||||
|
#define ST_STAR_RING_FIRST 8u
|
||||||
|
#define ST_STAR_RING_COUNT 8u
|
||||||
|
|
||||||
|
// The renderer wraps the sweep's phase with a mask rather than a
|
||||||
|
// modulo, so the ring count has to stay a power of two. Changing it to
|
||||||
|
// something else must fail to compile here instead of quietly making
|
||||||
|
// the warp skip rings.
|
||||||
|
typedef char stStarRingCountIsPow2[((ST_STAR_RING_COUNT & (ST_STAR_RING_COUNT - 1u)) == 0u) ? 1 : -1];
|
||||||
|
// Ring 0 is the centre, which in a warp is the VANISHING POINT: those
|
||||||
|
// stars are the far ones and get the small glyph. The big glyph belongs
|
||||||
|
// to the outer rings, the stars about to rush past the viewer.
|
||||||
|
#define ST_STAR_GLYPH_NEAR 0x2Au // '*', the outer rings
|
||||||
|
#define ST_STAR_GLYPH_FAR 0x2Eu // '.', the inner rings
|
||||||
|
#define ST_STAR_FAR_RINGS 5u // rings 0..4 are still distant
|
||||||
|
|
||||||
|
// VIC sprite block pointers the game selects by name. The cab's bit 0 is
|
||||||
|
// the landing gear, so each facing is a pair.
|
||||||
|
#define ST_SPRITE_CAB_RIGHT 0xC0u // +1 = gear down
|
||||||
|
#define ST_SPRITE_CAB_LEFT 0xDCu // +1 = gear down
|
||||||
|
#define ST_SPRITE_PASS_WALK_R 0xC2u // +walkParity
|
||||||
|
#define ST_SPRITE_PASS_WALK_L 0xC4u // +walkParity
|
||||||
|
#define ST_SPRITE_PASS_STAND 0xC7u
|
||||||
|
#define ST_SPRITE_PASS_BEAM 0xCBu // counts down to PASS_STAND
|
||||||
|
#define ST_SPRITE_PASS_GONE 0xCCu // one past PASS_BEAM: fully shrunk
|
||||||
|
#define ST_SPRITE_WRECK_FIRST 0xCCu // the wreck cel walk, FIRST..LAST-1
|
||||||
|
#define ST_SPRITE_WRECK_LAST 0xD1u
|
||||||
|
|
||||||
// Title logo ($4827): the logo is one character repeated over rows
|
// Title logo ($4827): the logo is one character repeated over rows
|
||||||
// 1..11, and the flip-book animates it by copying one of four frame
|
// 1..11, and the flip-book animates it by copying one of four frame
|
||||||
|
|
@ -88,6 +146,16 @@
|
||||||
// Distinct characters whose glyph can change in one frame before the
|
// Distinct characters whose glyph can change in one frame before the
|
||||||
// renderer gives up and repaints everything.
|
// renderer gives up and repaints everything.
|
||||||
#define ST_CHAR_DIRTY_MAX 8u
|
#define ST_CHAR_DIRTY_MAX 8u
|
||||||
|
// Characters that may hold an edited glyph at once. The game edits six
|
||||||
|
// across the whole game (the transporter hatch $67, level O's barrier
|
||||||
|
// $6F, the title logo $84 and level W's three laser glyphs $91..$93), so
|
||||||
|
// this is generous; an overflow parks the edit in the spill entry, where
|
||||||
|
// it is simply never displayed rather than corrupting another character.
|
||||||
|
#define ST_GLYPH_OVERRIDES 16u
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t rows[8];
|
||||||
|
} StGlyphT;
|
||||||
|
|
||||||
// Level image as loaded from a .dat (STL4). Kept in the C64's units.
|
// Level image as loaded from a .dat (STL4). Kept in the C64's units.
|
||||||
#define ST_MAX_PADS 10u
|
#define ST_MAX_PADS 10u
|
||||||
|
|
@ -98,6 +166,113 @@
|
||||||
#define ST_HOOK_BASE 0x7D98u
|
#define ST_HOOK_BASE 0x7D98u
|
||||||
#define ST_HOOK_BYTES (0x8000u - ST_HOOK_BASE)
|
#define ST_HOOK_BYTES (0x8000u - ST_HOOK_BASE)
|
||||||
|
|
||||||
|
// Per-level hook scratch. The original keeps a hook's running state
|
||||||
|
// inside the level's own data block, at fixed C64 addresses interleaved
|
||||||
|
// with its tables -- and, for some levels, on top of the hook's own 6502
|
||||||
|
// code, which this port never executes. A level reads its TABLES straight
|
||||||
|
// out of the shipped blob (the single source of truth) and keeps only the
|
||||||
|
// bytes it WRITES here. Seeded from
|
||||||
|
// the blob at scene load, so the first tick starts where the original's
|
||||||
|
// did. Every level is converted, so no C64-addressed working copy of the
|
||||||
|
// blob exists any more.
|
||||||
|
#define ST_HOOK_H_SEGS 11u // level H segment ids 0..10
|
||||||
|
#define ST_HOOK_W_LASERS 8u
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t segFlag[ST_HOOK_H_SEGS]; // $7E15..$7E1F wall segment up/down
|
||||||
|
uint8_t switchId; // $7E4C also read back as a table entry
|
||||||
|
uint8_t tick; // $7E53
|
||||||
|
uint8_t active; // $7E54 switch being animated
|
||||||
|
uint8_t step; // $7E55 0..3 of the animation
|
||||||
|
uint8_t tone; // $7E56 rising chime
|
||||||
|
} StHookHT;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t celPhase; // $7DD2 orb cel cycle 0..5
|
||||||
|
uint8_t tick; // $7E00
|
||||||
|
uint8_t lastSpot; // $7DD1 landing spot used last, 1..5
|
||||||
|
uint8_t hop; // $7DD3 ticks left in a teleport
|
||||||
|
uint8_t sweep; // $7F02 the hop's falling tone
|
||||||
|
} StHookGT;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t state[ST_HW_SPRITES]; // $7DB8 0 idle, 1 rising, 2 burst
|
||||||
|
uint8_t phase[ST_HW_SPRITES]; // $7DC0 cel phase
|
||||||
|
uint8_t dx[ST_HW_SPRITES]; // $7DC8
|
||||||
|
uint8_t dy[ST_HW_SPRITES]; // $7DD0
|
||||||
|
} StHookIT;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t parity; // $7DBD magnet pole flip
|
||||||
|
} StHookKT;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t tick; // $7DBD mod-8 scroll gate
|
||||||
|
} StHookOT;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t phase[ST_HW_SPRITES]; // $7E83 interference cel phase
|
||||||
|
} StHookQT;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t gateShut; // $7D9C
|
||||||
|
} StHookTT;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t colorPhase; // $7DAB
|
||||||
|
uint8_t state[ST_HOOK_W_LASERS]; // $7DAC 0 idle, 1 extending, 2 fading
|
||||||
|
uint8_t row[ST_HOOK_W_LASERS]; // $7DB5 beam head / fade phase
|
||||||
|
uint8_t started; // $7DE5
|
||||||
|
} StHookWT;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t celPhase[ST_HW_SPRITES]; // $7F4F
|
||||||
|
uint8_t state[ST_HW_SPRITES]; // $7F57 0 idle, 1 active
|
||||||
|
uint8_t dir[ST_HW_SPRITES]; // $7F5F entry edge 0..3
|
||||||
|
uint8_t life[ST_HW_SPRITES]; // $7F6E
|
||||||
|
uint8_t cooldown; // $7F6D ticks before the next bounce
|
||||||
|
} StHookUT;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t col; // $7D9C
|
||||||
|
uint8_t dir; // $7D9D
|
||||||
|
uint8_t tick; // $7D9E
|
||||||
|
} StHookXT;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t row; // $7D98 row the leaves grow along
|
||||||
|
uint8_t seg; // $7D99 leaves grown this cycle 0..4
|
||||||
|
uint8_t tick; // $7D9A ticks toward the next leaf
|
||||||
|
uint8_t padSlot; // $7D9B number of the next pad to hang
|
||||||
|
} StHookET;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t state[ST_HW_SPRITES]; // $7EDB 0 idle, 1 falling, 2 bursting
|
||||||
|
uint8_t phase[ST_HW_SPRITES]; // $7ED3 burst cel phase
|
||||||
|
} StHookJT;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t state[ST_HW_SPRITES]; // $7F16 0 idle, 1 falling, 2 melting
|
||||||
|
uint8_t phase[ST_HW_SPRITES]; // $7F0E melt cel phase
|
||||||
|
uint8_t parity; // $7F1F a melt advances every other count
|
||||||
|
} StHookPT;
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
StHookET e;
|
||||||
|
StHookGT g;
|
||||||
|
StHookHT h;
|
||||||
|
StHookIT i;
|
||||||
|
StHookJT j;
|
||||||
|
StHookKT k;
|
||||||
|
StHookOT o;
|
||||||
|
StHookPT p;
|
||||||
|
StHookQT q;
|
||||||
|
StHookTT t;
|
||||||
|
StHookUT u;
|
||||||
|
StHookWT w;
|
||||||
|
StHookXT x;
|
||||||
|
} StHookScratchT;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t x1Hi; // slot byte 0: landing X lower bound, high byte
|
uint8_t x1Hi; // slot byte 0: landing X lower bound, high byte
|
||||||
uint8_t x1Lo; // slot byte 1
|
uint8_t x1Lo; // slot byte 1
|
||||||
|
|
@ -128,7 +303,7 @@ typedef struct {
|
||||||
StPadT pads[ST_MAX_PADS]; // $7D0A..
|
StPadT pads[ST_MAX_PADS]; // $7D0A..
|
||||||
uint8_t screen[ST_SCREEN_CELLS]; // decompressed screen RAM
|
uint8_t screen[ST_SCREEN_CELLS]; // decompressed screen RAM
|
||||||
uint8_t color[ST_SCREEN_CELLS]; // decompressed colour RAM
|
uint8_t color[ST_SCREEN_CELLS]; // decompressed colour RAM
|
||||||
uint8_t levelIndex; // 0..23 = A..X, 24 = title
|
uint8_t levelIndex; // 0..23 = A..X, 24 = Y, 25 = title
|
||||||
uint8_t spriteCount;
|
uint8_t spriteCount;
|
||||||
StLevelSpriteT sprites[ST_MAX_LEVEL_SPRITES];
|
StLevelSpriteT sprites[ST_MAX_LEVEL_SPRITES];
|
||||||
uint8_t hookData[ST_HOOK_BYTES]; // $7D98..$7FFF as shipped
|
uint8_t hookData[ST_HOOK_BYTES]; // $7D98..$7FFF as shipped
|
||||||
|
|
@ -200,7 +375,34 @@ typedef struct {
|
||||||
uint16_t dirtyList[512]; // cells marked since the last frame
|
uint16_t dirtyList[512]; // cells marked since the last frame
|
||||||
uint16_t dirtyCount;
|
uint16_t dirtyCount;
|
||||||
bool dirtyAll; // list overflowed / whole screen
|
bool dirtyAll; // list overflowed / whole screen
|
||||||
uint8_t charset[ST_CHARSET_CHARS][8]; // $2800 working copy
|
// Reverse index: for each glyph, the exact set of cells showing it,
|
||||||
|
// as doubly-linked lists threaded through the cell array.
|
||||||
|
//
|
||||||
|
// The renderer has to repaint every cell showing a glyph whose
|
||||||
|
// BITMAP changed -- a chunky framebuffer bakes the pixels in at
|
||||||
|
// paste time, so unlike the C64's character mode there is no free
|
||||||
|
// re-composite. It used to find those cells by scanning a per-glyph
|
||||||
|
// [first, last] span, which PC profiling showed was the single
|
||||||
|
// hottest loop in the program (22% of all samples on level H). This
|
||||||
|
// gives the exact set instead: no scan, cost proportional to the
|
||||||
|
// cells that really show the glyph.
|
||||||
|
//
|
||||||
|
// screen[] is the only truth and these mirror it: setCell keeps them
|
||||||
|
// in step one cell at a time, and a bulk screen write re-derives
|
||||||
|
// them via stSimDirtyAll, which every bulk writer already calls.
|
||||||
|
// They live in the NOT-cleared block for the same reason screen[]
|
||||||
|
// does -- stSimNewGame must leave the live picture alone.
|
||||||
|
uint16_t glyphHead[ST_CHARSET_CHARS]; // ST_GLYPH_CELL_NONE = none
|
||||||
|
uint16_t cellNext[ST_SCREEN_CELLS];
|
||||||
|
uint16_t cellPrev[ST_SCREEN_CELLS];
|
||||||
|
// $2800. The charset is NOT copied: the ROM set is const and only the
|
||||||
|
// handful of glyphs the game rewrites at runtime are stored here, so
|
||||||
|
// this costs ~400 bytes instead of 2 KB and a scene load clears an
|
||||||
|
// index rather than copying 2 KB. Reach glyphs through stSimGlyph /
|
||||||
|
// stSimGlyphRow / stSimGlyphMut, never by indexing directly.
|
||||||
|
uint8_t glyphSlot[ST_CHARSET_CHARS]; // 0 = ROM glyph, else slot + 1
|
||||||
|
StGlyphT glyphOverride[ST_GLYPH_OVERRIDES + 1u]; // last = spill
|
||||||
|
uint8_t glyphOverrideCount;
|
||||||
// Characters whose glyph bits the game has edited since the last
|
// Characters whose glyph bits the game has edited since the last
|
||||||
// frame -- the transporter hatch, the title logo flip, the laser
|
// frame -- the transporter hatch, the title logo flip, the laser
|
||||||
// beams. A short list, not a 256-entry flag array: the renderer had
|
// beams. A short list, not a 256-entry flag array: the renderer had
|
||||||
|
|
@ -221,6 +423,8 @@ typedef struct {
|
||||||
// ---- level image (mutable copies: hooks edit the pad table) ----
|
// ---- level image (mutable copies: hooks edit the pad table) ----
|
||||||
const StLevelT *level;
|
const StLevelT *level;
|
||||||
StPadT pads[ST_MAX_PADS]; // $7D0A working copy
|
StPadT pads[ST_MAX_PADS]; // $7D0A working copy
|
||||||
|
uint8_t padCount; // $7D5A working copy (level E opens pads)
|
||||||
|
uint8_t specialPad; // $7D09 working copy
|
||||||
|
|
||||||
// ---- input ----
|
// ---- input ----
|
||||||
uint8_t inputMask; // $7169 EOR $FF of $DC00: 1 = pressed
|
uint8_t inputMask; // $7169 EOR $FF of $DC00: 1 = pressed
|
||||||
|
|
@ -255,12 +459,25 @@ typedef struct {
|
||||||
uint8_t hitDispatchResult; // $721D
|
uint8_t hitDispatchResult; // $721D
|
||||||
uint8_t spriteSpriteColl; // $71CA latched $D01E
|
uint8_t spriteSpriteColl; // $71CA latched $D01E
|
||||||
uint8_t spriteBgColl; // $71CB latched $D01F
|
uint8_t spriteBgColl; // $71CB latched $D01F
|
||||||
// Per-sprite 24-bit row masks of the displayed frame, rebuilt only
|
// Sprites besides the cab whose $D01F bit a hook reads (levels J
|
||||||
// when the sprite's bitmap or multicolour mode changes (derived
|
// and P watch their hazards hit the scenery); the rest are never
|
||||||
// state, not C64 memory).
|
// tested (derived state, not C64 memory).
|
||||||
uint32_t collMask[ST_HW_SPRITES][ST_SPRITE_H];
|
uint8_t bgCollSprites;
|
||||||
|
// Per-sprite row masks of the displayed frame (3 bytes per row, the
|
||||||
|
// leftmost pixel is bit 7 of byte 0), rebuilt only when the sprite's
|
||||||
|
// bitmap or multicolour mode changes (derived state, not C64 memory).
|
||||||
|
uint8_t collMask[ST_HW_SPRITES][ST_SPRITE_H][3];
|
||||||
const uint8_t *collMaskBm[ST_HW_SPRITES];
|
const uint8_t *collMaskBm[ST_HW_SPRITES];
|
||||||
uint8_t collMaskMulti[ST_HW_SPRITES];
|
uint8_t collMaskMulti[ST_HW_SPRITES];
|
||||||
|
// First and last rows of collMask[idx] with any pixel (first ==
|
||||||
|
// ST_SPRITE_H when the mask is empty): the row tests walk only these.
|
||||||
|
uint8_t collRowFirst[ST_HW_SPRITES];
|
||||||
|
uint8_t collRowLast[ST_HW_SPRITES];
|
||||||
|
// The cab's rows pre-shifted to each grid phase (cabWindows), built
|
||||||
|
// per phase on first use; bit s of cabWinValid says cabWin[s]
|
||||||
|
// matches collMask[0].
|
||||||
|
uint8_t cabWin[ST_ROW_PHASES][ST_SPRITE_H][4];
|
||||||
|
uint8_t cabWinValid;
|
||||||
|
|
||||||
// ---- fuel ----
|
// ---- fuel ----
|
||||||
uint8_t fuelCountdown; // $71C7
|
uint8_t fuelCountdown; // $71C7
|
||||||
|
|
@ -300,6 +517,12 @@ typedef struct {
|
||||||
uint8_t levelState; // $7215 screens completed
|
uint8_t levelState; // $7215 screens completed
|
||||||
uint8_t demoMode; // $721C
|
uint8_t demoMode; // $721C
|
||||||
uint8_t postMortem; // $5E9D
|
uint8_t postMortem; // $5E9D
|
||||||
|
// Jingles. The C64 plays its tunes BLOCKING ($44CF spins until the
|
||||||
|
// song ends), so the sim only RAISES a request here and the host
|
||||||
|
// plays it while holding the sim frozen -- tick-exactness survives
|
||||||
|
// because no tick happens while the tune sounds.
|
||||||
|
uint8_t jingleRequest; // song number + 1 to play now; 0 = none
|
||||||
|
uint8_t jingleRotate; // $5E9B: start-of-screen tune = ++this & 3
|
||||||
|
|
||||||
// ---- demo playback ($48F2): the $0902 buffer a recording is
|
// ---- demo playback ($48F2): the $0902 buffer a recording is
|
||||||
// loaded into; a shorter recording leaves the previous tail behind
|
// loaded into; a shorter recording leaves the previous tail behind
|
||||||
|
|
@ -318,8 +541,6 @@ typedef struct {
|
||||||
uint8_t logoColorIdx; // $48A4
|
uint8_t logoColorIdx; // $48A4
|
||||||
uint8_t logoCycleAux; // $48A5
|
uint8_t logoCycleAux; // $48A5
|
||||||
uint8_t logoDiv; // $48A6
|
uint8_t logoDiv; // $48A6
|
||||||
uint8_t starTimer[7]; // $44F8
|
|
||||||
uint8_t starReload[7]; // $4500
|
|
||||||
uint8_t introLoopCount; // $4508
|
uint8_t introLoopCount; // $4508
|
||||||
uint8_t introCabDx; // $450A
|
uint8_t introCabDx; // $450A
|
||||||
uint8_t introCabDxTimer; // $450B
|
uint8_t introCabDxTimer; // $450B
|
||||||
|
|
@ -327,7 +548,7 @@ typedef struct {
|
||||||
uint8_t spriteMc1; // $D026
|
uint8_t spriteMc1; // $D026
|
||||||
|
|
||||||
// ---- per-level hook scratch ($7D9C.. and friends) ----
|
// ---- per-level hook scratch ($7D9C.. and friends) ----
|
||||||
uint8_t hookRam[ST_HOOK_BYTES]; // $7D98..$7FFF working copy
|
StHookScratchT hookScratch; // per-level hook state
|
||||||
uint8_t hookLevel; // level index the hook state belongs to
|
uint8_t hookLevel; // level index the hook state belongs to
|
||||||
uint8_t warpCounter; // $5A66
|
uint8_t warpCounter; // $5A66
|
||||||
uint8_t warpMask; // $5CE8
|
uint8_t warpMask; // $5CE8
|
||||||
|
|
@ -357,6 +578,29 @@ bool stSimAdvancePlayer(StSimT *sim);
|
||||||
void stSimArchiveHud(StSimT *sim);
|
void stSimArchiveHud(StSimT *sim);
|
||||||
// Mark every cell dirty (scene change).
|
// Mark every cell dirty (scene change).
|
||||||
void stSimDirtyAll(StSimT *sim);
|
void stSimDirtyAll(StSimT *sim);
|
||||||
|
void stSimDirtyCells(StSimT *sim); // repaint all, glyph index kept
|
||||||
|
// $43A5 -- blank the fare meter back to its template.
|
||||||
|
void stSimHudInit(StSimT *sim);
|
||||||
|
// $4253 + $4293 -- snapshot the sprite shadows into the displayed frame.
|
||||||
|
void stSimMarshal(StSimT *sim);
|
||||||
|
// $63D0 -- rotate every row of a glyph one pixel right and mark it dirty.
|
||||||
|
void stSimRotateGlyph(StSimT *sim, uint8_t ch);
|
||||||
|
// A character's eight 1bpp rows: the ROM glyph, or the game's edit of it.
|
||||||
|
// kStBit[i] == 1 << i. A variable shift is a library bit loop on the
|
||||||
|
// 65816, so sprite-index bit tests go through this table instead.
|
||||||
|
extern const uint8_t kStBit[ST_HW_SPRITES];
|
||||||
|
|
||||||
|
const uint8_t *stSimGlyph(const StSimT *sim, uint8_t ch);
|
||||||
|
uint8_t stSimGlyphRow(const StSimT *sim, uint8_t ch, uint8_t row);
|
||||||
|
// The same eight rows, writable (copied from ROM on the first edit).
|
||||||
|
uint8_t *stSimGlyphMut(StSimT *sim, uint8_t ch);
|
||||||
|
// Drop every edit: the whole charset is the ROM set again (scene load).
|
||||||
|
void stSimGlyphReset(StSimT *sim);
|
||||||
|
// The 17-bit VIC sprite X (msb:col), read and signed-byte add ($411B).
|
||||||
|
uint16_t stSimSpriteX(const StSimT *sim, uint8_t idx);
|
||||||
|
void stSimSpriteAddX(StSimT *sim, uint8_t idx, uint8_t delta);
|
||||||
|
// $6C38 -- blank the 11-character message row.
|
||||||
|
void stSimClearMessage(StSimT *sim);
|
||||||
// A glyph's bits changed: every cell showing it has to be repainted.
|
// A glyph's bits changed: every cell showing it has to be repainted.
|
||||||
void stSimMarkChar(StSimT *sim, uint8_t ch);
|
void stSimMarkChar(StSimT *sim, uint8_t ch);
|
||||||
// Text and HUD primitives (screen RAM writes).
|
// Text and HUD primitives (screen RAM writes).
|
||||||
|
|
@ -404,13 +648,19 @@ void stHookPerTick2(StSimT *sim); // $7D6C
|
||||||
void stHookPerTick3(StSimT *sim); // $7D6F
|
void stHookPerTick3(StSimT *sim); // $7D6F
|
||||||
uint8_t stHookInput(StSimT *sim, uint8_t input); // $7D72
|
uint8_t stHookInput(StSimT *sim, uint8_t input); // $7D72
|
||||||
uint8_t stHookHitVerdict(StSimT *sim); // $7D75: 0 = passenger contact is safe
|
uint8_t stHookHitVerdict(StSimT *sim); // $7D75: 0 = passenger contact is safe
|
||||||
|
// Seed a converted level's scratch from the shipped blob (scene load).
|
||||||
|
void stHookSceneLoad(StSimT *sim);
|
||||||
|
|
||||||
// ---- audio sink (host provides; stAudio.c / stsim stub) ----
|
// ---- audio sink (host provides; stAudio.c / stsim stub) ----
|
||||||
void stAudioSfx(const uint8_t *program9); // $42E9 load
|
void stAudioSfx(const uint8_t *program9); // $42E9 load
|
||||||
void stAudioNoise(bool on); // $D412 = $81 / $80
|
void stAudioNoise(bool on); // $D412 = $81 / $80
|
||||||
void stAudioThrustSweep(uint8_t value); // $D400/$D401 = value
|
void stAudioThrustSweep(uint8_t value); // $D400/$D401 = value
|
||||||
void stAudioSpeech(uint8_t ch); // $9802
|
void stAudioSpeech(uint8_t ch); // $9802
|
||||||
|
void stAudioSpeechPhrase(const uint8_t *speech);
|
||||||
void stAudioSilence(void); // voices 1+2 gate off
|
void stAudioSilence(void); // voices 1+2 gate off
|
||||||
|
void stAudioJingle(uint8_t song); // $44CF: silence, then play song (caller blocks)
|
||||||
|
bool stAudioJingleActive(void); // still sounding?
|
||||||
|
void stAudioMusic(uint8_t song, bool loop); // $CB02: silence, then play song in the background
|
||||||
void stAudioVoice2(uint8_t freqLo, uint8_t freqHi, uint8_t ctrl); // level-hook tones
|
void stAudioVoice2(uint8_t freqLo, uint8_t freqHi, uint8_t ctrl); // level-hook tones
|
||||||
void stAudioVoice1Freq(uint8_t value); // $D400/$D401 pokes from the fuel pump
|
void stAudioVoice1Freq(uint8_t value); // $D400/$D401 pokes from the fuel pump
|
||||||
|
|
||||||
|
|
|
||||||
512
examples/spacetaxi/stSongData.h
Normal file
512
examples/spacetaxi/stSongData.h
Normal file
|
|
@ -0,0 +1,512 @@
|
||||||
|
// Space Taxi (C64) tunes as JoeyLib JYM1 streams. GENERATED by
|
||||||
|
// assets/genSongData.py -- do not hand-edit. The songs were extracted by
|
||||||
|
// running the game's own music player under a 6502 emulator
|
||||||
|
// (assets/sidCapture.py logs every SID write per PAL frame;
|
||||||
|
// assets/sidToSong.py turns that into songbake text in assets/songs/).
|
||||||
|
//
|
||||||
|
// Where the C64 plays them:
|
||||||
|
// BLOCKING via $44CF (the game spins until the tune ends; the port
|
||||||
|
// parks in ST_STATE_JINGLE with the sim frozen):
|
||||||
|
// 0..3 start of every screen, rotating on $5E9B ($6906)
|
||||||
|
// 4 level 25 start ($6935) and the bonus cab ($70B1)
|
||||||
|
// 5 game over ($5C1F), after the two-second GAME OVER! wait
|
||||||
|
// NON-BLOCKING via $CB02 (started and left to the IRQ player):
|
||||||
|
// 6 the high-score name entry screen ($4E35) -- no port screen yet
|
||||||
|
// 7 the high-score table ($4C1F, joystick UP on the title)
|
||||||
|
// 8 the level intro while the cab flies in ($459C); endless, so
|
||||||
|
// the source is trimmed to 30 s and looped
|
||||||
|
#ifndef ST_SONG_DATA_H
|
||||||
|
#define ST_SONG_DATA_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
static const uint8_t gStSong0[109] = {
|
||||||
|
0x4A, 0x59, 0x4D, 0x31, 0x14, 0x00, 0xFF, 0xFF, 0x81, 0x10, 0x50, 0x01,
|
||||||
|
0x02, 0x11, 0xA8, 0x01, 0x02, 0x83, 0x20, 0x21, 0x85, 0x10, 0x7A, 0x01,
|
||||||
|
0x02, 0x11, 0xA8, 0x01, 0x02, 0x83, 0x20, 0x21, 0x85, 0x10, 0x50, 0x01,
|
||||||
|
0x02, 0x11, 0xA8, 0x01, 0x02, 0x83, 0x20, 0x21, 0x85, 0x10, 0x7A, 0x01,
|
||||||
|
0x02, 0x11, 0xDC, 0x01, 0x02, 0x83, 0x20, 0x21, 0x85, 0x10, 0x3E, 0x01,
|
||||||
|
0x02, 0x11, 0xFC, 0x00, 0x02, 0x83, 0x21, 0x85, 0x11, 0xDC, 0x01, 0x02,
|
||||||
|
0x83, 0x20, 0x21, 0x85, 0x10, 0x1B, 0x01, 0x02, 0x11, 0xFC, 0x00, 0x02,
|
||||||
|
0x83, 0x21, 0x85, 0x11, 0xDC, 0x01, 0x02, 0x83, 0x20, 0x21, 0x85, 0x10,
|
||||||
|
0xFC, 0x00, 0x02, 0x11, 0xFC, 0x00, 0x02, 0x8B, 0x21, 0x88, 0x20, 0x86,
|
||||||
|
0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t gStSong1[119] = {
|
||||||
|
0x4A, 0x59, 0x4D, 0x31, 0x14, 0x00, 0xFF, 0xFF, 0x81, 0x10, 0xFC, 0x00,
|
||||||
|
0x07, 0x11, 0x9F, 0x00, 0x07, 0x87, 0x20, 0x21, 0x81, 0x10, 0x1B, 0x01,
|
||||||
|
0x07, 0x11, 0xA8, 0x00, 0x07, 0x87, 0x20, 0x21, 0x81, 0x10, 0xFC, 0x00,
|
||||||
|
0x07, 0x11, 0xBD, 0x00, 0x07, 0x87, 0x20, 0x21, 0x81, 0x10, 0x1B, 0x01,
|
||||||
|
0x07, 0x11, 0xD4, 0x00, 0x07, 0x87, 0x20, 0x21, 0x81, 0x10, 0x3E, 0x01,
|
||||||
|
0x07, 0x11, 0xBD, 0x00, 0x07, 0x87, 0x20, 0x21, 0x81, 0x10, 0x50, 0x01,
|
||||||
|
0x07, 0x11, 0xA8, 0x00, 0x07, 0x87, 0x20, 0x21, 0x81, 0x10, 0x3E, 0x01,
|
||||||
|
0x07, 0x11, 0x9F, 0x00, 0x07, 0x87, 0x20, 0x21, 0x81, 0x10, 0x50, 0x01,
|
||||||
|
0x07, 0x11, 0xA8, 0x00, 0x07, 0x87, 0x20, 0x21, 0x81, 0x10, 0x7A, 0x01,
|
||||||
|
0x07, 0x11, 0xBD, 0x00, 0x07, 0x87, 0x21, 0x90, 0x20, 0x82, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t gStSong2[128] = {
|
||||||
|
0x4A, 0x59, 0x4D, 0x31, 0x14, 0x00, 0xFF, 0xFF, 0x81, 0x10, 0x7E, 0x00,
|
||||||
|
0x00, 0x11, 0x9F, 0x00, 0x00, 0x85, 0x21, 0x82, 0x20, 0x81, 0x10, 0x7E,
|
||||||
|
0x00, 0x00, 0x11, 0xA8, 0x00, 0x00, 0x85, 0x21, 0x82, 0x20, 0x81, 0x10,
|
||||||
|
0x8D, 0x00, 0x00, 0x11, 0xBD, 0x00, 0x00, 0x85, 0x21, 0x82, 0x20, 0x81,
|
||||||
|
0x10, 0x9F, 0x00, 0x00, 0x11, 0x9F, 0x00, 0x00, 0x85, 0x21, 0x82, 0x20,
|
||||||
|
0x81, 0x10, 0x9F, 0x00, 0x00, 0x11, 0xA8, 0x00, 0x00, 0x85, 0x21, 0x82,
|
||||||
|
0x20, 0x81, 0x10, 0xA8, 0x00, 0x00, 0x11, 0xBD, 0x00, 0x00, 0x85, 0x21,
|
||||||
|
0x82, 0x20, 0x81, 0x10, 0xA8, 0x00, 0x00, 0x11, 0x9F, 0x00, 0x00, 0x85,
|
||||||
|
0x21, 0x82, 0x20, 0x81, 0x10, 0xBD, 0x00, 0x00, 0x11, 0xA8, 0x00, 0x00,
|
||||||
|
0x85, 0x21, 0x82, 0x20, 0x81, 0x11, 0x9F, 0x00, 0x00, 0x85, 0x21, 0x83,
|
||||||
|
0x11, 0x7E, 0x00, 0x00, 0x85, 0x21, 0x84, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t gStSong3[154] = {
|
||||||
|
0x4A, 0x59, 0x4D, 0x31, 0x14, 0x00, 0xFF, 0xFF, 0x81, 0x10, 0xFC, 0x00,
|
||||||
|
0x00, 0x11, 0x50, 0x01, 0x02, 0x12, 0x50, 0x01, 0x02, 0x83, 0x20, 0x21,
|
||||||
|
0x22, 0x82, 0x10, 0x1B, 0x01, 0x00, 0x83, 0x20, 0x82, 0x10, 0x3E, 0x01,
|
||||||
|
0x00, 0x11, 0x50, 0x01, 0x02, 0x12, 0x50, 0x01, 0x02, 0x83, 0x20, 0x21,
|
||||||
|
0x22, 0x82, 0x10, 0xFC, 0x00, 0x00, 0x83, 0x20, 0x82, 0x10, 0x1B, 0x01,
|
||||||
|
0x00, 0x11, 0x50, 0x01, 0x02, 0x12, 0x50, 0x01, 0x02, 0x83, 0x20, 0x21,
|
||||||
|
0x22, 0x82, 0x10, 0xFC, 0x00, 0x00, 0x83, 0x20, 0x82, 0x10, 0x1B, 0x01,
|
||||||
|
0x00, 0x11, 0x50, 0x01, 0x02, 0x12, 0x50, 0x01, 0x02, 0x83, 0x20, 0x21,
|
||||||
|
0x22, 0x82, 0x10, 0xDC, 0x01, 0x00, 0x83, 0x20, 0x82, 0x11, 0x3E, 0x01,
|
||||||
|
0x02, 0x12, 0x7A, 0x01, 0x02, 0x83, 0x21, 0x22, 0x87, 0x11, 0x50, 0x01,
|
||||||
|
0x02, 0x12, 0x7A, 0x01, 0x02, 0x83, 0x21, 0x22, 0x87, 0x11, 0x1B, 0x01,
|
||||||
|
0x02, 0x12, 0xFC, 0x00, 0x02, 0x83, 0x21, 0x22, 0x87, 0x11, 0xFC, 0x00,
|
||||||
|
0x02, 0x12, 0xDC, 0x01, 0x02, 0x83, 0x21, 0x22, 0x88, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t gStSong4[92] = {
|
||||||
|
0x4A, 0x59, 0x4D, 0x31, 0x14, 0x00, 0xFF, 0xFF, 0x81, 0x10, 0xF8, 0x01,
|
||||||
|
0x05, 0x11, 0xC1, 0x01, 0x05, 0x87, 0x20, 0x21, 0x89, 0x10, 0xF8, 0x01,
|
||||||
|
0x05, 0x11, 0xC1, 0x01, 0x05, 0x87, 0x20, 0x21, 0x81, 0x10, 0xA1, 0x02,
|
||||||
|
0x05, 0x11, 0xF8, 0x01, 0x05, 0x97, 0x20, 0x21, 0x99, 0x10, 0xF8, 0x01,
|
||||||
|
0x05, 0x11, 0xA8, 0x01, 0x05, 0x87, 0x20, 0x21, 0x89, 0x10, 0xA1, 0x02,
|
||||||
|
0x05, 0x11, 0xF8, 0x01, 0x05, 0x87, 0x20, 0x21, 0x81, 0x10, 0x50, 0x03,
|
||||||
|
0x05, 0x11, 0xA1, 0x02, 0x05, 0x88, 0x11, 0x7B, 0x02, 0x05, 0x88, 0x11,
|
||||||
|
0xA1, 0x02, 0x05, 0x87, 0x20, 0x21, 0x82, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t gStSong5[890] = {
|
||||||
|
0x4A, 0x59, 0x4D, 0x31, 0x14, 0x00, 0xFF, 0xFF, 0x81, 0x10, 0xF8, 0x01,
|
||||||
|
0x02, 0x11, 0x9F, 0x00, 0x02, 0x12, 0x6A, 0x00, 0x0A, 0x83, 0x21, 0x82,
|
||||||
|
0x12, 0xD4, 0x00, 0x0A, 0x85, 0x11, 0x9F, 0x00, 0x02, 0x12, 0x6A, 0x00,
|
||||||
|
0x0A, 0x83, 0x21, 0x82, 0x12, 0xD4, 0x00, 0x0A, 0x85, 0x11, 0x9F, 0x00,
|
||||||
|
0x02, 0x12, 0x6A, 0x00, 0x0A, 0x83, 0x21, 0x82, 0x12, 0xD4, 0x00, 0x0A,
|
||||||
|
0x85, 0x11, 0x9F, 0x00, 0x02, 0x12, 0x6A, 0x00, 0x0A, 0x83, 0x21, 0x81,
|
||||||
|
0x20, 0x81, 0x12, 0xD4, 0x00, 0x0A, 0x85, 0x10, 0xF8, 0x01, 0x02, 0x11,
|
||||||
|
0x9F, 0x00, 0x02, 0x12, 0x6A, 0x00, 0x0A, 0x83, 0x21, 0x82, 0x12, 0xD4,
|
||||||
|
0x00, 0x0A, 0x85, 0x11, 0x9F, 0x00, 0x02, 0x12, 0x6A, 0x00, 0x0A, 0x83,
|
||||||
|
0x21, 0x82, 0x12, 0xD4, 0x00, 0x0A, 0x85, 0x11, 0x9F, 0x00, 0x02, 0x12,
|
||||||
|
0x6A, 0x00, 0x0A, 0x83, 0x21, 0x82, 0x12, 0xD4, 0x00, 0x0A, 0x85, 0x11,
|
||||||
|
0x9F, 0x00, 0x02, 0x12, 0x6A, 0x00, 0x0A, 0x83, 0x21, 0x81, 0x20, 0x81,
|
||||||
|
0x12, 0xD4, 0x00, 0x0A, 0x85, 0x10, 0xF8, 0x01, 0x02, 0x11, 0x9F, 0x00,
|
||||||
|
0x02, 0x12, 0x6A, 0x00, 0x0A, 0x83, 0x21, 0x82, 0x12, 0xD4, 0x00, 0x0A,
|
||||||
|
0x85, 0x11, 0x9F, 0x00, 0x02, 0x12, 0x6A, 0x00, 0x0A, 0x83, 0x21, 0x82,
|
||||||
|
0x12, 0xD4, 0x00, 0x0A, 0x85, 0x11, 0x9F, 0x00, 0x02, 0x12, 0x6A, 0x00,
|
||||||
|
0x0A, 0x83, 0x21, 0x81, 0x20, 0x81, 0x12, 0xD4, 0x00, 0x0A, 0x83, 0x22,
|
||||||
|
0x82, 0x10, 0x36, 0x02, 0x02, 0x11, 0x8D, 0x00, 0x02, 0x12, 0x64, 0x00,
|
||||||
|
0x0A, 0x83, 0x21, 0x81, 0x20, 0x81, 0x12, 0xC8, 0x00, 0x0A, 0x85, 0x10,
|
||||||
|
0x36, 0x02, 0x02, 0x11, 0x8D, 0x00, 0x02, 0x12, 0x64, 0x00, 0x0A, 0x83,
|
||||||
|
0x21, 0x82, 0x12, 0xC8, 0x00, 0x0A, 0x85, 0x11, 0x8D, 0x00, 0x02, 0x12,
|
||||||
|
0x64, 0x00, 0x0A, 0x83, 0x21, 0x82, 0x12, 0xC8, 0x00, 0x0A, 0x85, 0x11,
|
||||||
|
0x8D, 0x00, 0x02, 0x12, 0x64, 0x00, 0x0A, 0x83, 0x21, 0x82, 0x12, 0xC8,
|
||||||
|
0x00, 0x0A, 0x83, 0x22, 0x82, 0x11, 0x8D, 0x00, 0x02, 0x12, 0x64, 0x00,
|
||||||
|
0x0A, 0x83, 0x21, 0x81, 0x20, 0x81, 0x12, 0xC8, 0x00, 0x0A, 0x85, 0x10,
|
||||||
|
0x7B, 0x02, 0x02, 0x11, 0xF8, 0x01, 0x02, 0x12, 0x5E, 0x00, 0x0A, 0x84,
|
||||||
|
0x20, 0x21, 0x81, 0x12, 0xBD, 0x00, 0x0A, 0x85, 0x12, 0x5E, 0x00, 0x0A,
|
||||||
|
0x85, 0x12, 0xBD, 0x00, 0x0A, 0x85, 0x10, 0xA1, 0x02, 0x02, 0x11, 0x36,
|
||||||
|
0x02, 0x02, 0x12, 0x54, 0x00, 0x0A, 0x84, 0x20, 0x21, 0x81, 0x12, 0xA8,
|
||||||
|
0x00, 0x0A, 0x85, 0x12, 0x54, 0x00, 0x0A, 0x85, 0x12, 0xA8, 0x00, 0x0A,
|
||||||
|
0x85, 0x10, 0xF3, 0x02, 0x02, 0x11, 0x7B, 0x02, 0x02, 0x12, 0x4F, 0x00,
|
||||||
|
0x0A, 0x84, 0x20, 0x21, 0x81, 0x12, 0x9F, 0x00, 0x0A, 0x85, 0x12, 0x4F,
|
||||||
|
0x00, 0x0A, 0x85, 0x12, 0x9F, 0x00, 0x0A, 0x85, 0x10, 0x20, 0x03, 0x02,
|
||||||
|
0x11, 0x7B, 0x02, 0x02, 0x12, 0x47, 0x00, 0x0A, 0x84, 0x20, 0x21, 0x81,
|
||||||
|
0x12, 0x8D, 0x00, 0x0A, 0x85, 0x12, 0x47, 0x00, 0x0A, 0x85, 0x12, 0x8D,
|
||||||
|
0x00, 0x0A, 0x85, 0x10, 0x50, 0x03, 0x02, 0x11, 0x7B, 0x02, 0x02, 0x12,
|
||||||
|
0x43, 0x00, 0x0A, 0x85, 0x12, 0x85, 0x00, 0x0A, 0x85, 0x12, 0x43, 0x00,
|
||||||
|
0x0A, 0x85, 0x12, 0x85, 0x00, 0x0A, 0x85, 0x12, 0x43, 0x00, 0x0A, 0x85,
|
||||||
|
0x12, 0x85, 0x00, 0x0A, 0x85, 0x12, 0x43, 0x00, 0x0A, 0x85, 0x12, 0x85,
|
||||||
|
0x00, 0x0A, 0x85, 0x10, 0xA8, 0x01, 0x02, 0x11, 0x3E, 0x01, 0x02, 0x12,
|
||||||
|
0x43, 0x00, 0x0A, 0x84, 0x20, 0x21, 0x81, 0x12, 0x85, 0x00, 0x0A, 0x85,
|
||||||
|
0x22, 0x9E, 0x10, 0x50, 0x03, 0x02, 0x11, 0x50, 0x01, 0x02, 0x12, 0xFC,
|
||||||
|
0x00, 0x0A, 0xA2, 0x20, 0x21, 0x84, 0x22, 0x82, 0x10, 0x50, 0x03, 0x02,
|
||||||
|
0x11, 0x50, 0x01, 0x02, 0x12, 0xFC, 0x00, 0x0A, 0xA2, 0x20, 0x21, 0x84,
|
||||||
|
0x22, 0x82, 0x10, 0x50, 0x03, 0x02, 0x11, 0x50, 0x01, 0x02, 0x12, 0xFC,
|
||||||
|
0x00, 0x0A, 0x98, 0x20, 0x21, 0x84, 0x22, 0x82, 0x10, 0xF3, 0x02, 0x02,
|
||||||
|
0x11, 0x3E, 0x01, 0x02, 0x12, 0xE1, 0x00, 0x0A, 0x84, 0x20, 0x21, 0x84,
|
||||||
|
0x22, 0x82, 0x10, 0xF3, 0x02, 0x02, 0x11, 0x3E, 0x01, 0x02, 0x12, 0xE1,
|
||||||
|
0x00, 0x0A, 0xA2, 0x20, 0x21, 0x84, 0x22, 0x82, 0x10, 0xA1, 0x02, 0x02,
|
||||||
|
0x11, 0x1B, 0x01, 0x02, 0x12, 0xD4, 0x00, 0x0A, 0xA2, 0x21, 0x84, 0x22,
|
||||||
|
0x82, 0x10, 0x7B, 0x02, 0x02, 0x11, 0xFC, 0x00, 0x02, 0x12, 0xBD, 0x00,
|
||||||
|
0x0A, 0x84, 0x20, 0x21, 0x84, 0x22, 0x8C, 0x10, 0x36, 0x02, 0x02, 0x11,
|
||||||
|
0x1B, 0x01, 0x02, 0x12, 0xA8, 0x00, 0x0A, 0x84, 0x20, 0x21, 0x84, 0x22,
|
||||||
|
0x8C, 0x10, 0xF3, 0x02, 0x02, 0x11, 0x3E, 0x01, 0x02, 0x12, 0x7E, 0x00,
|
||||||
|
0x0A, 0x85, 0x12, 0xBD, 0x00, 0x0A, 0x85, 0x12, 0x7E, 0x00, 0x0A, 0x85,
|
||||||
|
0x12, 0xBD, 0x00, 0x0A, 0x85, 0x12, 0x7E, 0x00, 0x0A, 0x85, 0x12, 0xBD,
|
||||||
|
0x00, 0x0A, 0x85, 0x12, 0x7E, 0x00, 0x0A, 0x85, 0x12, 0xBD, 0x00, 0x0A,
|
||||||
|
0x85, 0x12, 0x7E, 0x00, 0x0A, 0x84, 0x20, 0x81, 0x12, 0xBD, 0x00, 0x0A,
|
||||||
|
0x85, 0x10, 0x7B, 0x02, 0x02, 0x12, 0x7E, 0x00, 0x0A, 0x84, 0x20, 0x81,
|
||||||
|
0x12, 0xBD, 0x00, 0x0A, 0x85, 0x10, 0xF8, 0x01, 0x02, 0x12, 0x7E, 0x00,
|
||||||
|
0x0A, 0x84, 0x20, 0x81, 0x12, 0xBD, 0x00, 0x0A, 0x85, 0x10, 0x7B, 0x02,
|
||||||
|
0x02, 0x12, 0x7E, 0x00, 0x0A, 0x84, 0x20, 0x21, 0x81, 0x12, 0xBD, 0x00,
|
||||||
|
0x0A, 0x83, 0x22, 0x82, 0x10, 0xF3, 0x02, 0x02, 0x11, 0x1B, 0x01, 0x02,
|
||||||
|
0x12, 0x77, 0x00, 0x0A, 0x85, 0x12, 0xBD, 0x00, 0x0A, 0x85, 0x12, 0x77,
|
||||||
|
0x00, 0x0A, 0x84, 0x20, 0x81, 0x12, 0xBD, 0x00, 0x0A, 0x85, 0x10, 0x50,
|
||||||
|
0x03, 0x02, 0x12, 0x77, 0x00, 0x0A, 0x85, 0x12, 0xBD, 0x00, 0x0A, 0x85,
|
||||||
|
0x12, 0x77, 0x00, 0x0A, 0x84, 0x20, 0x81, 0x12, 0xBD, 0x00, 0x0A, 0x85,
|
||||||
|
0x10, 0xF3, 0x02, 0x02, 0x12, 0x77, 0x00, 0x0A, 0x84, 0x20, 0x81, 0x12,
|
||||||
|
0xBD, 0x00, 0x0A, 0x85, 0x10, 0xA1, 0x02, 0x02, 0x12, 0x77, 0x00, 0x0A,
|
||||||
|
0x84, 0x20, 0x81, 0x12, 0xBD, 0x00, 0x0A, 0x85, 0x10, 0x7B, 0x02, 0x02,
|
||||||
|
0x12, 0x77, 0x00, 0x0A, 0x84, 0x20, 0x81, 0x12, 0xBD, 0x00, 0x0A, 0x85,
|
||||||
|
0x10, 0x36, 0x02, 0x02, 0x12, 0x77, 0x00, 0x0A, 0x84, 0x20, 0x21, 0x81,
|
||||||
|
0x12, 0xBD, 0x00, 0x0A, 0x83, 0x22, 0x82, 0x10, 0xF8, 0x01, 0x02, 0x11,
|
||||||
|
0x3E, 0x01, 0x02, 0x12, 0x7E, 0x00, 0x0A, 0x84, 0x20, 0x21, 0x84, 0x22,
|
||||||
|
0x83, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t gStSong6[611] = {
|
||||||
|
0x4A, 0x59, 0x4D, 0x31, 0x14, 0x00, 0xFF, 0xFF, 0x81, 0x11, 0x7A, 0x01,
|
||||||
|
0x01, 0x12, 0xF8, 0x01, 0x01, 0x88, 0x21, 0x22, 0x90, 0x11, 0x7A, 0x01,
|
||||||
|
0x01, 0x12, 0xF8, 0x01, 0x01, 0x82, 0x21, 0x22, 0x84, 0x11, 0x7A, 0x01,
|
||||||
|
0x01, 0x12, 0xF8, 0x01, 0x01, 0x82, 0x21, 0x22, 0x84, 0x11, 0x7A, 0x01,
|
||||||
|
0x01, 0x12, 0xF8, 0x01, 0x01, 0xA0, 0x21, 0x22, 0x84, 0x10, 0x5E, 0x00,
|
||||||
|
0x00, 0x86, 0x20, 0x86, 0x10, 0x7E, 0x00, 0x00, 0x86, 0x20, 0x86, 0x10,
|
||||||
|
0x9F, 0x00, 0x00, 0x86, 0x20, 0x86, 0x10, 0xBD, 0x00, 0x00, 0x11, 0x50,
|
||||||
|
0x01, 0x01, 0x12, 0xC1, 0x01, 0x01, 0x88, 0x21, 0x22, 0x84, 0x11, 0x2C,
|
||||||
|
0x01, 0x01, 0x12, 0x90, 0x01, 0x01, 0x88, 0x21, 0x22, 0x84, 0x11, 0x50,
|
||||||
|
0x01, 0x01, 0x12, 0xC1, 0x01, 0x01, 0x88, 0x21, 0x22, 0x84, 0x11, 0x7A,
|
||||||
|
0x01, 0x01, 0x12, 0xF8, 0x01, 0x01, 0x88, 0x21, 0x22, 0x90, 0x11, 0x7A,
|
||||||
|
0x01, 0x01, 0x12, 0xF8, 0x01, 0x01, 0x82, 0x21, 0x22, 0x84, 0x20, 0x11,
|
||||||
|
0x7A, 0x01, 0x01, 0x12, 0xF8, 0x01, 0x01, 0x82, 0x21, 0x22, 0x84, 0x11,
|
||||||
|
0x7A, 0x01, 0x01, 0x12, 0xF8, 0x01, 0x01, 0x92, 0x10, 0xD4, 0x00, 0x00,
|
||||||
|
0x83, 0x20, 0x8B, 0x21, 0x22, 0x84, 0x10, 0xBD, 0x00, 0x00, 0x83, 0x20,
|
||||||
|
0x8F, 0x10, 0x9F, 0x00, 0x00, 0x83, 0x20, 0x83, 0x11, 0x7A, 0x01, 0x01,
|
||||||
|
0x12, 0xF8, 0x01, 0x01, 0x82, 0x21, 0x22, 0x84, 0x11, 0x7A, 0x01, 0x01,
|
||||||
|
0x12, 0xF8, 0x01, 0x01, 0x82, 0x21, 0x22, 0x84, 0x10, 0x8D, 0x00, 0x00,
|
||||||
|
0x11, 0xA8, 0x01, 0x01, 0x12, 0x36, 0x02, 0x01, 0x88, 0x21, 0x22, 0x84,
|
||||||
|
0x11, 0x7A, 0x01, 0x01, 0x12, 0xF8, 0x01, 0x01, 0x88, 0x21, 0x22, 0x84,
|
||||||
|
0x11, 0xA8, 0x01, 0x01, 0x12, 0x36, 0x02, 0x01, 0x86, 0x20, 0x82, 0x21,
|
||||||
|
0x22, 0x84, 0x10, 0x77, 0x00, 0x00, 0x11, 0xDC, 0x01, 0x01, 0x12, 0x7B,
|
||||||
|
0x02, 0x01, 0x88, 0x21, 0x22, 0x90, 0x11, 0xDC, 0x01, 0x01, 0x12, 0x7B,
|
||||||
|
0x02, 0x01, 0x82, 0x21, 0x22, 0x84, 0x11, 0xDC, 0x01, 0x01, 0x12, 0x7B,
|
||||||
|
0x02, 0x01, 0x82, 0x21, 0x22, 0x84, 0x11, 0xDC, 0x01, 0x01, 0x12, 0x7B,
|
||||||
|
0x02, 0x01, 0x92, 0x20, 0x8E, 0x21, 0x22, 0x84, 0x10, 0x3B, 0x00, 0x00,
|
||||||
|
0x98, 0x11, 0xDC, 0x01, 0x01, 0x12, 0x7B, 0x02, 0x01, 0x82, 0x21, 0x22,
|
||||||
|
0x84, 0x11, 0xDC, 0x01, 0x01, 0x12, 0x7B, 0x02, 0x01, 0x82, 0x21, 0x22,
|
||||||
|
0x84, 0x11, 0xDC, 0x01, 0x01, 0x12, 0x7B, 0x02, 0x01, 0x88, 0x21, 0x22,
|
||||||
|
0x84, 0x11, 0xA8, 0x01, 0x01, 0x12, 0x36, 0x02, 0x01, 0x86, 0x20, 0x82,
|
||||||
|
0x21, 0x22, 0x84, 0x11, 0xDC, 0x01, 0x01, 0x12, 0x7B, 0x02, 0x01, 0x88,
|
||||||
|
0x21, 0x22, 0x84, 0x10, 0x7E, 0x00, 0x00, 0x11, 0xF8, 0x01, 0x01, 0x12,
|
||||||
|
0xA1, 0x02, 0x01, 0x88, 0x21, 0x22, 0x90, 0x11, 0xF8, 0x01, 0x01, 0x12,
|
||||||
|
0xA1, 0x02, 0x01, 0x82, 0x21, 0x22, 0x84, 0x11, 0xF8, 0x01, 0x01, 0x12,
|
||||||
|
0xA1, 0x02, 0x01, 0x82, 0x21, 0x22, 0x84, 0x11, 0xF8, 0x01, 0x01, 0x12,
|
||||||
|
0xA1, 0x02, 0x01, 0x92, 0x20, 0x8E, 0x21, 0x22, 0x84, 0x10, 0x3F, 0x00,
|
||||||
|
0x00, 0x98, 0x11, 0xF8, 0x01, 0x01, 0x12, 0xA1, 0x02, 0x01, 0x82, 0x21,
|
||||||
|
0x22, 0x84, 0x20, 0x11, 0xF8, 0x01, 0x01, 0x12, 0xA1, 0x02, 0x01, 0x82,
|
||||||
|
0x21, 0x22, 0x84, 0x10, 0x7E, 0x00, 0x00, 0x11, 0x36, 0x02, 0x01, 0x12,
|
||||||
|
0xF3, 0x02, 0x01, 0x88, 0x21, 0x22, 0x84, 0x11, 0xF8, 0x01, 0x01, 0x12,
|
||||||
|
0xA1, 0x02, 0x01, 0x86, 0x20, 0x82, 0x21, 0x22, 0x84, 0x11, 0x36, 0x02,
|
||||||
|
0x01, 0x12, 0xF3, 0x02, 0x01, 0x88, 0x21, 0x22, 0x84, 0x10, 0x8D, 0x00,
|
||||||
|
0x00, 0x11, 0x7B, 0x02, 0x01, 0x12, 0x50, 0x03, 0x01, 0xB2, 0x21, 0x84,
|
||||||
|
0x20, 0x82, 0x22, 0x90, 0x10, 0x47, 0x00, 0x00, 0x98, 0x11, 0x7B, 0x02,
|
||||||
|
0x01, 0x12, 0x50, 0x03, 0x01, 0x82, 0x21, 0x22, 0x84, 0x20, 0x11, 0x7B,
|
||||||
|
0x02, 0x01, 0x12, 0x50, 0x03, 0x01, 0x82, 0x21, 0x22, 0x84, 0x10, 0x8D,
|
||||||
|
0x00, 0x00, 0x11, 0x36, 0x02, 0x01, 0x12, 0x50, 0x03, 0x01, 0x88, 0x21,
|
||||||
|
0x22, 0x84, 0x11, 0x36, 0x02, 0x01, 0x12, 0x50, 0x03, 0x01, 0x88, 0x21,
|
||||||
|
0x22, 0x84, 0x11, 0x36, 0x02, 0x01, 0x12, 0x50, 0x03, 0x01, 0x86, 0x20,
|
||||||
|
0x82, 0x21, 0x22, 0x84, 0x10, 0x9F, 0x00, 0x00, 0x11, 0x16, 0x02, 0x01,
|
||||||
|
0x12, 0x50, 0x03, 0x01, 0xEC, 0x20, 0x22, 0x82, 0x21, 0x85, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t gStSong7[777] = {
|
||||||
|
0x4A, 0x59, 0x4D, 0x31, 0x14, 0x00, 0xFF, 0xFF, 0x81, 0x10, 0x7A, 0x01,
|
||||||
|
0x00, 0x11, 0xF6, 0x04, 0x05, 0x12, 0x7E, 0x00, 0x07, 0x8A, 0x22, 0x84,
|
||||||
|
0x21, 0x86, 0x11, 0x42, 0x05, 0x05, 0x85, 0x11, 0xF6, 0x04, 0x05, 0x84,
|
||||||
|
0x21, 0x81, 0x11, 0x6C, 0x04, 0x05, 0x12, 0x77, 0x00, 0x07, 0x84, 0x21,
|
||||||
|
0x86, 0x11, 0xF6, 0x04, 0x05, 0x22, 0x84, 0x21, 0x86, 0x11, 0x42, 0x05,
|
||||||
|
0x05, 0x83, 0x20, 0x81, 0x21, 0x86, 0x10, 0xFC, 0x00, 0x00, 0x11, 0xF6,
|
||||||
|
0x04, 0x05, 0x12, 0x6A, 0x00, 0x07, 0x84, 0x21, 0x86, 0x11, 0x42, 0x05,
|
||||||
|
0x05, 0x22, 0x84, 0x21, 0x86, 0x11, 0xE6, 0x05, 0x05, 0x83, 0x20, 0x81,
|
||||||
|
0x21, 0x86, 0x10, 0x1B, 0x01, 0x00, 0x11, 0xF0, 0x03, 0x05, 0x12, 0x5E,
|
||||||
|
0x00, 0x07, 0x8A, 0x10, 0x3E, 0x01, 0x00, 0x22, 0x84, 0x21, 0x86, 0x10,
|
||||||
|
0x50, 0x01, 0x00, 0x85, 0x11, 0xF0, 0x03, 0x05, 0x85, 0x10, 0x7A, 0x01,
|
||||||
|
0x00, 0x12, 0x7E, 0x00, 0x07, 0x8A, 0x22, 0x85, 0x11, 0xB8, 0x03, 0x05,
|
||||||
|
0x85, 0x11, 0x50, 0x03, 0x05, 0x85, 0x11, 0xF3, 0x02, 0x05, 0x85, 0x11,
|
||||||
|
0x6C, 0x04, 0x05, 0x12, 0x77, 0x00, 0x07, 0x84, 0x21, 0x86, 0x11, 0xF6,
|
||||||
|
0x04, 0x05, 0x22, 0x84, 0x21, 0x86, 0x11, 0x42, 0x05, 0x05, 0x84, 0x21,
|
||||||
|
0x86, 0x11, 0xE6, 0x05, 0x05, 0x12, 0x7E, 0x00, 0x07, 0x83, 0x20, 0x81,
|
||||||
|
0x21, 0x86, 0x11, 0xE6, 0x05, 0x05, 0x22, 0x84, 0x21, 0x86, 0x11, 0xE6,
|
||||||
|
0x05, 0x05, 0x84, 0x21, 0x86, 0x10, 0xA8, 0x01, 0x00, 0x11, 0x6C, 0x04,
|
||||||
|
0x05, 0x12, 0x5E, 0x00, 0x07, 0x89, 0x21, 0x81, 0x10, 0x7A, 0x01, 0x00,
|
||||||
|
0x22, 0x8A, 0x10, 0x50, 0x01, 0x00, 0x11, 0x6C, 0x04, 0x05, 0x8A, 0x10,
|
||||||
|
0x7A, 0x01, 0x00, 0x11, 0xF6, 0x04, 0x05, 0x12, 0x7E, 0x00, 0x07, 0x89,
|
||||||
|
0x21, 0x81, 0x22, 0x83, 0x20, 0x91, 0x10, 0x50, 0x01, 0x00, 0x11, 0xB8,
|
||||||
|
0x03, 0x05, 0x12, 0x77, 0x00, 0x07, 0x89, 0x21, 0x81, 0x10, 0x3E, 0x01,
|
||||||
|
0x00, 0x22, 0x8A, 0x10, 0x1B, 0x01, 0x00, 0x11, 0xB8, 0x03, 0x05, 0x8A,
|
||||||
|
0x10, 0x3E, 0x01, 0x00, 0x11, 0xF0, 0x03, 0x05, 0x12, 0x6A, 0x00, 0x07,
|
||||||
|
0x89, 0x21, 0x81, 0x22, 0x83, 0x20, 0x91, 0x10, 0x1B, 0x01, 0x00, 0x11,
|
||||||
|
0xF3, 0x02, 0x05, 0x12, 0x5E, 0x00, 0x07, 0x89, 0x21, 0x81, 0x10, 0xFC,
|
||||||
|
0x00, 0x00, 0x22, 0x8A, 0x10, 0xEE, 0x00, 0x00, 0x11, 0xF3, 0x02, 0x05,
|
||||||
|
0x89, 0x21, 0x81, 0x10, 0xFC, 0x00, 0x00, 0x11, 0xF0, 0x03, 0x05, 0x12,
|
||||||
|
0x7E, 0x00, 0x07, 0x83, 0x20, 0x87, 0x11, 0xB8, 0x03, 0x05, 0x22, 0x8A,
|
||||||
|
0x10, 0x3E, 0x01, 0x00, 0x11, 0xF0, 0x03, 0x05, 0x89, 0x21, 0x81, 0x10,
|
||||||
|
0xBD, 0x00, 0x00, 0x11, 0x6C, 0x04, 0x05, 0x12, 0x77, 0x00, 0x07, 0x8A,
|
||||||
|
0x11, 0xF0, 0x03, 0x05, 0x22, 0x8A, 0x11, 0x6C, 0x04, 0x05, 0x8A, 0x11,
|
||||||
|
0xF6, 0x04, 0x05, 0x12, 0x6A, 0x00, 0x07, 0x8A, 0x11, 0x6C, 0x04, 0x05,
|
||||||
|
0x22, 0x83, 0x20, 0x87, 0x11, 0xF6, 0x04, 0x05, 0x8A, 0x10, 0xEE, 0x00,
|
||||||
|
0x00, 0x11, 0x42, 0x05, 0x05, 0x12, 0x5E, 0x00, 0x07, 0x8A, 0x10, 0xFC,
|
||||||
|
0x00, 0x00, 0x11, 0xF6, 0x04, 0x05, 0x22, 0x8A, 0x10, 0x1B, 0x01, 0x00,
|
||||||
|
0x11, 0x42, 0x05, 0x05, 0x89, 0x21, 0x81, 0x10, 0xFC, 0x00, 0x00, 0x11,
|
||||||
|
0xF6, 0x04, 0x05, 0x12, 0x7E, 0x00, 0x07, 0x83, 0x20, 0x86, 0x21, 0x81,
|
||||||
|
0x22, 0x8A, 0x11, 0xF6, 0x04, 0x05, 0x8A, 0x10, 0xEE, 0x00, 0x00, 0x11,
|
||||||
|
0xE6, 0x05, 0x05, 0x12, 0x77, 0x00, 0x07, 0x8A, 0x10, 0xFC, 0x00, 0x00,
|
||||||
|
0x22, 0x8A, 0x10, 0x1B, 0x01, 0x00, 0x8A, 0x10, 0x3E, 0x01, 0x00, 0x12,
|
||||||
|
0x6A, 0x00, 0x07, 0x83, 0x20, 0x86, 0x21, 0x81, 0x22, 0x94, 0x10, 0x1B,
|
||||||
|
0x01, 0x00, 0x11, 0xE6, 0x05, 0x05, 0x12, 0x5E, 0x00, 0x07, 0x83, 0x20,
|
||||||
|
0x87, 0x22, 0x89, 0x21, 0x81, 0x10, 0x1B, 0x01, 0x00, 0x8A, 0x10, 0x7A,
|
||||||
|
0x01, 0x00, 0x11, 0x6C, 0x04, 0x05, 0x12, 0x8D, 0x00, 0x07, 0x89, 0x21,
|
||||||
|
0x81, 0x22, 0x8A, 0x11, 0xF0, 0x03, 0x05, 0x8A, 0x11, 0xB8, 0x03, 0x05,
|
||||||
|
0x12, 0x77, 0x00, 0x07, 0x8A, 0x11, 0xF0, 0x03, 0x05, 0x22, 0x8A, 0x11,
|
||||||
|
0x6C, 0x04, 0x05, 0x8A, 0x11, 0xF3, 0x02, 0x05, 0x12, 0x6A, 0x00, 0x07,
|
||||||
|
0x83, 0x20, 0x86, 0x21, 0x81, 0x22, 0x8A, 0x10, 0x7A, 0x01, 0x00, 0x11,
|
||||||
|
0x6C, 0x04, 0x05, 0x81, 0x20, 0x84, 0x10, 0x7A, 0x01, 0x00, 0x81, 0x20,
|
||||||
|
0x84, 0x10, 0x7A, 0x01, 0x00, 0x11, 0xAF, 0x04, 0x05, 0x12, 0x5E, 0x00,
|
||||||
|
0x07, 0x83, 0x20, 0x87, 0x10, 0x7A, 0x01, 0x00, 0x22, 0x83, 0x20, 0x86,
|
||||||
|
0x21, 0x81, 0x10, 0x7A, 0x01, 0x00, 0x83, 0x20, 0x87, 0x10, 0x7A, 0x01,
|
||||||
|
0x00, 0x11, 0xF6, 0x04, 0x05, 0x12, 0x7E, 0x00, 0x07, 0x85, 0x11, 0xE6,
|
||||||
|
0x05, 0x05, 0x85, 0x11, 0xF6, 0x04, 0x05, 0x85, 0x11, 0xE6, 0x05, 0x05,
|
||||||
|
0x85, 0x11, 0xF6, 0x04, 0x05, 0x84, 0x12, 0x5E, 0x00, 0x07, 0x81, 0x11,
|
||||||
|
0xE6, 0x05, 0x05, 0x85, 0x11, 0xF6, 0x04, 0x05, 0x85, 0x11, 0xE6, 0x05,
|
||||||
|
0x05, 0x85, 0x11, 0xF6, 0x04, 0x05, 0x85, 0x11, 0xE6, 0x05, 0x05, 0x83,
|
||||||
|
0x12, 0x7E, 0x00, 0x07, 0x82, 0x11, 0xF6, 0x04, 0x05, 0x85, 0x11, 0xE6,
|
||||||
|
0x05, 0x05, 0x85, 0x11, 0xF6, 0x04, 0x05, 0x85, 0x11, 0xE6, 0x05, 0x05,
|
||||||
|
0x85, 0x11, 0xF6, 0x04, 0x05, 0x82, 0x12, 0x5E, 0x00, 0x07, 0x83, 0x11,
|
||||||
|
0xE6, 0x05, 0x05, 0x85, 0x11, 0xF6, 0x04, 0x05, 0x85, 0x11, 0xE6, 0x05,
|
||||||
|
0x05, 0x85, 0x11, 0xF6, 0x04, 0x05, 0x81, 0x22, 0x85, 0x12, 0x7E, 0x00,
|
||||||
|
0x07, 0x88, 0x21, 0x89, 0x20, 0x88, 0x22, 0x86, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t gStSong8[2338] = {
|
||||||
|
0x4A, 0x59, 0x4D, 0x31, 0x14, 0x00, 0x00, 0x00, 0x81, 0x10, 0x1F, 0x00,
|
||||||
|
0x07, 0x11, 0x7E, 0x00, 0x01, 0x13, 0x1C, 0x00, 0x0A, 0x82, 0x21, 0x84,
|
||||||
|
0x13, 0x1D, 0x00, 0x0A, 0x84, 0x11, 0xB2, 0x00, 0x01, 0x82, 0x21, 0x13,
|
||||||
|
0x1D, 0x00, 0x0A, 0x86, 0x13, 0x1D, 0x00, 0x0A, 0x82, 0x11, 0xD4, 0x00,
|
||||||
|
0x01, 0x82, 0x21, 0x82, 0x13, 0x1D, 0x00, 0x0A, 0x86, 0x11, 0xFC, 0x00,
|
||||||
|
0x01, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x21, 0x84, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x84, 0x11, 0x50, 0x01, 0x01, 0x82, 0x21, 0x13, 0x1E, 0x00, 0x0A, 0x86,
|
||||||
|
0x10, 0x21, 0x00, 0x07, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x11, 0xC1, 0x01,
|
||||||
|
0x01, 0x82, 0x21, 0x82, 0x13, 0x1E, 0x00, 0x0A, 0x86, 0x11, 0xF8, 0x01,
|
||||||
|
0x01, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x21, 0x84, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x84, 0x11, 0x57, 0x02, 0x01, 0x82, 0x21, 0x13, 0x1E, 0x00, 0x0A, 0x86,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x82, 0x11, 0xF3, 0x02, 0x01, 0x82, 0x21, 0x82,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x86, 0x11, 0x82, 0x03, 0x01, 0x13, 0x1E, 0x00,
|
||||||
|
0x0A, 0x82, 0x21, 0x84, 0x10, 0x23, 0x00, 0x07, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x84, 0x11, 0xF0, 0x03, 0x01, 0x82, 0x21, 0x13, 0x1E, 0x00, 0x0A, 0x86,
|
||||||
|
0x13, 0x1D, 0x00, 0x0A, 0x82, 0x11, 0x42, 0x05, 0x01, 0x82, 0x21, 0x82,
|
||||||
|
0x13, 0x1D, 0x00, 0x0A, 0x86, 0x11, 0x31, 0x06, 0x01, 0x13, 0x1D, 0x00,
|
||||||
|
0x0A, 0x82, 0x21, 0x84, 0x13, 0x1D, 0x00, 0x0A, 0x84, 0x11, 0xF6, 0x04,
|
||||||
|
0x01, 0x82, 0x21, 0x13, 0x1C, 0x00, 0x0A, 0x86, 0x13, 0x1D, 0x00, 0x0A,
|
||||||
|
0x82, 0x11, 0xF0, 0x03, 0x01, 0x82, 0x21, 0x82, 0x10, 0x21, 0x00, 0x07,
|
||||||
|
0x13, 0x1D, 0x00, 0x0A, 0x86, 0x11, 0x20, 0x03, 0x01, 0x13, 0x1D, 0x00,
|
||||||
|
0x0A, 0x82, 0x21, 0x84, 0x13, 0x1D, 0x00, 0x0A, 0x84, 0x11, 0xA1, 0x02,
|
||||||
|
0x01, 0x82, 0x21, 0x13, 0x1E, 0x00, 0x0A, 0x86, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x82, 0x11, 0x36, 0x02, 0x01, 0x82, 0x21, 0x82, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x86, 0x11, 0xF8, 0x01, 0x01, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x21, 0x84,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x84, 0x11, 0xC1, 0x01, 0x01, 0x82, 0x10, 0x1F,
|
||||||
|
0x00, 0x07, 0x21, 0x13, 0x1E, 0x00, 0x0A, 0x86, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x82, 0x11, 0x7A, 0x01, 0x01, 0x82, 0x21, 0x82, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x86, 0x11, 0x3E, 0x01, 0x01, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x21, 0x84,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x84, 0x11, 0xFC, 0x00, 0x01, 0x82, 0x21, 0x13,
|
||||||
|
0x1E, 0x00, 0x0A, 0x86, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x11, 0xD4, 0x00,
|
||||||
|
0x01, 0x82, 0x21, 0x82, 0x13, 0x1E, 0x00, 0x0A, 0x86, 0x10, 0x21, 0x00,
|
||||||
|
0x07, 0x11, 0x9F, 0x00, 0x01, 0x13, 0x1D, 0x00, 0x0A, 0x82, 0x21, 0x84,
|
||||||
|
0x13, 0x1D, 0x00, 0x0A, 0x84, 0x11, 0x7E, 0x00, 0x01, 0x82, 0x21, 0x13,
|
||||||
|
0x1D, 0x00, 0x0A, 0x86, 0x13, 0x1D, 0x00, 0x0A, 0x82, 0x11, 0xB2, 0x00,
|
||||||
|
0x01, 0x82, 0x21, 0x82, 0x13, 0x1C, 0x00, 0x0A, 0x86, 0x11, 0xD4, 0x00,
|
||||||
|
0x01, 0x13, 0x1D, 0x00, 0x0A, 0x82, 0x21, 0x84, 0x13, 0x1D, 0x00, 0x0A,
|
||||||
|
0x84, 0x11, 0xFC, 0x00, 0x01, 0x82, 0x21, 0x13, 0x1D, 0x00, 0x0A, 0x86,
|
||||||
|
0x10, 0x23, 0x00, 0x07, 0x13, 0x1D, 0x00, 0x0A, 0x82, 0x11, 0x50, 0x01,
|
||||||
|
0x01, 0x82, 0x21, 0x82, 0x13, 0x1E, 0x00, 0x0A, 0x86, 0x11, 0xC1, 0x01,
|
||||||
|
0x01, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x21, 0x84, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x84, 0x11, 0xF8, 0x01, 0x01, 0x82, 0x21, 0x13, 0x1E, 0x00, 0x0A, 0x86,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x82, 0x11, 0x57, 0x02, 0x01, 0x82, 0x21, 0x82,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x86, 0x11, 0xF3, 0x02, 0x01, 0x13, 0x1E, 0x00,
|
||||||
|
0x0A, 0x82, 0x21, 0x84, 0x10, 0x21, 0x00, 0x07, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x84, 0x11, 0x82, 0x03, 0x01, 0x82, 0x21, 0x13, 0x1E, 0x00, 0x0A, 0x86,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x82, 0x11, 0xF0, 0x03, 0x01, 0x82, 0x21, 0x82,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x86, 0x11, 0x42, 0x05, 0x01, 0x13, 0x1E, 0x00,
|
||||||
|
0x0A, 0x82, 0x21, 0x84, 0x13, 0x1E, 0x00, 0x0A, 0x84, 0x11, 0x31, 0x06,
|
||||||
|
0x01, 0x82, 0x21, 0x13, 0x1D, 0x00, 0x0A, 0x86, 0x13, 0x1D, 0x00, 0x0A,
|
||||||
|
0x82, 0x11, 0xF6, 0x04, 0x01, 0x82, 0x21, 0x82, 0x10, 0x1F, 0x00, 0x07,
|
||||||
|
0x13, 0x1D, 0x00, 0x0A, 0x86, 0x11, 0xF0, 0x03, 0x01, 0x13, 0x1D, 0x00,
|
||||||
|
0x0A, 0x82, 0x21, 0x84, 0x13, 0x1C, 0x00, 0x0A, 0x84, 0x11, 0x20, 0x03,
|
||||||
|
0x01, 0x82, 0x21, 0x13, 0x1D, 0x00, 0x0A, 0x86, 0x13, 0x1D, 0x00, 0x0A,
|
||||||
|
0x82, 0x11, 0xA1, 0x02, 0x01, 0x82, 0x21, 0x82, 0x13, 0x1D, 0x00, 0x0A,
|
||||||
|
0x86, 0x11, 0x36, 0x02, 0x01, 0x13, 0x1D, 0x00, 0x0A, 0x82, 0x21, 0x84,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x84, 0x11, 0xF8, 0x01, 0x01, 0x82, 0x10, 0x21,
|
||||||
|
0x00, 0x07, 0x21, 0x13, 0x1E, 0x00, 0x0A, 0x86, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x82, 0x11, 0xC1, 0x01, 0x01, 0x82, 0x21, 0x82, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x86, 0x11, 0x7A, 0x01, 0x01, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x21, 0x84,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x84, 0x11, 0x3E, 0x01, 0x01, 0x82, 0x21, 0x13,
|
||||||
|
0x1E, 0x00, 0x0A, 0x86, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x11, 0xFC, 0x00,
|
||||||
|
0x01, 0x82, 0x21, 0x82, 0x13, 0x1E, 0x00, 0x0A, 0x86, 0x10, 0x23, 0x00,
|
||||||
|
0x07, 0x11, 0xD4, 0x00, 0x01, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x21, 0x84,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x84, 0x11, 0x9F, 0x00, 0x01, 0x82, 0x21, 0x13,
|
||||||
|
0x1E, 0x00, 0x0A, 0x86, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x11, 0x7E, 0x00,
|
||||||
|
0x01, 0x82, 0x21, 0x82, 0x13, 0x1D, 0x00, 0x0A, 0x86, 0x11, 0xB2, 0x00,
|
||||||
|
0x01, 0x13, 0x1D, 0x00, 0x0A, 0x82, 0x21, 0x84, 0x13, 0x1D, 0x00, 0x0A,
|
||||||
|
0x84, 0x11, 0xD4, 0x00, 0x01, 0x82, 0x21, 0x13, 0x1D, 0x00, 0x0A, 0x86,
|
||||||
|
0x10, 0x21, 0x00, 0x07, 0x13, 0x1C, 0x00, 0x0A, 0x82, 0x11, 0xFC, 0x00,
|
||||||
|
0x01, 0x82, 0x21, 0x82, 0x13, 0x1D, 0x00, 0x0A, 0x86, 0x11, 0x50, 0x01,
|
||||||
|
0x01, 0x13, 0x1D, 0x00, 0x0A, 0x82, 0x21, 0x84, 0x13, 0x1D, 0x00, 0x0A,
|
||||||
|
0x84, 0x11, 0xC1, 0x01, 0x01, 0x82, 0x21, 0x13, 0x1D, 0x00, 0x0A, 0x86,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x82, 0x11, 0xF8, 0x01, 0x01, 0x82, 0x21, 0x82,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x86, 0x11, 0x57, 0x02, 0x01, 0x13, 0x1E, 0x00,
|
||||||
|
0x0A, 0x82, 0x21, 0x84, 0x10, 0x1F, 0x00, 0x07, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x84, 0x11, 0xF3, 0x02, 0x01, 0x82, 0x21, 0x13, 0x1E, 0x00, 0x0A, 0x86,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x82, 0x11, 0x82, 0x03, 0x01, 0x82, 0x21, 0x82,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x86, 0x11, 0xF0, 0x03, 0x01, 0x13, 0x1E, 0x00,
|
||||||
|
0x0A, 0x82, 0x21, 0x84, 0x13, 0x1E, 0x00, 0x0A, 0x84, 0x11, 0x42, 0x05,
|
||||||
|
0x01, 0x82, 0x21, 0x13, 0x1E, 0x00, 0x0A, 0x86, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x82, 0x11, 0x31, 0x06, 0x01, 0x82, 0x21, 0x82, 0x10, 0x21, 0x00, 0x07,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x86, 0x11, 0xF6, 0x04, 0x01, 0x13, 0x1E, 0x00,
|
||||||
|
0x0A, 0x82, 0x21, 0x84, 0x13, 0x1D, 0x00, 0x0A, 0x84, 0x11, 0xF0, 0x03,
|
||||||
|
0x01, 0x82, 0x21, 0x13, 0x1D, 0x00, 0x0A, 0x86, 0x13, 0x1D, 0x00, 0x0A,
|
||||||
|
0x82, 0x11, 0x20, 0x03, 0x01, 0x82, 0x21, 0x82, 0x13, 0x1D, 0x00, 0x0A,
|
||||||
|
0x86, 0x11, 0xA1, 0x02, 0x01, 0x13, 0x1C, 0x00, 0x0A, 0x82, 0x21, 0x84,
|
||||||
|
0x13, 0x1D, 0x00, 0x0A, 0x84, 0x11, 0x36, 0x02, 0x01, 0x82, 0x10, 0x23,
|
||||||
|
0x00, 0x07, 0x21, 0x13, 0x1D, 0x00, 0x0A, 0x86, 0x13, 0x1D, 0x00, 0x0A,
|
||||||
|
0x82, 0x11, 0xF8, 0x01, 0x01, 0x82, 0x21, 0x82, 0x13, 0x1D, 0x00, 0x0A,
|
||||||
|
0x86, 0x11, 0xC1, 0x01, 0x01, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x21, 0x84,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x84, 0x11, 0x7A, 0x01, 0x01, 0x82, 0x21, 0x13,
|
||||||
|
0x1E, 0x00, 0x0A, 0x86, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x11, 0x3E, 0x01,
|
||||||
|
0x01, 0x82, 0x21, 0x82, 0x13, 0x1E, 0x00, 0x0A, 0x86, 0x10, 0x21, 0x00,
|
||||||
|
0x07, 0x11, 0xFC, 0x00, 0x01, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x21, 0x84,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x84, 0x11, 0xD4, 0x00, 0x01, 0x82, 0x21, 0x13,
|
||||||
|
0x1E, 0x00, 0x0A, 0x86, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x11, 0x9F, 0x00,
|
||||||
|
0x01, 0x82, 0x21, 0x82, 0x13, 0x1E, 0x00, 0x0A, 0x86, 0x11, 0x7E, 0x00,
|
||||||
|
0x01, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x21, 0x84, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x84, 0x11, 0xB2, 0x00, 0x01, 0x82, 0x21, 0x13, 0x1E, 0x00, 0x0A, 0x86,
|
||||||
|
0x10, 0x1F, 0x00, 0x07, 0x13, 0x1D, 0x00, 0x0A, 0x82, 0x11, 0xD4, 0x00,
|
||||||
|
0x01, 0x82, 0x21, 0x82, 0x13, 0x1D, 0x00, 0x0A, 0x86, 0x11, 0xFC, 0x00,
|
||||||
|
0x01, 0x13, 0x1D, 0x00, 0x0A, 0x82, 0x21, 0x84, 0x13, 0x1D, 0x00, 0x0A,
|
||||||
|
0x84, 0x11, 0x50, 0x01, 0x01, 0x82, 0x21, 0x13, 0x1C, 0x00, 0x0A, 0x86,
|
||||||
|
0x13, 0x1D, 0x00, 0x0A, 0x82, 0x11, 0xC1, 0x01, 0x01, 0x82, 0x21, 0x82,
|
||||||
|
0x13, 0x1D, 0x00, 0x0A, 0x86, 0x11, 0xF8, 0x01, 0x01, 0x13, 0x1D, 0x00,
|
||||||
|
0x0A, 0x82, 0x21, 0x84, 0x10, 0x21, 0x00, 0x07, 0x13, 0x1D, 0x00, 0x0A,
|
||||||
|
0x84, 0x11, 0x57, 0x02, 0x01, 0x82, 0x21, 0x13, 0x1E, 0x00, 0x0A, 0x86,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x82, 0x11, 0xF3, 0x02, 0x01, 0x82, 0x21, 0x82,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x86, 0x11, 0x82, 0x03, 0x01, 0x13, 0x1E, 0x00,
|
||||||
|
0x0A, 0x82, 0x21, 0x84, 0x13, 0x1E, 0x00, 0x0A, 0x84, 0x11, 0xF0, 0x03,
|
||||||
|
0x01, 0x82, 0x21, 0x13, 0x1E, 0x00, 0x0A, 0x86, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x82, 0x11, 0x42, 0x05, 0x01, 0x82, 0x21, 0x82, 0x10, 0x23, 0x00, 0x07,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x86, 0x11, 0x31, 0x06, 0x01, 0x13, 0x1E, 0x00,
|
||||||
|
0x0A, 0x82, 0x21, 0x84, 0x13, 0x1E, 0x00, 0x0A, 0x84, 0x11, 0xF6, 0x04,
|
||||||
|
0x01, 0x82, 0x21, 0x13, 0x1E, 0x00, 0x0A, 0x86, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x82, 0x11, 0xF0, 0x03, 0x01, 0x82, 0x21, 0x82, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x86, 0x11, 0x20, 0x03, 0x01, 0x13, 0x1D, 0x00, 0x0A, 0x82, 0x21, 0x84,
|
||||||
|
0x13, 0x1D, 0x00, 0x0A, 0x84, 0x11, 0xA1, 0x02, 0x01, 0x82, 0x10, 0x21,
|
||||||
|
0x00, 0x07, 0x21, 0x13, 0x1D, 0x00, 0x0A, 0x86, 0x13, 0x1D, 0x00, 0x0A,
|
||||||
|
0x82, 0x11, 0x36, 0x02, 0x01, 0x82, 0x21, 0x82, 0x13, 0x1C, 0x00, 0x0A,
|
||||||
|
0x86, 0x11, 0xF8, 0x01, 0x01, 0x13, 0x1D, 0x00, 0x0A, 0x82, 0x21, 0x84,
|
||||||
|
0x13, 0x1D, 0x00, 0x0A, 0x84, 0x11, 0xC1, 0x01, 0x01, 0x82, 0x21, 0x13,
|
||||||
|
0x1D, 0x00, 0x0A, 0x86, 0x13, 0x1D, 0x00, 0x0A, 0x82, 0x11, 0x7A, 0x01,
|
||||||
|
0x01, 0x82, 0x21, 0x82, 0x13, 0x1E, 0x00, 0x0A, 0x86, 0x10, 0x1F, 0x00,
|
||||||
|
0x07, 0x11, 0x3E, 0x01, 0x01, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x21, 0x84,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x84, 0x11, 0xFC, 0x00, 0x01, 0x82, 0x21, 0x13,
|
||||||
|
0x1E, 0x00, 0x0A, 0x86, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x11, 0xD4, 0x00,
|
||||||
|
0x01, 0x82, 0x21, 0x82, 0x13, 0x1E, 0x00, 0x0A, 0x86, 0x11, 0x9F, 0x00,
|
||||||
|
0x01, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x21, 0x84, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x84, 0x11, 0x7E, 0x00, 0x01, 0x82, 0x21, 0x13, 0x1E, 0x00, 0x0A, 0x86,
|
||||||
|
0x10, 0x21, 0x00, 0x07, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x11, 0xB2, 0x00,
|
||||||
|
0x01, 0x82, 0x21, 0x82, 0x13, 0x1E, 0x00, 0x0A, 0x86, 0x11, 0xD4, 0x00,
|
||||||
|
0x01, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x21, 0x84, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x84, 0x11, 0xFC, 0x00, 0x01, 0x82, 0x21, 0x13, 0x1D, 0x00, 0x0A, 0x86,
|
||||||
|
0x13, 0x1D, 0x00, 0x0A, 0x82, 0x11, 0x50, 0x01, 0x01, 0x82, 0x21, 0x82,
|
||||||
|
0x13, 0x1D, 0x00, 0x0A, 0x86, 0x11, 0xC1, 0x01, 0x01, 0x13, 0x1D, 0x00,
|
||||||
|
0x0A, 0x82, 0x21, 0x84, 0x10, 0x23, 0x00, 0x07, 0x13, 0x1C, 0x00, 0x0A,
|
||||||
|
0x84, 0x11, 0xF8, 0x01, 0x01, 0x82, 0x21, 0x13, 0x1D, 0x00, 0x0A, 0x86,
|
||||||
|
0x13, 0x1D, 0x00, 0x0A, 0x82, 0x11, 0x57, 0x02, 0x01, 0x82, 0x21, 0x82,
|
||||||
|
0x13, 0x1D, 0x00, 0x0A, 0x86, 0x11, 0xF3, 0x02, 0x01, 0x13, 0x1D, 0x00,
|
||||||
|
0x0A, 0x82, 0x21, 0x84, 0x13, 0x1E, 0x00, 0x0A, 0x84, 0x11, 0x82, 0x03,
|
||||||
|
0x01, 0x82, 0x21, 0x13, 0x1E, 0x00, 0x0A, 0x86, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x82, 0x11, 0xF0, 0x03, 0x01, 0x82, 0x21, 0x82, 0x10, 0x21, 0x00, 0x07,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x86, 0x11, 0x42, 0x05, 0x01, 0x13, 0x1E, 0x00,
|
||||||
|
0x0A, 0x82, 0x21, 0x84, 0x13, 0x1E, 0x00, 0x0A, 0x84, 0x11, 0x31, 0x06,
|
||||||
|
0x01, 0x82, 0x21, 0x13, 0x1E, 0x00, 0x0A, 0x86, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x82, 0x11, 0xF6, 0x04, 0x01, 0x82, 0x21, 0x82, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x86, 0x11, 0xF0, 0x03, 0x01, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x21, 0x84,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x84, 0x11, 0x20, 0x03, 0x01, 0x82, 0x10, 0x1F,
|
||||||
|
0x00, 0x07, 0x21, 0x13, 0x1E, 0x00, 0x0A, 0x86, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x82, 0x11, 0xA1, 0x02, 0x01, 0x82, 0x21, 0x82, 0x13, 0x1D, 0x00, 0x0A,
|
||||||
|
0x86, 0x11, 0x36, 0x02, 0x01, 0x13, 0x1D, 0x00, 0x0A, 0x82, 0x21, 0x84,
|
||||||
|
0x13, 0x1D, 0x00, 0x0A, 0x84, 0x11, 0xF8, 0x01, 0x01, 0x82, 0x21, 0x13,
|
||||||
|
0x1D, 0x00, 0x0A, 0x86, 0x13, 0x1C, 0x00, 0x0A, 0x82, 0x11, 0xC1, 0x01,
|
||||||
|
0x01, 0x82, 0x21, 0x82, 0x13, 0x1D, 0x00, 0x0A, 0x86, 0x10, 0x21, 0x00,
|
||||||
|
0x07, 0x11, 0x7A, 0x01, 0x01, 0x13, 0x1D, 0x00, 0x0A, 0x82, 0x21, 0x84,
|
||||||
|
0x13, 0x1D, 0x00, 0x0A, 0x84, 0x11, 0x3E, 0x01, 0x01, 0x82, 0x21, 0x13,
|
||||||
|
0x1D, 0x00, 0x0A, 0x86, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x11, 0xFC, 0x00,
|
||||||
|
0x01, 0x82, 0x21, 0x82, 0x13, 0x1E, 0x00, 0x0A, 0x86, 0x11, 0xD4, 0x00,
|
||||||
|
0x01, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x21, 0x84, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x84, 0x11, 0x9F, 0x00, 0x01, 0x82, 0x21, 0x13, 0x1E, 0x00, 0x0A, 0x86,
|
||||||
|
0x10, 0x23, 0x00, 0x07, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x11, 0x7E, 0x00,
|
||||||
|
0x01, 0x82, 0x21, 0x82, 0x13, 0x1E, 0x00, 0x0A, 0x86, 0x11, 0xB2, 0x00,
|
||||||
|
0x01, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x21, 0x84, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x84, 0x11, 0xD4, 0x00, 0x01, 0x82, 0x21, 0x13, 0x1E, 0x00, 0x0A, 0x86,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x82, 0x11, 0xFC, 0x00, 0x01, 0x82, 0x21, 0x82,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x86, 0x11, 0x50, 0x01, 0x01, 0x13, 0x1E, 0x00,
|
||||||
|
0x0A, 0x82, 0x21, 0x84, 0x10, 0x21, 0x00, 0x07, 0x13, 0x1D, 0x00, 0x0A,
|
||||||
|
0x84, 0x11, 0xC1, 0x01, 0x01, 0x82, 0x21, 0x13, 0x1D, 0x00, 0x0A, 0x86,
|
||||||
|
0x13, 0x1D, 0x00, 0x0A, 0x82, 0x11, 0xF8, 0x01, 0x01, 0x82, 0x21, 0x82,
|
||||||
|
0x13, 0x1D, 0x00, 0x0A, 0x86, 0x11, 0x57, 0x02, 0x01, 0x13, 0x1C, 0x00,
|
||||||
|
0x0A, 0x82, 0x21, 0x84, 0x13, 0x1D, 0x00, 0x0A, 0x84, 0x11, 0xF3, 0x02,
|
||||||
|
0x01, 0x82, 0x21, 0x13, 0x1D, 0x00, 0x0A, 0x86, 0x13, 0x1D, 0x00, 0x0A,
|
||||||
|
0x82, 0x11, 0x82, 0x03, 0x01, 0x82, 0x21, 0x82, 0x10, 0x1F, 0x00, 0x07,
|
||||||
|
0x13, 0x1D, 0x00, 0x0A, 0x86, 0x11, 0xF0, 0x03, 0x01, 0x13, 0x1E, 0x00,
|
||||||
|
0x0A, 0x82, 0x21, 0x84, 0x13, 0x1E, 0x00, 0x0A, 0x84, 0x11, 0x42, 0x05,
|
||||||
|
0x01, 0x82, 0x21, 0x13, 0x1E, 0x00, 0x0A, 0x86, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x82, 0x11, 0x31, 0x06, 0x01, 0x82, 0x21, 0x82, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x86, 0x11, 0xF6, 0x04, 0x01, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x21, 0x84,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x84, 0x11, 0xF0, 0x03, 0x01, 0x82, 0x10, 0x21,
|
||||||
|
0x00, 0x07, 0x21, 0x13, 0x1E, 0x00, 0x0A, 0x86, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x82, 0x11, 0x20, 0x03, 0x01, 0x82, 0x21, 0x82, 0x13, 0x1E, 0x00, 0x0A,
|
||||||
|
0x86, 0x11, 0xA1, 0x02, 0x01, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x21, 0x84,
|
||||||
|
0x13, 0x1E, 0x00, 0x0A, 0x84, 0x11, 0x36, 0x02, 0x01, 0x82, 0x21, 0x13,
|
||||||
|
0x1E, 0x00, 0x0A, 0x86, 0x13, 0x1D, 0x00, 0x0A, 0x82, 0x11, 0xF8, 0x01,
|
||||||
|
0x01, 0x82, 0x21, 0x82, 0x13, 0x1D, 0x00, 0x0A, 0x86, 0x10, 0x23, 0x00,
|
||||||
|
0x07, 0x11, 0xC1, 0x01, 0x01, 0x13, 0x1D, 0x00, 0x0A, 0x82, 0x21, 0x84,
|
||||||
|
0x13, 0x1D, 0x00, 0x0A, 0x84, 0x11, 0x7A, 0x01, 0x01, 0x82, 0x21, 0x13,
|
||||||
|
0x1C, 0x00, 0x0A, 0x86, 0x13, 0x1D, 0x00, 0x0A, 0x82, 0x11, 0x3E, 0x01,
|
||||||
|
0x01, 0x82, 0x21, 0x82, 0x13, 0x1D, 0x00, 0x0A, 0x86, 0x11, 0xFC, 0x00,
|
||||||
|
0x01, 0x13, 0x1D, 0x00, 0x0A, 0x82, 0x21, 0x84, 0x13, 0x1D, 0x00, 0x0A,
|
||||||
|
0x84, 0x11, 0xD4, 0x00, 0x01, 0x82, 0x21, 0x13, 0x1E, 0x00, 0x0A, 0x86,
|
||||||
|
0x10, 0x21, 0x00, 0x07, 0x13, 0x1E, 0x00, 0x0A, 0x82, 0x11, 0x9F, 0x00,
|
||||||
|
0x01, 0x82, 0x21, 0x82, 0x13, 0x1E, 0x00, 0x0A, 0x85, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const uint8_t *data;
|
||||||
|
uint16_t length;
|
||||||
|
} StSongT;
|
||||||
|
|
||||||
|
#define ST_SONG_COUNT 9u
|
||||||
|
#define ST_SONG_BONUS 4u
|
||||||
|
#define ST_SONG_GAME_OVER 5u
|
||||||
|
#define ST_SONG_NAME_ENTRY 6u
|
||||||
|
#define ST_SONG_SCORES 7u
|
||||||
|
#define ST_SONG_INTRO 8u
|
||||||
|
|
||||||
|
static const StSongT kStSongs[ST_SONG_COUNT] = {
|
||||||
|
{ gStSong0, (uint16_t)sizeof(gStSong0) },
|
||||||
|
{ gStSong1, (uint16_t)sizeof(gStSong1) },
|
||||||
|
{ gStSong2, (uint16_t)sizeof(gStSong2) },
|
||||||
|
{ gStSong3, (uint16_t)sizeof(gStSong3) },
|
||||||
|
{ gStSong4, (uint16_t)sizeof(gStSong4) },
|
||||||
|
{ gStSong5, (uint16_t)sizeof(gStSong5) },
|
||||||
|
{ gStSong6, (uint16_t)sizeof(gStSong6) },
|
||||||
|
{ gStSong7, (uint16_t)sizeof(gStSong7) },
|
||||||
|
{ gStSong8, (uint16_t)sizeof(gStSong8) },
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ST_SONG_DATA_H
|
||||||
490
examples/spacetaxi/stSpeechData.c
Normal file
490
examples/spacetaxi/stSpeechData.c
Normal file
|
|
@ -0,0 +1,490 @@
|
||||||
|
// Generated by assets/extractSpeech.py. Do not hand-edit.
|
||||||
|
|
||||||
|
#include "stSpeechData.h"
|
||||||
|
|
||||||
|
const StSpeechEntryT kStSpeechIndex[ST_SPEECH_COUNT] = {
|
||||||
|
{ 0x50u, 0u, 562u }, // 'P'
|
||||||
|
{ 0x31u, 562u, 420u }, // '1'
|
||||||
|
{ 0x3Fu, 982u, 266u }, // '?'
|
||||||
|
{ 0x32u, 1248u, 480u }, // '2'
|
||||||
|
{ 0x33u, 1728u, 463u }, // '3'
|
||||||
|
{ 0x34u, 2191u, 315u }, // '4'
|
||||||
|
{ 0x35u, 2506u, 550u }, // '5'
|
||||||
|
{ 0x36u, 3056u, 527u }, // '6'
|
||||||
|
{ 0x37u, 3583u, 758u }, // '7'
|
||||||
|
{ 0x38u, 4341u, 385u }, // '8'
|
||||||
|
{ 0x39u, 4726u, 491u }, // '9'
|
||||||
|
{ 0x48u, 5217u, 520u }, // 'H'
|
||||||
|
{ 0x54u, 5737u, 632u }, // 'T'
|
||||||
|
{ 0x21u, 6369u, 708u }, // '!'
|
||||||
|
{ 0x55u, 7077u, 368u }, // 'U'
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t kStSpeechRle[ST_SPEECH_RLE_BYTES] = {
|
||||||
|
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
|
||||||
|
0xF2, 0x42, 0xCD, 0x27, 0x10, 0x09, 0xAB, 0x31, 0xB6, 0x12, 0x37, 0x09, 0x22, 0x04, 0x1C, 0x0D,
|
||||||
|
0x16, 0x0E, 0x2F, 0x0B, 0x19, 0x12, 0x0E, 0x12, 0x19, 0x06, 0x1A, 0x0F, 0x09, 0x19, 0x0D, 0x16,
|
||||||
|
0x13, 0x06, 0x05, 0x03, 0x38, 0x08, 0x0F, 0x17, 0x0E, 0x12, 0x0F, 0x12, 0x16, 0x09, 0x15, 0x15,
|
||||||
|
0x06, 0x13, 0x08, 0x15, 0x0C, 0x13, 0x18, 0x10, 0x15, 0x1C, 0x0C, 0x0C, 0x09, 0x02, 0x0C, 0x14,
|
||||||
|
0x15, 0x14, 0x1D, 0x11, 0x11, 0x0B, 0x0A, 0x02, 0x0A, 0x15, 0x15, 0x0B, 0x15, 0x05, 0x06, 0x07,
|
||||||
|
0x07, 0x07, 0x15, 0x08, 0x04, 0x08, 0x0B, 0x07, 0x03, 0x08, 0x0B, 0x04, 0x05, 0x0D, 0x13, 0x08,
|
||||||
|
0x04, 0x06, 0x24, 0x08, 0x04, 0x03, 0x02, 0x03, 0x09, 0x07, 0x05, 0x07, 0x0B, 0x05, 0x05, 0x0A,
|
||||||
|
0x14, 0x08, 0x06, 0x06, 0x18, 0x06, 0x06, 0x09, 0x17, 0x07, 0x05, 0x08, 0x0B, 0x04, 0x05, 0x0A,
|
||||||
|
0x14, 0x09, 0x04, 0x07, 0x09, 0x06, 0x06, 0x09, 0x07, 0x08, 0x05, 0x03, 0x0E, 0x07, 0x04, 0x09,
|
||||||
|
0x0B, 0x04, 0x06, 0x09, 0x15, 0x08, 0x04, 0x06, 0x17, 0x09, 0x07, 0x0C, 0x15, 0x0A, 0x01, 0x09,
|
||||||
|
0x16, 0x09, 0x15, 0x09, 0x06, 0x03, 0x0C, 0x05, 0x05, 0x0A, 0x09, 0x08, 0x07, 0x04, 0x0B, 0x07,
|
||||||
|
0x05, 0x08, 0x15, 0x0A, 0x14, 0x08, 0x05, 0x05, 0x0B, 0x04, 0x07, 0x0F, 0x04, 0x0A, 0x05, 0x08,
|
||||||
|
0x09, 0x07, 0x03, 0x09, 0x15, 0x0A, 0x14, 0x09, 0x04, 0x05, 0x18, 0x0A, 0x0A, 0x0A, 0x06, 0x03,
|
||||||
|
0x0C, 0x14, 0x15, 0x0A, 0x14, 0x14, 0x0A, 0x06, 0x05, 0x0E, 0x08, 0x09, 0x16, 0x08, 0x03, 0x08,
|
||||||
|
0x16, 0x09, 0x15, 0x13, 0x16, 0x0F, 0x08, 0x09, 0x05, 0x06, 0x0A, 0x08, 0x04, 0x07, 0x16, 0x0A,
|
||||||
|
0x06, 0x02, 0x0B, 0x13, 0x17, 0x09, 0x0F, 0x09, 0x07, 0x02, 0x0C, 0x08, 0x03, 0x08, 0x15, 0x0B,
|
||||||
|
0x13, 0x14, 0x0A, 0x14, 0x13, 0x0A, 0x15, 0x13, 0x12, 0x0F, 0x14, 0x0F, 0x10, 0x11, 0x10, 0x10,
|
||||||
|
0x15, 0x0A, 0x06, 0x02, 0x0D, 0x13, 0x14, 0x0B, 0x12, 0x11, 0x0E, 0x16, 0x15, 0x0A, 0x17, 0x07,
|
||||||
|
0x04, 0x05, 0x19, 0x06, 0x14, 0x0B, 0x14, 0x18, 0x07, 0x04, 0x0C, 0x07, 0x05, 0x05, 0x10, 0x02,
|
||||||
|
0x05, 0x07, 0x08, 0x02, 0x0C, 0x07, 0x06, 0x07, 0x08, 0x38, 0x16, 0x09, 0x05, 0x05, 0x0C, 0x06,
|
||||||
|
0x05, 0x07, 0x16, 0x08, 0x07, 0x03, 0x0A, 0x13, 0x0B, 0x1C, 0x08, 0x0B, 0x04, 0x07, 0x06, 0x04,
|
||||||
|
0x0D, 0x05, 0x06, 0x06, 0x0B, 0x02, 0x08, 0x07, 0x08, 0x05, 0x08, 0x08, 0x1F, 0x0A, 0x05, 0x08,
|
||||||
|
0x06, 0x04, 0x0C, 0x07, 0x05, 0x05, 0x18, 0x0A, 0x07, 0x02, 0x09, 0x08, 0x06, 0x07, 0x05, 0x09,
|
||||||
|
0x17, 0x10, 0x09, 0x07, 0x06, 0x06, 0x0C, 0x05, 0x05, 0x07, 0x17, 0x09, 0x07, 0x05, 0x0C, 0x13,
|
||||||
|
0x06, 0x06, 0x08, 0x06, 0x06, 0x0B, 0x05, 0x02, 0x0D, 0x07, 0x06, 0x03, 0x0F, 0x05, 0x05, 0x07,
|
||||||
|
0x17, 0x17, 0x07, 0x17, 0x24, 0x11, 0x11, 0x09, 0x17, 0x08, 0x03, 0x08, 0x16, 0x0B, 0x14, 0x13,
|
||||||
|
0x18, 0x06, 0x16, 0x0C, 0x0E, 0x0A, 0x04, 0x03, 0x10, 0x12, 0x16, 0x12, 0x0D, 0x14, 0x15, 0x0B,
|
||||||
|
0x16, 0x0C, 0x10, 0x0F, 0x15, 0x0A, 0x02, 0x05, 0x18, 0x0C, 0x13, 0x09, 0x05, 0x07, 0x30, 0x09,
|
||||||
|
0x11, 0x1B, 0x1A, 0x05, 0x05, 0x06, 0x1A, 0x15, 0x14, 0x0C, 0x20, 0x14, 0x18, 0x16, 0x03, 0x09,
|
||||||
|
0x05, 0x03, 0x0F, 0x05, 0x05, 0x09, 0x15, 0x16, 0x15, 0x13, 0x17, 0x09, 0x1E, 0x14, 0x0D, 0x0B,
|
||||||
|
0x19, 0x05, 0x03, 0x0B, 0x14, 0x16, 0x15, 0x14, 0x18, 0x08, 0x26, 0x12, 0x11, 0x0B, 0x16, 0x14,
|
||||||
|
0x15, 0x15, 0x16, 0x16, 0x0A, 0x23, 0x42, 0x0A, 0x18, 0x05, 0x05, 0x09, 0x24, 0x08, 0x18, 0x16,
|
||||||
|
0x13, 0x17, 0x15, 0x18, 0x1A, 0x0A, 0x23, 0x09, 0x25, 0x15, 0x17, 0x18, 0x11, 0x14, 0x3C, 0x10,
|
||||||
|
0x57, 0x0F, 0x16, 0x15, 0x18, 0x13, 0x48, 0x1A, 0x16, 0x13, 0x4E, 0x1C, 0x6B, 0x18, 0xFE, 0xFE,
|
||||||
|
0xFE, 0xFF, 0xFE, 0x63, 0x1D, 0xC8, 0x29, 0xBE, 0x1B, 0x59, 0x14, 0x48, 0x19, 0x1A, 0x0E, 0x1E,
|
||||||
|
0x1F, 0x20, 0x11, 0x0E, 0x23, 0x19, 0x10, 0x19, 0x22, 0x18, 0x12, 0x15, 0x20, 0x17, 0x13, 0x18,
|
||||||
|
0x1E, 0x17, 0x13, 0x17, 0x1D, 0x16, 0x14, 0x1A, 0x1A, 0x16, 0x12, 0x17, 0x1E, 0x16, 0x13, 0x17,
|
||||||
|
0x1D, 0x12, 0x15, 0x17, 0x1F, 0x16, 0x12, 0x17, 0x1C, 0x14, 0x14, 0x18, 0x1E, 0x15, 0x0F, 0x1A,
|
||||||
|
0x19, 0x13, 0x10, 0x1E, 0x1D, 0x12, 0x11, 0x1C, 0x17, 0x11, 0x1A, 0x1A, 0x1A, 0x11, 0x11, 0x1D,
|
||||||
|
0x14, 0x10, 0x11, 0x25, 0x19, 0x10, 0x12, 0x1C, 0x13, 0x10, 0x10, 0x0B, 0x09, 0x13, 0x18, 0x12,
|
||||||
|
0x11, 0x17, 0x16, 0x10, 0x0E, 0x0D, 0x0F, 0x0E, 0x19, 0x11, 0x10, 0x17, 0x16, 0x10, 0x0D, 0x0E,
|
||||||
|
0x10, 0x0D, 0x18, 0x10, 0x11, 0x18, 0x12, 0x14, 0x0D, 0x0F, 0x0C, 0x0F, 0x19, 0x10, 0x10, 0x19,
|
||||||
|
0x15, 0x11, 0x0D, 0x0C, 0x12, 0x0E, 0x17, 0x0F, 0x11, 0x1A, 0x11, 0x0C, 0x14, 0x0C, 0x17, 0x0B,
|
||||||
|
0x13, 0x01, 0x04, 0x0D, 0x10, 0x1B, 0x10, 0x0C, 0x0C, 0x0E, 0x0A, 0x0B, 0x0B, 0x0A, 0x14, 0x13,
|
||||||
|
0x0C, 0x1F, 0x0D, 0x0F, 0x0B, 0x0E, 0x1A, 0x0D, 0x20, 0x0C, 0x0B, 0x24, 0x09, 0x0E, 0x0C, 0x0C,
|
||||||
|
0x0B, 0x0A, 0x05, 0x0E, 0x22, 0x06, 0x01, 0x04, 0x0A, 0x0E, 0x03, 0x10, 0x0B, 0x0F, 0x0D, 0x0C,
|
||||||
|
0x0B, 0x06, 0x0A, 0x0B, 0x16, 0x04, 0x0E, 0x0C, 0x09, 0x0F, 0x02, 0x10, 0x0A, 0x0C, 0x0C, 0x0C,
|
||||||
|
0x1D, 0x0B, 0x09, 0x08, 0x09, 0x04, 0x0D, 0x0C, 0x0A, 0x0E, 0x02, 0x10, 0x0C, 0x0A, 0x0B, 0x02,
|
||||||
|
0x07, 0x0D, 0x0E, 0x18, 0x13, 0x07, 0x0D, 0x0C, 0x08, 0x09, 0x0A, 0x0D, 0x0D, 0x07, 0x08, 0x09,
|
||||||
|
0x0B, 0x09, 0x0B, 0x0B, 0x01, 0x0F, 0x0B, 0x10, 0x0F, 0x07, 0x0D, 0x07, 0x0A, 0x0D, 0x09, 0x0E,
|
||||||
|
0x05, 0x05, 0x0F, 0x0D, 0x10, 0x26, 0x18, 0x03, 0x02, 0x05, 0x0A, 0x0B, 0x05, 0x0D, 0x08, 0x0A,
|
||||||
|
0x09, 0x0A, 0x1B, 0x17, 0x08, 0x0D, 0x07, 0x0B, 0x04, 0x0A, 0x08, 0x03, 0x02, 0x05, 0x08, 0x0B,
|
||||||
|
0x07, 0x0C, 0x04, 0x09, 0x0B, 0x0D, 0x06, 0x05, 0x0D, 0x19, 0x06, 0x1A, 0x05, 0x0D, 0x04, 0x07,
|
||||||
|
0x08, 0x04, 0x02, 0x04, 0x05, 0x09, 0x0B, 0x0F, 0x01, 0x09, 0x0A, 0x09, 0x06, 0x08, 0x0A, 0x0A,
|
||||||
|
0x07, 0x0C, 0x04, 0x1A, 0x06, 0x0E, 0x0B, 0x05, 0x0C, 0x0C, 0x04, 0x09, 0x0A, 0x1A, 0x06, 0x0A,
|
||||||
|
0x07, 0x08, 0x09, 0x08, 0x0A, 0x2B, 0x04, 0x0B, 0x27, 0x03, 0x03, 0x05, 0x02, 0x0B, 0x0B, 0x05,
|
||||||
|
0x02, 0x11, 0x05, 0x02, 0x0D, 0x0A, 0x14, 0x0A, 0x07, 0x1A, 0x09, 0x06, 0x03, 0x12, 0x1F, 0x04,
|
||||||
|
0x02, 0x04, 0x03, 0x0B, 0x0A, 0x04, 0x08, 0x0D, 0x04, 0x05, 0x19, 0x0B, 0x1B, 0x18, 0x0F, 0x1E,
|
||||||
|
0x12, 0x04, 0x0E, 0x04, 0x04, 0x19, 0x06, 0x18, 0x0B, 0x58, 0x14, 0x14, 0x0B, 0x0D, 0x19, 0x1B,
|
||||||
|
0x04, 0x56, 0x16, 0x40, 0x29, 0x04, 0x15, 0x3A, 0x07, 0x37, 0x14, 0x55, 0x19, 0xFE, 0x80, 0x5B,
|
||||||
|
0xFE, 0x3C, 0x80, 0xFE, 0xFE, 0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xA2, 0x40, 0x48, 0x1D, 0x3C,
|
||||||
|
0x0B, 0xFE, 0xFE, 0xBB, 0x51, 0xFE, 0xFE, 0xFE, 0x0D, 0x13, 0x92, 0x24, 0x36, 0x30, 0x67, 0x23,
|
||||||
|
0x37, 0x25, 0x6D, 0x23, 0x36, 0x2B, 0x67, 0x21, 0x35, 0x46, 0x29, 0x20, 0x0A, 0x1F, 0x2F, 0x97,
|
||||||
|
0x11, 0x1A, 0x2E, 0x1C, 0x62, 0x44, 0x2D, 0x1E, 0x23, 0x15, 0x2B, 0x0F, 0x14, 0x1E, 0x2F, 0x20,
|
||||||
|
0x19, 0x16, 0x25, 0x1E, 0x0E, 0x1E, 0x2D, 0x20, 0x15, 0x18, 0x22, 0x4F, 0x30, 0x19, 0x21, 0x12,
|
||||||
|
0x27, 0x4B, 0x2F, 0x21, 0x15, 0x10, 0x30, 0x47, 0x2E, 0x16, 0x6D, 0x39, 0x2E, 0x2D, 0x48, 0x48,
|
||||||
|
0x2D, 0x24, 0x55, 0x3C, 0x30, 0x2D, 0x3C, 0x4C, 0x31, 0x29, 0x34, 0x57, 0x2E, 0x29, 0x2E, 0x34,
|
||||||
|
0x03, 0x24, 0x32, 0x26, 0x38, 0x29, 0x04, 0x23, 0x37, 0x24, 0x38, 0x50, 0x31, 0x21, 0x3B, 0x4F,
|
||||||
|
0x3C, 0x13, 0x43, 0x42, 0x31, 0x2A, 0x46, 0x3F, 0x2F, 0x2C, 0x3D, 0x3F, 0x33, 0x28, 0x3E, 0x48,
|
||||||
|
0x2F, 0x31, 0x41, 0x38, 0x31, 0x2A, 0x4E, 0x30, 0x2E, 0x2A, 0x46, 0x37, 0x2F, 0x2A, 0x46, 0x33,
|
||||||
|
0x31, 0x29, 0x49, 0x37, 0x2E, 0x2A, 0x35, 0x47, 0x2F, 0x26, 0x41, 0x42, 0x30, 0x25, 0x50, 0x2C,
|
||||||
|
0x32, 0x24, 0x38, 0x43, 0x31, 0x29, 0x35, 0x49, 0x32, 0x1D, 0x37, 0x4C, 0x32, 0x1E, 0x48, 0x32,
|
||||||
|
0xFE, 0xD3, 0x48, 0x74, 0x24, 0xAE, 0x2C, 0x45, 0x03, 0x50, 0x02, 0x1D, 0x26, 0x12, 0x02, 0x21,
|
||||||
|
0x02, 0x03, 0x02, 0x48, 0x02, 0x1F, 0x1B, 0x46, 0x2E, 0x13, 0x01, 0x2D, 0x02, 0x03, 0x03, 0x01,
|
||||||
|
0x03, 0x01, 0x03, 0xCC, 0x1B, 0x4B, 0x03, 0x03, 0x02, 0x01, 0x09, 0x74, 0x08, 0x02, 0x00, 0x02,
|
||||||
|
0x1F, 0x79, 0x0E, 0x03, 0x01, 0x03, 0x02, 0x03, 0x02, 0x04, 0x01, 0x24, 0x01, 0x36, 0x02, 0x03,
|
||||||
|
0x3C, 0x04, 0x01, 0x1D, 0x1F, 0xCF, 0x03, 0x01, 0x38, 0x90, 0x39, 0xFE, 0xFE, 0xFE, 0xFE, 0xFF,
|
||||||
|
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0x5C, 0x06, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03,
|
||||||
|
0x3F, 0x0E, 0x13, 0x08, 0x9B, 0x23, 0x2B, 0x36, 0x25, 0x02, 0x25, 0x01, 0x23, 0x03, 0x02, 0x02,
|
||||||
|
0x15, 0x02, 0x1B, 0x0A, 0x23, 0x02, 0x25, 0x02, 0x3E, 0x01, 0x05, 0x02, 0x14, 0x0F, 0x25, 0x18,
|
||||||
|
0x3E, 0x13, 0x6B, 0x05, 0x0B, 0x0A, 0x72, 0x21, 0x09, 0x16, 0xBA, 0x24, 0x45, 0x03, 0x05, 0x1A,
|
||||||
|
0x04, 0x09, 0x02, 0x0F, 0x12, 0x02, 0x1B, 0x04, 0x08, 0x03, 0x0F, 0x04, 0x04, 0x08, 0x04, 0x32,
|
||||||
|
0x42, 0x03, 0x07, 0x1A, 0x03, 0x08, 0x03, 0x23, 0x48, 0x0E, 0x05, 0x04, 0x05, 0x25, 0x18, 0x05,
|
||||||
|
0x06, 0x06, 0x23, 0x19, 0x05, 0x0B, 0x02, 0x09, 0x03, 0x0A, 0x17, 0x06, 0x07, 0x09, 0x09, 0x02,
|
||||||
|
0x19, 0x06, 0x05, 0x07, 0x06, 0x0A, 0x03, 0x09, 0x03, 0x0A, 0x0B, 0x03, 0x07, 0x05, 0x09, 0x08,
|
||||||
|
0x08, 0x03, 0x19, 0x07, 0x04, 0x07, 0x07, 0x16, 0x02, 0x0B, 0x0C, 0x02, 0x07, 0x07, 0x06, 0x0A,
|
||||||
|
0x08, 0x03, 0x09, 0x03, 0x0B, 0x07, 0x05, 0x06, 0x07, 0x0B, 0x02, 0x08, 0x03, 0x0A, 0x0C, 0x03,
|
||||||
|
0x07, 0x07, 0x07, 0x09, 0x08, 0x03, 0x18, 0x06, 0x07, 0x05, 0x0A, 0x09, 0x02, 0x08, 0x03, 0x0A,
|
||||||
|
0x0B, 0x04, 0x07, 0x06, 0x08, 0x09, 0x07, 0x04, 0x19, 0x05, 0x07, 0x05, 0x09, 0x08, 0x05, 0x08,
|
||||||
|
0x03, 0x0A, 0x0A, 0x05, 0x06, 0x07, 0x07, 0x09, 0x08, 0x04, 0x0B, 0x02, 0x0A, 0x07, 0x05, 0x06,
|
||||||
|
0x0B, 0x05, 0x06, 0x09, 0x04, 0x0A, 0x0B, 0x03, 0x07, 0x06, 0x08, 0x07, 0x0A, 0x04, 0x08, 0x05,
|
||||||
|
0x06, 0x0B, 0x07, 0x04, 0x1B, 0x09, 0x03, 0x0B, 0x0D, 0x01, 0x07, 0x05, 0x0A, 0x0A, 0x06, 0x04,
|
||||||
|
0x09, 0x03, 0x0A, 0x08, 0x05, 0x06, 0x07, 0x05, 0x06, 0x13, 0x02, 0x0F, 0x13, 0x04, 0x0A, 0x09,
|
||||||
|
0x08, 0x03, 0x19, 0x05, 0x07, 0x05, 0x06, 0x03, 0x17, 0x08, 0x03, 0x0D, 0x0A, 0x03, 0x07, 0x05,
|
||||||
|
0x0A, 0x0B, 0x04, 0x06, 0x09, 0x03, 0x0E, 0x04, 0x07, 0x04, 0x1B, 0x0F, 0x03, 0x0D, 0x0A, 0x02,
|
||||||
|
0x08, 0x04, 0x0B, 0x0B, 0x04, 0x06, 0x28, 0x03, 0x1A, 0x22, 0x16, 0x04, 0x0A, 0x0C, 0x04, 0x05,
|
||||||
|
0x1B, 0x04, 0x08, 0x04, 0x1A, 0x06, 0x04, 0x1A, 0x17, 0x08, 0x05, 0x0B, 0x05, 0x05, 0x1B, 0x05,
|
||||||
|
0x08, 0x09, 0x15, 0x25, 0x18, 0x08, 0x04, 0x0C, 0x26, 0x16, 0x17, 0x26, 0x0C, 0x03, 0x08, 0x19,
|
||||||
|
0x1A, 0x06, 0x06, 0x08, 0x1B, 0x04, 0x07, 0x19, 0x05, 0x09, 0x0C, 0x04, 0x07, 0x09, 0x09, 0x06,
|
||||||
|
0x08, 0x07, 0x09, 0x08, 0x06, 0x08, 0x19, 0x2B, 0x03, 0x0A, 0x0D, 0x02, 0x09, 0x06, 0x08, 0x0A,
|
||||||
|
0x08, 0x05, 0x0A, 0x0B, 0x05, 0x07, 0x18, 0x09, 0x06, 0x23, 0x03, 0x0C, 0x2C, 0x04, 0x1B, 0x19,
|
||||||
|
0x08, 0x06, 0x1A, 0x07, 0x07, 0x09, 0x04, 0x09, 0x04, 0x0B, 0x2B, 0x06, 0x1C, 0x07, 0x07, 0x0E,
|
||||||
|
0x03, 0x0A, 0x19, 0x05, 0x0A, 0x0A, 0x05, 0x08, 0x08, 0x06, 0x0D, 0x04, 0x0A, 0x0B, 0x07, 0x05,
|
||||||
|
0x09, 0x0E, 0x03, 0x1D, 0x2A, 0x1A, 0x0F, 0x1C, 0x28, 0x1C, 0x09, 0x04, 0x0A, 0x18, 0x2F, 0x06,
|
||||||
|
0x19, 0x1C, 0x1F, 0x05, 0x05, 0x1B, 0x1E, 0x14, 0x23, 0x1C, 0x19, 0x1D, 0x21, 0x07, 0x04, 0x0D,
|
||||||
|
0x36, 0x13, 0x1F, 0x17, 0x1C, 0x24, 0x36, 0x15, 0x26, 0x1C, 0x5D, 0x20, 0x63, 0x2B, 0x53, 0x2A,
|
||||||
|
0x7C, 0x28, 0x61, 0x2D, 0xFE, 0x23, 0x22, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFF,
|
||||||
|
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0x5C, 0xFE, 0xFE, 0xFE, 0xFE, 0x76, 0x23, 0x6F,
|
||||||
|
0x28, 0x84, 0x32, 0x22, 0x05, 0x08, 0x04, 0x1C, 0x16, 0x1B, 0x2E, 0x52, 0x0B, 0x26, 0x2C, 0x54,
|
||||||
|
0x08, 0x1D, 0x34, 0x54, 0x0B, 0x1B, 0x32, 0x24, 0x05, 0x2C, 0x0A, 0x1B, 0x25, 0x03, 0x08, 0x25,
|
||||||
|
0x05, 0x2C, 0x09, 0x0C, 0x04, 0x0A, 0x2F, 0x24, 0x09, 0x2A, 0x0A, 0x1B, 0x20, 0x02, 0x09, 0x25,
|
||||||
|
0x08, 0x4F, 0x1C, 0x04, 0x09, 0x26, 0x07, 0x4F, 0x1B, 0x05, 0x09, 0x24, 0x08, 0x20, 0x08, 0x26,
|
||||||
|
0x1B, 0x04, 0x08, 0x0E, 0x05, 0x09, 0x10, 0x52, 0x18, 0x04, 0x08, 0x0E, 0x06, 0x06, 0x13, 0x1E,
|
||||||
|
0x18, 0x1B, 0x17, 0x05, 0x07, 0x10, 0x02, 0x09, 0x14, 0x1D, 0x0F, 0x25, 0x16, 0x05, 0x08, 0x0B,
|
||||||
|
0x08, 0x07, 0x12, 0x1B, 0x09, 0x05, 0x09, 0x20, 0x18, 0x06, 0x07, 0x0C, 0x08, 0x05, 0x14, 0x19,
|
||||||
|
0x17, 0x20, 0x1A, 0x07, 0x07, 0x0B, 0x08, 0x06, 0x0F, 0x1D, 0x08, 0x06, 0x08, 0x21, 0x1B, 0x07,
|
||||||
|
0x07, 0x0A, 0x09, 0x06, 0x0B, 0x1E, 0x08, 0x06, 0x08, 0x22, 0x1C, 0x08, 0x06, 0x0B, 0x07, 0x07,
|
||||||
|
0x0A, 0x1E, 0x0A, 0x04, 0x09, 0x1A, 0x27, 0x07, 0x07, 0x0A, 0x07, 0x07, 0x09, 0x1E, 0x09, 0x06,
|
||||||
|
0x08, 0x1A, 0x2B, 0x07, 0x06, 0x0A, 0x07, 0x07, 0x09, 0x1D, 0x07, 0x07, 0x08, 0x19, 0x06, 0x05,
|
||||||
|
0x24, 0x07, 0x07, 0x09, 0x07, 0x07, 0x09, 0x1D, 0x06, 0x07, 0x08, 0x08, 0x05, 0x0A, 0x06, 0x07,
|
||||||
|
0x09, 0x07, 0x09, 0x03, 0x0B, 0x06, 0x07, 0x0A, 0x06, 0x08, 0x09, 0x08, 0x06, 0x0C, 0x06, 0x07,
|
||||||
|
0x09, 0x08, 0x04, 0x0B, 0x05, 0x07, 0x09, 0x06, 0x08, 0x07, 0x0A, 0x08, 0x06, 0x09, 0x06, 0x08,
|
||||||
|
0x09, 0x07, 0x07, 0x0B, 0x06, 0x07, 0x0A, 0x06, 0x05, 0x19, 0x2D, 0x05, 0x09, 0x09, 0x05, 0x09,
|
||||||
|
0x07, 0x09, 0x04, 0x1B, 0x07, 0x05, 0x08, 0x18, 0x18, 0x07, 0x08, 0x05, 0x09, 0x05, 0x08, 0x09,
|
||||||
|
0x04, 0x0A, 0x07, 0x09, 0x06, 0x09, 0x06, 0x08, 0x17, 0x1F, 0x17, 0x09, 0x05, 0x02, 0x07, 0x04,
|
||||||
|
0x0A, 0x1A, 0x04, 0x0A, 0x04, 0x0C, 0x02, 0x09, 0x08, 0x05, 0x09, 0x06, 0x05, 0x07, 0x07, 0x06,
|
||||||
|
0x08, 0x07, 0x06, 0x0B, 0x07, 0x05, 0x08, 0x05, 0x08, 0x19, 0x06, 0x07, 0x07, 0x08, 0x03, 0x0A,
|
||||||
|
0x07, 0x05, 0x16, 0x09, 0x04, 0x09, 0x03, 0x0A, 0x05, 0x0A, 0x07, 0x0A, 0x09, 0x04, 0x08, 0x0A,
|
||||||
|
0x02, 0x0B, 0x06, 0x07, 0x07, 0x08, 0x05, 0x08, 0x25, 0x15, 0x04, 0x0B, 0x02, 0x1A, 0x04, 0x06,
|
||||||
|
0x07, 0x05, 0x08, 0x18, 0x06, 0x06, 0x08, 0x06, 0x05, 0x0A, 0x04, 0x08, 0x09, 0x04, 0x08, 0x55,
|
||||||
|
0x08, 0x04, 0x09, 0x06, 0x05, 0x0C, 0x04, 0x08, 0x07, 0x05, 0x0B, 0x15, 0x1D, 0x52, 0x0A, 0x02,
|
||||||
|
0x0B, 0x04, 0x04, 0x1B, 0x04, 0x06, 0x06, 0x24, 0x18, 0x26, 0x1C, 0x14, 0x0A, 0x02, 0x0B, 0x24,
|
||||||
|
0x1F, 0x15, 0x1B, 0x24, 0x1A, 0x19, 0x0E, 0x02, 0x07, 0x27, 0x06, 0x05, 0x0C, 0x1B, 0x16, 0x6C,
|
||||||
|
0x0B, 0x03, 0x06, 0x09, 0x03, 0x25, 0x16, 0x13, 0x05, 0x8A, 0x06, 0x03, 0x0C, 0x03, 0x03, 0x6E,
|
||||||
|
0x1D, 0x56, 0x22, 0x2C, 0x16, 0x26, 0x1B, 0x5F, 0x24, 0x8C, 0x11, 0x5A, 0x20, 0x62, 0x44, 0x5B,
|
||||||
|
0x23, 0x80, 0x1E, 0x70, 0x20, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFF, 0xFE,
|
||||||
|
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xBF, 0x04, 0xFE, 0xCF, 0x1A, 0x51, 0x0F, 0x23,
|
||||||
|
0x23, 0x36, 0x08, 0x18, 0x0E, 0x1C, 0x26, 0x56, 0x12, 0x16, 0x27, 0x1C, 0x16, 0x26, 0x10, 0x16,
|
||||||
|
0x26, 0x1C, 0x14, 0x27, 0x12, 0x15, 0x25, 0x1C, 0x17, 0x24, 0x10, 0x1A, 0x22, 0x1D, 0x1A, 0x20,
|
||||||
|
0x11, 0x19, 0x23, 0x1C, 0x19, 0x22, 0x11, 0x1B, 0x22, 0x1A, 0x19, 0x23, 0x13, 0x18, 0x24, 0x1B,
|
||||||
|
0x17, 0x25, 0x11, 0x19, 0x23, 0x1B, 0x17, 0x24, 0x12, 0x18, 0x24, 0x1B, 0x16, 0x25, 0x11, 0x19,
|
||||||
|
0x24, 0x1B, 0x17, 0x25, 0x10, 0x1A, 0x24, 0x19, 0x18, 0x24, 0x11, 0x19, 0x25, 0x1B, 0x15, 0x26,
|
||||||
|
0x11, 0x1A, 0x25, 0x19, 0x15, 0x27, 0x12, 0x19, 0x27, 0x18, 0x14, 0x28, 0x12, 0x18, 0x2A, 0x18,
|
||||||
|
0x13, 0x2B, 0x10, 0x18, 0x2B, 0x16, 0x14, 0x2E, 0x0B, 0x19, 0x2D, 0x15, 0x14, 0x2D, 0x0D, 0x19,
|
||||||
|
0x2F, 0x15, 0x12, 0x2E, 0x10, 0x16, 0x1A, 0x02, 0x15, 0x14, 0x11, 0x2E, 0x11, 0x16, 0x18, 0x06,
|
||||||
|
0x14, 0x15, 0x10, 0x2D, 0x10, 0x16, 0x37, 0x15, 0x0D, 0x2B, 0x11, 0x19, 0x1B, 0x0D, 0x15, 0x2B,
|
||||||
|
0x0A, 0x13, 0x11, 0x22, 0x14, 0x11, 0x18, 0x1A, 0x12, 0x1B, 0x1A, 0x15, 0x16, 0x0F, 0x1E, 0x47,
|
||||||
|
0x15, 0x17, 0x18, 0x1C, 0x17, 0x22, 0x10, 0x15, 0x18, 0x14, 0x12, 0x16, 0x26, 0x15, 0x1B, 0x16,
|
||||||
|
0x1E, 0x12, 0x12, 0x21, 0x21, 0x1B, 0x13, 0x13, 0x24, 0x0F, 0x0F, 0x1E, 0x10, 0x10, 0x0F, 0x10,
|
||||||
|
0x0A, 0x08, 0x0C, 0x0F, 0x11, 0x07, 0x0D, 0x11, 0x0E, 0x1D, 0x0E, 0x13, 0x16, 0x21, 0x0E, 0x16,
|
||||||
|
0x1A, 0x0F, 0x15, 0x18, 0x0F, 0x1F, 0x17, 0x1E, 0x0F, 0x14, 0x1D, 0x14, 0x12, 0x1A, 0x11, 0x16,
|
||||||
|
0x24, 0x15, 0x16, 0x15, 0x1B, 0x16, 0x15, 0x15, 0x13, 0x15, 0x2F, 0x15, 0x16, 0x1F, 0x11, 0x1E,
|
||||||
|
0x10, 0x11, 0x1B, 0x13, 0x21, 0x05, 0x0F, 0x22, 0x0A, 0x20, 0x0E, 0x17, 0x16, 0x1C, 0x10, 0x1B,
|
||||||
|
0x0F, 0x0C, 0x08, 0x09, 0x0E, 0x21, 0x0C, 0x20, 0x0E, 0x1F, 0x26, 0x0C, 0x19, 0x15, 0x17, 0x12,
|
||||||
|
0x21, 0x13, 0x0F, 0x1C, 0x0B, 0x1F, 0x0E, 0x18, 0x0D, 0x4D, 0x3A, 0x40, 0x47, 0xB0, 0x18, 0x72,
|
||||||
|
0x30, 0xC7, 0x11, 0x1D, 0x1E, 0x78, 0x0E, 0x4C, 0x70, 0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
|
||||||
|
0xFE, 0xFE, 0xFE, 0xC4, 0x0E, 0x4A, 0x1A, 0x1D, 0x0F, 0x19, 0x14, 0x19, 0x11, 0x18, 0x1B, 0x11,
|
||||||
|
0x19, 0x13, 0x1A, 0x11, 0x1B, 0x14, 0x1A, 0x0E, 0x1C, 0x10, 0x11, 0x18, 0x0D, 0x0D, 0x09, 0x0B,
|
||||||
|
0x0A, 0x07, 0x0D, 0x0E, 0x1B, 0x0F, 0x10, 0x17, 0x0F, 0x0C, 0x11, 0x15, 0x10, 0x0D, 0x1A, 0x0F,
|
||||||
|
0x0F, 0x17, 0x0D, 0x0E, 0x0E, 0x0D, 0x1A, 0x0E, 0x0E, 0x1A, 0x0E, 0x10, 0x15, 0x0D, 0x0F, 0x0D,
|
||||||
|
0x19, 0x0E, 0x0E, 0x1B, 0x0D, 0x0D, 0x19, 0x0C, 0x0E, 0x0D, 0x1B, 0x0D, 0x0D, 0x1D, 0x0C, 0x0C,
|
||||||
|
0x0D, 0x1A, 0x0D, 0x0D, 0x1D, 0x0C, 0x0E, 0x0C, 0x03, 0x0D, 0x0B, 0x0C, 0x0D, 0x1B, 0x0C, 0x0E,
|
||||||
|
0x1E, 0x0B, 0x0E, 0x1D, 0x0C, 0x0C, 0x0B, 0x1D, 0x0D, 0x0B, 0x20, 0x0C, 0x0D, 0x0C, 0x05, 0x0C,
|
||||||
|
0x0B, 0x0C, 0x0C, 0x0A, 0x07, 0x0A, 0x0E, 0x0D, 0x0F, 0x02, 0x0C, 0x0C, 0x0D, 0x0C, 0x05, 0x0C,
|
||||||
|
0x0A, 0x0C, 0x0C, 0x20, 0x0B, 0x09, 0x21, 0x0C, 0x0D, 0x1D, 0x0B, 0x0B, 0x0C, 0x22, 0x09, 0x0D,
|
||||||
|
0x04, 0x0A, 0x0D, 0x0C, 0x0E, 0x1C, 0x0B, 0x0B, 0x0D, 0x0C, 0x20, 0x15, 0x15, 0x0C, 0x0D, 0x1D,
|
||||||
|
0x0A, 0x0C, 0x0B, 0x0D, 0x0C, 0x09, 0x0A, 0x16, 0x15, 0x0C, 0x0D, 0x0C, 0x05, 0x0B, 0x09, 0x0B,
|
||||||
|
0x0C, 0x0C, 0x0C, 0x0C, 0x0A, 0x16, 0x13, 0x0B, 0x0D, 0x0D, 0x06, 0x0B, 0x08, 0x0C, 0x0B, 0x0C,
|
||||||
|
0x0C, 0x0C, 0x0E, 0x11, 0x13, 0x0B, 0x0E, 0x10, 0x02, 0x0A, 0x09, 0x0C, 0x0B, 0x0C, 0x0C, 0x0C,
|
||||||
|
0x0D, 0x12, 0x14, 0x0B, 0x0D, 0x0E, 0x05, 0x0A, 0x09, 0x0C, 0x0B, 0x0A, 0x0C, 0x0E, 0x0C, 0x15,
|
||||||
|
0x12, 0x0C, 0x0E, 0x0E, 0x04, 0x0B, 0x09, 0x0B, 0x0C, 0x0A, 0x0B, 0x0D, 0x0F, 0x14, 0x16, 0x0B,
|
||||||
|
0x0D, 0x0B, 0x06, 0x0B, 0x09, 0x0B, 0x0C, 0x0C, 0x09, 0x0C, 0x30, 0x05, 0x09, 0x0B, 0x0C, 0x0C,
|
||||||
|
0x06, 0x0B, 0x09, 0x0B, 0x0B, 0x0B, 0x0A, 0x0B, 0x17, 0x0D, 0x0C, 0x07, 0x0A, 0x0A, 0x0B, 0x0B,
|
||||||
|
0x07, 0x0A, 0x0A, 0x0B, 0x0B, 0x0B, 0x08, 0x0A, 0x0D, 0x18, 0x0D, 0x0A, 0x0A, 0x0A, 0x0B, 0x0B,
|
||||||
|
0x06, 0x0B, 0x0A, 0x0B, 0x0C, 0x0B, 0x06, 0x09, 0x0E, 0x26, 0x02, 0x09, 0x0B, 0x0A, 0x0C, 0x09,
|
||||||
|
0x08, 0x09, 0x0B, 0x0B, 0x0E, 0x15, 0x0A, 0x12, 0x08, 0x0C, 0x0B, 0x09, 0x08, 0x0C, 0x0B, 0x0A,
|
||||||
|
0x09, 0x0A, 0x09, 0x0B, 0x0B, 0x09, 0x03, 0x09, 0x0C, 0x0E, 0x0F, 0x09, 0x0B, 0x0B, 0x0B, 0x09,
|
||||||
|
0x0B, 0x0A, 0x07, 0x0B, 0x0B, 0x0B, 0x16, 0x0A, 0x0C, 0x19, 0x03, 0x0D, 0x06, 0x0E, 0x09, 0x0B,
|
||||||
|
0x0C, 0x0A, 0x09, 0x0B, 0x0A, 0x0C, 0x16, 0x0A, 0x0B, 0x18, 0x04, 0x0B, 0x08, 0x0D, 0x0A, 0x0C,
|
||||||
|
0x0C, 0x09, 0x05, 0x0C, 0x0C, 0x0B, 0x0F, 0x12, 0x0B, 0x18, 0x06, 0x0B, 0x09, 0x0C, 0x0D, 0x0C,
|
||||||
|
0x0E, 0x19, 0x0A, 0x0C, 0x0A, 0x09, 0x08, 0x0A, 0x10, 0x0F, 0x04, 0x0B, 0x0A, 0x0A, 0x12, 0x0B,
|
||||||
|
0x0C, 0x09, 0x08, 0x0C, 0x0B, 0x0D, 0x05, 0x07, 0x0B, 0x0F, 0x0A, 0x44, 0x0A, 0x0C, 0x0C, 0x09,
|
||||||
|
0x04, 0x0B, 0x0C, 0x0A, 0x09, 0x07, 0x0B, 0x0B, 0x09, 0x25, 0x0E, 0x18, 0x0C, 0x0B, 0x0C, 0x08,
|
||||||
|
0x04, 0x0C, 0x0C, 0x17, 0x0D, 0x0A, 0x18, 0x1D, 0x07, 0x15, 0x0A, 0x04, 0x09, 0x0B, 0x0B, 0x1B,
|
||||||
|
0x0B, 0x17, 0x0B, 0x10, 0x13, 0x1A, 0x0A, 0x13, 0x0E, 0x08, 0x08, 0x0C, 0x08, 0x0A, 0x06, 0x0D,
|
||||||
|
0x0A, 0x18, 0x0C, 0x13, 0x0A, 0x4E, 0x13, 0x0B, 0x1A, 0x0A, 0x0C, 0x19, 0x08, 0x17, 0x08, 0x19,
|
||||||
|
0x09, 0x18, 0x0A, 0x14, 0x0E, 0x0F, 0x14, 0x1C, 0x09, 0x0A, 0x0B, 0x19, 0x0B, 0x0F, 0x11, 0x2E,
|
||||||
|
0x06, 0x08, 0x0E, 0x19, 0x08, 0x19, 0x0B, 0x1A, 0x08, 0x0F, 0x14, 0x0E, 0x11, 0x19, 0x08, 0x19,
|
||||||
|
0x0C, 0x12, 0x13, 0x81, 0x07, 0x17, 0x09, 0x2F, 0x1A, 0x83, 0x0B, 0x12, 0x2D, 0x1E, 0x09, 0x54,
|
||||||
|
0x0B, 0x37, 0x2C, 0xFE, 0x30, 0x36, 0x59, 0x54, 0xFE, 0xFE, 0x09, 0x44, 0xC5, 0x1A, 0xFE, 0xFF,
|
||||||
|
0xFE, 0xFE, 0xFE, 0xFE, 0x02, 0x03, 0x02, 0x4C, 0x03, 0x03, 0x02, 0x5B, 0x03, 0x03, 0x02, 0x35,
|
||||||
|
0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x03, 0x02, 0x03, 0x02, 0x03, 0x03, 0x02, 0x04, 0x01, 0x11,
|
||||||
|
0x01, 0x55, 0x0F, 0x01, 0x03, 0x03, 0x02, 0x6B, 0x15, 0x03, 0x02, 0x15, 0x03, 0x02, 0x03, 0x04,
|
||||||
|
0x01, 0x20, 0x02, 0x01, 0x04, 0x02, 0x03, 0x03, 0x02, 0x04, 0x02, 0x21, 0x03, 0x2B, 0x03, 0x02,
|
||||||
|
0x03, 0x01, 0x04, 0x09, 0x02, 0x03, 0x02, 0x38, 0x03, 0x79, 0x19, 0x02, 0x03, 0x57, 0x03, 0x02,
|
||||||
|
0x02, 0x2C, 0x21, 0x01, 0x03, 0x10, 0x02, 0x2C, 0x02, 0x02, 0x03, 0x02, 0x03, 0x1A, 0x02, 0x0A,
|
||||||
|
0x07, 0x02, 0x03, 0x02, 0x03, 0x23, 0x03, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x11, 0x02, 0x02,
|
||||||
|
0x03, 0x03, 0x02, 0x3B, 0x01, 0x11, 0x02, 0x04, 0x01, 0x03, 0x02, 0x1B, 0x07, 0x1C, 0x03, 0x02,
|
||||||
|
0x04, 0x01, 0x0A, 0x01, 0x03, 0x1E, 0x02, 0x04, 0x01, 0x19, 0x01, 0x09, 0x04, 0x01, 0x03, 0x03,
|
||||||
|
0x02, 0x04, 0x02, 0x22, 0x0A, 0x02, 0x02, 0x11, 0x02, 0x03, 0x02, 0x03, 0x03, 0x02, 0x03, 0x03,
|
||||||
|
0x03, 0x03, 0x02, 0x03, 0x02, 0x03, 0x0F, 0x16, 0x02, 0x02, 0x03, 0x03, 0x01, 0x10, 0x02, 0x02,
|
||||||
|
0x03, 0x02, 0x03, 0x02, 0x02, 0x0A, 0x03, 0x02, 0x03, 0x1E, 0x03, 0x01, 0x09, 0x10, 0x03, 0x01,
|
||||||
|
0x09, 0x02, 0x03, 0x0E, 0x03, 0x01, 0x04, 0x02, 0x02, 0x03, 0x01, 0x11, 0x02, 0x39, 0x0C, 0x02,
|
||||||
|
0x03, 0x29, 0x01, 0x02, 0x09, 0x01, 0x03, 0x03, 0x03, 0x03, 0x02, 0x22, 0x03, 0x1C, 0x14, 0x1A,
|
||||||
|
0x03, 0x02, 0x03, 0x03, 0x03, 0xFE, 0x5B, 0x1A, 0x1C, 0x05, 0x05, 0x0C, 0x25, 0x08, 0x23, 0x1E,
|
||||||
|
0x09, 0x03, 0x0E, 0x07, 0x05, 0x0A, 0x1A, 0x08, 0x05, 0x06, 0x1B, 0x2B, 0x0B, 0x02, 0x0D, 0x08,
|
||||||
|
0x06, 0x04, 0x1A, 0x17, 0x18, 0x20, 0x02, 0x0B, 0x0A, 0x03, 0x0C, 0x08, 0x06, 0x04, 0x1A, 0x0B,
|
||||||
|
0x23, 0x22, 0x01, 0x05, 0x02, 0x03, 0x0B, 0x03, 0x0C, 0x09, 0x06, 0x03, 0x19, 0x14, 0x19, 0x18,
|
||||||
|
0x02, 0x09, 0x03, 0x04, 0x02, 0x04, 0x0A, 0x04, 0x07, 0x0B, 0x25, 0x12, 0x1B, 0x16, 0x06, 0x08,
|
||||||
|
0x03, 0x06, 0x01, 0x03, 0x0B, 0x04, 0x06, 0x0B, 0x1A, 0x14, 0x23, 0x16, 0x07, 0x0B, 0x03, 0x0A,
|
||||||
|
0x0B, 0x04, 0x06, 0x0B, 0x18, 0x16, 0x19, 0x1F, 0x0D, 0x0C, 0x02, 0x05, 0x03, 0x02, 0x0B, 0x04,
|
||||||
|
0x04, 0x0C, 0x19, 0x15, 0x1B, 0x18, 0x13, 0x0F, 0x01, 0x05, 0x04, 0x01, 0x0B, 0x05, 0x03, 0x0C,
|
||||||
|
0x1A, 0x14, 0x18, 0x15, 0x15, 0x13, 0x04, 0x03, 0x04, 0x01, 0x0B, 0x05, 0x04, 0x0B, 0x18, 0x16,
|
||||||
|
0x17, 0x16, 0x15, 0x17, 0x05, 0x03, 0x11, 0x06, 0x04, 0x09, 0x18, 0x16, 0x17, 0x15, 0x16, 0x1A,
|
||||||
|
0x05, 0x03, 0x11, 0x05, 0x06, 0x07, 0x1A, 0x17, 0x1A, 0x10, 0x19, 0x1A, 0x1A, 0x05, 0x29, 0x16,
|
||||||
|
0x17, 0x14, 0x19, 0x16, 0x04, 0x07, 0x1A, 0x05, 0x06, 0x08, 0x1A, 0x17, 0x0C, 0x16, 0x41, 0x08,
|
||||||
|
0x0B, 0x02, 0x0C, 0x07, 0x06, 0x07, 0x19, 0x16, 0x0E, 0x1A, 0x4E, 0x0B, 0x0A, 0x03, 0x0A, 0x16,
|
||||||
|
0x19, 0x17, 0x18, 0x17, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
|
||||||
|
0xFE, 0xFE, 0xFE, 0xFE, 0xEE, 0x02, 0x03, 0x03, 0xFE, 0x3D, 0x08, 0x01, 0x03, 0x02, 0x03, 0x03,
|
||||||
|
0x02, 0x03, 0x02, 0x65, 0x1A, 0xCF, 0x02, 0x3E, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03,
|
||||||
|
0x03, 0x3E, 0x03, 0x02, 0x02, 0x03, 0x02, 0x04, 0x02, 0x1B, 0x02, 0x16, 0x02, 0x03, 0x02, 0x5F,
|
||||||
|
0x08, 0x01, 0x03, 0x03, 0x02, 0x04, 0x02, 0xED, 0x02, 0x03, 0x02, 0x16, 0x02, 0xFE, 0x4A, 0x40,
|
||||||
|
0xE7, 0x14, 0x6F, 0x02, 0x03, 0x02, 0x80, 0x02, 0x03, 0x02, 0xFE, 0xFE, 0xFE, 0xFE, 0xFF, 0xFE,
|
||||||
|
0x7C, 0x24, 0x9B, 0x03, 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x0E, 0x08, 0x01, 0x03, 0x52, 0x02,
|
||||||
|
0x03, 0x09, 0x01, 0x03, 0x03, 0x02, 0x03, 0x03, 0x03, 0x01, 0x0A, 0x01, 0x03, 0x02, 0x1F, 0x02,
|
||||||
|
0x02, 0x03, 0x03, 0x07, 0x14, 0x02, 0x03, 0x02, 0x03, 0x03, 0x3A, 0x03, 0x02, 0x09, 0x02, 0x02,
|
||||||
|
0x3B, 0x01, 0x16, 0x01, 0x03, 0x03, 0x01, 0x03, 0x02, 0x03, 0x23, 0x03, 0x02, 0x03, 0x02, 0x03,
|
||||||
|
0x03, 0x04, 0x02, 0x02, 0x1E, 0x08, 0x0E, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x02, 0x41, 0x01,
|
||||||
|
0x03, 0x02, 0x11, 0x1A, 0x02, 0x17, 0x02, 0x03, 0x03, 0x02, 0x0D, 0x03, 0x01, 0x03, 0x02, 0x03,
|
||||||
|
0x02, 0x04, 0x01, 0x03, 0x10, 0x01, 0x03, 0x01, 0x34, 0x02, 0x03, 0x02, 0x04, 0x19, 0x01, 0x03,
|
||||||
|
0x01, 0x0F, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x02, 0x10, 0x01, 0x03, 0x02, 0x03, 0x02,
|
||||||
|
0x16, 0x02, 0x03, 0x02, 0x03, 0x03, 0x03, 0x02, 0x0E, 0x02, 0x02, 0x02, 0x03, 0x01, 0x0B, 0x02,
|
||||||
|
0x02, 0x02, 0x03, 0x02, 0x03, 0x01, 0x0A, 0x03, 0x01, 0x03, 0x03, 0x02, 0x18, 0x02, 0x03, 0x02,
|
||||||
|
0x1D, 0x02, 0x04, 0x01, 0x03, 0x03, 0x03, 0x02, 0x03, 0x02, 0x03, 0x03, 0x02, 0x03, 0x02, 0x03,
|
||||||
|
0x02, 0x03, 0x15, 0x02, 0x03, 0x02, 0x04, 0x01, 0x0C, 0x02, 0x10, 0x02, 0x03, 0x02, 0x1A, 0x02,
|
||||||
|
0x0F, 0x02, 0x02, 0x03, 0x01, 0x04, 0x02, 0x02, 0x14, 0x03, 0x02, 0x03, 0x02, 0x03, 0x04, 0x01,
|
||||||
|
0x0B, 0x01, 0x03, 0x02, 0x03, 0x02, 0x15, 0x02, 0x02, 0x03, 0x03, 0x02, 0x05, 0x02, 0x02, 0x03,
|
||||||
|
0x02, 0x03, 0x02, 0x03, 0x12, 0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x01, 0x04, 0x01, 0x03, 0x01,
|
||||||
|
0x16, 0x02, 0x28, 0x02, 0x02, 0x0E, 0x02, 0x03, 0x19, 0x03, 0x02, 0x03, 0x02, 0x02, 0x11, 0x01,
|
||||||
|
0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x04, 0x02, 0x04, 0x01, 0x04, 0x01, 0x04, 0x01, 0x18, 0x02,
|
||||||
|
0x04, 0x02, 0x03, 0x02, 0x04, 0x01, 0x0A, 0x01, 0x0C, 0x02, 0x01, 0x0B, 0x02, 0x03, 0x02, 0x04,
|
||||||
|
0x01, 0x03, 0x02, 0x03, 0x03, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x02, 0x26, 0x02, 0x10, 0x02,
|
||||||
|
0x0F, 0x03, 0x01, 0x04, 0x02, 0x03, 0x02, 0x03, 0x02, 0x02, 0x03, 0x02, 0x0B, 0x03, 0x02, 0x03,
|
||||||
|
0x19, 0x02, 0x03, 0x02, 0x2A, 0x03, 0x01, 0x04, 0x01, 0x03, 0x03, 0x03, 0x34, 0x02, 0x02, 0x02,
|
||||||
|
0x03, 0x02, 0x08, 0x02, 0x03, 0x03, 0x03, 0x02, 0x0C, 0x03, 0x01, 0x03, 0x02, 0x02, 0x14, 0x02,
|
||||||
|
0x03, 0x02, 0x03, 0x01, 0x04, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x01, 0x0C, 0x02, 0x02, 0x03,
|
||||||
|
0x03, 0x01, 0x3E, 0x03, 0x01, 0x11, 0x02, 0x03, 0x02, 0x03, 0x03, 0x03, 0x02, 0x03, 0x01, 0x03,
|
||||||
|
0x02, 0x03, 0x03, 0x02, 0x2D, 0x02, 0x35, 0x37, 0x5D, 0x22, 0x1C, 0x15, 0x1D, 0x15, 0x1A, 0x28,
|
||||||
|
0x13, 0x01, 0x05, 0x09, 0x0C, 0x02, 0x0C, 0x02, 0x0B, 0x0A, 0x22, 0x21, 0x03, 0x05, 0x0B, 0x0A,
|
||||||
|
0x05, 0x08, 0x0C, 0x02, 0x0A, 0x0E, 0x02, 0x05, 0x18, 0x1A, 0x06, 0x10, 0x01, 0x05, 0x0B, 0x03,
|
||||||
|
0x0C, 0x08, 0x0C, 0x03, 0x0A, 0x0B, 0x04, 0x05, 0x0A, 0x04, 0x0B, 0x05, 0x13, 0x03, 0x03, 0x11,
|
||||||
|
0x02, 0x04, 0x0B, 0x0C, 0x03, 0x08, 0x19, 0x0D, 0x14, 0x15, 0x13, 0x14, 0x02, 0x02, 0x04, 0x03,
|
||||||
|
0x0B, 0x16, 0x11, 0x02, 0x04, 0x0B, 0x17, 0x13, 0x0D, 0x1C, 0x0B, 0x03, 0x0A, 0x16, 0x11, 0x12,
|
||||||
|
0x17, 0x09, 0x18, 0x1E, 0x0A, 0x05, 0x08, 0x0E, 0x05, 0x02, 0x11, 0x13, 0x18, 0x07, 0x17, 0x14,
|
||||||
|
0x02, 0x09, 0x03, 0x03, 0x03, 0x04, 0x0A, 0x15, 0x10, 0x14, 0x0F, 0x01, 0x06, 0x08, 0x17, 0x14,
|
||||||
|
0x02, 0x0B, 0x0A, 0x03, 0x09, 0x13, 0x12, 0x14, 0x0E, 0x02, 0x06, 0x08, 0x17, 0x0A, 0x06, 0x02,
|
||||||
|
0x05, 0x0D, 0x08, 0x03, 0x09, 0x0F, 0x14, 0x15, 0x0F, 0x12, 0x16, 0x09, 0x0E, 0x0F, 0x14, 0x0F,
|
||||||
|
0x14, 0x16, 0x0D, 0x12, 0x17, 0x0A, 0x0F, 0x0E, 0x09, 0x02, 0x09, 0x0D, 0x15, 0x0E, 0x03, 0x05,
|
||||||
|
0x0B, 0x14, 0x0C, 0x13, 0x0A, 0x15, 0x0B, 0x03, 0x08, 0x0C, 0x16, 0x0C, 0x07, 0x03, 0x0B, 0x0A,
|
||||||
|
0x15, 0x14, 0x0C, 0x14, 0x0C, 0x04, 0x07, 0x0B, 0x17, 0x0B, 0x08, 0x05, 0x0A, 0x08, 0x08, 0x05,
|
||||||
|
0x08, 0x09, 0x07, 0x05, 0x09, 0x16, 0x0A, 0x06, 0x08, 0x0B, 0x18, 0x0B, 0x05, 0x06, 0x0C, 0x07,
|
||||||
|
0x06, 0x08, 0x18, 0x06, 0x0A, 0x08, 0x03, 0x0A, 0x0B, 0x05, 0x08, 0x0C, 0x17, 0x0C, 0x04, 0x07,
|
||||||
|
0x0A, 0x09, 0x05, 0x08, 0x09, 0x08, 0x06, 0x08, 0x08, 0x09, 0x04, 0x09, 0x0B, 0x05, 0x08, 0x0B,
|
||||||
|
0x18, 0x0C, 0x04, 0x07, 0x0C, 0x05, 0x08, 0x09, 0x07, 0x07, 0x0A, 0x09, 0x05, 0x09, 0x06, 0x09,
|
||||||
|
0x0A, 0x06, 0x09, 0x0B, 0x0B, 0x02, 0x0C, 0x0A, 0x03, 0x09, 0x0B, 0x04, 0x08, 0x0C, 0x04, 0x07,
|
||||||
|
0x0B, 0x05, 0x08, 0x0B, 0x05, 0x08, 0x0B, 0x07, 0x08, 0x0B, 0x09, 0x05, 0x0E, 0x06, 0x07, 0x0D,
|
||||||
|
0x15, 0x17, 0x17, 0x0C, 0x08, 0x08, 0x0B, 0x06, 0x0C, 0x0A, 0x0A, 0x04, 0x1B, 0x0D, 0x16, 0x19,
|
||||||
|
0x1D, 0x05, 0x0E, 0x08, 0x0A, 0x05, 0x3F, 0x31, 0x43, 0x20, 0xB4, 0x23, 0xBE, 0x39, 0x80, 0x16,
|
||||||
|
0x10, 0x24, 0xFE, 0xBF, 0x81, 0x75, 0x19, 0x73, 0x11, 0x5C, 0x1C, 0xE7, 0x1B, 0xA1, 0x15, 0x38,
|
||||||
|
0x1C, 0x83, 0x2E, 0x42, 0x1A, 0x8B, 0x16, 0x5B, 0x3B, 0xEC, 0x31, 0xFE, 0x47, 0x75, 0xFE, 0xFE,
|
||||||
|
0xFE, 0xFE, 0xFE, 0xFE, 0xFF, 0xFE, 0xFE, 0x20, 0x03, 0x04, 0x05, 0x17, 0x1A, 0x22, 0x0A, 0x1F,
|
||||||
|
0x1B, 0x3E, 0x1C, 0x18, 0x12, 0x20, 0x10, 0x1F, 0x0A, 0x17, 0x18, 0x05, 0x02, 0x19, 0x13, 0x17,
|
||||||
|
0x17, 0x3A, 0x1B, 0x19, 0x12, 0x16, 0x09, 0x02, 0x09, 0x1E, 0x1B, 0x03, 0x0F, 0x02, 0x06, 0x19,
|
||||||
|
0x04, 0x22, 0x05, 0x04, 0x0D, 0x26, 0x18, 0x01, 0x06, 0x03, 0x05, 0x03, 0x02, 0x13, 0x04, 0x05,
|
||||||
|
0x05, 0x18, 0x04, 0x03, 0x15, 0x1F, 0x23, 0x02, 0x06, 0x02, 0x03, 0x06, 0x01, 0x0A, 0x04, 0x05,
|
||||||
|
0x05, 0x18, 0x03, 0x07, 0x09, 0x03, 0x06, 0x1E, 0x19, 0x01, 0x07, 0x03, 0x05, 0x18, 0x05, 0x04,
|
||||||
|
0x07, 0x16, 0x02, 0x07, 0x09, 0x05, 0x04, 0x20, 0x1F, 0x03, 0x05, 0x19, 0x04, 0x05, 0x07, 0x16,
|
||||||
|
0x02, 0x0F, 0x02, 0x05, 0x05, 0x1D, 0x1D, 0x03, 0x05, 0x04, 0x02, 0x12, 0x05, 0x03, 0x13, 0x1B,
|
||||||
|
0x0D, 0x20, 0x1A, 0x03, 0x04, 0x05, 0x01, 0x14, 0x03, 0x04, 0x05, 0x21, 0x03, 0x03, 0x11, 0x1B,
|
||||||
|
0x1A, 0x02, 0x05, 0x04, 0x02, 0x1B, 0x15, 0x24, 0x09, 0x1F, 0x0F, 0x02, 0x06, 0x03, 0x02, 0x1B,
|
||||||
|
0x19, 0x24, 0x09, 0x20, 0x0A, 0x02, 0x06, 0x03, 0x03, 0x1A, 0x04, 0x05, 0x16, 0x3D, 0x04, 0x03,
|
||||||
|
0x0B, 0x03, 0x05, 0x03, 0x04, 0x1B, 0x02, 0x09, 0x15, 0x04, 0x03, 0x23, 0x25, 0x03, 0x05, 0x03,
|
||||||
|
0x04, 0x2F, 0x13, 0x05, 0x02, 0x22, 0x03, 0x04, 0x04, 0x04, 0x12, 0x02, 0x06, 0x03, 0x04, 0x1C,
|
||||||
|
0x01, 0x07, 0x03, 0x07, 0x11, 0x4A, 0x08, 0x02, 0x06, 0x03, 0x04, 0x25, 0x1D, 0x24, 0x0B, 0x03,
|
||||||
|
0x03, 0x13, 0x0B, 0x03, 0x05, 0x03, 0x05, 0x23, 0x12, 0x27, 0x06, 0x03, 0x06, 0x17, 0x03, 0x03,
|
||||||
|
0x10, 0x03, 0x05, 0x03, 0x04, 0x24, 0x04, 0x06, 0x04, 0x13, 0x03, 0x0A, 0x02, 0x04, 0x10, 0x24,
|
||||||
|
0x11, 0x02, 0x05, 0x04, 0x04, 0x25, 0x03, 0x22, 0x04, 0x3F, 0x16, 0x03, 0x05, 0x03, 0x04, 0x47,
|
||||||
|
0x10, 0x06, 0x03, 0x08, 0x44, 0x03, 0x04, 0x04, 0x04, 0x47, 0x05, 0x06, 0x04, 0x04, 0x04, 0x06,
|
||||||
|
0x05, 0x39, 0x11, 0x02, 0x06, 0x02, 0x05, 0x2E, 0x05, 0x04, 0x13, 0x10, 0x1D, 0x3D, 0x07, 0x03,
|
||||||
|
0x05, 0x03, 0x04, 0x39, 0x38, 0x50, 0x04, 0x03, 0x05, 0x03, 0x04, 0x04, 0x02, 0x1D, 0x02, 0x05,
|
||||||
|
0x2C, 0x11, 0x03, 0x09, 0x02, 0x06, 0x01, 0x2D, 0x1E, 0x02, 0x05, 0x03, 0x05, 0x03, 0x02, 0x04,
|
||||||
|
0x02, 0x1E, 0x09, 0x03, 0x03, 0x04, 0x03, 0x25, 0x03, 0x0B, 0x01, 0x05, 0x10, 0x1E, 0x03, 0x06,
|
||||||
|
0x34, 0x32, 0x1E, 0x80, 0x04, 0x03, 0x05, 0x03, 0x05, 0xD7, 0x19, 0x03, 0x18, 0xF5, 0x1B, 0xFE,
|
||||||
|
0xFE, 0x0D, 0x8F, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
|
||||||
|
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFF, 0xFE, 0xFE, 0x0A, 0x1A, 0xB8, 0x1B, 0xB9, 0x0D, 0x5F, 0x2F,
|
||||||
|
0x33, 0x0D, 0x1A, 0x08, 0x1C, 0x09, 0x18, 0x0C, 0x55, 0x09, 0x0C, 0x07, 0x0A, 0x07, 0x11, 0x03,
|
||||||
|
0x0A, 0x0A, 0x0E, 0x15, 0x0B, 0x0A, 0x1B, 0x13, 0x0E, 0x0A, 0x0B, 0x09, 0x0A, 0x07, 0x0E, 0x06,
|
||||||
|
0x0A, 0x09, 0x0B, 0x0B, 0x05, 0x07, 0x0A, 0x09, 0x1C, 0x14, 0x09, 0x0E, 0x0B, 0x0B, 0x09, 0x06,
|
||||||
|
0x0E, 0x07, 0x0A, 0x09, 0x0A, 0x18, 0x09, 0x0A, 0x1D, 0x0F, 0x0B, 0x0D, 0x0C, 0x0A, 0x09, 0x08,
|
||||||
|
0x0B, 0x0B, 0x07, 0x0A, 0x0A, 0x0C, 0x56, 0x0D, 0x0C, 0x0A, 0x09, 0x08, 0x0C, 0x0A, 0x0A, 0x07,
|
||||||
|
0x0B, 0x0C, 0x0F, 0x07, 0x2B, 0x0B, 0x07, 0x0B, 0x09, 0x0B, 0x0A, 0x09, 0x0C, 0x08, 0x0B, 0x09,
|
||||||
|
0x0A, 0x0B, 0x0B, 0x08, 0x3E, 0x0A, 0x0A, 0x0B, 0x0C, 0x09, 0x0C, 0x08, 0x0B, 0x0A, 0x09, 0x0A,
|
||||||
|
0x09, 0x0A, 0x0D, 0x09, 0x23, 0x0B, 0x0A, 0x0C, 0x0B, 0x07, 0x0D, 0x08, 0x0C, 0x09, 0x09, 0x0A,
|
||||||
|
0x0B, 0x0B, 0x3B, 0x0B, 0x08, 0x0C, 0x0A, 0x09, 0x0C, 0x0A, 0x0B, 0x08, 0x0A, 0x0A, 0x0A, 0x0B,
|
||||||
|
0x22, 0x09, 0x0D, 0x0D, 0x06, 0x0C, 0x0B, 0x09, 0x0C, 0x09, 0x0B, 0x09, 0x0A, 0x09, 0x0B, 0x0A,
|
||||||
|
0x24, 0x08, 0x0D, 0x09, 0x0A, 0x0C, 0x0C, 0x08, 0x0B, 0x0A, 0x0B, 0x0A, 0x09, 0x0A, 0x09, 0x0B,
|
||||||
|
0x10, 0x06, 0x0D, 0x08, 0x0F, 0x08, 0x0B, 0x0B, 0x0D, 0x08, 0x0B, 0x0A, 0x0B, 0x09, 0x09, 0x0A,
|
||||||
|
0x0A, 0x0B, 0x0F, 0x07, 0x0C, 0x0A, 0x0C, 0x09, 0x0B, 0x0C, 0x0D, 0x07, 0x0C, 0x0A, 0x0A, 0x0A,
|
||||||
|
0x0A, 0x09, 0x0A, 0x0A, 0x0F, 0x09, 0x0B, 0x0A, 0x0F, 0x0C, 0x06, 0x0C, 0x0C, 0x08, 0x0C, 0x0A,
|
||||||
|
0x0A, 0x0A, 0x09, 0x0B, 0x09, 0x0B, 0x0C, 0x09, 0x0D, 0x0A, 0x0E, 0x0F, 0x07, 0x0C, 0x0B, 0x09,
|
||||||
|
0x0B, 0x0A, 0x0B, 0x09, 0x09, 0x0A, 0x0A, 0x0B, 0x0C, 0x0A, 0x0D, 0x09, 0x15, 0x0C, 0x05, 0x04,
|
||||||
|
0x02, 0x05, 0x0E, 0x06, 0x0C, 0x0A, 0x0B, 0x09, 0x09, 0x0B, 0x0A, 0x0A, 0x0C, 0x08, 0x0E, 0x08,
|
||||||
|
0x0F, 0x05, 0x04, 0x0C, 0x0A, 0x09, 0x0B, 0x08, 0x0B, 0x0A, 0x0B, 0x09, 0x09, 0x0A, 0x0A, 0x0B,
|
||||||
|
0x44, 0x0D, 0x06, 0x0C, 0x0D, 0x06, 0x0C, 0x0A, 0x0B, 0x09, 0x09, 0x0B, 0x0B, 0x0B, 0x45, 0x0C,
|
||||||
|
0x0B, 0x0A, 0x0B, 0x07, 0x0C, 0x09, 0x0B, 0x08, 0x0A, 0x0A, 0x0C, 0x0B, 0x22, 0x09, 0x0D, 0x19,
|
||||||
|
0x08, 0x0B, 0x11, 0x02, 0x0C, 0x0A, 0x0B, 0x08, 0x08, 0x0B, 0x0C, 0x0B, 0x3B, 0x1A, 0x06, 0x0C,
|
||||||
|
0x21, 0x0A, 0x0B, 0x1C, 0x0D, 0x0C, 0x3A, 0x1B, 0x07, 0x0B, 0x0D, 0x07, 0x0C, 0x09, 0x0E, 0x09,
|
||||||
|
0x4D, 0x09, 0x0D, 0x0F, 0x04, 0x0B, 0x08, 0x0A, 0x0D, 0x07, 0x0C, 0x0A, 0x0B, 0x0A, 0x0A, 0x0C,
|
||||||
|
0x64, 0x0B, 0x09, 0x0A, 0x0B, 0x09, 0x0B, 0x09, 0x0C, 0x09, 0x0A, 0x08, 0x0B, 0x0B, 0x57, 0x0C,
|
||||||
|
0x06, 0x0B, 0x0B, 0x09, 0x0C, 0x06, 0x0C, 0x09, 0x0A, 0x0A, 0x0A, 0x08, 0x0A, 0x09, 0x45, 0x0D,
|
||||||
|
0x0B, 0x0B, 0x0A, 0x09, 0x0A, 0x09, 0x0A, 0x0A, 0x09, 0x09, 0x0C, 0x09, 0x65, 0x08, 0x0A, 0x0B,
|
||||||
|
0x0B, 0x05, 0x0D, 0x08, 0xA4, 0x08, 0x0B, 0x0A, 0x0B, 0x05, 0x0E, 0x05, 0x0D, 0x05, 0x98, 0x08,
|
||||||
|
0x0B, 0x0B, 0x0A, 0x04, 0x99, 0x1A, 0x08, 0x0E, 0x0B, 0x06, 0x10, 0x03, 0x1B, 0x0A, 0x2C, 0x09,
|
||||||
|
0x5E, 0x12, 0x0C, 0x06, 0xD9, 0x09, 0x1A, 0x08, 0x3C, 0x07, 0x87, 0x07, 0x0E, 0x02, 0x09, 0x09,
|
||||||
|
0x59, 0x07, 0x6A, 0x07, 0x1A, 0x16, 0xC4, 0x07, 0xFA, 0x07, 0xFE, 0x03, 0x17, 0xFE, 0xFE, 0xFE,
|
||||||
|
0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0x9C, 0x6F, 0xFE, 0x97, 0x39, 0x70,
|
||||||
|
0x18, 0x16, 0x0D, 0x17, 0x18, 0x1B, 0x12, 0x17, 0x15, 0x17, 0x0E, 0x18, 0x12, 0x20, 0x16, 0x0A,
|
||||||
|
0x0B, 0x02, 0x06, 0x10, 0x04, 0x03, 0x0D, 0x15, 0x14, 0x14, 0x15, 0x0A, 0x05, 0x05, 0x0D, 0x04,
|
||||||
|
0x03, 0x10, 0x06, 0x03, 0x0B, 0x16, 0x13, 0x14, 0x0C, 0x16, 0x18, 0x04, 0x03, 0x0F, 0x07, 0x04,
|
||||||
|
0x0A, 0x15, 0x14, 0x14, 0x13, 0x0F, 0x1A, 0x16, 0x09, 0x03, 0x09, 0x15, 0x14, 0x15, 0x0A, 0x16,
|
||||||
|
0x1A, 0x16, 0x08, 0x04, 0x09, 0x15, 0x14, 0x16, 0x08, 0x16, 0x17, 0x05, 0x02, 0x05, 0x01, 0x08,
|
||||||
|
0x08, 0x03, 0x09, 0x15, 0x15, 0x15, 0x0A, 0x15, 0x17, 0x06, 0x02, 0x0D, 0x09, 0x03, 0x09, 0x15,
|
||||||
|
0x0E, 0x1C, 0x09, 0x15, 0x07, 0x03, 0x0A, 0x04, 0x03, 0x03, 0x02, 0x08, 0x08, 0x03, 0x0A, 0x14,
|
||||||
|
0x15, 0x0E, 0x0F, 0x15, 0x06, 0x04, 0x09, 0x06, 0x07, 0x09, 0x08, 0x03, 0x09, 0x15, 0x13, 0x15,
|
||||||
|
0x09, 0x07, 0x02, 0x0C, 0x04, 0x04, 0x0A, 0x05, 0x07, 0x0A, 0x06, 0x04, 0x0A, 0x13, 0x14, 0x15,
|
||||||
|
0x09, 0x07, 0x04, 0x09, 0x12, 0x06, 0x01, 0x04, 0x01, 0x09, 0x07, 0x04, 0x09, 0x14, 0x14, 0x15,
|
||||||
|
0x0A, 0x1D, 0x08, 0x04, 0x03, 0x03, 0x02, 0x09, 0x06, 0x04, 0x0A, 0x13, 0x15, 0x15, 0x14, 0x12,
|
||||||
|
0x08, 0x04, 0x03, 0x03, 0x02, 0x09, 0x07, 0x03, 0x0A, 0x14, 0x15, 0x15, 0x0B, 0x04, 0x06, 0x0F,
|
||||||
|
0x08, 0x04, 0x04, 0x0E, 0x06, 0x04, 0x09, 0x14, 0x15, 0x16, 0x14, 0x0F, 0x08, 0x04, 0x09, 0x08,
|
||||||
|
0x07, 0x04, 0x0A, 0x13, 0x15, 0x15, 0x07, 0x1F, 0x07, 0x03, 0x05, 0x03, 0x01, 0x09, 0x06, 0x04,
|
||||||
|
0x09, 0x14, 0x14, 0x17, 0x06, 0x06, 0x06, 0x12, 0x08, 0x03, 0x09, 0x09, 0x06, 0x04, 0x0A, 0x13,
|
||||||
|
0x15, 0x16, 0x07, 0x06, 0x05, 0x13, 0x07, 0x04, 0x09, 0x09, 0x05, 0x05, 0x0A, 0x13, 0x15, 0x16,
|
||||||
|
0x06, 0x08, 0x03, 0x14, 0x08, 0x03, 0x09, 0x09, 0x06, 0x04, 0x0A, 0x14, 0x14, 0x15, 0x15, 0x13,
|
||||||
|
0x09, 0x03, 0x05, 0x0E, 0x04, 0x05, 0x09, 0x14, 0x14, 0x16, 0x14, 0x15, 0x09, 0x03, 0x05, 0x0F,
|
||||||
|
0x04, 0x05, 0x0B, 0x12, 0x14, 0x17, 0x13, 0x15, 0x0B, 0x03, 0x05, 0x0E, 0x04, 0x05, 0x0A, 0x14,
|
||||||
|
0x14, 0x16, 0x13, 0x18, 0x0B, 0x03, 0x04, 0x10, 0x05, 0x03, 0x0B, 0x14, 0x13, 0x18, 0x0E, 0x16,
|
||||||
|
0x11, 0x04, 0x04, 0x0F, 0x05, 0x03, 0x0A, 0x14, 0x15, 0x16, 0x11, 0x18, 0x10, 0x03, 0x04, 0x0F,
|
||||||
|
0x05, 0x04, 0x07, 0x17, 0x05, 0x02, 0x0F, 0x16, 0x14, 0x17, 0x06, 0x01, 0x08, 0x03, 0x04, 0x10,
|
||||||
|
0x04, 0x04, 0x0C, 0x13, 0x03, 0x04, 0x12, 0x13, 0x10, 0x17, 0x16, 0x04, 0x03, 0x10, 0x04, 0x05,
|
||||||
|
0x0B, 0x13, 0x03, 0x04, 0x10, 0x14, 0x03, 0x04, 0x0C, 0x1F, 0x11, 0x03, 0x04, 0x10, 0x04, 0x04,
|
||||||
|
0x0C, 0x13, 0x04, 0x02, 0x11, 0x18, 0x16, 0x16, 0x19, 0x02, 0x04, 0x1A, 0x06, 0x20, 0x17, 0x16,
|
||||||
|
0x16, 0x16, 0x19, 0x04, 0x03, 0x11, 0x03, 0x05, 0x06, 0x18, 0x03, 0x0A, 0x12, 0x17, 0x19, 0x12,
|
||||||
|
0x21, 0x04, 0x04, 0x1A, 0x04, 0x28, 0x11, 0x18, 0x16, 0x16, 0x14, 0x0C, 0x07, 0x02, 0x05, 0x19,
|
||||||
|
0x05, 0x22, 0x17, 0x05, 0x02, 0x0D, 0x1A, 0x1E, 0x0B, 0x15, 0x05, 0x03, 0x05, 0x1A, 0x03, 0x05,
|
||||||
|
0x0B, 0x09, 0x02, 0x0B, 0x1F, 0x40, 0x11, 0x13, 0x07, 0x02, 0x05, 0x04, 0x02, 0x12, 0x15, 0x20,
|
||||||
|
0x11, 0x19, 0x10, 0x22, 0x2D, 0x03, 0x05, 0x19, 0x05, 0x03, 0x0C, 0x1F, 0x17, 0x4A, 0x2E, 0x03,
|
||||||
|
0x05, 0x19, 0x03, 0x3E, 0x11, 0x22, 0x0B, 0x44, 0x18, 0x1B, 0x17, 0x1B, 0x19, 0x19, 0x12, 0x4B,
|
||||||
|
0x1D, 0xE1, 0x27, 0xF4, 0x1E, 0x3C, 0x49, 0xFE, 0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
|
||||||
|
0xAA, 0x1C, 0xD0, 0x2F, 0x09, 0x03, 0x0F, 0x03, 0x04, 0x01, 0x0A, 0x03, 0x42, 0x17, 0x23, 0x16,
|
||||||
|
0x1C, 0x0E, 0x10, 0x10, 0x32, 0x09, 0x12, 0x0F, 0x03, 0x02, 0x26, 0x02, 0x22, 0x13, 0x81, 0x08,
|
||||||
|
0x20, 0x2B, 0x11, 0x0F, 0x16, 0x03, 0x2C, 0x04, 0x21, 0x03, 0x09, 0x08, 0x08, 0x04, 0x10, 0x0D,
|
||||||
|
0x06, 0x06, 0x04, 0x03, 0x3A, 0x03, 0x09, 0x06, 0x06, 0x09, 0x02, 0x15, 0x11, 0x18, 0x4F, 0x02,
|
||||||
|
0x1A, 0x1A, 0x09, 0x14, 0x18, 0x06, 0x04, 0x05, 0x1A, 0x05, 0x05, 0x3E, 0x0B, 0x10, 0x19, 0x07,
|
||||||
|
0x24, 0x06, 0x0A, 0x04, 0x04, 0x0B, 0x02, 0x1B, 0x10, 0x11, 0x18, 0x08, 0x18, 0x04, 0x08, 0x05,
|
||||||
|
0x0A, 0x18, 0x01, 0x1A, 0x09, 0x09, 0x03, 0x06, 0x17, 0x07, 0x19, 0x05, 0x08, 0x05, 0x11, 0x1E,
|
||||||
|
0x02, 0x0C, 0x08, 0x09, 0x02, 0x08, 0x0D, 0x03, 0x05, 0x07, 0x18, 0x04, 0x0A, 0x04, 0x17, 0x1C,
|
||||||
|
0x01, 0x0C, 0x08, 0x0A, 0x01, 0x09, 0x0C, 0x04, 0x05, 0x06, 0x17, 0x05, 0x0B, 0x03, 0x0B, 0x04,
|
||||||
|
0x0A, 0x06, 0x05, 0x12, 0x01, 0x06, 0x03, 0x01, 0x07, 0x15, 0x0E, 0x03, 0x03, 0x08, 0x16, 0x08,
|
||||||
|
0x16, 0x06, 0x07, 0x06, 0x0A, 0x12, 0x02, 0x05, 0x03, 0x02, 0x05, 0x15, 0x0E, 0x10, 0x17, 0x06,
|
||||||
|
0x17, 0x08, 0x07, 0x03, 0x0B, 0x13, 0x03, 0x04, 0x03, 0x03, 0x03, 0x15, 0x0D, 0x12, 0x18, 0x04,
|
||||||
|
0x17, 0x08, 0x16, 0x15, 0x02, 0x06, 0x02, 0x04, 0x03, 0x0E, 0x01, 0x04, 0x0E, 0x0B, 0x02, 0x03,
|
||||||
|
0x1A, 0x04, 0x17, 0x08, 0x15, 0x14, 0x03, 0x04, 0x03, 0x04, 0x02, 0x0F, 0x01, 0x03, 0x0E, 0x10,
|
||||||
|
0x17, 0x07, 0x17, 0x09, 0x0F, 0x1A, 0x02, 0x06, 0x03, 0x03, 0x03, 0x0D, 0x04, 0x01, 0x11, 0x07,
|
||||||
|
0x23, 0x03, 0x16, 0x09, 0x07, 0x04, 0x05, 0x19, 0x02, 0x06, 0x02, 0x03, 0x04, 0x0C, 0x04, 0x02,
|
||||||
|
0x0F, 0x09, 0x05, 0x03, 0x14, 0x09, 0x14, 0x08, 0x07, 0x07, 0x06, 0x15, 0x03, 0x04, 0x03, 0x02,
|
||||||
|
0x05, 0x0C, 0x05, 0x02, 0x0F, 0x08, 0x04, 0x04, 0x14, 0x09, 0x15, 0x08, 0x06, 0x07, 0x09, 0x13,
|
||||||
|
0x05, 0x03, 0x0C, 0x0A, 0x05, 0x04, 0x0D, 0x09, 0x02, 0x05, 0x13, 0x0B, 0x14, 0x0B, 0x02, 0x07,
|
||||||
|
0x09, 0x16, 0x06, 0x02, 0x0B, 0x0B, 0x06, 0x03, 0x0C, 0x08, 0x05, 0x06, 0x09, 0x03, 0x07, 0x07,
|
||||||
|
0x15, 0x09, 0x09, 0x03, 0x0A, 0x0A, 0x02, 0x0A, 0x06, 0x06, 0x07, 0x0A, 0x06, 0x04, 0x0A, 0x06,
|
||||||
|
0x07, 0x06, 0x08, 0x06, 0x08, 0x06, 0x09, 0x05, 0x06, 0x0E, 0x13, 0x05, 0x09, 0x0A, 0x04, 0x08,
|
||||||
|
0x06, 0x05, 0x09, 0x05, 0x18, 0x07, 0x06, 0x07, 0x08, 0x08, 0x06, 0x09, 0x05, 0x18, 0x11, 0x08,
|
||||||
|
0x05, 0x07, 0x05, 0x07, 0x07, 0x08, 0x05, 0x08, 0x08, 0x04, 0x08, 0x07, 0x07, 0x05, 0x08, 0x08,
|
||||||
|
0x15, 0x17, 0x14, 0x0B, 0x11, 0x07, 0x07, 0x05, 0x15, 0x07, 0x08, 0x02, 0x0C, 0x04, 0x07, 0x07,
|
||||||
|
0x09, 0x05, 0x05, 0x1B, 0x08, 0x12, 0x3E, 0x09, 0x58, 0x09, 0x15, 0x0C, 0x13, 0x04, 0x3C, 0x09,
|
||||||
|
0x20, 0x07, 0x7B, 0x16, 0x05, 0x26, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0x40, 0x04, 0x3B, 0x07, 0xCB,
|
||||||
|
0x03, 0x07, 0x08, 0x72, 0x08, 0x19, 0x07, 0x04, 0x08, 0x03, 0x03, 0x0F, 0x03, 0x01, 0x03, 0x03,
|
||||||
|
0x02, 0x02, 0x03, 0x02, 0x02, 0x2A, 0x02, 0x18, 0x02, 0x03, 0x02, 0x5D, 0x09, 0x01, 0x03, 0x03,
|
||||||
|
0x02, 0x11, 0x1A, 0x20, 0x01, 0x1D, 0x03, 0x03, 0x01, 0x32, 0x38, 0x03, 0x02, 0x21, 0x02, 0x51,
|
||||||
|
0x01, 0x0A, 0x02, 0x02, 0x02, 0x03, 0x02, 0x04, 0x02, 0x02, 0x08, 0x03, 0x02, 0x0E, 0x03, 0x01,
|
||||||
|
0x02, 0x03, 0x02, 0x44, 0x03, 0x02, 0x03, 0x03, 0x01, 0x2F, 0x02, 0x29, 0x08, 0x2A, 0x07, 0x02,
|
||||||
|
0x04, 0x01, 0x0A, 0x02, 0x03, 0x02, 0x03, 0x03, 0x02, 0x6C, 0x15, 0x02, 0x02, 0x02, 0x03, 0x10,
|
||||||
|
0x01, 0x17, 0x02, 0x03, 0x01, 0x11, 0x02, 0x02, 0x03, 0x50, 0x02, 0x02, 0x02, 0x38, 0x03, 0x1A,
|
||||||
|
0x29, 0x02, 0x03, 0x03, 0x02, 0x04, 0x01, 0x67, 0x01, 0x03, 0x02, 0x03, 0x01, 0x1A, 0x02, 0x04,
|
||||||
|
0x01, 0x0A, 0x01, 0x04, 0x01, 0x04, 0x02, 0x1A, 0x01, 0x02, 0x03, 0x03, 0x02, 0x03, 0x02, 0x03,
|
||||||
|
0x02, 0x27, 0x01, 0x02, 0x03, 0x03, 0x01, 0x22, 0x02, 0x03, 0x03, 0x03, 0x02, 0x39, 0x0F, 0x02,
|
||||||
|
0x03, 0x0E, 0x03, 0x2A, 0x02, 0x03, 0x02, 0xFE, 0xFE, 0x34, 0x1B, 0xED, 0x20, 0xE7, 0x21, 0x40,
|
||||||
|
0x59, 0x57, 0x2E, 0xE9, 0x23, 0xFE, 0x01, 0x27, 0x40, 0x5B, 0xFE, 0xFE, 0x16, 0x71, 0xFE, 0xFE,
|
||||||
|
0xFF, 0x0F, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
|
||||||
|
0x4D, 0x20, 0x22, 0x19, 0x1A, 0x16, 0x1B, 0x19, 0x1B, 0x19, 0x1C, 0x13, 0x1C, 0x1D, 0x19, 0x10,
|
||||||
|
0x2E, 0x08, 0x14, 0x2B, 0x17, 0x0E, 0x4B, 0x12, 0x09, 0x13, 0x13, 0x10, 0x0E, 0x0C, 0x14, 0x09,
|
||||||
|
0x12, 0x10, 0x0B, 0x12, 0x13, 0x10, 0x0F, 0x0A, 0x16, 0x04, 0x16, 0x0E, 0x0D, 0x12, 0x13, 0x11,
|
||||||
|
0x0E, 0x0E, 0x12, 0x04, 0x17, 0x0E, 0x0D, 0x11, 0x13, 0x12, 0x0F, 0x08, 0x15, 0x08, 0x15, 0x0D,
|
||||||
|
0x0B, 0x13, 0x13, 0x12, 0x0C, 0x07, 0x17, 0x04, 0x17, 0x2C, 0x22, 0x16, 0x18, 0x05, 0x17, 0x08,
|
||||||
|
0x04, 0x07, 0x04, 0x14, 0x31, 0x06, 0x17, 0x06, 0x17, 0x28, 0x10, 0x0B, 0x13, 0x0B, 0x18, 0x05,
|
||||||
|
0x17, 0x26, 0x12, 0x0B, 0x16, 0x06, 0x37, 0x05, 0x06, 0x18, 0x12, 0x0C, 0x12, 0x07, 0x18, 0x04,
|
||||||
|
0x19, 0x07, 0x07, 0x04, 0x03, 0x0F, 0x12, 0x09, 0x0A, 0x03, 0x06, 0x07, 0x15, 0x0A, 0x16, 0x13,
|
||||||
|
0x05, 0x0E, 0x12, 0x09, 0x0A, 0x03, 0x06, 0x07, 0x16, 0x09, 0x17, 0x10, 0x06, 0x0E, 0x12, 0x09,
|
||||||
|
0x13, 0x09, 0x15, 0x0A, 0x18, 0x10, 0x04, 0x0D, 0x12, 0x0A, 0x0A, 0x03, 0x04, 0x09, 0x15, 0x0A,
|
||||||
|
0x15, 0x08, 0x08, 0x01, 0x05, 0x0E, 0x12, 0x0A, 0x0A, 0x02, 0x05, 0x09, 0x15, 0x07, 0x17, 0x09,
|
||||||
|
0x10, 0x0E, 0x11, 0x0A, 0x13, 0x09, 0x14, 0x09, 0x15, 0x13, 0x07, 0x0F, 0x10, 0x0A, 0x13, 0x09,
|
||||||
|
0x14, 0x08, 0x17, 0x12, 0x08, 0x13, 0x0C, 0x0A, 0x13, 0x09, 0x15, 0x08, 0x16, 0x14, 0x06, 0x0E,
|
||||||
|
0x12, 0x09, 0x13, 0x09, 0x15, 0x13, 0x0B, 0x13, 0x08, 0x0D, 0x13, 0x08, 0x16, 0x07, 0x16, 0x08,
|
||||||
|
0x16, 0x12, 0x08, 0x15, 0x0B, 0x09, 0x15, 0x08, 0x15, 0x08, 0x06, 0x03, 0x0C, 0x13, 0x09, 0x15,
|
||||||
|
0x0C, 0x08, 0x03, 0x06, 0x0A, 0x09, 0x05, 0x04, 0x0A, 0x13, 0x15, 0x09, 0x0B, 0x14, 0x17, 0x09,
|
||||||
|
0x18, 0x04, 0x0C, 0x03, 0x06, 0x06, 0x15, 0x08, 0x10, 0x12, 0x13, 0x0E, 0x17, 0x05, 0x12, 0x08,
|
||||||
|
0x14, 0x08, 0x04, 0x04, 0x09, 0x12, 0x0F, 0x12, 0x0F, 0x0D, 0x13, 0x07, 0x14, 0x11, 0x0A, 0x13,
|
||||||
|
0x10, 0x10, 0x15, 0x09, 0x2F, 0x0D, 0x10, 0x13, 0x0F, 0x10, 0x16, 0x09, 0x2D, 0x0F, 0x10, 0x14,
|
||||||
|
0x0F, 0x0F, 0x16, 0x08, 0x17, 0x04, 0x14, 0x0D, 0x11, 0x15, 0x11, 0x0E, 0x12, 0x0C, 0x26, 0x14,
|
||||||
|
0x0F, 0x1E, 0x19, 0x06, 0x18, 0x06, 0x07, 0x02, 0x18, 0x06, 0x08, 0x05, 0x09, 0x13, 0x04, 0x08,
|
||||||
|
0x03, 0x06, 0x17, 0x08, 0x05, 0x03, 0x0F, 0x03, 0x31, 0x05, 0x07, 0x14, 0x0B, 0x13, 0x16, 0x11,
|
||||||
|
0x14, 0x0B, 0x2E, 0x08, 0x15, 0x12, 0x02, 0x05, 0x17, 0x0A, 0x05, 0x01, 0x19, 0x07, 0x3C, 0x13,
|
||||||
|
0x04, 0x16, 0x16, 0x08, 0x1B, 0x12, 0x21, 0x11, 0x14, 0x16, 0x02, 0x0E, 0x2F, 0x04, 0x06, 0x0A,
|
||||||
|
0x10, 0x14, 0x18, 0x07, 0x09, 0x14, 0x0B, 0x05, 0x02, 0x06, 0x26, 0x02, 0x2E, 0x12, 0x2B, 0x11,
|
||||||
|
0x14, 0x07, 0x03, 0x05, 0x30, 0x02, 0x06, 0x04, 0x18, 0x1F, 0x58, 0x06, 0x02, 0x05, 0x2F, 0x04,
|
||||||
|
0x04, 0x06, 0x09, 0x02, 0x04, 0x31, 0x22, 0x3A, 0xC5, 0x16, 0x3F, 0x03, 0x0A, 0x02, 0x51, 0x12,
|
||||||
|
0x12, 0x10, 0xFE, 0xD0, 0x35, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
|
||||||
|
0xFE, 0xFE, 0x63, 0x02, 0x04, 0x02, 0x03, 0x03, 0x81, 0x02, 0x03, 0x02, 0x47, 0x09, 0x0A, 0x02,
|
||||||
|
0x02, 0x03, 0x03, 0x02, 0x2B, 0x01, 0x04, 0x02, 0x04, 0x02, 0x04, 0x01, 0x38, 0x02, 0x80, 0x03,
|
||||||
|
0x03, 0x02, 0x63, 0x01, 0x03, 0x02, 0x03, 0x02, 0x56, 0x02, 0x03, 0x02, 0x03, 0x03, 0x19, 0x02,
|
||||||
|
0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x03, 0x01, 0x60, 0x2C, 0x01, 0x03, 0x3E, 0x01,
|
||||||
|
0x04, 0x01, 0x32, 0x02, 0x02, 0x03, 0x02, 0x17, 0x01, 0x03, 0x3D, 0x02, 0x03, 0x03, 0x03, 0x02,
|
||||||
|
0x0A, 0x02, 0x5B, 0x02, 0x1F, 0x03, 0x02, 0x03, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x04, 0x01,
|
||||||
|
0x09, 0x02, 0x03, 0x02, 0x02, 0x03, 0x02, 0x02, 0x04, 0x01, 0x03, 0x02, 0x03, 0x02, 0x04, 0x01,
|
||||||
|
0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x2C, 0x02, 0x02, 0x03, 0x03, 0x02, 0x03, 0x03,
|
||||||
|
0x04, 0x01, 0x23, 0x02, 0x03, 0x02, 0x25, 0x03, 0x02, 0x02, 0x03, 0x02, 0x1B, 0x02, 0x03, 0x02,
|
||||||
|
0x04, 0x01, 0x37, 0x02, 0x04, 0x02, 0x12, 0x02, 0x02, 0x03, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02,
|
||||||
|
0x10, 0x02, 0x04, 0x01, 0x0D, 0x01, 0x04, 0x01, 0x04, 0x01, 0x25, 0x02, 0x03, 0x02, 0x03, 0x02,
|
||||||
|
0x04, 0x02, 0x0A, 0x02, 0x0B, 0x02, 0x04, 0x02, 0x04, 0x03, 0x02, 0x03, 0x03, 0x0E, 0x3E, 0x02,
|
||||||
|
0x04, 0x18, 0x01, 0x03, 0x03, 0x02, 0x04, 0x0A, 0x87, 0x11, 0x03, 0x02, 0x03, 0x02, 0x52, 0x02,
|
||||||
|
0x04, 0x02, 0x12, 0x02, 0x30, 0x02, 0x03, 0x02, 0x04, 0x02, 0x04, 0x01, 0x04, 0x02, 0x02, 0x03,
|
||||||
|
0x02, 0x02, 0x02, 0x03, 0x03, 0x01, 0x27, 0x02, 0xA4, 0x02, 0x03, 0x02, 0x18, 0x02, 0x03, 0x02,
|
||||||
|
0x24, 0x16, 0x01, 0x1B, 0xFF, 0x01, 0x0F, 0x1A, 0x0D, 0x0B, 0x20, 0x0E, 0x07, 0x0C, 0x0A, 0x0B,
|
||||||
|
0x22, 0x0A, 0x0D, 0x09, 0x0A, 0x0B, 0x1E, 0x10, 0x04, 0x0D, 0x10, 0x0C, 0x1E, 0x0A, 0x0B, 0x08,
|
||||||
|
0x0C, 0x0A, 0x10, 0x07, 0x09, 0x0C, 0x04, 0x0E, 0x0C, 0x0C, 0x1F, 0x0E, 0x1D, 0x08, 0x11, 0x1A,
|
||||||
|
0x03, 0x0D, 0x0C, 0x0B, 0x21, 0x0C, 0x10, 0x13, 0x14, 0x12, 0x0A, 0x0D, 0x0B, 0x0B, 0x0E, 0x03,
|
||||||
|
0x10, 0x0C, 0x1E, 0x09, 0x11, 0x13, 0x08, 0x0B, 0x0C, 0x0C, 0x0C, 0x04, 0x10, 0x0E, 0x3E, 0x1F,
|
||||||
|
0x0A, 0x0D, 0x0F, 0x02, 0x0B, 0x0F, 0x0E, 0x0C, 0x20, 0x04, 0x03, 0x1E, 0x0B, 0x0D, 0x1C, 0x0E,
|
||||||
|
0x0E, 0x0C, 0x1E, 0x17, 0x02, 0x0C, 0x0B, 0x0D, 0x1B, 0x11, 0x1D, 0x06, 0x20, 0x0F, 0x02, 0x0B,
|
||||||
|
0x0B, 0x0E, 0x0C, 0x04, 0x0F, 0x0B, 0x10, 0x12, 0x1E, 0x20, 0x0B, 0x0B, 0x0E, 0x04, 0x09, 0x03,
|
||||||
|
0x02, 0x0A, 0x0B, 0x0D, 0x04, 0x08, 0x1D, 0x10, 0x05, 0x0D, 0x0B, 0x0C, 0x0D, 0x04, 0x0F, 0x0B,
|
||||||
|
0x0D, 0x0A, 0x0B, 0x08, 0x0D, 0x0E, 0x11, 0x12, 0x0B, 0x0B, 0x0E, 0x03, 0x0A, 0x0E, 0x0C, 0x0C,
|
||||||
|
0x0B, 0x0A, 0x09, 0x0B, 0x0D, 0x1D, 0x0B, 0x0C, 0x0E, 0x04, 0x0A, 0x0D, 0x0C, 0x0D, 0x0B, 0x09,
|
||||||
|
0x0B, 0x07, 0x0E, 0x0E, 0x03, 0x0B, 0x03, 0x02, 0x06, 0x01, 0x02, 0x0B, 0x0F, 0x03, 0x0B, 0x0B,
|
||||||
|
0x0D, 0x0E, 0x0C, 0x07, 0x09, 0x0A, 0x0C, 0x0A, 0x0B, 0x0C, 0x04, 0x01, 0x05, 0x10, 0x10, 0x02,
|
||||||
|
0x0B, 0x03, 0x02, 0x04, 0x0F, 0x09, 0x11, 0x09, 0x03, 0x0D, 0x0B, 0x0C, 0x0A, 0x12, 0x01, 0x03,
|
||||||
|
0x0B, 0x0B, 0x1D, 0x02, 0x02, 0x09, 0x0F, 0x0C, 0x13, 0x12, 0x0F, 0x0F, 0x0E, 0x0D, 0x0B, 0x03,
|
||||||
|
0x02, 0x05, 0x22, 0x09, 0x10, 0x0A, 0x02, 0x04, 0x10, 0x0E, 0x0D, 0x1B, 0x0A, 0x0C, 0x0C, 0x0B,
|
||||||
|
0x0E, 0x03, 0x0F, 0x0C, 0x0D, 0x0B, 0x05, 0x0B, 0x0A, 0x0C, 0x0D, 0x0F, 0x09, 0x03, 0x0B, 0x0C,
|
||||||
|
0x0C, 0x0C, 0x0C, 0x04, 0x0F, 0x0C, 0x0C, 0x0D, 0x06, 0x0C, 0x0D, 0x0A, 0x0B, 0x08, 0x0E, 0x09,
|
||||||
|
0x0D, 0x0D, 0x0B, 0x0E, 0x1E, 0x0C, 0x0E, 0x1B, 0x0D, 0x0C, 0x0C, 0x0E, 0x1D, 0x09, 0x0B, 0x0A,
|
||||||
|
0x0C, 0x0C, 0x0A, 0x0A, 0x0E, 0x0B, 0x0B, 0x09, 0x0C, 0x0A, 0x0E, 0x0F, 0x07, 0x0C, 0x2F, 0x0A,
|
||||||
|
0x10, 0x09, 0x15, 0x0B, 0x19, 0x0E, 0x13, 0x10, 0x11, 0x11, 0x11, 0x14, 0x31, 0x1F, 0x54, 0x11,
|
||||||
|
0x5B, 0x0F, 0x2F, 0x0A, 0x12, 0x34, 0x9E, 0x0C, 0x12, 0x18, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
|
||||||
|
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
|
||||||
|
0xFE, 0xFE, 0xFE, 0xFE, 0xFF,
|
||||||
|
};
|
||||||
26
examples/spacetaxi/stSpeechData.h
Normal file
26
examples/spacetaxi/stSpeechData.h
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Generated by assets/extractSpeech.py. Do not hand-edit.
|
||||||
|
//
|
||||||
|
// Space Taxi's digitised speech as the C64 stores it: 1-bit
|
||||||
|
// run-length streams. Each byte is one half-cycle length; the
|
||||||
|
// player toggles its output after each. $FE is a raster sync
|
||||||
|
// and $FF ends an utterance. stAudio.c expands these to PCM
|
||||||
|
// through the jlAudioPlaySfxStream fill callback.
|
||||||
|
|
||||||
|
#ifndef ST_SPEECH_DATA_H
|
||||||
|
#define ST_SPEECH_DATA_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t code; // PETSCII: 'H' 'T' '!' 'P' '1'..'9' '?' 'U'
|
||||||
|
uint16_t offset; // into kStSpeechRle
|
||||||
|
uint16_t length;
|
||||||
|
} StSpeechEntryT;
|
||||||
|
|
||||||
|
#define ST_SPEECH_COUNT 15u
|
||||||
|
#define ST_SPEECH_RLE_BYTES 7445u
|
||||||
|
|
||||||
|
extern const StSpeechEntryT kStSpeechIndex[ST_SPEECH_COUNT];
|
||||||
|
extern const uint8_t kStSpeechRle[ST_SPEECH_RLE_BYTES];
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -11,6 +11,11 @@
|
||||||
// Level intro ($4523..$4727): a black screen with the level name, the
|
// Level intro ($4523..$4727): a black screen with the level name, the
|
||||||
// cab rising from the bottom and seven star sprites streaming out of
|
// cab rising from the bottom and seven star sprites streaming out of
|
||||||
// the centre, until the cab reaches mid-screen.
|
// the centre, until the cab reaches mid-screen.
|
||||||
|
//
|
||||||
|
// The seven star sprites are replaced by a static starfield whose
|
||||||
|
// colours are cycled -- see ST_STAR_RING_FIRST in stSim.h for why and
|
||||||
|
// how. The cab still flies, and it is still what ends the intro, so the
|
||||||
|
// sequence keeps its length and its cue; only the warp changes.
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
|
@ -28,24 +33,49 @@ static const uint8_t kLogoColor[8] = { 0x02, 0x08, 0x07, 0x05, 0x06, 0x0E, 0x03
|
||||||
// Intro cab drift per loop count ($470D).
|
// Intro cab drift per loop count ($470D).
|
||||||
static const int8_t kIntroCabDy[4] = { -2, 1, -1, 1 };
|
static const int8_t kIntroCabDy[4] = { -2, 1, -1, 1 };
|
||||||
// Intro texts ($4624 / $463F).
|
// Intro texts ($4624 / $463F).
|
||||||
static const uint8_t kTextDemoExit[] = { 0x44, 0x45, 0x4D, 0x4F, 0x2C, 0x20, 0x55, 0x53, 0x45, 0x20, 0x4A, 0x4F, 0x59, 0x53, 0x54, 0x49, 0x43, 0x4B, 0x20, 0x54, 0x4F, 0x20, 0x45, 0x58, 0x49, 0x54, 0 };
|
static const uint8_t kTextDemoExit[] = "DEMO, USE JOYSTICK TO EXIT";
|
||||||
static const uint8_t kTextScreens[] = { 0x54, 0x48, 0x49, 0x53, 0x20, 0x49, 0x53, 0x20, 0x31, 0x20, 0x4F, 0x46, 0x20, 0x32, 0x35, 0x20, 0x44, 0x49, 0x46, 0x46, 0x45, 0x52, 0x45, 0x4E, 0x54, 0x20, 0x53, 0x43, 0x52, 0x45, 0x45, 0x4E, 0x53, 0x21, 0 };
|
static const uint8_t kTextScreens[] = "THIS IS 1 OF 25 DIFFERENT SCREENS!";
|
||||||
|
|
||||||
#define ST_TITLE_CAB_LIFT_ROW 0x14u
|
#define ST_TITLE_CAB_LIFT_ROW 0x14u
|
||||||
#define ST_TITLE_SPARKLE_TICKS 0x5Au
|
#define ST_TITLE_SPARKLE_TICKS 0x5Au
|
||||||
#define ST_TITLE_HOVER_COL 0x28u
|
#define ST_TITLE_HOVER_COL 0x28u
|
||||||
#define ST_INTRO_STAR_PTR 0xDAu
|
#define ST_INTRO_STAR_PTR 0xDAu
|
||||||
#define ST_INTRO_STAR_RELOAD 0x20u
|
|
||||||
#define ST_INTRO_CAB_END_ROW 0x94u
|
#define ST_INTRO_CAB_END_ROW 0x94u
|
||||||
#define ST_INTRO_CAB_DX_RELOAD 0x28u
|
#define ST_INTRO_CAB_DX_RELOAD 0x28u
|
||||||
|
|
||||||
|
// The palette-cycled starfield. Seven stars per ring so the bright band
|
||||||
|
// stays the same weight all the way out, placed at fixed pseudo-random
|
||||||
|
// angles and clear of the three lines the intro screen writes (the level
|
||||||
|
// name on row 12, and the demo hints on rows 3 and 24).
|
||||||
|
typedef struct {
|
||||||
|
uint8_t col;
|
||||||
|
uint8_t row;
|
||||||
|
uint8_t ring;
|
||||||
|
} StStarT;
|
||||||
|
|
||||||
|
static const StStarT kIntroStar[] = {
|
||||||
|
{ 18u, 10u, 0u }, { 19u, 10u, 0u }, { 17u, 11u, 0u }, { 18u, 13u, 0u },
|
||||||
|
{ 21u, 13u, 0u }, { 22u, 14u, 0u }, { 18u, 15u, 0u }, { 18u, 8u, 1u },
|
||||||
|
{ 19u, 8u, 1u }, { 14u, 10u, 1u }, { 25u, 14u, 1u }, { 21u, 15u, 1u },
|
||||||
|
{ 25u, 15u, 1u }, { 20u, 17u, 1u }, { 26u, 7u, 2u }, { 12u, 10u, 2u },
|
||||||
|
{ 29u, 11u, 2u }, { 11u, 13u, 2u }, { 29u, 14u, 2u }, { 21u, 17u, 2u },
|
||||||
|
{ 23u, 18u, 2u }, { 21u, 4u, 3u }, { 24u, 4u, 3u }, { 11u, 8u, 3u },
|
||||||
|
{ 12u, 8u, 3u }, { 10u, 10u, 3u }, { 30u, 16u, 3u }, { 14u, 17u, 3u },
|
||||||
|
{ 15u, 2u, 4u }, { 8u, 8u, 4u }, { 32u, 9u, 4u }, { 9u, 17u, 4u },
|
||||||
|
{ 32u, 17u, 4u }, { 32u, 18u, 4u }, { 24u, 22u, 4u }, { 16u, 1u, 5u },
|
||||||
|
{ 28u, 1u, 5u }, { 31u, 2u, 5u }, { 36u, 13u, 5u }, { 6u, 15u, 5u },
|
||||||
|
{ 4u, 16u, 5u }, { 5u, 19u, 5u }, { 8u, 0u, 6u }, { 30u, 0u, 6u },
|
||||||
|
{ 1u, 8u, 6u }, { 2u, 12u, 6u }, { 37u, 16u, 6u }, { 7u, 21u, 6u },
|
||||||
|
{ 32u, 22u, 6u }, { 0u, 4u, 7u }, { 39u, 7u, 7u }, { 0u, 10u, 7u },
|
||||||
|
{ 38u, 20u, 7u }, { 38u, 21u, 7u }, { 4u, 22u, 7u }, { 35u, 22u, 7u },
|
||||||
|
};
|
||||||
|
#define ST_INTRO_STAR_COUNT (sizeof(kIntroStar) / sizeof(kIntroStar[0]))
|
||||||
|
|
||||||
|
|
||||||
static void addToRow(StSimT *sim, uint8_t idx, uint8_t delta);
|
static void addToRow(StSimT *sim, uint8_t idx, uint8_t delta);
|
||||||
static void addToSpriteX(StSimT *sim, uint8_t idx, uint8_t delta);
|
|
||||||
static void logoColorCycle(StSimT *sim);
|
static void logoColorCycle(StSimT *sim);
|
||||||
static void logoFlip(StSimT *sim);
|
static void logoFlip(StSimT *sim);
|
||||||
static void marshalFrame(StSimT *sim);
|
static void paintStarfield(StSimT *sim);
|
||||||
static void starRespawn(StSimT *sim, uint8_t idx);
|
|
||||||
|
|
||||||
|
|
||||||
// $4113
|
// $4113
|
||||||
|
|
@ -54,16 +84,6 @@ static void addToRow(StSimT *sim, uint8_t idx, uint8_t delta) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// $411B
|
|
||||||
static void addToSpriteX(StSimT *sim, uint8_t idx, uint8_t delta) {
|
|
||||||
uint16_t x = (uint16_t)(((uint16_t)sim->spr[idx].msb << 8) | sim->spr[idx].col);
|
|
||||||
|
|
||||||
x = (uint16_t)(x + (uint16_t)(int16_t)(int8_t)delta);
|
|
||||||
sim->spr[idx].col = (uint8_t)x;
|
|
||||||
sim->spr[idx].msb = (uint8_t)(x >> 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// $4861 -- the next logo colour, onto sprites 3..7 and onto the logo
|
// $4861 -- the next logo colour, onto sprites 3..7 and onto the logo
|
||||||
// itself. The C64 writes the colour into all 407 cells of rows 1..11,
|
// itself. The C64 writes the colour into all 407 cells of rows 1..11,
|
||||||
// columns 1..37; only the cells showing the logo character take any
|
// columns 1..37; only the cells showing the logo character take any
|
||||||
|
|
@ -90,7 +110,8 @@ static void logoFlip(StSimT *sim) {
|
||||||
if (sim->logoDiv != 0u) {
|
if (sim->logoDiv != 0u) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
memcpy(sim->charset[ST_LOGO_CHAR], sim->charset[ST_LOGO_CHAR + kLogoFlip[sim->logoCycleAux]], 8u);
|
memcpy(stSimGlyphMut(sim, ST_LOGO_CHAR),
|
||||||
|
stSimGlyph(sim, (uint8_t)(ST_LOGO_CHAR + kLogoFlip[sim->logoCycleAux])), 8u);
|
||||||
stSimMarkChar(sim, ST_LOGO_CHAR);
|
stSimMarkChar(sim, ST_LOGO_CHAR);
|
||||||
sim->logoCycleAux++;
|
sim->logoCycleAux++;
|
||||||
if (sim->logoCycleAux == 6u) {
|
if (sim->logoCycleAux == 6u) {
|
||||||
|
|
@ -103,32 +124,21 @@ static void logoFlip(StSimT *sim) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// $4253 + $4293 as the title and intro loops call them.
|
// Paint the level-intro starfield into screen RAM, once. A star's cell
|
||||||
static void marshalFrame(StSimT *sim) {
|
// is coloured ST_STAR_RING_FIRST + its ring and never touched again --
|
||||||
uint8_t i;
|
// the renderer animates the warp by cycling those colours, which costs
|
||||||
uint8_t enable = 0u;
|
// no dirty rows at all. See ST_STAR_RING_FIRST in stSim.h.
|
||||||
|
static void paintStarfield(StSimT *sim) {
|
||||||
|
const StStarT *star;
|
||||||
|
uint8_t one[2];
|
||||||
|
uint8_t k;
|
||||||
|
|
||||||
for (i = 0u; i < ST_HW_SPRITES; i++) {
|
one[1] = 0u; // stSimDrawText stops below screen code 6
|
||||||
sim->frame.x[i] = (uint16_t)(((uint16_t)(sim->spr[i].msb != 0u ? 1u : 0u) << 8) | sim->spr[i].col);
|
for (k = 0u; k < (uint8_t)ST_INTRO_STAR_COUNT; k++) {
|
||||||
sim->frame.y[i] = sim->spr[i].row;
|
star = &kIntroStar[k];
|
||||||
sim->frame.ptr[i] = sim->spr[i].ptr;
|
one[0] = (star->ring < ST_STAR_FAR_RINGS) ? ST_STAR_GLYPH_FAR : ST_STAR_GLYPH_NEAR;
|
||||||
sim->frame.color[i] = sim->spr[i].color;
|
stSimDrawText(sim, star->col, star->row, one, (uint8_t)(ST_STAR_RING_FIRST + star->ring));
|
||||||
if (sim->spr[i].enable != 0u) {
|
|
||||||
enable |= (uint8_t)(1u << i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
sim->frame.enableMask = enable;
|
|
||||||
sim->frame.multiMask = sim->multiColorMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// $4675 -- a star reappears near the centre, seeded by star 0's row.
|
|
||||||
static void starRespawn(StSimT *sim, uint8_t idx) {
|
|
||||||
sim->spr[idx].enable = 1u;
|
|
||||||
sim->spr[idx].msb = 0u;
|
|
||||||
sim->spr[idx].col = (uint8_t)((sim->spr[0].row & 7u) + 0xA6u);
|
|
||||||
sim->spr[idx].row = (uint8_t)(((sim->spr[0].row & 0x30u) >> 4) + 0x89u);
|
|
||||||
sim->starReload[idx] = ST_INTRO_STAR_RELOAD;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -159,21 +169,26 @@ void stIntroEnter(StSimT *sim, const StLevelT *level) {
|
||||||
sim->spr[idx].color = 0x07u;
|
sim->spr[idx].color = 0x07u;
|
||||||
sim->spr[idx].ptr = ST_INTRO_STAR_PTR;
|
sim->spr[idx].ptr = ST_INTRO_STAR_PTR;
|
||||||
sim->spr[idx].enable = 0u;
|
sim->spr[idx].enable = 0u;
|
||||||
sim->starTimer[idx] = stSimRng(sim, 0x20u);
|
// $44F8/$4500 held each star's spawn timer and reload. Nothing
|
||||||
sim->starReload[idx] = 0u;
|
// reads them now that the warp is painted rather than flown, but
|
||||||
|
// the seven RNG draws MUST stay: they advance the shared RNG and
|
||||||
|
// the level that follows draws from it, so dropping them would
|
||||||
|
// change the game that comes after the intro.
|
||||||
|
(void)stSimRng(sim, 0x20u);
|
||||||
}
|
}
|
||||||
sim->borderColor = 0u;
|
sim->borderColor = 0u;
|
||||||
sim->bgColor = 0u;
|
sim->bgColor = 0u;
|
||||||
sim->multiColorMask = 0x80u;
|
sim->multiColorMask = 0x80u;
|
||||||
marshalFrame(sim);
|
stSimMarshal(sim);
|
||||||
// $40CA: blank screen.
|
// $40CA: blank screen.
|
||||||
memset(sim->screen, ST_CHAR_SPACE, ST_SCREEN_CELLS);
|
memset(sim->screen, ST_CHAR_SPACE, ST_SCREEN_CELLS);
|
||||||
memset(sim->color, 0, ST_SCREEN_CELLS);
|
memset(sim->color, 0, ST_SCREEN_CELLS);
|
||||||
stSimDirtyAll(sim);
|
stSimDirtyAll(sim);
|
||||||
memcpy(sim->charset, stC64Charset(), sizeof(sim->charset));
|
stSimGlyphReset(sim);
|
||||||
sim->charDirtyAll = true;
|
sim->charDirtyAll = true;
|
||||||
sim->charDirtyCount = 0u;
|
sim->charDirtyCount = 0u;
|
||||||
stSimDrawText(sim, 10u, 12u, level->name, 3u);
|
stSimDrawText(sim, 10u, 12u, level->name, 3u);
|
||||||
|
paintStarfield(sim);
|
||||||
if (sim->demoMode != 0u && sim->postMortem == 0u) {
|
if (sim->demoMode != 0u && sim->postMortem == 0u) {
|
||||||
stSimDrawText(sim, 7u, 3u, kTextDemoExit, 5u);
|
stSimDrawText(sim, 7u, 3u, kTextDemoExit, 5u);
|
||||||
stSimDrawText(sim, 3u, 24u, kTextScreens, 4u);
|
stSimDrawText(sim, 3u, 24u, kTextScreens, 4u);
|
||||||
|
|
@ -187,27 +202,11 @@ void stIntroEnter(StSimT *sim, const StLevelT *level) {
|
||||||
bool stIntroStep(StSimT *sim) {
|
bool stIntroStep(StSimT *sim) {
|
||||||
uint8_t k;
|
uint8_t k;
|
||||||
|
|
||||||
for (k = 0u; k < 7u; k++) {
|
stSimMarshal(sim);
|
||||||
uint8_t idx = (uint8_t)(6u - k);
|
|
||||||
if (sim->starTimer[idx] == 0u) {
|
|
||||||
addToSpriteX(sim, idx, (uint8_t)stC64IntroStarDx(idx));
|
|
||||||
addToRow(sim, idx, (uint8_t)stC64IntroStarDy(idx));
|
|
||||||
sim->starReload[idx]--;
|
|
||||||
if (sim->starReload[idx] == 0u) {
|
|
||||||
starRespawn(sim, idx);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
sim->starTimer[idx]--;
|
|
||||||
if (sim->starTimer[idx] == 0u) {
|
|
||||||
starRespawn(sim, idx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
marshalFrame(sim);
|
|
||||||
sim->introLoopCount = (uint8_t)((sim->introLoopCount + 1u) & 3u);
|
sim->introLoopCount = (uint8_t)((sim->introLoopCount + 1u) & 3u);
|
||||||
addToRow(sim, 7u, (uint8_t)kIntroCabDy[sim->introLoopCount]);
|
addToRow(sim, 7u, (uint8_t)kIntroCabDy[sim->introLoopCount]);
|
||||||
if ((sim->introLoopCount & 1u) == 0u) {
|
if ((sim->introLoopCount & 1u) == 0u) {
|
||||||
addToSpriteX(sim, 7u, sim->introCabDx);
|
stSimSpriteAddX(sim, 7u, sim->introCabDx);
|
||||||
sim->introCabDxTimer--;
|
sim->introCabDxTimer--;
|
||||||
if (sim->introCabDxTimer == 0u) {
|
if (sim->introCabDxTimer == 0u) {
|
||||||
sim->introCabDxTimer = ST_INTRO_CAB_DX_RELOAD;
|
sim->introCabDxTimer = ST_INTRO_CAB_DX_RELOAD;
|
||||||
|
|
@ -222,7 +221,7 @@ bool stIntroStep(StSimT *sim) {
|
||||||
for (k = 0u; k < ST_HW_SPRITES; k++) {
|
for (k = 0u; k < ST_HW_SPRITES; k++) {
|
||||||
sim->spr[k].enable = 0u;
|
sim->spr[k].enable = 0u;
|
||||||
}
|
}
|
||||||
marshalFrame(sim);
|
stSimMarshal(sim);
|
||||||
sim->rngT1 = 0x06u;
|
sim->rngT1 = 0x06u;
|
||||||
sim->rngT2 = 0x17u;
|
sim->rngT2 = 0x17u;
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -237,7 +236,7 @@ void stTitleEnter(StSimT *sim, const StLevelT *title) {
|
||||||
memcpy(sim->screen, title->screen, ST_SCREEN_CELLS);
|
memcpy(sim->screen, title->screen, ST_SCREEN_CELLS);
|
||||||
memcpy(sim->color, title->color, ST_SCREEN_CELLS);
|
memcpy(sim->color, title->color, ST_SCREEN_CELLS);
|
||||||
stSimDirtyAll(sim);
|
stSimDirtyAll(sim);
|
||||||
memcpy(sim->charset, stC64Charset(), sizeof(sim->charset));
|
stSimGlyphReset(sim);
|
||||||
sim->charDirtyAll = true;
|
sim->charDirtyAll = true;
|
||||||
sim->charDirtyCount = 0u;
|
sim->charDirtyCount = 0u;
|
||||||
logoColorCycle(sim);
|
logoColorCycle(sim);
|
||||||
|
|
@ -260,7 +259,7 @@ void stTitleEnter(StSimT *sim, const StLevelT *title) {
|
||||||
sim->multiColorMask = 0x07u;
|
sim->multiColorMask = 0x07u;
|
||||||
sim->spriteMc1 = 0x07u;
|
sim->spriteMc1 = 0x07u;
|
||||||
sim->spriteMc0 = 0x02u;
|
sim->spriteMc0 = 0x02u;
|
||||||
marshalFrame(sim);
|
stSimMarshal(sim);
|
||||||
sim->stage = ST_STAGE_WAIT;
|
sim->stage = ST_STAGE_WAIT;
|
||||||
sim->introFrameCount = ST_TITLE_SPARKLE_TICKS;
|
sim->introFrameCount = ST_TITLE_SPARKLE_TICKS;
|
||||||
sim->hoverXFrac = 0u;
|
sim->hoverXFrac = 0u;
|
||||||
|
|
@ -313,7 +312,7 @@ bool stTitleTick(StSimT *sim) {
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
marshalFrame(sim);
|
stSimMarshal(sim);
|
||||||
logoFlip(sim);
|
logoFlip(sim);
|
||||||
return sim->stage == ST_STAGE_DROP_IN;
|
return sim->stage == ST_STAGE_DROP_IN;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1062,11 +1062,13 @@ static void PROBE_ATTR op_probeCleanErases(void) {
|
||||||
|
|
||||||
// Wrap a probe timeOp with counter-delta logging. The IIgs skips the
|
// Wrap a probe timeOp with counter-delta logging. The IIgs skips the
|
||||||
// wrapper entirely: its present is asm and never touches the
|
// wrapper entirely: its present is asm and never touches the
|
||||||
// counters (always zero there), and the IIgs -DUBER_PROBE build dies
|
// counters (always zero there). The IIgs -DUBER_PROBE build also used
|
||||||
// before main when this block is compiled in (bisected 2026-07-07:
|
// to die mid-run (bisected 2026-07-07, blamed on this wrapper; the real
|
||||||
// trivial-op probe launches, counter/fn-ptr wrapper variants do not,
|
// cause, found 2026-09-22, was hal.c asking MMStartUp for a NEW Memory
|
||||||
// optnone does not help -- llvm-mos issue, flagged to Scott). Direct
|
// Manager ID on every allocation until the ID pool ran dry and the
|
||||||
// timeOp calls carry the IIgs probe rows instead.
|
// gameFrameClean row's jlSurfaceCreate BRK'd -- fixed in hal.c). The
|
||||||
|
// direct-timeOp IIgs arm below is a near duplicate of this path that
|
||||||
|
// could now be folded back in.
|
||||||
#ifndef JOEYLIB_PLATFORM_IIGS
|
#ifndef JOEYLIB_PLATFORM_IIGS
|
||||||
static void PROBE_ATTR probeOp(const char *name, void (*fn)(void)) {
|
static void PROBE_ATTR probeOp(const char *name, void (*fn)(void)) {
|
||||||
uint32_t bytes0;
|
uint32_t bytes0;
|
||||||
|
|
@ -1791,10 +1793,9 @@ static void __attribute__((noinline)) runAllTests(void) {
|
||||||
// for mark-path changes. IIgs: no counter wrapper (see probeOp),
|
// for mark-path changes. IIgs: no counter wrapper (see probeOp),
|
||||||
// no P5 (mark inertness there is proven by object-code compare).
|
// no P5 (mark inertness there is proven by object-code compare).
|
||||||
#ifdef JOEYLIB_PLATFORM_IIGS
|
#ifdef JOEYLIB_PLATFORM_IIGS
|
||||||
// Direct delta logging: the probeOp fn-pointer wrapper dies before
|
// Direct delta logging (kept from the days the probeOp wrapper was
|
||||||
// main on llvm-mos (W0 bisect); direct extern reads are the
|
// blamed for the IIgs probe death; see probeOp). Same rows +
|
||||||
// bisected-safe shape. Same rows + steady-delta protocol as the
|
// steady-delta protocol as the probeOp arm below.
|
||||||
// probeOp arm below.
|
|
||||||
{
|
{
|
||||||
uint32_t probeBytes0;
|
uint32_t probeBytes0;
|
||||||
uint32_t probeRows0;
|
uint32_t probeRows0;
|
||||||
|
|
@ -1821,8 +1822,8 @@ static void __attribute__((noinline)) runAllTests(void) {
|
||||||
// SaveAndDraw rows, so each diff vs its golden row = the
|
// SaveAndDraw rows, so each diff vs its golden row = the
|
||||||
// validation the trusted entry skips. Placed BEFORE the present
|
// validation the trusted entry skips. Placed BEFORE the present
|
||||||
// pair below so the stage writes these do are re-synced to SHR
|
// pair below so the stage writes these do are re-synced to SHR
|
||||||
// before op_probeShrParity. timeOp (not probeOp) because the
|
// before op_probeShrParity. timeOp (not probeOp) to match the
|
||||||
// probeOp fn-pointer wrapper dies pre-main on llvm-mos.
|
// rest of this arm (see probeOp).
|
||||||
gProbeRestoreBackup.sprite = gSprite;
|
gProbeRestoreBackup.sprite = gSprite;
|
||||||
gProbeRestoreBackup.bytes = gProbeRestoreBackupBytes;
|
gProbeRestoreBackup.bytes = gProbeRestoreBackupBytes;
|
||||||
jlSpriteSaveUnder(gStage, gSprite, 40, 30, &gProbeRestoreBackup);
|
jlSpriteSaveUnder(gStage, gSprite, 40, 30, &gProbeRestoreBackup);
|
||||||
|
|
@ -1844,8 +1845,8 @@ static void __attribute__((noinline)) runAllTests(void) {
|
||||||
// 16-21 ms unattributed residual). Same recipe as the golden
|
// 16-21 ms unattributed residual). Same recipe as the golden
|
||||||
// row (gCleanSurface was destroyed after it): re-create with
|
// row (gCleanSurface was destroyed after it): re-create with
|
||||||
// clean == stage everywhere, run the two split rows via direct
|
// clean == stage everywhere, run the two split rows via direct
|
||||||
// timeOp (the probeOp wrapper dies pre-main on llvm-mos),
|
// timeOp (this arm does not use probeOp; see probeOp), destroy
|
||||||
// destroy again.
|
// again.
|
||||||
gCleanSurface = jlSurfaceCreate();
|
gCleanSurface = jlSurfaceCreate();
|
||||||
if (gCleanSurface != NULL) {
|
if (gCleanSurface != NULL) {
|
||||||
paintFrameStatic();
|
paintFrameStatic();
|
||||||
|
|
|
||||||
|
|
@ -68,11 +68,17 @@ bool jlAudioIsPlayingMod(void);
|
||||||
// the sample was recorded at; the engine pitches as needed for its
|
// the sample was recorded at; the engine pitches as needed for its
|
||||||
// own output rate. If the slot is currently playing, the new sample
|
// own output rate. If the slot is currently playing, the new sample
|
||||||
// replaces it.
|
// replaces it.
|
||||||
|
//
|
||||||
|
// The full -128..127 range is accepted everywhere, but the IIgs plays
|
||||||
|
// -128 as -127: its DOC reads 0x00 (which is what -128 becomes in the
|
||||||
|
// chip's unsigned format) as a wave-end marker and would halt the voice
|
||||||
|
// on that sample. One level out of 256, inaudible in practice.
|
||||||
void jlAudioPlaySfx(uint8_t slot, const uint8_t *sample, uint32_t length, uint16_t rateHz);
|
void jlAudioPlaySfx(uint8_t slot, const uint8_t *sample, uint32_t length, uint16_t rateHz);
|
||||||
|
|
||||||
// Stream-fill callback signature. Called by the mixer to refill a
|
// Stream-fill callback signature. Called by the mixer to refill a
|
||||||
// streaming slot's prefetch buffer; should write up to `count` signed
|
// streaming slot's prefetch buffer; should write up to `count` signed
|
||||||
// 8-bit samples to `dst` and return the number written. Returning 0
|
// 8-bit samples to `dst` and return the number written (see
|
||||||
|
// jlAudioPlaySfx for the one clamp the IIgs applies). Returning 0
|
||||||
// signals end-of-stream and the slot auto-deactivates. Called from
|
// signals end-of-stream and the slot auto-deactivates. Called from
|
||||||
// the same context as the audio engine's refill (main thread on
|
// the same context as the audio engine's refill (main thread on
|
||||||
// DOS/ST, audio ISR on Amiga/IIgs); must not block or allocate.
|
// DOS/ST, audio ISR on Amiga/IIgs); must not block or allocate.
|
||||||
|
|
@ -84,10 +90,55 @@ typedef uint32_t (*jlAudioStreamFillT)(void *ctx, int8_t *dst, uint32_t count);
|
||||||
// allocation. `rateHz` is the sample rate `fill` produces; the
|
// allocation. `rateHz` is the sample rate `fill` produces; the
|
||||||
// engine resamples (with linear interpolation) to its own output
|
// engine resamples (with linear interpolation) to its own output
|
||||||
// rate. If the slot is currently playing, the new stream replaces
|
// rate. If the slot is currently playing, the new stream replaces
|
||||||
// it. No-op on ports whose audio engine doesn't expose the shared
|
// it. Implemented on every port: DOS/ST/X68000 pull through the shared
|
||||||
// SFX overlay mixer.
|
// SFX overlay mixer, while the IIgs (NTPstreamsound) and Amiga
|
||||||
|
// (PTPlayer mt_playfx) play one chunk at a time and fire the next from
|
||||||
|
// jlAudioFrameTick as the current one runs down -- so on those two the
|
||||||
|
// host must be calling jlAudioFrameTick for a stream to continue past
|
||||||
|
// its first chunk, and a seam can carry up to one frame of jitter.
|
||||||
void jlAudioPlaySfxStream(uint8_t slot, jlAudioStreamFillT fill, void *ctx, uint16_t rateHz);
|
void jlAudioPlaySfxStream(uint8_t slot, jlAudioStreamFillT fill, void *ctx, uint16_t rateHz);
|
||||||
|
|
||||||
|
// Raw-format streaming: jlAudioPlaySfxStream, except `fill` delivers
|
||||||
|
// bytes already in the port's raw stream format (jlAudioSfxRawFormat),
|
||||||
|
// so the port skips its per-byte conversion of every chunk. On the IIgs
|
||||||
|
// that conversion is a 65816 byte loop over each 3.5 KB chunk -- it
|
||||||
|
// stalled Space Taxi's ride for a third of a second per chunk of speech.
|
||||||
|
// Every other port streams signed PCM natively, so there Raw is the
|
||||||
|
// plain call. A producer that cannot make the port's format uses
|
||||||
|
// jlAudioPlaySfxStream.
|
||||||
|
#define JL_SFX_RAW_SIGNED8 0u // int8_t PCM, the jlAudioPlaySfxStream contract
|
||||||
|
#define JL_SFX_RAW_UNSIGNED8_NOZERO 1u // uint8_t PCM biased 0x80; 0x00 is forbidden (DOC stop byte)
|
||||||
|
uint8_t jlAudioSfxRawFormat(void);
|
||||||
|
void jlAudioPlaySfxStreamRaw(uint8_t slot, jlAudioStreamFillT fill, void *ctx, uint16_t rateHz);
|
||||||
|
|
||||||
|
// Stereo placement for a SFX slot.
|
||||||
|
typedef enum {
|
||||||
|
AUDIO_PAN_LEFT = 0,
|
||||||
|
AUDIO_PAN_CENTER,
|
||||||
|
AUDIO_PAN_RIGHT,
|
||||||
|
|
||||||
|
AUDIO_PAN_COUNT
|
||||||
|
} jlAudioPanE;
|
||||||
|
|
||||||
|
// Place a SFX slot in the stereo field. Slots start at AUDIO_PAN_CENTER, and
|
||||||
|
// the setting sticks to the SLOT, so it survives retriggers -- set it once
|
||||||
|
// when a sound is meant to come from one side.
|
||||||
|
//
|
||||||
|
// What each port can actually do, because the hardware differs:
|
||||||
|
// IIgs all three. The DOC pans per OSCILLATOR (control bits 7:4 are the
|
||||||
|
// output channel, and per Apple TN #19 odd = left, even = right),
|
||||||
|
// so a centred sound is played on two oscillators, one per side.
|
||||||
|
// A stock IIgs sums to mono anyway; this only shows up on a stereo
|
||||||
|
// card or an emulator.
|
||||||
|
// Amiga left and right, by picking the Paula channel side (0 and 3 are
|
||||||
|
// left, 1 and 2 right). Paula cannot place one voice in both ears,
|
||||||
|
// so AUDIO_PAN_CENTER keeps PTPlayer's own channel choice.
|
||||||
|
// X68000 ignored. Everything is mixed into ONE MSM6258 ADPCM stream, so
|
||||||
|
// its pan bits would move all audio at once, not one slot.
|
||||||
|
// DOS, ST ignored -- their output is mono.
|
||||||
|
// Passing an out-of-range pan is ignored.
|
||||||
|
void jlAudioSetPan(uint8_t slot, jlAudioPanE pan);
|
||||||
|
|
||||||
// Stop a SFX slot early. No-op if the slot is already idle.
|
// Stop a SFX slot early. No-op if the slot is already idle.
|
||||||
void jlAudioStopSfx(uint8_t slot);
|
void jlAudioStopSfx(uint8_t slot);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,11 @@ void *jlAlloc(uint32_t bytes);
|
||||||
// Release a buffer returned by jlAlloc. NULL is accepted and ignored.
|
// Release a buffer returned by jlAlloc. NULL is accepted and ignored.
|
||||||
void jlFree(void *p);
|
void jlFree(void *p);
|
||||||
|
|
||||||
|
// memcpy for big buffers. Same contract as memcpy (no overlap), but the
|
||||||
|
// IIgs runs it as MVN block moves (7 cycles a byte) instead of the libc
|
||||||
|
// byte loop, split at bank boundaries by the port.
|
||||||
|
void jlMemCopy(void *dst, const void *src, uint32_t bytes);
|
||||||
|
|
||||||
// Block the calling thread until the next display vertical blank.
|
// Block the calling thread until the next display vertical blank.
|
||||||
// Used to pace game loops to the display's native refresh rate
|
// Used to pace game loops to the display's native refresh rate
|
||||||
// (~70 Hz on VGA mode 13h, ~50 or ~60 Hz on Amiga/ST PAL/NTSC, ~60 Hz
|
// (~70 Hz on VGA mode 13h, ~50 or ~60 Hz on Amiga/ST PAL/NTSC, ~60 Hz
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@
|
||||||
#define JL_HAS_STAGE_FREE_PIXELS // no-op (pinned), function
|
#define JL_HAS_STAGE_FREE_PIXELS // no-op (pinned), function
|
||||||
#define JL_HAS_FRAME_HZ // runtime 50/60 from battery RAM, function
|
#define JL_HAS_FRAME_HZ // runtime 50/60 from battery RAM, function
|
||||||
// (frameCount: port.h #defines jlpFrameCount straight to iigsGetTickWord,
|
// (frameCount: port.h #defines jlpFrameCount straight to iigsGetTickWord,
|
||||||
// the GetTick asm wrapper -- no function override to register here.)
|
// the VBL heartbeat tick read -- no function override to register here.)
|
||||||
#define JL_HAS_WAIT_VBL // $C019 VBL poll, function
|
#define JL_HAS_WAIT_VBL // $C019 VBL poll, function
|
||||||
// audio: every real port implements the full engine
|
// audio: every real port implements the full engine
|
||||||
#define JL_HAS_AUDIO_INIT
|
#define JL_HAS_AUDIO_INIT
|
||||||
|
|
@ -144,6 +144,8 @@
|
||||||
#define JL_HAS_AUDIO_IS_PLAYING_MOD
|
#define JL_HAS_AUDIO_IS_PLAYING_MOD
|
||||||
#define JL_HAS_AUDIO_PLAY_SFX
|
#define JL_HAS_AUDIO_PLAY_SFX
|
||||||
#define JL_HAS_AUDIO_PLAY_SFX_STREAM
|
#define JL_HAS_AUDIO_PLAY_SFX_STREAM
|
||||||
|
#define JL_HAS_AUDIO_PLAY_SFX_STREAM_RAW
|
||||||
|
#define JL_HAS_AUDIO_SET_PAN
|
||||||
#define JL_HAS_AUDIO_STOP_SFX
|
#define JL_HAS_AUDIO_STOP_SFX
|
||||||
#define JL_HAS_AUDIO_TONE
|
#define JL_HAS_AUDIO_TONE
|
||||||
#define JL_HAS_AUDIO_VOICE
|
#define JL_HAS_AUDIO_VOICE
|
||||||
|
|
@ -216,6 +218,7 @@
|
||||||
#define JL_HAS_AUDIO_IS_PLAYING_MOD
|
#define JL_HAS_AUDIO_IS_PLAYING_MOD
|
||||||
#define JL_HAS_AUDIO_PLAY_SFX
|
#define JL_HAS_AUDIO_PLAY_SFX
|
||||||
#define JL_HAS_AUDIO_PLAY_SFX_STREAM
|
#define JL_HAS_AUDIO_PLAY_SFX_STREAM
|
||||||
|
#define JL_HAS_AUDIO_SET_PAN
|
||||||
#define JL_HAS_AUDIO_STOP_SFX
|
#define JL_HAS_AUDIO_STOP_SFX
|
||||||
#define JL_HAS_AUDIO_TONE
|
#define JL_HAS_AUDIO_TONE
|
||||||
#define JL_HAS_AUDIO_VOICE
|
#define JL_HAS_AUDIO_VOICE
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ ADV2_SRC := $(EXAMPLES)/adventure2/adventure2.c
|
||||||
ADV2_BIN := $(BINDIR)/Adv2
|
ADV2_BIN := $(BINDIR)/Adv2
|
||||||
AGI_SRCS := $(EXAMPLES)/agi/agi.c $(EXAMPLES)/agi/agiRes.c $(EXAMPLES)/agi/agiPic.c $(EXAMPLES)/agi/agiView.c $(EXAMPLES)/agi/agiVm.c $(EXAMPLES)/agi/agiObj.c $(EXAMPLES)/agi/agiText.c
|
AGI_SRCS := $(EXAMPLES)/agi/agi.c $(EXAMPLES)/agi/agiRes.c $(EXAMPLES)/agi/agiPic.c $(EXAMPLES)/agi/agiView.c $(EXAMPLES)/agi/agiVm.c $(EXAMPLES)/agi/agiObj.c $(EXAMPLES)/agi/agiText.c
|
||||||
AGI_BIN := $(BINDIR)/Agi
|
AGI_BIN := $(BINDIR)/Agi
|
||||||
STAXI_SRCS := $(EXAMPLES)/spacetaxi/spacetaxi.c $(EXAMPLES)/spacetaxi/stLevel.c $(EXAMPLES)/spacetaxi/stLevelFile.c $(EXAMPLES)/spacetaxi/stRender.c $(EXAMPLES)/spacetaxi/stSim.c $(EXAMPLES)/spacetaxi/stFare.c $(EXAMPLES)/spacetaxi/stHooks.c $(EXAMPLES)/spacetaxi/stTitle.c $(EXAMPLES)/spacetaxi/stC64Data.c $(EXAMPLES)/spacetaxi/stCels.c $(EXAMPLES)/spacetaxi/stAudio.c
|
STAXI_SRCS := $(EXAMPLES)/spacetaxi/spacetaxi.c $(EXAMPLES)/spacetaxi/stLevel.c $(EXAMPLES)/spacetaxi/stLevelFile.c $(EXAMPLES)/spacetaxi/stRender.c $(EXAMPLES)/spacetaxi/stSim.c $(EXAMPLES)/spacetaxi/stFare.c $(EXAMPLES)/spacetaxi/stHooks.c $(EXAMPLES)/spacetaxi/stTitle.c $(EXAMPLES)/spacetaxi/stC64Data.c $(EXAMPLES)/spacetaxi/stCels.c $(EXAMPLES)/spacetaxi/stAudio.c $(EXAMPLES)/spacetaxi/stLevelCels.c
|
||||||
STAXI_INSTALL_DIR := $(BINDIR)/Taxi
|
STAXI_INSTALL_DIR := $(BINDIR)/Taxi
|
||||||
STAXI_BIN := $(STAXI_INSTALL_DIR)/Taxi
|
STAXI_BIN := $(STAXI_INSTALL_DIR)/Taxi
|
||||||
|
|
||||||
|
|
@ -130,6 +130,24 @@ SPRITEBAKE_BIN := $(REPO_DIR)/build/tools/spritebake-amiga
|
||||||
# stCels.c + C64 bitmaps) and compiled to a .spc by the host spritebake, so
|
# stCels.c + C64 bitmaps) and compiled to a .spc by the host spritebake, so
|
||||||
# STAXI loads its cels already compiled instead of JIT-compiling them at boot.
|
# STAXI loads its cels already compiled instead of JIT-compiling them at boot.
|
||||||
STAXIBAKE_BIN := $(REPO_DIR)/build/tools/staxibake
|
STAXIBAKE_BIN := $(REPO_DIR)/build/tools/staxibake
|
||||||
|
|
||||||
|
# Per-level hook-sprite banks: the cels stLevelCelList names for each
|
||||||
|
# level that has hook sprites, baked from its level file and pre-compiled
|
||||||
|
# for this target, so no cel is JIT-compiled mid-ride. Levels without
|
||||||
|
# hook sprites have no bank; the game JIT-compiles at level entry when a
|
||||||
|
# bank is missing.
|
||||||
|
STAXI_CEL_LEVELS := 07 08 09 10 12 16 17 21
|
||||||
|
STAXILEVEL_SPR_GEN := $(foreach n,$(STAXI_CEL_LEVELS),$(STAXI_GEN_DIR)/sprites/level$(n).spr)
|
||||||
|
STAXILEVEL_SPC_GEN := $(STAXILEVEL_SPR_GEN:.spr=.spc)
|
||||||
|
STAXILEVEL_SPC_RUN := $(patsubst $(STAXI_GEN_DIR)/%,$(STAXI_RUN_DIR)/%,$(STAXILEVEL_SPC_GEN))
|
||||||
|
STAXI_ASSET_DSTS += $(STAXILEVEL_SPC_RUN)
|
||||||
|
|
||||||
|
# Speech baked to sample bytes per passenger pitch (assets/genSpeechPcm.py):
|
||||||
|
# expanding bits at play time is what keeps "HEY TAXI" from stalling the
|
||||||
|
# ride; the C64 run-length source stays in stSpeechData.c as the input.
|
||||||
|
STAXI_SPEECH_GEN := $(STAXI_GEN_DIR)/speech.bin
|
||||||
|
STAXI_SPEECH_RUN := $(STAXI_RUN_DIR)/speech.bin
|
||||||
|
STAXI_ASSET_DSTS += $(STAXI_SPEECH_RUN)
|
||||||
STAXICELS_SPR_GEN := $(STAXI_GEN_DIR)/sprites/staxicels.spr
|
STAXICELS_SPR_GEN := $(STAXI_GEN_DIR)/sprites/staxicels.spr
|
||||||
STAXICELS_SPC_GEN := $(STAXICELS_SPR_GEN:.spr=.spc)
|
STAXICELS_SPC_GEN := $(STAXICELS_SPR_GEN:.spr=.spc)
|
||||||
STAXICELS_SPC_RUN := $(patsubst $(STAXI_GEN_DIR)/%,$(STAXI_RUN_DIR)/%,$(STAXICELS_SPC_GEN))
|
STAXICELS_SPC_RUN := $(patsubst $(STAXI_GEN_DIR)/%,$(STAXI_RUN_DIR)/%,$(STAXICELS_SPC_GEN))
|
||||||
|
|
@ -251,7 +269,7 @@ $(STAXI_BIN): $(STAXI_SRCS) $(LIB)
|
||||||
|
|
||||||
# Space Taxi asset pipeline. .SECONDARY keeps the generated/ artifacts
|
# Space Taxi asset pipeline. .SECONDARY keeps the generated/ artifacts
|
||||||
# from being auto-deleted after staging.
|
# from being auto-deleted after staging.
|
||||||
.SECONDARY: $(STAXI_LEVEL_GEN) $(STAXI_TBK_GEN) $(STAXI_SPR_GEN) $(STAXI_SPC_GEN) $(STAXICELS_SPC_GEN)
|
.SECONDARY: $(STAXI_LEVEL_GEN) $(STAXI_TBK_GEN) $(STAXI_SPR_GEN) $(STAXI_SPC_GEN) $(STAXICELS_SPC_GEN) $(STAXILEVEL_SPR_GEN) $(STAXILEVEL_SPC_GEN)
|
||||||
|
|
||||||
$(STAXI_ROM_LEVEL_GEN) &: $(STAXI_RAW_DUMP) $(ROMTOLEVEL_BIN)
|
$(STAXI_ROM_LEVEL_GEN) &: $(STAXI_RAW_DUMP) $(ROMTOLEVEL_BIN)
|
||||||
@mkdir -p $(STAXI_GEN_DIR)/levels
|
@mkdir -p $(STAXI_GEN_DIR)/levels
|
||||||
|
|
@ -318,3 +336,16 @@ clean: clean-amiga
|
||||||
# the .c files that include it, leaving a frankenstein binary where
|
# the .c files that include it, leaving a frankenstein binary where
|
||||||
# different TUs see different struct layouts.
|
# different TUs see different struct layouts.
|
||||||
-include $(LIB_OBJS:.o=.d)
|
-include $(LIB_OBJS:.o=.d)
|
||||||
|
|
||||||
|
# Shorter stem than the %.spr-from-%.png pattern, so it wins for level banks.
|
||||||
|
$(STAXI_GEN_DIR)/sprites/level%.spr: $(STAXI_GEN_DIR)/levels/level%.dat $(STAXIBAKE_BIN) $(EXAMPLES)/spacetaxi/stLevelCels.c
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(STAXIBAKE_BIN) --level $< $@
|
||||||
|
|
||||||
|
# Speech bake rules live down here so they never become the default goal.
|
||||||
|
$(STAXI_SPEECH_GEN): $(EXAMPLES)/spacetaxi/stSpeechData.c $(EXAMPLES)/spacetaxi/assets/genSpeechPcm.py
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
python3 $(EXAMPLES)/spacetaxi/assets/genSpeechPcm.py $< $@
|
||||||
|
$(STAXI_SPEECH_RUN): $(STAXI_SPEECH_GEN)
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
cp $< $@
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ ADV2_SRC := $(EXAMPLES)/adventure2/adventure2.c
|
||||||
ADV2_BIN := $(BINDIR)/ADV2.PRG
|
ADV2_BIN := $(BINDIR)/ADV2.PRG
|
||||||
AGI_SRCS := $(EXAMPLES)/agi/agi.c $(EXAMPLES)/agi/agiRes.c $(EXAMPLES)/agi/agiPic.c $(EXAMPLES)/agi/agiView.c $(EXAMPLES)/agi/agiVm.c $(EXAMPLES)/agi/agiObj.c $(EXAMPLES)/agi/agiText.c
|
AGI_SRCS := $(EXAMPLES)/agi/agi.c $(EXAMPLES)/agi/agiRes.c $(EXAMPLES)/agi/agiPic.c $(EXAMPLES)/agi/agiView.c $(EXAMPLES)/agi/agiVm.c $(EXAMPLES)/agi/agiObj.c $(EXAMPLES)/agi/agiText.c
|
||||||
AGI_BIN := $(BINDIR)/AGI.PRG
|
AGI_BIN := $(BINDIR)/AGI.PRG
|
||||||
STAXI_SRCS := $(EXAMPLES)/spacetaxi/spacetaxi.c $(EXAMPLES)/spacetaxi/stLevel.c $(EXAMPLES)/spacetaxi/stLevelFile.c $(EXAMPLES)/spacetaxi/stRender.c $(EXAMPLES)/spacetaxi/stSim.c $(EXAMPLES)/spacetaxi/stFare.c $(EXAMPLES)/spacetaxi/stHooks.c $(EXAMPLES)/spacetaxi/stTitle.c $(EXAMPLES)/spacetaxi/stC64Data.c $(EXAMPLES)/spacetaxi/stCels.c $(EXAMPLES)/spacetaxi/stAudio.c
|
STAXI_SRCS := $(EXAMPLES)/spacetaxi/spacetaxi.c $(EXAMPLES)/spacetaxi/stLevel.c $(EXAMPLES)/spacetaxi/stLevelFile.c $(EXAMPLES)/spacetaxi/stRender.c $(EXAMPLES)/spacetaxi/stSim.c $(EXAMPLES)/spacetaxi/stFare.c $(EXAMPLES)/spacetaxi/stHooks.c $(EXAMPLES)/spacetaxi/stTitle.c $(EXAMPLES)/spacetaxi/stC64Data.c $(EXAMPLES)/spacetaxi/stCels.c $(EXAMPLES)/spacetaxi/stAudio.c $(EXAMPLES)/spacetaxi/stLevelCels.c
|
||||||
STAXI_INSTALL_DIR := $(BINDIR)/STAXI
|
STAXI_INSTALL_DIR := $(BINDIR)/STAXI
|
||||||
STAXI_BIN := $(STAXI_INSTALL_DIR)/STAXI.PRG
|
STAXI_BIN := $(STAXI_INSTALL_DIR)/STAXI.PRG
|
||||||
|
|
||||||
|
|
@ -110,6 +110,24 @@ SPRITEBAKE_BIN := $(REPO_DIR)/build/tools/spritebake-atarist
|
||||||
# stCels.c + C64 bitmaps) and compiled to a .spc by the host spritebake, so
|
# stCels.c + C64 bitmaps) and compiled to a .spc by the host spritebake, so
|
||||||
# STAXI loads its cels already compiled instead of JIT-compiling them at boot.
|
# STAXI loads its cels already compiled instead of JIT-compiling them at boot.
|
||||||
STAXIBAKE_BIN := $(REPO_DIR)/build/tools/staxibake
|
STAXIBAKE_BIN := $(REPO_DIR)/build/tools/staxibake
|
||||||
|
|
||||||
|
# Per-level hook-sprite banks: the cels stLevelCelList names for each
|
||||||
|
# level that has hook sprites, baked from its level file and pre-compiled
|
||||||
|
# for this target, so no cel is JIT-compiled mid-ride. Levels without
|
||||||
|
# hook sprites have no bank; the game JIT-compiles at level entry when a
|
||||||
|
# bank is missing.
|
||||||
|
STAXI_CEL_LEVELS := 07 08 09 10 12 16 17 21
|
||||||
|
STAXILEVEL_SPR_GEN := $(foreach n,$(STAXI_CEL_LEVELS),$(STAXI_GEN_DIR)/sprites/level$(n).spr)
|
||||||
|
STAXILEVEL_SPC_GEN := $(STAXILEVEL_SPR_GEN:.spr=.spc)
|
||||||
|
STAXILEVEL_SPC_RUN := $(patsubst $(STAXI_GEN_DIR)/%,$(STAXI_RUN_DIR)/%,$(STAXILEVEL_SPC_GEN))
|
||||||
|
STAXI_ASSET_DSTS += $(STAXILEVEL_SPC_RUN)
|
||||||
|
|
||||||
|
# Speech baked to sample bytes per passenger pitch (assets/genSpeechPcm.py):
|
||||||
|
# expanding bits at play time is what keeps "HEY TAXI" from stalling the
|
||||||
|
# ride; the C64 run-length source stays in stSpeechData.c as the input.
|
||||||
|
STAXI_SPEECH_GEN := $(STAXI_GEN_DIR)/speech.bin
|
||||||
|
STAXI_SPEECH_RUN := $(STAXI_RUN_DIR)/speech.bin
|
||||||
|
STAXI_ASSET_DSTS += $(STAXI_SPEECH_RUN)
|
||||||
STAXICELS_SPR_GEN := $(STAXI_GEN_DIR)/sprites/staxicels.spr
|
STAXICELS_SPR_GEN := $(STAXI_GEN_DIR)/sprites/staxicels.spr
|
||||||
STAXICELS_SPC_GEN := $(STAXICELS_SPR_GEN:.spr=.spc)
|
STAXICELS_SPC_GEN := $(STAXICELS_SPR_GEN:.spr=.spc)
|
||||||
STAXICELS_SPC_RUN := $(patsubst $(STAXI_GEN_DIR)/%,$(STAXI_RUN_DIR)/%,$(STAXICELS_SPC_GEN))
|
STAXICELS_SPC_RUN := $(patsubst $(STAXI_GEN_DIR)/%,$(STAXI_RUN_DIR)/%,$(STAXICELS_SPC_GEN))
|
||||||
|
|
@ -237,7 +255,7 @@ $(STAXI_BIN): $(STAXI_SRCS) $(LIB) $(LIBXMP_AR)
|
||||||
$(ST_CC) $(CFLAGS) $(STAXI_SRCS) $(LIB) $(LIBXMP_AR) -o $@ $(LDFLAGS)
|
$(ST_CC) $(CFLAGS) $(STAXI_SRCS) $(LIB) $(LIBXMP_AR) -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
# Space Taxi asset pipeline (mirrors dos.mk + amiga.mk).
|
# Space Taxi asset pipeline (mirrors dos.mk + amiga.mk).
|
||||||
.SECONDARY: $(STAXI_LEVEL_GEN) $(STAXI_TBK_GEN) $(STAXI_SPR_GEN) $(STAXI_SPC_GEN) $(STAXICELS_SPC_GEN)
|
.SECONDARY: $(STAXI_LEVEL_GEN) $(STAXI_TBK_GEN) $(STAXI_SPR_GEN) $(STAXI_SPC_GEN) $(STAXICELS_SPC_GEN) $(STAXILEVEL_SPR_GEN) $(STAXILEVEL_SPC_GEN)
|
||||||
|
|
||||||
$(STAXI_ROM_LEVEL_GEN) &: $(STAXI_RAW_DUMP) $(ROMTOLEVEL_BIN)
|
$(STAXI_ROM_LEVEL_GEN) &: $(STAXI_RAW_DUMP) $(ROMTOLEVEL_BIN)
|
||||||
@mkdir -p $(STAXI_GEN_DIR)/levels
|
@mkdir -p $(STAXI_GEN_DIR)/levels
|
||||||
|
|
@ -304,3 +322,16 @@ clean: clean-atarist
|
||||||
# the .c files that include it, leaving a frankenstein binary where
|
# the .c files that include it, leaving a frankenstein binary where
|
||||||
# different TUs see different struct layouts.
|
# different TUs see different struct layouts.
|
||||||
-include $(LIB_OBJS:.o=.d)
|
-include $(LIB_OBJS:.o=.d)
|
||||||
|
|
||||||
|
# Shorter stem than the %.spr-from-%.png pattern, so it wins for level banks.
|
||||||
|
$(STAXI_GEN_DIR)/sprites/level%.spr: $(STAXI_GEN_DIR)/levels/level%.dat $(STAXIBAKE_BIN) $(EXAMPLES)/spacetaxi/stLevelCels.c
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(STAXIBAKE_BIN) --level $< $@
|
||||||
|
|
||||||
|
# Speech bake rules live down here so they never become the default goal.
|
||||||
|
$(STAXI_SPEECH_GEN): $(EXAMPLES)/spacetaxi/stSpeechData.c $(EXAMPLES)/spacetaxi/assets/genSpeechPcm.py
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
python3 $(EXAMPLES)/spacetaxi/assets/genSpeechPcm.py $< $@
|
||||||
|
$(STAXI_SPEECH_RUN): $(STAXI_SPEECH_GEN)
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
cp $< $@
|
||||||
|
|
|
||||||
35
make/dos.mk
35
make/dos.mk
|
|
@ -71,7 +71,7 @@ ADV2_SRC := $(EXAMPLES)/adventure2/adventure2.c
|
||||||
ADV2_BIN := $(BINDIR)/ADV2.EXE
|
ADV2_BIN := $(BINDIR)/ADV2.EXE
|
||||||
AGI_SRCS := $(EXAMPLES)/agi/agi.c $(EXAMPLES)/agi/agiRes.c $(EXAMPLES)/agi/agiPic.c $(EXAMPLES)/agi/agiView.c $(EXAMPLES)/agi/agiVm.c $(EXAMPLES)/agi/agiObj.c $(EXAMPLES)/agi/agiText.c
|
AGI_SRCS := $(EXAMPLES)/agi/agi.c $(EXAMPLES)/agi/agiRes.c $(EXAMPLES)/agi/agiPic.c $(EXAMPLES)/agi/agiView.c $(EXAMPLES)/agi/agiVm.c $(EXAMPLES)/agi/agiObj.c $(EXAMPLES)/agi/agiText.c
|
||||||
AGI_BIN := $(BINDIR)/AGI.EXE
|
AGI_BIN := $(BINDIR)/AGI.EXE
|
||||||
STAXI_SRCS := $(EXAMPLES)/spacetaxi/spacetaxi.c $(EXAMPLES)/spacetaxi/stLevel.c $(EXAMPLES)/spacetaxi/stLevelFile.c $(EXAMPLES)/spacetaxi/stRender.c $(EXAMPLES)/spacetaxi/stSim.c $(EXAMPLES)/spacetaxi/stFare.c $(EXAMPLES)/spacetaxi/stHooks.c $(EXAMPLES)/spacetaxi/stTitle.c $(EXAMPLES)/spacetaxi/stC64Data.c $(EXAMPLES)/spacetaxi/stCels.c $(EXAMPLES)/spacetaxi/stAudio.c
|
STAXI_SRCS := $(EXAMPLES)/spacetaxi/spacetaxi.c $(EXAMPLES)/spacetaxi/stLevel.c $(EXAMPLES)/spacetaxi/stLevelFile.c $(EXAMPLES)/spacetaxi/stRender.c $(EXAMPLES)/spacetaxi/stSim.c $(EXAMPLES)/spacetaxi/stFare.c $(EXAMPLES)/spacetaxi/stHooks.c $(EXAMPLES)/spacetaxi/stTitle.c $(EXAMPLES)/spacetaxi/stC64Data.c $(EXAMPLES)/spacetaxi/stCels.c $(EXAMPLES)/spacetaxi/stAudio.c $(EXAMPLES)/spacetaxi/stLevelCels.c
|
||||||
# JoeyLib install convention: each app lives in its own bin subdir
|
# JoeyLib install convention: each app lives in its own bin subdir
|
||||||
# with a DATA/ folder alongside the binary for runtime assets.
|
# with a DATA/ folder alongside the binary for runtime assets.
|
||||||
STAXI_INSTALL_DIR := $(BINDIR)/STAXI
|
STAXI_INSTALL_DIR := $(BINDIR)/STAXI
|
||||||
|
|
@ -131,6 +131,24 @@ SPRITEBAKE_BIN := $(REPO_DIR)/build/tools/spritebake-dos
|
||||||
# stCels.c + C64 bitmaps) and compiled to a .spc by the host spritebake, so
|
# stCels.c + C64 bitmaps) and compiled to a .spc by the host spritebake, so
|
||||||
# STAXI loads its cels already compiled instead of JIT-compiling them at boot.
|
# STAXI loads its cels already compiled instead of JIT-compiling them at boot.
|
||||||
STAXIBAKE_BIN := $(REPO_DIR)/build/tools/staxibake
|
STAXIBAKE_BIN := $(REPO_DIR)/build/tools/staxibake
|
||||||
|
|
||||||
|
# Per-level hook-sprite banks: the cels stLevelCelList names for each
|
||||||
|
# level that has hook sprites, baked from its level file and pre-compiled
|
||||||
|
# for this target, so no cel is JIT-compiled mid-ride. Levels without
|
||||||
|
# hook sprites have no bank; the game JIT-compiles at level entry when a
|
||||||
|
# bank is missing.
|
||||||
|
STAXI_CEL_LEVELS := 07 08 09 10 12 16 17 21
|
||||||
|
STAXILEVEL_SPR_GEN := $(foreach n,$(STAXI_CEL_LEVELS),$(STAXI_GEN_DIR)/sprites/level$(n).spr)
|
||||||
|
STAXILEVEL_SPC_GEN := $(STAXILEVEL_SPR_GEN:.spr=.spc)
|
||||||
|
STAXILEVEL_SPC_RUN := $(patsubst $(STAXI_GEN_DIR)/%,$(STAXI_RUN_DIR)/%,$(STAXILEVEL_SPC_GEN))
|
||||||
|
STAXI_ASSET_DSTS += $(STAXILEVEL_SPC_RUN)
|
||||||
|
|
||||||
|
# Speech baked to sample bytes per passenger pitch (assets/genSpeechPcm.py):
|
||||||
|
# expanding bits at play time is what keeps "HEY TAXI" from stalling the
|
||||||
|
# ride; the C64 run-length source stays in stSpeechData.c as the input.
|
||||||
|
STAXI_SPEECH_GEN := $(STAXI_GEN_DIR)/speech.bin
|
||||||
|
STAXI_SPEECH_RUN := $(STAXI_RUN_DIR)/speech.bin
|
||||||
|
STAXI_ASSET_DSTS += $(STAXI_SPEECH_RUN)
|
||||||
STAXICELS_SPR_GEN := $(STAXI_GEN_DIR)/sprites/staxicels.spr
|
STAXICELS_SPR_GEN := $(STAXI_GEN_DIR)/sprites/staxicels.spr
|
||||||
STAXICELS_SPC_GEN := $(STAXICELS_SPR_GEN:.spr=.spc)
|
STAXICELS_SPC_GEN := $(STAXICELS_SPR_GEN:.spr=.spc)
|
||||||
STAXICELS_SPC_RUN := $(patsubst $(STAXI_GEN_DIR)/%,$(STAXI_RUN_DIR)/%,$(STAXICELS_SPC_GEN))
|
STAXICELS_SPC_RUN := $(patsubst $(STAXI_GEN_DIR)/%,$(STAXI_RUN_DIR)/%,$(STAXICELS_SPC_GEN))
|
||||||
|
|
@ -253,7 +271,7 @@ $(STAXI_BIN): $(STAXI_SRCS) $(LIB) $(LIBXMP_AR)
|
||||||
# Mark these .SECONDARY so make doesn't auto-delete them after the
|
# Mark these .SECONDARY so make doesn't auto-delete them after the
|
||||||
# stage-B copy into bin/ -- they're the canonical distribution
|
# stage-B copy into bin/ -- they're the canonical distribution
|
||||||
# artifacts.
|
# artifacts.
|
||||||
.SECONDARY: $(STAXI_LEVEL_GEN) $(STAXI_TBK_GEN) $(STAXI_SPR_GEN) $(STAXI_SPC_GEN) $(STAXICELS_SPC_GEN)
|
.SECONDARY: $(STAXI_LEVEL_GEN) $(STAXI_TBK_GEN) $(STAXI_SPR_GEN) $(STAXI_SPC_GEN) $(STAXICELS_SPC_GEN) $(STAXILEVEL_SPR_GEN) $(STAXILEVEL_SPC_GEN)
|
||||||
|
|
||||||
# Bulk-extract all 24 canonical levels plus the title from the C64
|
# Bulk-extract all 24 canonical levels plus the title from the C64
|
||||||
# ROM in one invocation. Grouped target (&:) means make treats the
|
# ROM in one invocation. Grouped target (&:) means make treats the
|
||||||
|
|
@ -335,3 +353,16 @@ clean-spacetaxi:
|
||||||
# the .c files that include it, leaving a frankenstein binary where
|
# the .c files that include it, leaving a frankenstein binary where
|
||||||
# different TUs see different struct layouts.
|
# different TUs see different struct layouts.
|
||||||
-include $(LIB_OBJS:.o=.d)
|
-include $(LIB_OBJS:.o=.d)
|
||||||
|
|
||||||
|
# Shorter stem than the %.spr-from-%.png pattern, so it wins for level banks.
|
||||||
|
$(STAXI_GEN_DIR)/sprites/level%.spr: $(STAXI_GEN_DIR)/levels/level%.dat $(STAXIBAKE_BIN) $(EXAMPLES)/spacetaxi/stLevelCels.c
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(STAXIBAKE_BIN) --level $< $@
|
||||||
|
|
||||||
|
# Speech bake rules live down here so they never become the default goal.
|
||||||
|
$(STAXI_SPEECH_GEN): $(EXAMPLES)/spacetaxi/stSpeechData.c $(EXAMPLES)/spacetaxi/assets/genSpeechPcm.py
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
python3 $(EXAMPLES)/spacetaxi/assets/genSpeechPcm.py $< $@
|
||||||
|
$(STAXI_SPEECH_RUN): $(STAXI_SPEECH_GEN)
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
cp $< $@
|
||||||
|
|
|
||||||
37
make/iigs.mk
37
make/iigs.mk
|
|
@ -66,7 +66,7 @@ UBER_SRC := $(EXAMPLES)/uber/uber.c
|
||||||
ADV_SRC := $(EXAMPLES)/adventure/adventure.c
|
ADV_SRC := $(EXAMPLES)/adventure/adventure.c
|
||||||
ADV2_SRC := $(EXAMPLES)/adventure2/adventure2.c
|
ADV2_SRC := $(EXAMPLES)/adventure2/adventure2.c
|
||||||
AGI_SRCS := $(EXAMPLES)/agi/agi.c $(EXAMPLES)/agi/agiRes.c $(EXAMPLES)/agi/agiPic.c $(EXAMPLES)/agi/agiView.c $(EXAMPLES)/agi/agiVm.c $(EXAMPLES)/agi/agiObj.c $(EXAMPLES)/agi/agiText.c
|
AGI_SRCS := $(EXAMPLES)/agi/agi.c $(EXAMPLES)/agi/agiRes.c $(EXAMPLES)/agi/agiPic.c $(EXAMPLES)/agi/agiView.c $(EXAMPLES)/agi/agiVm.c $(EXAMPLES)/agi/agiObj.c $(EXAMPLES)/agi/agiText.c
|
||||||
STAXI_SRCS := $(EXAMPLES)/spacetaxi/stC64Data.c $(EXAMPLES)/spacetaxi/stCels.c $(EXAMPLES)/spacetaxi/stSim.c $(EXAMPLES)/spacetaxi/spacetaxi.c $(EXAMPLES)/spacetaxi/stLevel.c $(EXAMPLES)/spacetaxi/stLevelFile.c $(EXAMPLES)/spacetaxi/stRender.c $(EXAMPLES)/spacetaxi/stFare.c $(EXAMPLES)/spacetaxi/stHooks.c $(EXAMPLES)/spacetaxi/stTitle.c $(EXAMPLES)/spacetaxi/stAudio.c
|
STAXI_SRCS := $(EXAMPLES)/spacetaxi/stC64Data.c $(EXAMPLES)/spacetaxi/stCels.c $(EXAMPLES)/spacetaxi/stSim.c $(EXAMPLES)/spacetaxi/spacetaxi.c $(EXAMPLES)/spacetaxi/stLevel.c $(EXAMPLES)/spacetaxi/stLevelFile.c $(EXAMPLES)/spacetaxi/stRender.c $(EXAMPLES)/spacetaxi/stFare.c $(EXAMPLES)/spacetaxi/stHooks.c $(EXAMPLES)/spacetaxi/stTitle.c $(EXAMPLES)/spacetaxi/stAudio.c $(EXAMPLES)/spacetaxi/stLevelCels.c $(EXAMPLES)/spacetaxi/stMarshalIigs.s
|
||||||
AUDIO_SRC := $(EXAMPLES)/audio/audio.c
|
AUDIO_SRC := $(EXAMPLES)/audio/audio.c
|
||||||
|
|
||||||
# NinjaTrackerPlus replayer: Merlin32-assemble ninjatrackerplus.s to a 34KB
|
# NinjaTrackerPlus replayer: Merlin32-assemble ninjatrackerplus.s to a 34KB
|
||||||
|
|
@ -113,6 +113,17 @@ iigs-examples: $(BINDIR)/PATTERN $(BINDIR)/DRAW $(BINDIR)/KEYS $(BINDIR)/SERIAL
|
||||||
# (jlSpriteBankLoadPrecompiled) instead of JIT-compiling every cel.
|
# (jlSpriteBankLoadPrecompiled) instead of JIT-compiling every cel.
|
||||||
STAXICELS_SPR := $(EXAMPLES)/spacetaxi/generated/iigs/sprites/staxicels.spr
|
STAXICELS_SPR := $(EXAMPLES)/spacetaxi/generated/iigs/sprites/staxicels.spr
|
||||||
STAXI_IIGS_SPC := $(STAXICELS_SPR:.spr=.spc)
|
STAXI_IIGS_SPC := $(STAXICELS_SPR:.spr=.spc)
|
||||||
|
# Per-level hook-sprite banks (see make/dos.mk): baked from the level file
|
||||||
|
# by staxibake --level, pre-compiled for the 65816. make-iigs-disk.sh copies
|
||||||
|
# them onto the floppy last and only while there is room; a missing bank
|
||||||
|
# makes the game JIT-compile that level's cels at entry instead.
|
||||||
|
STAXI_CEL_LEVELS := 07 08 09 10 12 16 17 21
|
||||||
|
STAXILEVEL_IIGS_SPC := $(foreach n,$(STAXI_CEL_LEVELS),$(EXAMPLES)/spacetaxi/generated/iigs/sprites/level$(n).spc)
|
||||||
|
# Speech baked to 1-bit PCM per passenger pitch (assets/genSpeechPcm.py);
|
||||||
|
# make-iigs-disk.sh picks it up from the generated tree like font.tbk.
|
||||||
|
STAXI_IIGS_SPEECH := $(EXAMPLES)/spacetaxi/generated/iigs/speech.bin
|
||||||
|
$(STAXI_IIGS_SPEECH): $(EXAMPLES)/spacetaxi/stSpeechData.c $(EXAMPLES)/spacetaxi/assets/genSpeechPcm.py
|
||||||
|
python3 $(EXAMPLES)/spacetaxi/assets/genSpeechPcm.py $< $@ --biased
|
||||||
SPRITEBAKE_IIGS := $(REPO_DIR)/build/tools/spritebake-iigs
|
SPRITEBAKE_IIGS := $(REPO_DIR)/build/tools/spritebake-iigs
|
||||||
STAXIBAKE := $(REPO_DIR)/build/tools/staxibake
|
STAXIBAKE := $(REPO_DIR)/build/tools/staxibake
|
||||||
|
|
||||||
|
|
@ -126,10 +137,14 @@ $(STAXICELS_SPR): $(STAXIBAKE) $(EXAMPLES)/spacetaxi/stCels.c $(EXAMPLES)/spacet
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
$(STAXIBAKE) $@
|
$(STAXIBAKE) $@
|
||||||
|
|
||||||
|
$(EXAMPLES)/spacetaxi/generated/iigs/sprites/level%.spr: $(EXAMPLES)/spacetaxi/generated/iigs/levels/level%.dat $(STAXIBAKE) $(EXAMPLES)/spacetaxi/stLevelCels.c
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(STAXIBAKE) --level $< $@
|
||||||
|
|
||||||
$(EXAMPLES)/spacetaxi/generated/iigs/sprites/%.spc: $(EXAMPLES)/spacetaxi/generated/iigs/sprites/%.spr $(SPRITEBAKE_IIGS)
|
$(EXAMPLES)/spacetaxi/generated/iigs/sprites/%.spc: $(EXAMPLES)/spacetaxi/generated/iigs/sprites/%.spr $(SPRITEBAKE_IIGS)
|
||||||
$(SPRITEBAKE_IIGS) $< $@
|
$(SPRITEBAKE_IIGS) $< $@
|
||||||
|
|
||||||
iigs-disk: iigs-examples $(NTP_BIN) $(STAXI_IIGS_SPC)
|
iigs-disk: iigs-examples $(NTP_BIN) $(STAXI_IIGS_SPC) $(STAXILEVEL_IIGS_SPC) $(STAXI_IIGS_SPEECH)
|
||||||
BINDIR="$(BINDIR)" NTP_BIN="$(NTP_BIN)" $(REPO_DIR)/scripts/make-iigs-disk.sh $(BINDIR)/joey.2mg
|
BINDIR="$(BINDIR)" NTP_BIN="$(NTP_BIN)" $(REPO_DIR)/scripts/make-iigs-disk.sh $(BINDIR)/joey.2mg
|
||||||
|
|
||||||
# Every launchable example on one image. The full set (~1.7 MB of binaries
|
# Every launchable example on one image. The full set (~1.7 MB of binaries
|
||||||
|
|
@ -137,7 +152,7 @@ iigs-disk: iigs-examples $(NTP_BIN) $(STAXI_IIGS_SPC)
|
||||||
# ProDOS hard-disk image (joey-all.2mg): boot it in MAME with
|
# ProDOS hard-disk image (joey-all.2mg): boot it in MAME with
|
||||||
# `-sl7 cffa2 -hard1 <GS/OS> -hard2 build/iigs/bin/joey-all.2mg` and launch
|
# `-sl7 cffa2 -hard1 <GS/OS> -hard2 build/iigs/bin/joey-all.2mg` and launch
|
||||||
# any example from the JOEYLIB volume in the Finder.
|
# any example from the JOEYLIB volume in the Finder.
|
||||||
iigs-disk-all: iigs-examples $(NTP_BIN) $(STAXI_IIGS_SPC)
|
iigs-disk-all: iigs-examples $(NTP_BIN) $(STAXI_IIGS_SPC) $(STAXILEVEL_IIGS_SPC) $(STAXI_IIGS_SPEECH)
|
||||||
BINDIR="$(BINDIR)" NTP_BIN="$(NTP_BIN)" \
|
BINDIR="$(BINDIR)" NTP_BIN="$(NTP_BIN)" \
|
||||||
JOEY_DISK_SIZE="16MB" \
|
JOEY_DISK_SIZE="16MB" \
|
||||||
JOEY_DISK_EXAMPLES="DRAW PATTERN KEYS JOY SPRITE SERIAL SERTEST SAVE AUDIO UBER ADV2 ADV AGI STAXI" \
|
JOEY_DISK_EXAMPLES="DRAW PATTERN KEYS JOY SPRITE SERIAL SERTEST SAVE AUDIO UBER ADV2 ADV AGI STAXI" \
|
||||||
|
|
@ -291,10 +306,26 @@ $(BINDIR)/ADV2: $(ADV2_SRC) $(LIB) $(IIGS_CLANG_BUILD)
|
||||||
@mkdir -p $(dir $@) $(DEP_DIR)
|
@mkdir -p $(dir $@) $(DEP_DIR)
|
||||||
$(IIGS_CLANG_BUILD) -M $(DEP_DIR)/ADV2.d $(INCLUDES) -o $@ $(ADV2_SRC) $(LIB)
|
$(IIGS_CLANG_BUILD) -M $(DEP_DIR)/ADV2.d $(INCLUDES) -o $@ $(ADV2_SRC) $(LIB)
|
||||||
|
|
||||||
|
# AGI opts into large overflow segments. --segment-cap bounds the ENTRY
|
||||||
|
# bank (which also holds rodata + bss + heap under the $$C000 IO window);
|
||||||
|
# overflow banks carry text only, so capping them the same way wasted
|
||||||
|
# ~50K per bank. With this AGI links 5 bank-aligned segments instead of
|
||||||
|
# 19, which matters because it already needs -ramsize 4M. Verified by
|
||||||
|
# running KQ3 headless and diffing AGI's own joeylog.txt against the
|
||||||
|
# 19-segment build: identical apart from the embedded build timestamp.
|
||||||
|
#
|
||||||
|
# Both AGI and STAXI opt in; see clang-build.sh for the measurements.
|
||||||
|
$(BINDIR)/AGI: export JOEY_OVERFLOW_CAP := 0xF000
|
||||||
$(BINDIR)/AGI: $(AGI_SRCS) $(LIB) $(IIGS_CLANG_BUILD)
|
$(BINDIR)/AGI: $(AGI_SRCS) $(LIB) $(IIGS_CLANG_BUILD)
|
||||||
@mkdir -p $(dir $@) $(DEP_DIR)
|
@mkdir -p $(dir $@) $(DEP_DIR)
|
||||||
$(IIGS_CLANG_BUILD) -M $(DEP_DIR)/AGI.d $(INCLUDES) -I$(EXAMPLES)/agi -o $@ $(AGI_SRCS) $(LIB)
|
$(IIGS_CLANG_BUILD) -M $(DEP_DIR)/AGI.d $(INCLUDES) -I$(EXAMPLES)/agi -o $@ $(AGI_SRCS) $(LIB)
|
||||||
|
|
||||||
|
# STAXI now also opts into large overflow segments: 26 bank-aligned
|
||||||
|
# segments starved the Memory Manager, so NTP's 34K NewHandle failed and
|
||||||
|
# the IIgs build ran SILENT (gNTPReady stayed 0). Freeing that memory lets
|
||||||
|
# audio initialise; the latent crash that then surfaced is fixed in
|
||||||
|
# src/iigs/audio_full.c (sfxTrigger now requires a running replayer).
|
||||||
|
$(BINDIR)/STAXI: export JOEY_OVERFLOW_CAP := 0xF000
|
||||||
$(BINDIR)/STAXI: $(STAXI_SRCS) $(LIB) $(IIGS_CLANG_BUILD)
|
$(BINDIR)/STAXI: $(STAXI_SRCS) $(LIB) $(IIGS_CLANG_BUILD)
|
||||||
@mkdir -p $(dir $@) $(DEP_DIR)
|
@mkdir -p $(dir $@) $(DEP_DIR)
|
||||||
$(IIGS_CLANG_BUILD) -M $(DEP_DIR)/STAXI.d $(INCLUDES) -I$(EXAMPLES)/spacetaxi -o $@ $(STAXI_SRCS) $(LIB)
|
$(IIGS_CLANG_BUILD) -M $(DEP_DIR)/STAXI.d $(INCLUDES) -I$(EXAMPLES)/spacetaxi -o $@ $(STAXI_SRCS) $(LIB)
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ MKSTLEVEL_BIN := $(BUILD_DIR)/mkstlevel
|
||||||
# plain host tool (the .spr is cross-platform); spritebake then compiles
|
# plain host tool (the .spr is cross-platform); spritebake then compiles
|
||||||
# it per target.
|
# it per target.
|
||||||
STAXI_DIR := $(REPO_DIR)/examples/spacetaxi
|
STAXI_DIR := $(REPO_DIR)/examples/spacetaxi
|
||||||
STAXIBAKE_SRC := $(TOOLS_DIR)/staxibake/staxibake.c $(STAXI_DIR)/stCels.c $(STAXI_DIR)/stC64Data.c
|
STAXIBAKE_SRC := $(TOOLS_DIR)/staxibake/staxibake.c $(STAXI_DIR)/stCels.c $(STAXI_DIR)/stC64Data.c $(STAXI_DIR)/stLevelCels.c $(STAXI_DIR)/stLevelFile.c
|
||||||
STAXIBAKE_BIN := $(BUILD_DIR)/staxibake
|
STAXIBAKE_BIN := $(BUILD_DIR)/staxibake
|
||||||
|
|
||||||
# spritebake -- offline sprite pre-compiler. Built ONCE PER TARGET on the
|
# spritebake -- offline sprite pre-compiler. Built ONCE PER TARGET on the
|
||||||
|
|
@ -42,7 +42,7 @@ SPRITEBAKE_SRC := $(TOOLS_DIR)/spritebake/spritebake.c $(SRC_CG)/spriteStage.c
|
||||||
SPRITEBAKE_INC := -I$(INCLUDE_DIR) -I$(SRC_CORE) -I$(SRC_CG)
|
SPRITEBAKE_INC := -I$(INCLUDE_DIR) -I$(SRC_CORE) -I$(SRC_CG)
|
||||||
|
|
||||||
.PHONY: all tools clean-tools modloopend mkstlevel staxibake \
|
.PHONY: all tools clean-tools modloopend mkstlevel staxibake \
|
||||||
spritebake-dos spritebake-amiga spritebake-atarist spritebake-iigs
|
spritebake-dos spritebake-amiga spritebake-atarist spritebake-iigs spritebake-x68000
|
||||||
all tools: $(MODLOOPEND_BIN) $(MKSTLEVEL_BIN)
|
all tools: $(MODLOOPEND_BIN) $(MKSTLEVEL_BIN)
|
||||||
modloopend: $(MODLOOPEND_BIN)
|
modloopend: $(MODLOOPEND_BIN)
|
||||||
mkstlevel: $(MKSTLEVEL_BIN)
|
mkstlevel: $(MKSTLEVEL_BIN)
|
||||||
|
|
@ -51,6 +51,7 @@ spritebake-dos: $(BUILD_DIR)/spritebake-dos
|
||||||
spritebake-amiga: $(BUILD_DIR)/spritebake-amiga
|
spritebake-amiga: $(BUILD_DIR)/spritebake-amiga
|
||||||
spritebake-atarist: $(BUILD_DIR)/spritebake-atarist
|
spritebake-atarist: $(BUILD_DIR)/spritebake-atarist
|
||||||
spritebake-iigs: $(BUILD_DIR)/spritebake-iigs
|
spritebake-iigs: $(BUILD_DIR)/spritebake-iigs
|
||||||
|
spritebake-x68000: $(BUILD_DIR)/spritebake-x68000
|
||||||
|
|
||||||
$(MODLOOPEND_BIN): $(MODLOOPEND_SRC)
|
$(MODLOOPEND_BIN): $(MODLOOPEND_SRC)
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
|
|
@ -72,6 +73,12 @@ $(BUILD_DIR)/spritebake-amiga: $(SPRITEBAKE_SRC) $(SRC_68K)/spriteEmitPlanar68k.
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
$(HOST_CC) $(HOST_CFLAGS) -DJOEYLIB_PLATFORM_AMIGA $(SPRITEBAKE_INC) -I$(SRC_DIR)/amiga -I$(SRC_68K) $^ -o $@
|
$(HOST_CC) $(HOST_CFLAGS) -DJOEYLIB_PLATFORM_AMIGA $(SPRITEBAKE_INC) -I$(SRC_DIR)/amiga -I$(SRC_68K) $^ -o $@
|
||||||
|
|
||||||
|
# The X68000 runs the Amiga's planar 68k emitter unchanged (see
|
||||||
|
# make/x68000.mk), so its bake is the same source under its own target byte.
|
||||||
|
$(BUILD_DIR)/spritebake-x68000: $(SPRITEBAKE_SRC) $(SRC_68K)/spriteEmitPlanar68k.c
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(HOST_CC) $(HOST_CFLAGS) -DJOEYLIB_PLATFORM_X68000 $(SPRITEBAKE_INC) -I$(SRC_DIR)/x68000 -I$(SRC_68K) $^ -o $@
|
||||||
|
|
||||||
$(BUILD_DIR)/spritebake-atarist: $(SPRITEBAKE_SRC) $(SRC_68K)/spriteEmitInterleaved68k.c
|
$(BUILD_DIR)/spritebake-atarist: $(SPRITEBAKE_SRC) $(SRC_68K)/spriteEmitInterleaved68k.c
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
$(HOST_CC) $(HOST_CFLAGS) -DJOEYLIB_PLATFORM_ATARIST $(SPRITEBAKE_INC) -I$(SRC_DIR)/atarist -I$(SRC_68K) $^ -o $@
|
$(HOST_CC) $(HOST_CFLAGS) -DJOEYLIB_PLATFORM_ATARIST $(SPRITEBAKE_INC) -I$(SRC_DIR)/atarist -I$(SRC_68K) $^ -o $@
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ SERIAL_SRC := $(EXAMPLES)/serial/serial.c
|
||||||
UBER_SRC := $(EXAMPLES)/uber/uber.c
|
UBER_SRC := $(EXAMPLES)/uber/uber.c
|
||||||
AUDIO_SRC := $(EXAMPLES)/audio/audio.c
|
AUDIO_SRC := $(EXAMPLES)/audio/audio.c
|
||||||
KEYS_SRC := $(EXAMPLES)/keys/keys.c
|
KEYS_SRC := $(EXAMPLES)/keys/keys.c
|
||||||
STAXI_SRCS := $(EXAMPLES)/spacetaxi/spacetaxi.c $(EXAMPLES)/spacetaxi/stLevel.c $(EXAMPLES)/spacetaxi/stLevelFile.c $(EXAMPLES)/spacetaxi/stRender.c $(EXAMPLES)/spacetaxi/stSim.c $(EXAMPLES)/spacetaxi/stFare.c $(EXAMPLES)/spacetaxi/stHooks.c $(EXAMPLES)/spacetaxi/stTitle.c $(EXAMPLES)/spacetaxi/stC64Data.c $(EXAMPLES)/spacetaxi/stCels.c $(EXAMPLES)/spacetaxi/stAudio.c
|
STAXI_SRCS := $(EXAMPLES)/spacetaxi/spacetaxi.c $(EXAMPLES)/spacetaxi/stLevel.c $(EXAMPLES)/spacetaxi/stLevelFile.c $(EXAMPLES)/spacetaxi/stRender.c $(EXAMPLES)/spacetaxi/stSim.c $(EXAMPLES)/spacetaxi/stFare.c $(EXAMPLES)/spacetaxi/stHooks.c $(EXAMPLES)/spacetaxi/stTitle.c $(EXAMPLES)/spacetaxi/stC64Data.c $(EXAMPLES)/spacetaxi/stCels.c $(EXAMPLES)/spacetaxi/stAudio.c $(EXAMPLES)/spacetaxi/stLevelCels.c
|
||||||
|
|
||||||
.PHONY: all x68000 x68000-lib x68000-examples x68000-verify-serial x68000-verify-mouse x68000-verify-golden clean-x68000 clean
|
.PHONY: all x68000 x68000-lib x68000-examples x68000-verify-serial x68000-verify-mouse x68000-verify-golden clean-x68000 clean
|
||||||
|
|
||||||
|
|
@ -167,15 +167,88 @@ $(BINDIR)/KEYS.X: $(KEYS_SRC) $(LIB) $(LIBXMP_AR)
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
$(X68K_CC) $(CFLAGS) $(KEYS_SRC) $(LIB) $(LIBXMP_AR) $(LDFLAGS) -o $@
|
$(X68K_CC) $(CFLAGS) $(KEYS_SRC) $(LIB) $(LIBXMP_AR) $(LDFLAGS) -o $@
|
||||||
|
|
||||||
|
# Space Taxi asset variables (the rules follow the binary below); make
|
||||||
|
# expands prerequisites when it reads the rule, so these come first.
|
||||||
|
STAXI_GEN_DIR := $(EXAMPLES)/spacetaxi/generated/x68000
|
||||||
|
STAXI_RUN_DIR := $(BINDIR)/DATA
|
||||||
|
STAXI_RAW_DUMP := $(REPO_DIR)/stuff/spacetaxi/raw.bin
|
||||||
|
ROMTOLEVEL_BIN := $(REPO_DIR)/stuff/spacetaxi/romToLevel.py
|
||||||
|
STAXI_LEVEL_NUMS := 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
||||||
|
STAXI_LEVEL_GEN := $(foreach n,$(STAXI_LEVEL_NUMS),$(STAXI_GEN_DIR)/levels/level$(n).dat) \
|
||||||
|
$(STAXI_GEN_DIR)/levels/title.dat
|
||||||
|
STAXI_LEVEL_RUN := $(patsubst $(STAXI_GEN_DIR)/%,$(STAXI_RUN_DIR)/%,$(STAXI_LEVEL_GEN))
|
||||||
|
SPRITEBAKE_BIN := $(REPO_DIR)/build/tools/spritebake-x68000
|
||||||
|
STAXIBAKE_BIN := $(REPO_DIR)/build/tools/staxibake
|
||||||
|
STAXICELS_SPR_GEN := $(STAXI_GEN_DIR)/sprites/staxicels.spr
|
||||||
|
STAXICELS_SPC_GEN := $(STAXICELS_SPR_GEN:.spr=.spc)
|
||||||
|
STAXICELS_SPC_RUN := $(patsubst $(STAXI_GEN_DIR)/%,$(STAXI_RUN_DIR)/%,$(STAXICELS_SPC_GEN))
|
||||||
|
STAXI_ASSET_DSTS := $(STAXI_LEVEL_RUN) $(STAXICELS_SPC_RUN)
|
||||||
|
|
||||||
|
# Per-level hook-sprite banks: the cels stLevelCelList names for each
|
||||||
|
# level that has hook sprites, baked from its level file and pre-compiled
|
||||||
|
# for this target, so no cel is JIT-compiled mid-ride. Levels without
|
||||||
|
# hook sprites have no bank; the game JIT-compiles at level entry when a
|
||||||
|
# bank is missing.
|
||||||
|
STAXI_CEL_LEVELS := 07 08 09 10 12 16 17 21
|
||||||
|
STAXILEVEL_SPR_GEN := $(foreach n,$(STAXI_CEL_LEVELS),$(STAXI_GEN_DIR)/sprites/level$(n).spr)
|
||||||
|
STAXILEVEL_SPC_GEN := $(STAXILEVEL_SPR_GEN:.spr=.spc)
|
||||||
|
STAXILEVEL_SPC_RUN := $(patsubst $(STAXI_GEN_DIR)/%,$(STAXI_RUN_DIR)/%,$(STAXILEVEL_SPC_GEN))
|
||||||
|
STAXI_ASSET_DSTS += $(STAXILEVEL_SPC_RUN)
|
||||||
|
|
||||||
|
# Speech baked to sample bytes per passenger pitch (assets/genSpeechPcm.py):
|
||||||
|
# expanding bits at play time is what keeps "HEY TAXI" from stalling the
|
||||||
|
# ride; the C64 run-length source stays in stSpeechData.c as the input.
|
||||||
|
STAXI_SPEECH_GEN := $(STAXI_GEN_DIR)/speech.bin
|
||||||
|
STAXI_SPEECH_RUN := $(STAXI_RUN_DIR)/speech.bin
|
||||||
|
STAXI_ASSET_DSTS += $(STAXI_SPEECH_RUN)
|
||||||
|
$(STAXI_SPEECH_GEN): $(EXAMPLES)/spacetaxi/stSpeechData.c $(EXAMPLES)/spacetaxi/assets/genSpeechPcm.py
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
python3 $(EXAMPLES)/spacetaxi/assets/genSpeechPcm.py $< $@
|
||||||
|
$(STAXI_SPEECH_RUN): $(STAXI_SPEECH_GEN)
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
cp $< $@
|
||||||
|
|
||||||
# Space Taxi. -s (strip) because the image it ships on is a 1232 KB
|
# Space Taxi. -s (strip) because the image it ships on is a 1232 KB
|
||||||
# Human68k floppy: the unstripped binary is most of the volume on its
|
# Human68k floppy: the unstripped binary is most of the volume on its
|
||||||
# own. No precompiled sprite bank is staged -- this port has a real 68k
|
# own. The DATA tree next to it (levels + the precompiled cel bank) is
|
||||||
# planar emitter, so the boot prewarm JITs its cels in well under a
|
# what scripts/run-x68000.sh copies onto the data floppy.
|
||||||
# second (the reason the IIgs needs a baked .spc is its ~0.4 s per cel).
|
$(BINDIR)/STAXI.X: $(STAXI_SRCS) $(LIB) $(LIBXMP_AR) $(STAXI_ASSET_DSTS)
|
||||||
$(BINDIR)/STAXI.X: $(STAXI_SRCS) $(LIB) $(LIBXMP_AR)
|
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
$(X68K_CC) $(CFLAGS) -s -I$(EXAMPLES)/spacetaxi $(STAXI_SRCS) $(LIB) $(LIBXMP_AR) $(LDFLAGS) -o $@
|
$(X68K_CC) $(CFLAGS) -s -I$(EXAMPLES)/spacetaxi $(STAXI_SRCS) $(LIB) $(LIBXMP_AR) $(LDFLAGS) -o $@
|
||||||
|
|
||||||
|
# Space Taxi asset staging, the amiga.mk pattern cut down to what this
|
||||||
|
# port loads at run time: the 24 levels + title extracted from the C64
|
||||||
|
# dump, and the cel bank baked by staxibake and pre-compiled for THIS
|
||||||
|
# target by spritebake-x68000 (the planar 68k emitter under target byte
|
||||||
|
# 5, so an Amiga or DOS bank is refused by the loader). Tiles and the
|
||||||
|
# font come from the C64 data in stC64Data.c, so no .tbk is staged.
|
||||||
|
.SECONDARY: $(STAXI_LEVEL_GEN) $(STAXICELS_SPR_GEN) $(STAXICELS_SPC_GEN) $(STAXILEVEL_SPR_GEN) $(STAXILEVEL_SPC_GEN)
|
||||||
|
|
||||||
|
$(STAXI_LEVEL_GEN) &: $(STAXI_RAW_DUMP) $(ROMTOLEVEL_BIN)
|
||||||
|
@mkdir -p $(STAXI_GEN_DIR)/levels
|
||||||
|
python3 $(ROMTOLEVEL_BIN) $(STAXI_RAW_DUMP) $(STAXI_GEN_DIR)/levels
|
||||||
|
|
||||||
|
$(SPRITEBAKE_BIN):
|
||||||
|
$(MAKE) -f $(REPO_DIR)/make/tools.mk spritebake-x68000
|
||||||
|
|
||||||
|
$(STAXIBAKE_BIN):
|
||||||
|
$(MAKE) -f $(REPO_DIR)/make/tools.mk staxibake
|
||||||
|
|
||||||
|
$(STAXICELS_SPR_GEN): $(STAXIBAKE_BIN) $(EXAMPLES)/spacetaxi/stCels.c $(EXAMPLES)/spacetaxi/stC64Data.c
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(STAXIBAKE_BIN) $@
|
||||||
|
|
||||||
|
$(STAXI_GEN_DIR)/sprites/%.spc: $(STAXI_GEN_DIR)/sprites/%.spr $(SPRITEBAKE_BIN)
|
||||||
|
$(SPRITEBAKE_BIN) $< $@
|
||||||
|
|
||||||
|
$(STAXI_RUN_DIR)/levels/%.dat: $(STAXI_GEN_DIR)/levels/%.dat
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
cp $< $@
|
||||||
|
|
||||||
|
$(STAXI_RUN_DIR)/sprites/%.spc: $(STAXI_GEN_DIR)/sprites/%.spc
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
cp $< $@
|
||||||
|
|
||||||
# Both-directions RS-232C gate (~1 min).
|
# Both-directions RS-232C gate (~1 min).
|
||||||
x68000-verify-serial: $(BINDIR)/SERIAL.X
|
x68000-verify-serial: $(BINDIR)/SERIAL.X
|
||||||
$(REPO_DIR)/scripts/verify-x68000-serial.sh
|
$(REPO_DIR)/scripts/verify-x68000-serial.sh
|
||||||
|
|
@ -199,3 +272,8 @@ clean-x68000:
|
||||||
clean: clean-x68000
|
clean: clean-x68000
|
||||||
|
|
||||||
-include $(shell find $(BUILD) -name '*.d' 2>/dev/null)
|
-include $(shell find $(BUILD) -name '*.d' 2>/dev/null)
|
||||||
|
|
||||||
|
# Shorter stem than the %.spr-from-%.png pattern, so it wins for level banks.
|
||||||
|
$(STAXI_GEN_DIR)/sprites/level%.spr: $(STAXI_GEN_DIR)/levels/level%.dat $(STAXIBAKE_BIN) $(EXAMPLES)/spacetaxi/stLevelCels.c
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(STAXIBAKE_BIN) --level $< $@
|
||||||
|
|
|
||||||
69
scripts/make-iigs-boot-disk.sh
Executable file
69
scripts/make-iigs-boot-disk.sh
Executable file
|
|
@ -0,0 +1,69 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# make-iigs-boot-disk.sh - Build a GS/OS boot floppy that launches one
|
||||||
|
# JoeyLib example DIRECTLY, with no Finder in the way.
|
||||||
|
#
|
||||||
|
# WHY: the GS/OS Finder cannot be driven from a headless harness. Under
|
||||||
|
# gssquared, injected keystrokes reach the $C000 latch but never reach the
|
||||||
|
# Finder -- Open-Apple-A, Tab, arrow keys and plain letters all leave the
|
||||||
|
# desktop untouched (the single redraw seen after the first key of a run is
|
||||||
|
# the Finder's own settling: it was byte-identical for three different
|
||||||
|
# keys). Reaching the desktop also costs ~61s of boot per run. MAME's Lua
|
||||||
|
# harness drives the Finder by a different route, but that is one more
|
||||||
|
# moving part per probe.
|
||||||
|
#
|
||||||
|
# This replaces /System/Start (normally the Finder) with tools/iigsStart,
|
||||||
|
# whose whole job is a GS/OS QuitGS to the pathname baked in at build time.
|
||||||
|
# Boot the result as the FIRST drive and the example's own disk as the
|
||||||
|
# second; the example volume is mounted before Start runs.
|
||||||
|
#
|
||||||
|
# Usage: scripts/make-iigs-boot-disk.sh <out.po> <EXAMPLE> [volume]
|
||||||
|
# EXAMPLE uppercase binary name as it appears on the data disk
|
||||||
|
# volume data volume name, default JOEYLIB
|
||||||
|
# Requires: toolchains/env.sh sourced.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
out=${1:?usage: make-iigs-boot-disk.sh <out.po> <EXAMPLE> [volume]}
|
||||||
|
prog=${2:?usage: make-iigs-boot-disk.sh <out.po> <EXAMPLE> [volume]}
|
||||||
|
vol=${3:-JOEYLIB}
|
||||||
|
|
||||||
|
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||||
|
: "${LLVM816_ROOT:?make-iigs-boot-disk.sh: source toolchains/env.sh first}"
|
||||||
|
CADIUS="${CADIUS:-$LLVM816_ROOT/tools/cadius/cadius}"
|
||||||
|
sysDisk="$repo/toolchains/emulators/support/gsos-system.po"
|
||||||
|
|
||||||
|
[ -x "$CADIUS" ] || { echo "make-iigs-boot-disk.sh: cadius not found at $CADIUS" >&2; exit 2; }
|
||||||
|
[ -f "$sysDisk" ] || { echo "make-iigs-boot-disk.sh: missing $sysDisk" >&2; exit 2; }
|
||||||
|
|
||||||
|
work=$(mktemp -d -t joeylib-boot.XXXXXX)
|
||||||
|
trap 'rm -rf "$work"' EXIT
|
||||||
|
|
||||||
|
# The GS/OS system disk's own volume name, which cadius paths are relative to.
|
||||||
|
# Match the volume line, which is a WHOLE line of the form "/NAME/". An
|
||||||
|
# unanchored match also hits the absolute image path in cadius's header
|
||||||
|
# (it yielded "home" from /home/scott/...).
|
||||||
|
sysVol=$("$CADIUS" CATALOG "$sysDisk" 2>/dev/null \
|
||||||
|
| grep -oE '^/[A-Za-z0-9._]+/$' | head -1 | tr -d '/')
|
||||||
|
[ -n "$sysVol" ] || { echo "make-iigs-boot-disk.sh: cannot read system volume name" >&2; exit 1; }
|
||||||
|
|
||||||
|
cp "$sysDisk" "$out"
|
||||||
|
|
||||||
|
# Out with the Finder (~200KB), in with the launcher. Deleting first is what
|
||||||
|
# makes room: the stock disk has under 50KB free.
|
||||||
|
"$CADIUS" DELETEFILE "$out" "/$sysVol/System/Start" >/dev/null
|
||||||
|
|
||||||
|
"$repo/toolchains/iigs/clang-build.sh" \
|
||||||
|
-DJOEY_START_PATH="\"/$vol/$prog\"" \
|
||||||
|
-o "$work/Start#B30000" \
|
||||||
|
"$repo/tools/iigsStart/iigsStart.c" \
|
||||||
|
"$repo/tools/iigsStart/iigsStartQuit.s" >/dev/null
|
||||||
|
|
||||||
|
"$CADIUS" ADDFILE "$out" "/$sysVol/System" "$work/Start#B30000" >/dev/null
|
||||||
|
|
||||||
|
# Never trust the exit code of a disk build here -- cadius reports success
|
||||||
|
# for a file it silently dropped when the volume was full.
|
||||||
|
if ! "$CADIUS" CATALOG "$out" 2>/dev/null | grep -qE '^ +Start +S16'; then
|
||||||
|
echo "make-iigs-boot-disk.sh: Start was not written (volume full?)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "iigs-boot-disk: $out (boots straight into /$vol/$prog)"
|
||||||
|
|
@ -17,6 +17,12 @@ repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||||
: "${LLVM816_ROOT:?make-iigs-disk.sh: source toolchains/env.sh first}"
|
: "${LLVM816_ROOT:?make-iigs-disk.sh: source toolchains/env.sh first}"
|
||||||
CADIUS="${CADIUS:-$LLVM816_ROOT/tools/cadius/cadius}"
|
CADIUS="${CADIUS:-$LLVM816_ROOT/tools/cadius/cadius}"
|
||||||
BINDIR="${BINDIR:-$repo/build/iigs/bin}"
|
BINDIR="${BINDIR:-$repo/build/iigs/bin}"
|
||||||
|
# The NTP replayer. DEFAULTED, not optional: without it on the volume every
|
||||||
|
# IIgs example is silent except the ROM boot beep, because jlpAudioInit
|
||||||
|
# fopen()s "ntpplayer" and returns before ntpArm() when that read fails --
|
||||||
|
# with no diagnostic. Callers used to have to pass NTP_BIN and one of them
|
||||||
|
# passed the wrong path, which is exactly how Space Taxi ended up mute.
|
||||||
|
NTP_BIN="${NTP_BIN:-$repo/build/iigs/audio/ntpplayer.bin}"
|
||||||
OUT="${1:-$BINDIR/joey.2mg}"
|
OUT="${1:-$BINDIR/joey.2mg}"
|
||||||
VOL=JOEYLIB
|
VOL=JOEYLIB
|
||||||
# Volume size passed to cadius CREATEVOLUME. Defaults to an 800KB 3.5"
|
# Volume size passed to cadius CREATEVOLUME. Defaults to an 800KB 3.5"
|
||||||
|
|
@ -77,18 +83,33 @@ done
|
||||||
# The NTP replayer binary (audio): jlpAudioInit fopen()s "ntpplayer" from
|
# The NTP replayer binary (audio): jlpAudioInit fopen()s "ntpplayer" from
|
||||||
# the volume at runtime (see src/iigs/audio_full.c NTP_PATH). Plain ProDOS
|
# the volume at runtime (see src/iigs/audio_full.c NTP_PATH). Plain ProDOS
|
||||||
# BIN ($06) file. Skipped with a warning if it doesn't fit or wasn't built.
|
# BIN ($06) file. Skipped with a warning if it doesn't fit or wasn't built.
|
||||||
if [ -n "${NTP_BIN:-}" ] && [ -f "$NTP_BIN" ]; then
|
ntpOk=0
|
||||||
|
if [ -f "$NTP_BIN" ]; then
|
||||||
ntpSz=$(stat -c%s "$NTP_BIN")
|
ntpSz=$(stat -c%s "$NTP_BIN")
|
||||||
ntpNeed=$(( (ntpSz + 511) / 512 + 2 ))
|
ntpNeed=$(( (ntpSz + 511) / 512 + 2 ))
|
||||||
if [ "$ntpNeed" -le "$(freeBlocks)" ]; then
|
if [ "$ntpNeed" -le "$(freeBlocks)" ]; then
|
||||||
cp "$NTP_BIN" "$work/NTPPLAYER#060000"
|
cp "$NTP_BIN" "$work/NTPPLAYER#060000"
|
||||||
"$CADIUS" ADDFILE "$OUT" "/$VOL" "$work/NTPPLAYER#060000" >/dev/null
|
"$CADIUS" ADDFILE "$OUT" "/$VOL" "$work/NTPPLAYER#060000" >/dev/null
|
||||||
rm -f "$work/NTPPLAYER#060000"
|
rm -f "$work/NTPPLAYER#060000"
|
||||||
added+=(NTPPLAYER)
|
# Never trust the exit code here (see the launch check below).
|
||||||
else
|
if "$CADIUS" CATALOG "$OUT" 2>/dev/null | grep -qE '^ +NTPPLAYER +BIN'; then
|
||||||
skipped+=(NTPPLAYER)
|
added+=(NTPPLAYER)
|
||||||
|
ntpOk=1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
if [ "$ntpOk" -eq 0 ]; then
|
||||||
|
skipped+=(NTPPLAYER)
|
||||||
|
cat >&2 <<EOF
|
||||||
|
make-iigs-disk.sh: WARNING - NTPPLAYER is NOT on $OUT.
|
||||||
|
Every example built against this volume will be SILENT (you will hear the
|
||||||
|
ROM boot beep and nothing else). jlpAudioInit fopen()s "ntpplayer" and
|
||||||
|
gives up quietly when it is missing; \$E1002C keeps its ROM-bank default.
|
||||||
|
Looked for: $NTP_BIN
|
||||||
|
Build it with 'make -f make/iigs.mk \$(BUILD)/audio/ntpplayer.bin', or set
|
||||||
|
NTP_BIN to its location.
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
# Space Taxi runtime data: the game fopen()s a DATA/ tree (levels/*.dat,
|
# Space Taxi runtime data: the game fopen()s a DATA/ tree (levels/*.dat,
|
||||||
# tiles/tbank*.tbk, sprites/sprites.spr, font.tbk -- see stRender.c /
|
# tiles/tbank*.tbk, sprites/sprites.spr, font.tbk -- see stRender.c /
|
||||||
|
|
@ -117,8 +138,28 @@ if [[ " ${added[*]:-} " == *" STAXI "* ]]; then
|
||||||
"$CADIUS" ADDFILE "$OUT" "$dst" "$work/${base^^}#060000" >/dev/null
|
"$CADIUS" ADDFILE "$OUT" "$dst" "$work/${base^^}#060000" >/dev/null
|
||||||
rm -f "$work/${base^^}#060000"
|
rm -f "$work/${base^^}#060000"
|
||||||
dataAdded=$((dataAdded + 1))
|
dataAdded=$((dataAdded + 1))
|
||||||
done < <(find "$staxiData" -type f | sort)
|
done < <(find "$staxiData" -type f -not -path '*/sprites/level*.spc' | sort)
|
||||||
|
# The per-level hook-sprite banks are optional (the game JIT-compiles
|
||||||
|
# a level's cels at entry when its bank is missing) and large, so
|
||||||
|
# they go on last and only while the floppy has room for them.
|
||||||
|
levelSkipped=0
|
||||||
|
while IFS= read -r f; do
|
||||||
|
base=$(basename "$f")
|
||||||
|
sz=$(stat -c%s "$f")
|
||||||
|
need=$(( (sz + 511) / 512 + (sz + 131071) / 131072 + 2 ))
|
||||||
|
if [ "$need" -gt "$(freeBlocks)" ]; then
|
||||||
|
levelSkipped=$((levelSkipped + 1))
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
cp "$f" "$work/${base^^}#060000"
|
||||||
|
"$CADIUS" ADDFILE "$OUT" "/$VOL/DATA/SPRITES" "$work/${base^^}#060000" >/dev/null
|
||||||
|
rm -f "$work/${base^^}#060000"
|
||||||
|
dataAdded=$((dataAdded + 1))
|
||||||
|
done < <(find "$staxiData" -type f -path '*/sprites/level*.spc' | sort)
|
||||||
added+=("DATA[$dataAdded files]")
|
added+=("DATA[$dataAdded files]")
|
||||||
|
if [ "$levelSkipped" -gt 0 ]; then
|
||||||
|
echo " note: $levelSkipped level sprite bank(s) left off (floppy full; those levels JIT at entry)" >&2
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo " WARNING: STAXI packed without its DATA tree ($staxiData missing)" >&2
|
echo " WARNING: STAXI packed without its DATA tree ($staxiData missing)" >&2
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@
|
||||||
# scripts/run-iigs.sh staxi MAME auto-launch: builds a
|
# scripts/run-iigs.sh staxi MAME auto-launch: builds a
|
||||||
# single-example run disk
|
# single-example run disk
|
||||||
# (joey-run.2mg -- joey.2mg is left
|
# (joey-run.2mg -- joey.2mg is left
|
||||||
# alone so benches stay honest),
|
# alone so benches stay honest) plus a
|
||||||
# boots GS/OS at real speed, and
|
# Finder-less boot disk whose
|
||||||
# drives Finder via Lua to launch
|
# /System/Start IS the launcher, so
|
||||||
# the example. No clicking needed.
|
# the example is on screen in ~50 s
|
||||||
# GS/OS itself takes ~50 s to boot;
|
# with no Finder and no keystroke
|
||||||
# that part is authentic.
|
# driving. No clicking needed.
|
||||||
# scripts/run-iigs.sh --all MAME + every example on one 16 MB
|
# scripts/run-iigs.sh --all MAME + every example on one 16 MB
|
||||||
# CFFA2 hard disk (the full set does
|
# CFFA2 hard disk (the full set does
|
||||||
# not fit an 800KB floppy): boots
|
# not fit an 800KB floppy): boots
|
||||||
|
|
@ -97,7 +97,7 @@ if [[ $all_mode -eq 1 ]]; then
|
||||||
fi
|
fi
|
||||||
if [[ $need_disk -eq 1 ]]; then
|
if [[ $need_disk -eq 1 ]]; then
|
||||||
echo "run-iigs: building all-examples disk ($all_disk)..."
|
echo "run-iigs: building all-examples disk ($all_disk)..."
|
||||||
BINDIR="$bin_dir" NTP_BIN="$bin_dir/ntpplayer.bin" \
|
BINDIR="$bin_dir" \
|
||||||
JOEY_DISK_SIZE="16MB" JOEY_DISK_EXAMPLES="$all_examples" \
|
JOEY_DISK_SIZE="16MB" JOEY_DISK_EXAMPLES="$all_examples" \
|
||||||
"$repo/scripts/make-iigs-disk.sh" "$all_disk"
|
"$repo/scripts/make-iigs-disk.sh" "$all_disk"
|
||||||
else
|
else
|
||||||
|
|
@ -433,42 +433,27 @@ EOF
|
||||||
}
|
}
|
||||||
trap cleanup EXIT INT TERM
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
cp "$sys_disk" "$work/boot.po"
|
# Finder-less boot disk: /System/Start is tools/iigsStart, which
|
||||||
|
# SetPrefixGS's to the data volume and QuitGS-launches the example.
|
||||||
|
# This replaces the old Lua keyboard driver, which typed J / Cmd-O /
|
||||||
|
# name / Cmd-O at calibrated frame counts -- workable but fragile,
|
||||||
|
# and it made every run sit through the Finder's ~60 s desktop paint
|
||||||
|
# before anything happened. Straight to the example in ~50 s now.
|
||||||
|
if [[ -z "${LLVM816_ROOT:-}" ]]; then
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
source "$repo/toolchains/env.sh" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
echo "run-iigs: building Finder-less boot disk for $target..."
|
||||||
|
"$repo/scripts/make-iigs-boot-disk.sh" "$work/boot.po" "$target" >/dev/null
|
||||||
cp "$run_disk" "$work/joey-run.2mg"
|
cp "$run_disk" "$work/joey-run.2mg"
|
||||||
|
|
||||||
# Slim Finder driver -- the keyboard-injection steps proven in
|
|
||||||
# run-iigs-mame.sh's harness, without the debugger/crash plumbing:
|
|
||||||
# at calibrated frame counts, type J (select the JOEYLIB volume),
|
|
||||||
# Cmd-O (open it), the example's full name (select; full-name
|
|
||||||
# typing disambiguates e.g. DRAW vs DATA), Cmd-O (launch). Frame
|
|
||||||
# counts are emulated frames, so throttling doesn't shift them.
|
|
||||||
headless=${MAME_HEADLESS:-0}
|
headless=${MAME_HEADLESS:-0}
|
||||||
cat > "$work/launch.lua" <<LUA
|
cat > "$work/launch.lua" <<LUA
|
||||||
local nat = manager.machine.natkeyboard
|
-- The boot disk launches the example by itself (tools/iigsStart), so
|
||||||
|
-- nothing here drives the Finder any more. This script only exists for
|
||||||
local function get_field(port, field_name)
|
-- the headless snapshot/exit schedule.
|
||||||
local p = manager.machine.ioport.ports[port]
|
|
||||||
if p == nil then return nil end
|
|
||||||
return p.fields[field_name]
|
|
||||||
end
|
|
||||||
|
|
||||||
local key_cmd = get_field(":macadb:KEY3", "Command / Open Apple")
|
|
||||||
|
|
||||||
local function press(f) if f then f:set_value(1) end end
|
|
||||||
local function release(f) if f then f:set_value(0) end end
|
|
||||||
|
|
||||||
local headless = ($headless == 1)
|
local headless = ($headless == 1)
|
||||||
local frames = 0
|
local frames = 0
|
||||||
local steps = {
|
local steps = {}
|
||||||
{ 3000, function() nat:post("J") end },
|
|
||||||
{ 3120, function() press(key_cmd) end },
|
|
||||||
{ 3126, function() nat:post("o") end },
|
|
||||||
{ 3180, function() release(key_cmd) end },
|
|
||||||
{ 3540, function() nat:post("$target") end },
|
|
||||||
{ 3660, function() press(key_cmd) end },
|
|
||||||
{ 3666, function() nat:post("o") end },
|
|
||||||
{ 3720, function() release(key_cmd) end },
|
|
||||||
}
|
|
||||||
if headless then
|
if headless then
|
||||||
-- Batch verification: snapshot right after the launch lands, mid
|
-- Batch verification: snapshot right after the launch lands, mid
|
||||||
-- asset load, and after the example settles on its first screen,
|
-- asset load, and after the example settles on its first screen,
|
||||||
|
|
@ -515,9 +500,9 @@ LUA
|
||||||
|
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
MAME apple2gs: auto-launching $target off $run_disk.
|
MAME apple2gs: auto-launching $target off $run_disk.
|
||||||
GS/OS takes ~50 s to reach Finder; the Lua driver then opens JOEYLIB
|
Booting a Finder-less GS/OS whose startup application IS $target, so it
|
||||||
and launches $target for you. Quit MAME (or close its window) to end
|
comes up in ~50 s with no Finder in the way. Quit MAME (or close its
|
||||||
the session.
|
window) to end the session.
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
cd "$work"
|
cd "$work"
|
||||||
|
|
|
||||||
237
scripts/verify-iigs-audio.sh
Executable file
237
scripts/verify-iigs-audio.sh
Executable file
|
|
@ -0,0 +1,237 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# verify-iigs-audio.sh - Headless gate proving NTP is actually running on
|
||||||
|
# the IIgs, modelled on verify-iigs.sh (same Finder launch timeline, same
|
||||||
|
# "read emulated memory, not a snapshot" approach).
|
||||||
|
#
|
||||||
|
# Sound cannot be graded from a -sound none run, so this grades the two
|
||||||
|
# pieces of state that make sound possible:
|
||||||
|
#
|
||||||
|
# irqVec $E1002C is the GS sound-interrupt vector. NTPprepare writes
|
||||||
|
# $5C (JML) + the address of its interrupt_handler + its own
|
||||||
|
# bank there (setup_interrupt), and NTPstop puts the previous
|
||||||
|
# vector back. $5C plus a RAM bank therefore means "NTP's DOC
|
||||||
|
# interrupt is installed". Before the ntpArm change nothing
|
||||||
|
# ever called NTPprepare unless the app played a .NTP module,
|
||||||
|
# so an SFX-only app left this at the system value and every
|
||||||
|
# streamed sample reached the DOC exactly once, unrefilled.
|
||||||
|
#
|
||||||
|
# docVol $E100CA is the Sound Tool Set's copy of the sound control
|
||||||
|
# register, and its LOW NIBBLE is the DOC master volume. NTP
|
||||||
|
# copies this byte into $C03C before every DOC access and
|
||||||
|
# never writes it, so if nothing started the Sound Tool Set it
|
||||||
|
# is whatever GS/OS left behind -- and a zero nibble means the
|
||||||
|
# DOC streams perfectly while outputting SILENCE. Everything
|
||||||
|
# below passed in exactly that state ("I don't hear any sound
|
||||||
|
# other than the IIgs startup beep"), which is why this check
|
||||||
|
# exists: streaming is necessary, not sufficient.
|
||||||
|
#
|
||||||
|
# slotN MAME exposes the Ensoniq 5503's 64KB wavetable RAM as the
|
||||||
|
# ":doc" device's "rom" space. JoeyLib's SFX slots live at DOC
|
||||||
|
# pages 0xC0, 0xC2, ... (SFX_BASE_DOC_PAGE / SFX_DOC_PAGE_STEP
|
||||||
|
# in src/iigs/audio_full.c), and bytes only land there when
|
||||||
|
# NTPstreamsound runs. Every slot is hashed twice, frames
|
||||||
|
# apart. Which slots an application uses is its own business
|
||||||
|
# -- AUDIO's tracker takes 0-2, Space Taxi only ever reaches
|
||||||
|
# the noise slot 3 -- so the gate asks that SOME slot carry a
|
||||||
|
# waveform and that SOME slot's hash move between the probes.
|
||||||
|
# A moving hash is the live-interrupt evidence: NTP streams
|
||||||
|
# the sample through the page in 256-byte halves, so a dead
|
||||||
|
# interrupt leaves whatever the one initial copy wrote.
|
||||||
|
#
|
||||||
|
# Usage: scripts/verify-iigs-audio.sh [example]
|
||||||
|
# example lowercase name, default "audio". A single-example
|
||||||
|
# disk is built for it (joey.2mg cannot hold them all).
|
||||||
|
# Requires: toolchains/env.sh sourced; the example built under build/iigs/bin.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||||
|
example=${1:-audio}
|
||||||
|
NAME=${example^^}
|
||||||
|
|
||||||
|
sys_disk=$repo/toolchains/emulators/support/gsos-system.po
|
||||||
|
rompath="${MAME_ROMPATH:-$HOME/.mame/roms}"
|
||||||
|
probeA="${MAME_PROBE_A:-5400}"
|
||||||
|
probeB="${MAME_PROBE_B:-9000}"
|
||||||
|
|
||||||
|
# Mirrors JOEY_AUDIO_SFX_SLOTS / SFX_BASE_DOC_PAGE / SFX_DOC_PAGE_STEP
|
||||||
|
# in src/iigs/audio_full.c.
|
||||||
|
slots=5
|
||||||
|
basePage=0xC0
|
||||||
|
pageStep=2
|
||||||
|
|
||||||
|
[ -f "$sys_disk" ] || { echo "verify-iigs-audio: missing $sys_disk" >&2; exit 2; }
|
||||||
|
[ -f "$repo/build/iigs/bin/$NAME" ] || { echo "verify-iigs-audio: missing build/iigs/bin/$NAME (run 'make iigs-disk')" >&2; exit 2; }
|
||||||
|
|
||||||
|
work=$(mktemp -d -t joeylib-audio.XXXXXX)
|
||||||
|
trap 'rm -rf "$work"' EXIT
|
||||||
|
|
||||||
|
# One-example volume: the 800KB floppy cannot hold every example, and the
|
||||||
|
# shared joey.2mg drops AUDIO and STAXI first (see make-iigs-disk.sh).
|
||||||
|
JOEY_DISK_EXAMPLES="$NAME" BINDIR="$repo/build/iigs/bin" \
|
||||||
|
NTP_BIN="$repo/build/iigs/audio/ntpplayer.bin" \
|
||||||
|
"$repo/scripts/make-iigs-disk.sh" "$work/joey.2mg" >/dev/null
|
||||||
|
cp "$sys_disk" "$work/boot.po"
|
||||||
|
|
||||||
|
cat > "$work/audio.lua" <<LUA
|
||||||
|
local cpu = manager.machine.devices[":maincpu"]
|
||||||
|
local mem = cpu.spaces["program"]
|
||||||
|
local nat = manager.machine.natkeyboard
|
||||||
|
local frame = 0
|
||||||
|
local idx = 1
|
||||||
|
|
||||||
|
local function field(port, name)
|
||||||
|
local p = manager.machine.ioport.ports[port]
|
||||||
|
if p == nil then return nil end
|
||||||
|
return p.fields[name]
|
||||||
|
end
|
||||||
|
local key_cmd = field(":macadb:KEY3", "Command / Open Apple")
|
||||||
|
local function press(f) if f then f:set_value(1) end end
|
||||||
|
local function release(f) if f then f:set_value(0) end end
|
||||||
|
|
||||||
|
-- The DOC's wavetable RAM. Guarded: if a MAME build ever renames the
|
||||||
|
-- device or its space, say so instead of silently reporting zeroes.
|
||||||
|
local docSpace = nil
|
||||||
|
do
|
||||||
|
local ok = pcall(function()
|
||||||
|
docSpace = manager.machine.devices[":doc"].spaces["rom"]
|
||||||
|
end)
|
||||||
|
if not ok then docSpace = nil end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- FNV-1a over a DOC page pair (one JoeyLib SFX slot = 512 bytes), plus a
|
||||||
|
-- count of bytes that are neither 0x00 (never written) nor 0x80 (DOC
|
||||||
|
-- silence), i.e. bytes that carry an actual waveform.
|
||||||
|
local function docSlot(page)
|
||||||
|
if docSpace == nil then return 0, -1 end
|
||||||
|
local h = 2166136261
|
||||||
|
local nz = 0
|
||||||
|
for i = 0, 511 do
|
||||||
|
local b = docSpace:read_u8((page * 256) + i)
|
||||||
|
if b ~= 0 and b ~= 0x80 then nz = nz + 1 end
|
||||||
|
h = (h ~ b) & 0xFFFFFFFF
|
||||||
|
h = (h * 16777619) & 0xFFFFFFFF
|
||||||
|
end
|
||||||
|
return h, nz
|
||||||
|
end
|
||||||
|
|
||||||
|
local function report(tag)
|
||||||
|
local v0 = mem:read_u8(0xE1002C)
|
||||||
|
local v1 = mem:read_u8(0xE1002D)
|
||||||
|
local v2 = mem:read_u8(0xE1002E)
|
||||||
|
local v3 = mem:read_u8(0xE1002F)
|
||||||
|
local scb0 = mem:read_u8(0xE19D00)
|
||||||
|
local docVol = mem:read_u8(0xE100CA)
|
||||||
|
local line = string.format(
|
||||||
|
"VERIFY-AUDIO name=$NAME tag=%s frame=%d scb0=%02X irqVec=%02X%02X%02X%02X docVol=%02X",
|
||||||
|
tag, frame, scb0, v3, v2, v1, v0, docVol)
|
||||||
|
for slot = 0, $slots - 1 do
|
||||||
|
local h, n = docSlot($basePage + slot * $pageStep)
|
||||||
|
line = line .. string.format(" slot%d=%08X slot%dNZ=%d", slot, h, slot, n)
|
||||||
|
end
|
||||||
|
io.write(line .. "\n")
|
||||||
|
io.flush()
|
||||||
|
end
|
||||||
|
|
||||||
|
local steps = {
|
||||||
|
{3000, function() nat:post("J") end},
|
||||||
|
{3120, function() press(key_cmd) end},
|
||||||
|
{3126, function() nat:post("o") end},
|
||||||
|
{3180, function() release(key_cmd) end},
|
||||||
|
{3540, function() nat:post("$NAME") end},
|
||||||
|
{3660, function() press(key_cmd) end},
|
||||||
|
{3666, function() nat:post("o") end},
|
||||||
|
{3720, function() release(key_cmd) end},
|
||||||
|
{$probeA, function() report("A") end},
|
||||||
|
{$probeB, function() report("B"); manager.machine:exit() end},
|
||||||
|
}
|
||||||
|
|
||||||
|
emu.register_frame_done(function()
|
||||||
|
frame = frame + 1
|
||||||
|
while idx <= #steps and frame >= steps[idx][1] do
|
||||||
|
steps[idx][2]()
|
||||||
|
idx = idx + 1
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
LUA
|
||||||
|
|
||||||
|
cd "$work"
|
||||||
|
out=$(QT_QPA_PLATFORM=offscreen SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy \
|
||||||
|
timeout 400 mame apple2gs \
|
||||||
|
-rompath "$rompath" \
|
||||||
|
-flop3 "$work/boot.po" -flop4 "$work/joey.2mg" \
|
||||||
|
-video none -sound none -nothrottle \
|
||||||
|
-autoboot_script "$work/audio.lua" </dev/null 2>&1) || true
|
||||||
|
|
||||||
|
lineA=$(echo "$out" | grep -E '^VERIFY-AUDIO .* tag=A ' | tail -1)
|
||||||
|
lineB=$(echo "$out" | grep -E '^VERIFY-AUDIO .* tag=B ' | tail -1)
|
||||||
|
echo "$lineA"
|
||||||
|
echo "$lineB"
|
||||||
|
if [ -z "$lineA" ] || [ -z "$lineB" ]; then
|
||||||
|
echo "verify-iigs-audio: FAIL - no probe report (boot/launch failed)" >&2
|
||||||
|
echo "$out" | tail -15 >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
field() { echo "$2" | sed -E "s/.*$1=([0-9A-Fa-f]+).*/\1/"; }
|
||||||
|
|
||||||
|
# Launch gate first, exactly as verify-iigs.sh: the Finder desktop alone
|
||||||
|
# would otherwise be graded as if it were the example.
|
||||||
|
scb0=$(field scb0 "$lineB")
|
||||||
|
if [ $((16#$scb0)) -ge 16 ]; then
|
||||||
|
echo "verify-iigs-audio: FAIL ($NAME never launched - SHR SCB[0]=0x$scb0 is the Finder's 640-mode fill)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
vec=$(field irqVec "$lineB")
|
||||||
|
if [ "${vec:6:2}" != "5C" ]; then
|
||||||
|
echo "verify-iigs-audio: FAIL (sound interrupt vector is $vec, not a JML - NTPprepare never ran, so there is no interrupt to refill a streamed sample)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# The vector the ROM leaves in place is a JML too ($5C 62 CD FC on this
|
||||||
|
# machine), so "is it a JML" is not enough -- the control run with the
|
||||||
|
# pre-ntpArm audio_full.c passed that test. NTP lives in a Memory
|
||||||
|
# Manager handle, always in RAM, so a ROM bank ($F0 and up) means the
|
||||||
|
# system vector is still installed and NTPprepare never ran.
|
||||||
|
bank=${vec:0:2}
|
||||||
|
if [ $((16#$bank)) -ge 240 ]; then
|
||||||
|
echo "verify-iigs-audio: FAIL (sound interrupt vector $vec still points into ROM bank \$$bank - NTPprepare never ran)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Audible-output precondition: the DOC master volume must not be zero.
|
||||||
|
docVol=$(field docVol "$lineB")
|
||||||
|
if [ $(( 16#$docVol & 0x0F )) -eq 0 ]; then
|
||||||
|
echo "verify-iigs-audio: FAIL (DOC master volume is 0 -- \$E100CA=0x$docVol. The Sound Tool Set was never started, so NTP copies a zero volume into \$C03C: the DOC streams but nothing is audible)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$(field slot0NZ "$lineA")" = "-1" ]; then
|
||||||
|
echo "verify-iigs-audio: FAIL (MAME exposed no :doc/rom space - the DOC RAM probe read nothing)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
loud=""
|
||||||
|
moved=""
|
||||||
|
slot=0
|
||||||
|
while [ "$slot" -lt "$slots" ]; do
|
||||||
|
nz=$(field "slot${slot}NZ" "$lineB")
|
||||||
|
if [ "$nz" -gt 0 ]; then
|
||||||
|
loud="$loud $slot"
|
||||||
|
fi
|
||||||
|
if [ "$(field "slot$slot" "$lineA")" != "$(field "slot$slot" "$lineB")" ]; then
|
||||||
|
moved="$moved $slot"
|
||||||
|
fi
|
||||||
|
slot=$((slot + 1))
|
||||||
|
done
|
||||||
|
if [ -z "$loud" ]; then
|
||||||
|
echo "verify-iigs-audio: FAIL (no SFX slot's DOC page holds a waveform - NTPstreamsound never reached the DOC)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# A live NTP interrupt keeps rewriting the DOC page it streams through.
|
||||||
|
# A slot parked on the resident tone square (toneSquareEnsure) hashes
|
||||||
|
# the same every time -- every refill writes the same square -- so this
|
||||||
|
# only warns when NO slot moved at all.
|
||||||
|
if [ -z "$moved" ]; then
|
||||||
|
echo "verify-iigs-audio: WARN (no DOC slot changed between frames $probeA and $probeB - samples are present but nothing is refilling them)"
|
||||||
|
fi
|
||||||
|
echo "verify-iigs-audio: PASS ($NAME: NTP interrupt installed at bank $bank; DOC slots with a waveform:$loud; refilled between probes:$moved)"
|
||||||
|
|
@ -79,9 +79,29 @@ extern UBYTE mt_E8Trigger;
|
||||||
|
|
||||||
// ----- Module state -----
|
// ----- Module state -----
|
||||||
|
|
||||||
|
// Streaming SFX chunk size, in bytes. PTPlayer's mt_playfx plays a
|
||||||
|
// one-shot sample and does not queue, so a stream is played one chunk at
|
||||||
|
// a time with the next one fired from jlpAudioFrameTick when the current
|
||||||
|
// is due to end. Bigger chunks mean fewer seams; 4 KB is ~0.8 s at 5 kHz,
|
||||||
|
// which covers a whole speech sample with no seam at all.
|
||||||
|
#define SFX_STREAM_CHUNK 4096u
|
||||||
|
|
||||||
|
// The host calls jlpAudioFrameTick once per display frame, and this port
|
||||||
|
// is PAL throughout (see PAULA_CLOCK_PAL in periodForRate), so chunk
|
||||||
|
// lifetimes are counted in 50 Hz frames.
|
||||||
|
#define AMIGA_FRAME_HZ 50u
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
UBYTE *samples; // Chip-RAM copy of one SFX sample
|
UBYTE *samples; // Chip-RAM copy of one SFX sample
|
||||||
UWORD lengthWords; // sample length in 16-bit words
|
UWORD lengthWords; // sample length in 16-bit words
|
||||||
|
// ---- streaming (jlpAudioPlaySfxStream) ----
|
||||||
|
UBYTE *chunk; // Chip-RAM chunk buffer, SFX_STREAM_CHUNK
|
||||||
|
BYTE cha; // Paula channel, -1 = let PTPlayer pick
|
||||||
|
jlAudioStreamFillT fill;
|
||||||
|
void *ctx;
|
||||||
|
uint16_t rateHz;
|
||||||
|
uint16_t framesLeft; // until the playing chunk runs out
|
||||||
|
bool active;
|
||||||
} SfxSlotT;
|
} SfxSlotT;
|
||||||
|
|
||||||
static UBYTE *gModuleChip = NULL; // Chip-RAM copy of the playing module
|
static UBYTE *gModuleChip = NULL; // Chip-RAM copy of the playing module
|
||||||
|
|
@ -157,6 +177,9 @@ bool jlpAudioInit(void) {
|
||||||
for (i = 0; i < JOEY_AUDIO_SFX_SLOTS; i++) {
|
for (i = 0; i < JOEY_AUDIO_SFX_SLOTS; i++) {
|
||||||
gSfxSlots[i].samples = NULL;
|
gSfxSlots[i].samples = NULL;
|
||||||
gSfxSlots[i].lengthWords = 0;
|
gSfxSlots[i].lengthWords = 0;
|
||||||
|
// -1, not 0: zero is Paula channel 0, which would silently pin every
|
||||||
|
// slot hard left instead of letting PTPlayer choose.
|
||||||
|
gSfxSlots[i].cha = -1;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -202,6 +225,11 @@ void jlpAudioShutdown(void) {
|
||||||
gSfxSlots[i].samples = NULL;
|
gSfxSlots[i].samples = NULL;
|
||||||
gSfxSlots[i].lengthWords = 0;
|
gSfxSlots[i].lengthWords = 0;
|
||||||
}
|
}
|
||||||
|
gSfxSlots[i].active = false;
|
||||||
|
if (gSfxSlots[i].chunk != NULL) {
|
||||||
|
FreeMem(gSfxSlots[i].chunk, (ULONG)SFX_STREAM_CHUNK);
|
||||||
|
gSfxSlots[i].chunk = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -247,6 +275,42 @@ void jlpAudioPlayMod(const uint8_t *data, uint32_t length, bool loop) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Pull one chunk from a streaming slot and hand it to PTPlayer.
|
||||||
|
// Returns false when the stream has ended (fill returned 0).
|
||||||
|
static bool streamChunk(uint8_t slot) {
|
||||||
|
SfxSlotT *s = &gSfxSlots[slot];
|
||||||
|
SfxStructure sfx;
|
||||||
|
uint32_t got;
|
||||||
|
|
||||||
|
// The fill contract is SIGNED 8-bit, which is exactly what Paula
|
||||||
|
// wants, so the callback writes straight into Chip RAM.
|
||||||
|
got = s->fill(s->ctx, (int8_t *)s->chunk, (uint32_t)SFX_STREAM_CHUNK);
|
||||||
|
if (got < 2u || got > (uint32_t)SFX_STREAM_CHUNK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
got &= ~1u; // Paula counts 16-bit words
|
||||||
|
|
||||||
|
sfx.sfx_ptr = s->chunk;
|
||||||
|
sfx.sfx_len = (UWORD)(got >> 1);
|
||||||
|
sfx.sfx_per = periodForRate(s->rateHz);
|
||||||
|
sfx.sfx_vol = SFX_VOLUME_MAX;
|
||||||
|
sfx.sfx_cha = s->cha;
|
||||||
|
sfx.sfx_pri = (BYTE)(slot + 1);
|
||||||
|
mt_playfx((void *)&custom, &sfx);
|
||||||
|
|
||||||
|
// Fire the next chunk a frame early rather than leaving a gap.
|
||||||
|
{
|
||||||
|
uint32_t frames = (got * (uint32_t)AMIGA_FRAME_HZ) / (uint32_t)s->rateHz;
|
||||||
|
|
||||||
|
if (frames > 1u) {
|
||||||
|
frames--;
|
||||||
|
}
|
||||||
|
s->framesLeft = (uint16_t)frames;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void jlpAudioPlaySfx(uint8_t slot, const uint8_t *sample, uint32_t length, uint16_t rateHz) {
|
void jlpAudioPlaySfx(uint8_t slot, const uint8_t *sample, uint32_t length, uint16_t rateHz) {
|
||||||
SfxStructure sfx;
|
SfxStructure sfx;
|
||||||
SfxSlotT *s;
|
SfxSlotT *s;
|
||||||
|
|
@ -281,22 +345,42 @@ void jlpAudioPlaySfx(uint8_t slot, const uint8_t *sample, uint32_t length, uint1
|
||||||
sfx.sfx_len = words;
|
sfx.sfx_len = words;
|
||||||
sfx.sfx_per = periodForRate(rateHz);
|
sfx.sfx_per = periodForRate(rateHz);
|
||||||
sfx.sfx_vol = SFX_VOLUME_MAX;
|
sfx.sfx_vol = SFX_VOLUME_MAX;
|
||||||
sfx.sfx_cha = -1; // best free channel
|
sfx.sfx_cha = s->cha; // -1 = best free channel
|
||||||
sfx.sfx_pri = (BYTE)(slot + 1); // priority 1..N from slot index
|
sfx.sfx_pri = (BYTE)(slot + 1); // priority 1..N from slot index
|
||||||
mt_playfx((void *)&custom, &sfx);
|
mt_playfx((void *)&custom, &sfx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Streaming SFX is not wired on Amiga: PTPlayer's mt_playfx wants a
|
// Streaming SFX. PTPlayer's mt_playfx wants a complete sample at fire
|
||||||
// complete sample at fire time. Implementing streaming here would
|
// time, so the stream is chunked rather than refilled mid-playback:
|
||||||
// need a per-slot Paula buffer with mid-playback refill from the
|
// arm the slot, push the first chunk now, and let jlpAudioFrameTick
|
||||||
// audio IRQ. No-op until then; callers that depend on streaming
|
// push each following one as the previous is due to run out. The seam
|
||||||
// will hear silence on Amiga.
|
// therefore quantises to a 50 Hz frame, which is the jitter the public
|
||||||
|
// contract documents. Each slot gets one SFX_STREAM_CHUNK Chip-RAM
|
||||||
|
// buffer, allocated on first use and freed in jlpAudioShutdown.
|
||||||
void jlpAudioPlaySfxStream(uint8_t slot, jlAudioStreamFillT fill, void *ctx, uint16_t rateHz) {
|
void jlpAudioPlaySfxStream(uint8_t slot, jlAudioStreamFillT fill, void *ctx, uint16_t rateHz) {
|
||||||
(void)slot;
|
SfxSlotT *s;
|
||||||
(void)fill;
|
|
||||||
(void)ctx;
|
if (!gInstalled || slot >= JOEY_AUDIO_SFX_SLOTS) {
|
||||||
(void)rateHz;
|
return;
|
||||||
|
}
|
||||||
|
if (fill == (jlAudioStreamFillT)0 || rateHz == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s = &gSfxSlots[slot];
|
||||||
|
if (s->chunk == NULL) {
|
||||||
|
s->chunk = (UBYTE *)AllocMem((ULONG)SFX_STREAM_CHUNK, MEMF_CHIP);
|
||||||
|
if (s->chunk == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s->fill = fill;
|
||||||
|
s->ctx = ctx;
|
||||||
|
s->rateHz = rateHz;
|
||||||
|
s->active = true;
|
||||||
|
if (!streamChunk(slot)) {
|
||||||
|
s->active = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -429,12 +513,36 @@ void jlpAudioStopMod(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Paula pans per CHANNEL: 0 and 3 feed the left output, 1 and 2 the right.
|
||||||
|
// One voice cannot reach both ears, so AUDIO_PAN_CENTER keeps PTPlayer's own
|
||||||
|
// "best free channel" pick (sfx_cha = -1) -- which is what every slot did
|
||||||
|
// before this call existed, so the default behaviour is unchanged. A panned
|
||||||
|
// slot pins its SFX to a channel on that side instead.
|
||||||
|
void jlpAudioSetPan(uint8_t slot, uint8_t pan) {
|
||||||
|
if (slot >= JOEY_AUDIO_SFX_SLOTS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (pan) {
|
||||||
|
case AUDIO_PAN_LEFT:
|
||||||
|
gSfxSlots[slot].cha = 3;
|
||||||
|
break;
|
||||||
|
case AUDIO_PAN_RIGHT:
|
||||||
|
gSfxSlots[slot].cha = 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
gSfxSlots[slot].cha = -1; // PTPlayer picks
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void jlpAudioStopSfx(uint8_t slot) {
|
void jlpAudioStopSfx(uint8_t slot) {
|
||||||
UBYTE i;
|
UBYTE i;
|
||||||
|
|
||||||
if (!gInstalled || slot >= JOEY_AUDIO_SFX_SLOTS) {
|
if (!gInstalled || slot >= JOEY_AUDIO_SFX_SLOTS) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
gSfxSlots[slot].active = false;
|
||||||
// PTPlayer addresses SFX by Paula channel (0..3), but mt_playfx
|
// PTPlayer addresses SFX by Paula channel (0..3), but mt_playfx
|
||||||
// picks a channel dynamically and does not return which one. We
|
// picks a channel dynamically and does not return which one. We
|
||||||
// can't reliably map slot -> channel after the fact, so stop all
|
// can't reliably map slot -> channel after the fact, so stop all
|
||||||
|
|
@ -447,8 +555,27 @@ void jlpAudioStopSfx(uint8_t slot) {
|
||||||
|
|
||||||
|
|
||||||
void jlpAudioFrameTick(void) {
|
void jlpAudioFrameTick(void) {
|
||||||
// PTPlayer drives itself off CIA-B; the only host-loop work is the
|
// Streaming slots: mt_playfx plays one chunk and stops, so the next
|
||||||
// play-once watchdog. When the song author placed an `E8FF` at
|
// chunk goes out here as the previous one runs down.
|
||||||
|
{
|
||||||
|
uint8_t slot;
|
||||||
|
|
||||||
|
for (slot = 0u; slot < JOEY_AUDIO_SFX_SLOTS; slot++) {
|
||||||
|
if (!gSfxSlots[slot].active) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (gSfxSlots[slot].framesLeft != 0u) {
|
||||||
|
gSfxSlots[slot].framesLeft--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!streamChunk(slot)) {
|
||||||
|
gSfxSlots[slot].active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PTPlayer drives itself off CIA-B; the only other host-loop work is
|
||||||
|
// the play-once watchdog. When the song author placed an `E8FF` at
|
||||||
// song end, mt_E8Trigger latches to 0xFF -- if the caller passed
|
// song end, mt_E8Trigger latches to 0xFF -- if the caller passed
|
||||||
// loop=false to PlayMod, that's our cue to stop the song.
|
// loop=false to PlayMod, that's our cue to stop the song.
|
||||||
if (gWantLoopOnce && gModPlaying && mt_E8Trigger == PTPLAYER_LOOP_END_MARKER) {
|
if (gWantLoopOnce && gModPlaying && mt_E8Trigger == PTPLAYER_LOOP_END_MARKER) {
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@ bool jlSpriteCompile(jlSpriteT *sp) {
|
||||||
// Copy the staged image into the slot in one shot. routineOffsets are
|
// Copy the staged image into the slot in one shot. routineOffsets are
|
||||||
// already slot-relative (offset within `staging` == offset within the
|
// already slot-relative (offset within `staging` == offset within the
|
||||||
// slot after this copy), so nothing else needs fixing up.
|
// slot after this copy), so nothing else needs fixing up.
|
||||||
memcpy(codegenArenaBase() + slot->offset, staging, (size_t)total);
|
memcpy(codegenArenaSlotBase(slot), staging, (size_t)total);
|
||||||
sp->slot = slot;
|
sp->slot = slot;
|
||||||
// Registry (#34): every creation path (including jlSpriteBankLoad,
|
// Registry (#34): every creation path (including jlSpriteBankLoad,
|
||||||
// #32) already registered sp via spriteInitFields; this idempotent
|
// #32) already registered sp via spriteInitFields; this idempotent
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,22 @@ typedef char AssetTileSizeCheckT[(sizeof(jlTileT) == TILE_BYTES) ? 1 : -1];
|
||||||
#error "Unknown platform for asset loader"
|
#error "Unknown platform for asset loader"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Per-platform target byte for .spc files (pre-compiled sprite code, so
|
||||||
|
// it is per MACHINE CODE, not per pixel format: the X68000 shares DOS's
|
||||||
|
// chunky tile byte above but runs 68k planar sprite routines, and must
|
||||||
|
// never accept an x86 bank). Mirrors SPC_TARGET in tools/spritebake.
|
||||||
|
#if defined(JOEYLIB_PLATFORM_AMIGA)
|
||||||
|
#define EXPECTED_SPC_TARGET 1u
|
||||||
|
#elif defined(JOEYLIB_PLATFORM_ATARIST)
|
||||||
|
#define EXPECTED_SPC_TARGET 2u
|
||||||
|
#elif defined(JOEYLIB_PLATFORM_DOS) || defined(JOEYLIB_PLATFORM_BLANK)
|
||||||
|
#define EXPECTED_SPC_TARGET 3u
|
||||||
|
#elif defined(JOEYLIB_PLATFORM_IIGS)
|
||||||
|
#define EXPECTED_SPC_TARGET 4u
|
||||||
|
#elif defined(JOEYLIB_PLATFORM_X68000)
|
||||||
|
#define EXPECTED_SPC_TARGET 5u
|
||||||
|
#endif
|
||||||
|
|
||||||
// ----- Prototypes -----
|
// ----- Prototypes -----
|
||||||
|
|
||||||
static bool openReadValidateHeader(const char *path, const char *magic, uint8_t expectedTarget, uint8_t *header, uint16_t *outPalette, FILE **outFp);
|
static bool openReadValidateHeader(const char *path, const char *magic, uint8_t expectedTarget, uint8_t *header, uint16_t *outPalette, FILE **outFp);
|
||||||
|
|
@ -290,8 +306,8 @@ uint16_t jlSpriteBankLoadPrecompiled(const char *path, jlSpriteT **outCels, uint
|
||||||
return 0u;
|
return 0u;
|
||||||
}
|
}
|
||||||
// "JSC1" + the per-machine target byte (pre-compiled code is machine-
|
// "JSC1" + the per-machine target byte (pre-compiled code is machine-
|
||||||
// specific, validated like a .tbk via the shared EXPECTED target).
|
// specific, validated like a .tbk but against the .spc target table).
|
||||||
if (!openReadValidateHeader(path, SPC_MAGIC, EXPECTED_TILE_TARGET, header, outPalette, &fp)) {
|
if (!openReadValidateHeader(path, SPC_MAGIC, EXPECTED_SPC_TARGET, header, outPalette, &fp)) {
|
||||||
return 0u;
|
return 0u;
|
||||||
}
|
}
|
||||||
widthTiles = header[SPC_OFF_WIDTH_TILES];
|
widthTiles = header[SPC_OFF_WIDTH_TILES];
|
||||||
|
|
@ -400,7 +416,7 @@ uint16_t jlSpriteBankLoadPrecompiled(const char *path, jlSpriteT **outCels, uint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (slot != NULL) {
|
if (slot != NULL) {
|
||||||
uint8_t *slotBase = codegenArenaBase() + slot->offset;
|
uint8_t *slotBase = codegenArenaSlotBase(slot);
|
||||||
|
|
||||||
memcpy(slotBase, imageBuf, (size_t)imageSize);
|
memcpy(slotBase, imageBuf, (size_t)imageSize);
|
||||||
for (shift = 0; shift < shiftCount; shift++) {
|
for (shift = 0; shift < shiftCount; shift++) {
|
||||||
|
|
@ -470,7 +486,7 @@ bool jlSpriteBankSavePrecompiled(const char *path, jlSpriteT **cels, uint16_t co
|
||||||
|
|
||||||
// Shared 44-byte header (mirrors tools/spritebake + jlSpriteBankLoadPrecompiled).
|
// Shared 44-byte header (mirrors tools/spritebake + jlSpriteBankLoadPrecompiled).
|
||||||
fwrite(SPC_MAGIC, 1, 4, fp);
|
fwrite(SPC_MAGIC, 1, 4, fp);
|
||||||
fputc((int)EXPECTED_TILE_TARGET, fp);
|
fputc((int)EXPECTED_SPC_TARGET, fp);
|
||||||
fputc(palette != NULL ? 1 : 0, fp);
|
fputc(palette != NULL ? 1 : 0, fp);
|
||||||
fputc((int)widthTiles, fp);
|
fputc((int)widthTiles, fp);
|
||||||
fputc((int)heightTiles, fp);
|
fputc((int)heightTiles, fp);
|
||||||
|
|
@ -534,7 +550,7 @@ bool jlSpriteBankSavePrecompiled(const char *path, jlSpriteT **cels, uint16_t co
|
||||||
}
|
}
|
||||||
// image: the compiled routines, straight from the arena slot.
|
// image: the compiled routines, straight from the arena slot.
|
||||||
if (imageSize > 0u) {
|
if (imageSize > 0u) {
|
||||||
fwrite(codegenArenaBase() + sp->slot->offset, 1, (size_t)imageSize, fp);
|
fwrite(codegenArenaSlotBase(sp->slot), 1, (size_t)imageSize, fp);
|
||||||
}
|
}
|
||||||
for (r = 0; r < relocCount; r++) {
|
for (r = 0; r < relocCount; r++) {
|
||||||
fputc((int)(relocOffsets[r] & 0xFFu), fp);
|
fputc((int)(relocOffsets[r] & 0xFFu), fp);
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,22 @@ void jlAudioPlaySfxStream(uint8_t slot, jlAudioStreamFillT fill, void *ctx, uint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlAudioPlaySfxStreamRaw(uint8_t slot, jlAudioStreamFillT fill, void *ctx, uint16_t rateHz) {
|
||||||
|
if (!gAudioReady || fill == NULL || rateHz == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (slot >= JOEY_AUDIO_SFX_SLOTS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
jlpAudioPlaySfxStreamRaw(slot, fill, ctx, rateHz);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t jlAudioSfxRawFormat(void) {
|
||||||
|
return jlpAudioSfxRawFormat();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void jlAudioTone(uint16_t freqHz) {
|
void jlAudioTone(uint16_t freqHz) {
|
||||||
// Deliberately NOT gated on gAudioReady -- jlAudioInit is for
|
// Deliberately NOT gated on gAudioReady -- jlAudioInit is for
|
||||||
// PCM/SFX paths that need DMA + libxmp wiring. The PC speaker
|
// PCM/SFX paths that need DMA + libxmp wiring. The PC speaker
|
||||||
|
|
@ -124,6 +140,17 @@ void jlAudioCriticalExit(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlAudioSetPan(uint8_t slot, jlAudioPanE pan) {
|
||||||
|
if (!gAudioReady || slot >= JOEY_AUDIO_SFX_SLOTS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (pan >= AUDIO_PAN_COUNT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
jlpAudioSetPan(slot, (uint8_t)pan);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void jlAudioStopSfx(uint8_t slot) {
|
void jlAudioStopSfx(uint8_t slot) {
|
||||||
if (!gAudioReady || slot >= JOEY_AUDIO_SFX_SLOTS) {
|
if (!gAudioReady || slot >= JOEY_AUDIO_SFX_SLOTS) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -140,10 +167,12 @@ void jlAudioFrameTick(void) {
|
||||||
if (!gAudioReady) {
|
if (!gAudioReady) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#ifndef JOEYLIB_PLATFORM_IIGS
|
// This used to be #ifndef'd out on the IIgs, on the grounds that
|
||||||
// IIgs: NTPstreamsound is fully DOC-IRQ-driven, jlpAudioFrameTick
|
// NTPstreamsound is DOC-IRQ driven so jlpAudioFrameTick "is an empty
|
||||||
// is an empty no-op there. Skip the wrapper JSL entirely on IIgs
|
// no-op there". It is not: it pumps a registered jlAudioTickRegister
|
||||||
// so per-frame audio cost stays at the gAudioReady branch above.
|
// callback, and since streaming SFX landed it also fires the next
|
||||||
|
// chunk of every active stream. Skipping it meant a stream played
|
||||||
|
// its first chunk and then stopped forever -- Space Taxi spoke its
|
||||||
|
// first word and never another.
|
||||||
jlpAudioFrameTick();
|
jlpAudioFrameTick();
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
// Codegen arena: free-list allocator with adjacent-hole coalescing
|
// Codegen arena: free-list allocator with adjacent-hole coalescing
|
||||||
// and manual compaction. See codegenArenaInternal.h for the contract.
|
// and manual compaction, over one bank (most ports) or several 64 KB
|
||||||
|
// banks (IIgs). See codegenArenaInternal.h for the contract.
|
||||||
|
|
||||||
#include "joey/platform.h"
|
#include "joey/platform.h"
|
||||||
|
|
||||||
|
|
@ -22,6 +23,7 @@
|
||||||
#define attrLocked 0x8000
|
#define attrLocked 0x8000
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
|
@ -30,36 +32,154 @@
|
||||||
|
|
||||||
|
|
||||||
// ----- Module state -----
|
// ----- Module state -----
|
||||||
|
//
|
||||||
// gCodegenArenaBase / gCodegenArenaBaseAddr are non-static so jlSpriteCompile.c can read them
|
// gCodegenBankBase / gCodegenBankAddr are non-static so the sprite
|
||||||
// directly via extern instead of paying a JSL/RTL per access through
|
// dispatch reads them directly via extern instead of paying a JSL/RTL
|
||||||
// the codegenArenaBase() / codegenArenaBaseAddr() wrappers. Both are
|
// per draw through a wrapper. They are set once at codegenArenaInit and
|
||||||
// set once at codegenArenaInit and never moved (the underlying
|
// never moved (a bank is locked in place on the IIgs). Callers MUST
|
||||||
// Memory Manager handle is locked-in-place on IIgs). Callers MUST
|
|
||||||
// treat them as read-only.
|
// treat them as read-only.
|
||||||
uint8_t *gCodegenArenaBase = NULL;
|
|
||||||
// gCodegenArenaBaseAddr mirrors gCodegenArenaBase as a 24-bit absolute
|
uint8_t *gCodegenBankBase[CODEGEN_ARENA_BANKS_MAX];
|
||||||
// address that JSL targets read directly (captured by copying the
|
uint32_t gCodegenBankAddr[CODEGEN_ARENA_BANKS_MAX];
|
||||||
// handle pointer's raw bytes -- see codegenArenaInit).
|
static uint32_t gBankSize[CODEGEN_ARENA_BANKS_MAX];
|
||||||
uint32_t gCodegenArenaBaseAddr = 0;
|
static ArenaSlotT *gBankFirst[CODEGEN_ARENA_BANKS_MAX];
|
||||||
|
#if defined(JOEYLIB_PLATFORM_IIGS)
|
||||||
|
static void *gBankHandle[CODEGEN_ARENA_BANKS_MAX];
|
||||||
|
#endif
|
||||||
|
static uint8_t gBankCount = 0u;
|
||||||
static uint32_t gTotalBytes = 0;
|
static uint32_t gTotalBytes = 0;
|
||||||
static uint32_t gUsedBytes = 0;
|
static uint32_t gUsedBytes = 0;
|
||||||
static ArenaSlotT *gFirstSlot = NULL;
|
|
||||||
#if defined(JOEYLIB_PLATFORM_IIGS)
|
|
||||||
static void *gCodegenArenaBaseHandle = NULL;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// ----- Prototypes -----
|
// ----- Prototypes -----
|
||||||
|
|
||||||
static ArenaSlotT *newSlot(uint32_t offset, uint32_t size, bool used);
|
static bool bankAlloc(uint8_t bank, uint32_t bytes);
|
||||||
|
static void bankCompact(uint8_t bank);
|
||||||
|
static void bankFree(uint8_t bank);
|
||||||
static void coalesceWithNext(ArenaSlotT *slot);
|
static void coalesceWithNext(ArenaSlotT *slot);
|
||||||
|
static ArenaSlotT *newSlot(uint32_t offset, uint32_t size, bool used);
|
||||||
|
|
||||||
|
|
||||||
// ----- Internal helpers (alphabetical) -----
|
// ----- Helpers -----
|
||||||
|
|
||||||
|
// One bank's memory. On the IIgs a Memory Manager block that may not
|
||||||
|
// cross a bank boundary (a normal C heap allocation returns bank-0
|
||||||
|
// system RAM, and JSL'ing into that lands on whatever lives there);
|
||||||
|
// attrFixed | attrLocked | attrPage | attrNoCross gives a fixed
|
||||||
|
// page-aligned region in one bank. Elsewhere, malloc.
|
||||||
|
static bool bankAlloc(uint8_t bank, uint32_t bytes) {
|
||||||
|
#if defined(JOEYLIB_PLATFORM_IIGS)
|
||||||
|
void *h;
|
||||||
|
void *p;
|
||||||
|
uint8_t raw[4];
|
||||||
|
|
||||||
|
h = NewHandle((unsigned long)bytes, MMStartUp(), attrFixed | attrLocked | attrPage | attrNoCross, (void *)0);
|
||||||
|
if (h == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
HLock(h);
|
||||||
|
p = *(void **)h;
|
||||||
|
if (p == NULL) {
|
||||||
|
DisposeHandle(h);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// The 24-bit absolute address is the master pointer's raw bytes;
|
||||||
|
// byte 3 (bank-high/flags) is masked off.
|
||||||
|
memcpy(raw, &p, 4);
|
||||||
|
gBankHandle[bank] = h;
|
||||||
|
gCodegenBankBase[bank] = (uint8_t *)p;
|
||||||
|
gCodegenBankAddr[bank] = (uint32_t)raw[0] | ((uint32_t)raw[1] << 8) | ((uint32_t)raw[2] << 16);
|
||||||
|
#else
|
||||||
|
uint8_t *p = (uint8_t *)malloc(bytes);
|
||||||
|
|
||||||
|
if (p == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
gCodegenBankBase[bank] = p;
|
||||||
|
gCodegenBankAddr[bank] = (uint32_t)(uintptr_t)p;
|
||||||
|
#endif
|
||||||
|
gBankSize[bank] = bytes;
|
||||||
|
gBankFirst[bank] = newSlot((uint32_t)bank << CODEGEN_ARENA_BANK_SHIFT, bytes, false);
|
||||||
|
if (gBankFirst[bank] == NULL) {
|
||||||
|
bankFree(bank);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void bankCompact(uint8_t bank) {
|
||||||
|
uint8_t *base = gCodegenBankBase[bank];
|
||||||
|
ArenaSlotT *slot;
|
||||||
|
ArenaSlotT *next;
|
||||||
|
ArenaSlotT *trailing;
|
||||||
|
uint32_t cursor = 0;
|
||||||
|
|
||||||
|
slot = gBankFirst[bank];
|
||||||
|
while (slot != NULL) {
|
||||||
|
next = slot->next;
|
||||||
|
if (slot->used) {
|
||||||
|
uint32_t at = slot->offset & CODEGEN_ARENA_BANK_MASK;
|
||||||
|
if (at != cursor) {
|
||||||
|
memmove(base + cursor, base + at, slot->size);
|
||||||
|
slot->offset = ((uint32_t)bank << CODEGEN_ARENA_BANK_SHIFT) | cursor;
|
||||||
|
}
|
||||||
|
cursor += slot->size;
|
||||||
|
slot = next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (slot->prev != NULL) {
|
||||||
|
slot->prev->next = next;
|
||||||
|
} else {
|
||||||
|
gBankFirst[bank] = next;
|
||||||
|
}
|
||||||
|
if (next != NULL) {
|
||||||
|
next->prev = slot->prev;
|
||||||
|
}
|
||||||
|
free(slot);
|
||||||
|
slot = next;
|
||||||
|
}
|
||||||
|
if (cursor < gBankSize[bank]) {
|
||||||
|
trailing = newSlot(((uint32_t)bank << CODEGEN_ARENA_BANK_SHIFT) | cursor, gBankSize[bank] - cursor, false);
|
||||||
|
if (trailing == NULL) {
|
||||||
|
return; // Compaction succeeded; just skip the free-slot record.
|
||||||
|
}
|
||||||
|
trailing->prev = NULL;
|
||||||
|
for (slot = gBankFirst[bank]; slot != NULL && slot->next != NULL; slot = slot->next) {
|
||||||
|
}
|
||||||
|
if (slot == NULL) {
|
||||||
|
gBankFirst[bank] = trailing;
|
||||||
|
} else {
|
||||||
|
slot->next = trailing;
|
||||||
|
trailing->prev = slot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void bankFree(uint8_t bank) {
|
||||||
|
ArenaSlotT *slot;
|
||||||
|
ArenaSlotT *next;
|
||||||
|
|
||||||
|
for (slot = gBankFirst[bank]; slot != NULL; slot = next) {
|
||||||
|
next = slot->next;
|
||||||
|
free(slot);
|
||||||
|
}
|
||||||
|
#if defined(JOEYLIB_PLATFORM_IIGS)
|
||||||
|
if (gBankHandle[bank] != NULL) {
|
||||||
|
DisposeHandle(gBankHandle[bank]);
|
||||||
|
gBankHandle[bank] = NULL;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
free(gCodegenBankBase[bank]);
|
||||||
|
#endif
|
||||||
|
gCodegenBankBase[bank] = NULL;
|
||||||
|
gCodegenBankAddr[bank] = 0;
|
||||||
|
gBankSize[bank] = 0;
|
||||||
|
gBankFirst[bank] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// If `slot` is free and slot->next is also free, merge them into a
|
|
||||||
// single free slot. Caller must already have ensured slot is free.
|
|
||||||
static void coalesceWithNext(ArenaSlotT *slot) {
|
static void coalesceWithNext(ArenaSlotT *slot) {
|
||||||
ArenaSlotT *victim;
|
ArenaSlotT *victim;
|
||||||
|
|
||||||
|
|
@ -95,49 +215,43 @@ static ArenaSlotT *newSlot(uint32_t offset, uint32_t size, bool used) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ----- Public-internal API (alphabetical) -----
|
// ----- Public API -----
|
||||||
|
|
||||||
ArenaSlotT *codegenArenaAlloc(uint32_t bytes) {
|
ArenaSlotT *codegenArenaAlloc(uint32_t bytes) {
|
||||||
ArenaSlotT *slot;
|
ArenaSlotT *slot;
|
||||||
ArenaSlotT *remainder;
|
ArenaSlotT *remainder;
|
||||||
|
uint8_t bank;
|
||||||
|
|
||||||
if (gCodegenArenaBase == NULL || bytes == 0) {
|
if (gBankCount == 0u || bytes == 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
for (slot = gFirstSlot; slot != NULL; slot = slot->next) {
|
for (bank = 0u; bank < gBankCount; bank++) {
|
||||||
if (slot->used || slot->size < bytes) {
|
for (slot = gBankFirst[bank]; slot != NULL; slot = slot->next) {
|
||||||
continue;
|
if (slot->used || slot->size < bytes) {
|
||||||
}
|
continue;
|
||||||
// First fit. If there's slack, split: shrink this slot to
|
|
||||||
// exactly `bytes` and insert a new free slot for the rest.
|
|
||||||
if (slot->size > bytes) {
|
|
||||||
remainder = newSlot(slot->offset + bytes, slot->size - bytes, false);
|
|
||||||
if (remainder == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
remainder->prev = slot;
|
if (slot->size > bytes) {
|
||||||
remainder->next = slot->next;
|
remainder = newSlot(slot->offset + bytes, slot->size - bytes, false);
|
||||||
if (slot->next != NULL) {
|
if (remainder == NULL) {
|
||||||
slot->next->prev = remainder;
|
return NULL;
|
||||||
|
}
|
||||||
|
remainder->prev = slot;
|
||||||
|
remainder->next = slot->next;
|
||||||
|
if (slot->next != NULL) {
|
||||||
|
slot->next->prev = remainder;
|
||||||
|
}
|
||||||
|
slot->next = remainder;
|
||||||
|
slot->size = bytes;
|
||||||
}
|
}
|
||||||
slot->next = remainder;
|
slot->used = true;
|
||||||
slot->size = bytes;
|
gUsedBytes += bytes;
|
||||||
|
return slot;
|
||||||
}
|
}
|
||||||
slot->used = true;
|
|
||||||
gUsedBytes += bytes;
|
|
||||||
return slot;
|
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// codegenArenaBase() / codegenArenaBaseAddr() are now header-only
|
|
||||||
// macros that read gCodegenArenaBase / gCodegenArenaBaseAddr
|
|
||||||
// directly, so the C function bodies that used to live here are
|
|
||||||
// gone. The wrappers cost ~30 cyc per call on IIgs and were hit
|
|
||||||
// 3x per sprite frame.
|
|
||||||
|
|
||||||
|
|
||||||
uint32_t codegenArenaBytesTotal(void) {
|
uint32_t codegenArenaBytesTotal(void) {
|
||||||
return gTotalBytes;
|
return gTotalBytes;
|
||||||
}
|
}
|
||||||
|
|
@ -149,63 +263,16 @@ uint32_t codegenArenaBytesUsed(void) {
|
||||||
|
|
||||||
|
|
||||||
void codegenArenaCompact(void) {
|
void codegenArenaCompact(void) {
|
||||||
ArenaSlotT *slot;
|
uint8_t bank;
|
||||||
ArenaSlotT *next;
|
|
||||||
ArenaSlotT *trailing;
|
|
||||||
uint32_t cursor;
|
|
||||||
|
|
||||||
if (gCodegenArenaBase == NULL) {
|
for (bank = 0u; bank < gBankCount; bank++) {
|
||||||
return;
|
bankCompact(bank);
|
||||||
}
|
|
||||||
cursor = 0;
|
|
||||||
slot = gFirstSlot;
|
|
||||||
while (slot != NULL) {
|
|
||||||
next = slot->next;
|
|
||||||
if (slot->used) {
|
|
||||||
if (slot->offset != cursor) {
|
|
||||||
memmove(gCodegenArenaBase + cursor, gCodegenArenaBase + slot->offset, slot->size);
|
|
||||||
slot->offset = cursor;
|
|
||||||
}
|
|
||||||
cursor += slot->size;
|
|
||||||
slot = next;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Free slot: drop from the list. The caller-side ArenaSlotT*
|
|
||||||
// for any free slot was already invalidated when it was
|
|
||||||
// freed (coalesce released the struct); nothing live points
|
|
||||||
// at it.
|
|
||||||
if (slot->prev != NULL) {
|
|
||||||
slot->prev->next = next;
|
|
||||||
} else {
|
|
||||||
gFirstSlot = next;
|
|
||||||
}
|
|
||||||
if (next != NULL) {
|
|
||||||
next->prev = slot->prev;
|
|
||||||
}
|
|
||||||
free(slot);
|
|
||||||
slot = next;
|
|
||||||
}
|
|
||||||
if (cursor < gTotalBytes) {
|
|
||||||
trailing = newSlot(cursor, gTotalBytes - cursor, false);
|
|
||||||
if (trailing == NULL) {
|
|
||||||
return; // Compaction succeeded; just skip the free-slot record.
|
|
||||||
}
|
|
||||||
trailing->prev = NULL;
|
|
||||||
for (slot = gFirstSlot; slot != NULL && slot->next != NULL; slot = slot->next) {
|
|
||||||
// walk to last
|
|
||||||
}
|
|
||||||
if (slot == NULL) {
|
|
||||||
gFirstSlot = trailing;
|
|
||||||
} else {
|
|
||||||
slot->next = trailing;
|
|
||||||
trailing->prev = slot;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void codegenArenaFree(ArenaSlotT *slot) {
|
void codegenArenaFree(ArenaSlotT *slot) {
|
||||||
if (slot == NULL || gCodegenArenaBase == NULL) {
|
if (slot == NULL || gBankCount == 0u) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!slot->used) {
|
if (!slot->used) {
|
||||||
|
|
@ -220,100 +287,51 @@ void codegenArenaFree(ArenaSlotT *slot) {
|
||||||
|
|
||||||
|
|
||||||
bool codegenArenaInit(uint32_t totalBytes) {
|
bool codegenArenaInit(uint32_t totalBytes) {
|
||||||
if (gCodegenArenaBase != NULL) {
|
uint32_t want;
|
||||||
|
|
||||||
|
if (gBankCount != 0u) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (totalBytes == 0) {
|
if (totalBytes == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#if defined(JOEYLIB_PLATFORM_IIGS)
|
gTotalBytes = 0;
|
||||||
// attrNoCross forbids the block from crossing a 64KB bank boundary,
|
|
||||||
// so any request larger than one bank (65536 bytes) is unsatisfiable
|
|
||||||
// and NewHandle would always return NULL. Clamp to a single bank.
|
|
||||||
// The clamped request can still fail below -- a 64KB attrNoCross
|
|
||||||
// block needs a completely free bank -- and the caller's error
|
|
||||||
// message covers that.
|
|
||||||
if (totalBytes > 65536UL) {
|
|
||||||
totalBytes = 65536UL;
|
|
||||||
}
|
|
||||||
// MMStartUp() returns this application's master user ID (idempotent;
|
|
||||||
// the system starts the Memory Manager before the app runs). A NULL
|
|
||||||
// handle is the failure signal.
|
|
||||||
{
|
|
||||||
uint16_t ownerId;
|
|
||||||
|
|
||||||
ownerId = MMStartUp();
|
|
||||||
gCodegenArenaBaseHandle = NewHandle((unsigned long)totalBytes, ownerId,
|
|
||||||
attrFixed | attrLocked | attrPage | attrNoCross,
|
|
||||||
(void *)0);
|
|
||||||
}
|
|
||||||
if (gCodegenArenaBaseHandle == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
HLock(gCodegenArenaBaseHandle);
|
|
||||||
// Capture the 24-bit absolute address by copying the master
|
|
||||||
// pointer's raw bytes; bytes[3] (bank-high/flags) is masked off.
|
|
||||||
{
|
|
||||||
void *p;
|
|
||||||
uint8_t bytes[4];
|
|
||||||
|
|
||||||
p = *(void **)gCodegenArenaBaseHandle;
|
|
||||||
gCodegenArenaBase = (uint8_t *)p;
|
|
||||||
memcpy(bytes, &p, 4);
|
|
||||||
gCodegenArenaBaseAddr = (uint32_t)bytes[0]
|
|
||||||
| ((uint32_t)bytes[1] << 8)
|
|
||||||
| ((uint32_t)bytes[2] << 16);
|
|
||||||
}
|
|
||||||
if (gCodegenArenaBase == NULL) {
|
|
||||||
DisposeHandle(gCodegenArenaBaseHandle);
|
|
||||||
gCodegenArenaBaseHandle = NULL;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
gCodegenArenaBase = (uint8_t *)malloc(totalBytes);
|
|
||||||
if (gCodegenArenaBase == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
gCodegenArenaBaseAddr = (uint32_t)gCodegenArenaBase;
|
|
||||||
#endif
|
|
||||||
gFirstSlot = newSlot(0, totalBytes, false);
|
|
||||||
if (gFirstSlot == NULL) {
|
|
||||||
#if defined(JOEYLIB_PLATFORM_IIGS)
|
|
||||||
DisposeHandle(gCodegenArenaBaseHandle);
|
|
||||||
gCodegenArenaBaseHandle = NULL;
|
|
||||||
#else
|
|
||||||
free(gCodegenArenaBase);
|
|
||||||
#endif
|
|
||||||
gCodegenArenaBase = NULL;
|
|
||||||
gCodegenArenaBaseAddr = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
gTotalBytes = totalBytes;
|
|
||||||
gUsedBytes = 0;
|
gUsedBytes = 0;
|
||||||
return true;
|
#if defined(JOEYLIB_PLATFORM_IIGS)
|
||||||
|
// 64 KB banks until the request is met or the machine runs out: a
|
||||||
|
// whole bank needs a completely free one, so a bank that is refused
|
||||||
|
// is retried at 60 KB (any page-aligned run inside a bank) before
|
||||||
|
// giving up. Whatever was granted is the arena.
|
||||||
|
want = totalBytes;
|
||||||
|
while (want > 0u && gBankCount < CODEGEN_ARENA_BANKS_MAX) {
|
||||||
|
uint32_t bytes = (want > CODEGEN_ARENA_IIGS_BANK) ? CODEGEN_ARENA_IIGS_BANK : want;
|
||||||
|
if (!bankAlloc(gBankCount, bytes)) {
|
||||||
|
if (bytes <= 60ul * 1024ul || !bankAlloc(gBankCount, 60ul * 1024ul)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gTotalBytes += gBankSize[gBankCount];
|
||||||
|
gBankCount++;
|
||||||
|
want = (want > gBankSize[gBankCount - 1u]) ? want - gBankSize[gBankCount - 1u] : 0u;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
want = totalBytes;
|
||||||
|
if (bankAlloc(0u, want)) {
|
||||||
|
gTotalBytes = want;
|
||||||
|
gBankCount = 1u;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return gBankCount != 0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void codegenArenaShutdown(void) {
|
void codegenArenaShutdown(void) {
|
||||||
ArenaSlotT *slot;
|
uint8_t bank;
|
||||||
ArenaSlotT *next;
|
|
||||||
|
|
||||||
if (gCodegenArenaBase == NULL) {
|
for (bank = 0u; bank < gBankCount; bank++) {
|
||||||
return;
|
bankFree(bank);
|
||||||
}
|
}
|
||||||
for (slot = gFirstSlot; slot != NULL; slot = next) {
|
gBankCount = 0u;
|
||||||
next = slot->next;
|
|
||||||
free(slot);
|
|
||||||
}
|
|
||||||
#if defined(JOEYLIB_PLATFORM_IIGS)
|
|
||||||
DisposeHandle(gCodegenArenaBaseHandle);
|
|
||||||
gCodegenArenaBaseHandle = NULL;
|
|
||||||
#else
|
|
||||||
free(gCodegenArenaBase);
|
|
||||||
#endif
|
|
||||||
gCodegenArenaBase = NULL;
|
|
||||||
gCodegenArenaBaseAddr = 0;
|
|
||||||
gFirstSlot = NULL;
|
|
||||||
gTotalBytes = 0;
|
gTotalBytes = 0;
|
||||||
gUsedBytes = 0;
|
gUsedBytes = 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,21 @@
|
||||||
// coalescing on free; manual compaction (codegenArenaCompact)
|
// coalescing on free; manual compaction (codegenArenaCompact)
|
||||||
// memmoves live slots down to consolidate free space.
|
// memmoves live slots down to consolidate free space.
|
||||||
//
|
//
|
||||||
|
// The arena is one or more BANKS. Every port but the IIgs has a single
|
||||||
|
// bank of jlConfigT.codegenBytes from the C heap. On the IIgs a bank is
|
||||||
|
// a Memory Manager block that may not cross a 64 KB boundary (compiled
|
||||||
|
// routines are called by 24-bit JSL and run with PC inside one bank), so
|
||||||
|
// a request larger than 64 KB becomes several banks and a compiled cel
|
||||||
|
// lives wholly inside one of them. A slot's `offset` carries its bank in
|
||||||
|
// the top byte and its byte offset within the bank below.
|
||||||
|
//
|
||||||
// jlSpriteT references slots via ArenaSlotT*, never raw function
|
// jlSpriteT references slots via ArenaSlotT*, never raw function
|
||||||
// pointers. jlSpriteDraw computes the call address each invocation as
|
// pointers. jlSpriteDraw computes the call address each invocation as
|
||||||
// `gArenaBase + slot->offset + routineOffsets[shift][op]` so a
|
// `codegenArenaSlotAddr(slot) + routineOffsets[shift][op]` so a
|
||||||
// compaction that moves a slot's bytes is transparent to callers.
|
// compaction that moves a slot's bytes is transparent to callers.
|
||||||
//
|
//
|
||||||
// Allocations go through plain malloc; on every supported port
|
// Allocations go through plain malloc off the IIgs; on every supported
|
||||||
// (IIgs, Amiga libnix, ST mintlib, DJGPP+CWSDPMI) the heap is
|
// port (Amiga libnix, ST mintlib, DJGPP+CWSDPMI, X68000) the heap is
|
||||||
// readable, writable, AND executable. We are not running in a W^X
|
// readable, writable, AND executable. We are not running in a W^X
|
||||||
// environment.
|
// environment.
|
||||||
//
|
//
|
||||||
|
|
@ -24,23 +32,30 @@
|
||||||
#include "joey/types.h"
|
#include "joey/types.h"
|
||||||
|
|
||||||
typedef struct ArenaSlotT {
|
typedef struct ArenaSlotT {
|
||||||
uint32_t offset; // byte offset within the arena base
|
uint32_t offset; // bank << CODEGEN_ARENA_BANK_SHIFT | byte offset within the bank
|
||||||
uint32_t size; // bytes occupied by this slot
|
uint32_t size; // bytes occupied by this slot
|
||||||
bool used; // true = held by a sprite; false = free
|
bool used; // true = held by a sprite; false = free
|
||||||
struct ArenaSlotT *next; // linked-list, sorted by offset
|
struct ArenaSlotT *next; // linked-list, sorted by offset, one list per bank
|
||||||
struct ArenaSlotT *prev;
|
struct ArenaSlotT *prev;
|
||||||
} ArenaSlotT;
|
} ArenaSlotT;
|
||||||
|
|
||||||
|
// Banks: at most this many, and the IIgs's bank is one 64 KB block (the
|
||||||
|
// Memory Manager refuses a no-cross block any larger).
|
||||||
|
#define CODEGEN_ARENA_BANKS_MAX 4u
|
||||||
|
#define CODEGEN_ARENA_BANK_SHIFT 24u
|
||||||
|
#define CODEGEN_ARENA_BANK_MASK 0x00FFFFFFul
|
||||||
|
#define CODEGEN_ARENA_IIGS_BANK 65536ul
|
||||||
|
|
||||||
// Lifecycle. Returns false if totalBytes is 0 or the underlying
|
// Lifecycle. Returns false if totalBytes is 0 or no bank at all could be
|
||||||
// allocation fails. Idempotent: calling Init twice without an
|
// allocated; a request the machine cannot fully satisfy yields as many
|
||||||
// intervening Shutdown is a no-op.
|
// banks as it can (codegenArenaBytesTotal tells). Idempotent: calling
|
||||||
|
// Init twice without an intervening Shutdown is a no-op.
|
||||||
bool codegenArenaInit(uint32_t totalBytes);
|
bool codegenArenaInit(uint32_t totalBytes);
|
||||||
void codegenArenaShutdown(void);
|
void codegenArenaShutdown(void);
|
||||||
|
|
||||||
// First-fit allocate `bytes` of executable memory. Returns NULL if
|
// First-fit allocate `bytes` of executable memory, in the first bank with
|
||||||
// no free slot is large enough -- the caller should run
|
// room. Returns NULL if no free slot is large enough -- the caller should
|
||||||
// codegenArenaCompact and retry, or surface the failure.
|
// run codegenArenaCompact and retry, or surface the failure.
|
||||||
ArenaSlotT *codegenArenaAlloc(uint32_t bytes);
|
ArenaSlotT *codegenArenaAlloc(uint32_t bytes);
|
||||||
|
|
||||||
// Mark the slot free and merge with adjacent free neighbors. The
|
// Mark the slot free and merge with adjacent free neighbors. The
|
||||||
|
|
@ -48,29 +63,23 @@ ArenaSlotT *codegenArenaAlloc(uint32_t bytes);
|
||||||
// freed (if it coalesced into a neighbor).
|
// freed (if it coalesced into a neighbor).
|
||||||
void codegenArenaFree(ArenaSlotT *slot);
|
void codegenArenaFree(ArenaSlotT *slot);
|
||||||
|
|
||||||
// Walk live slots in offset order, memmove each down to fill any
|
// In every bank: walk live slots in offset order, memmove each down to
|
||||||
// preceding hole, drop all free slots from the list, and finish with
|
// fill any preceding hole, drop all free slots from the list, and finish
|
||||||
// one trailing free slot covering the remaining space. Per-slot
|
// with one trailing free slot covering the remaining space. Per-slot
|
||||||
// `offset` fields are updated atomically with the memmove so any
|
// `offset` fields are updated atomically with the memmove so any callers
|
||||||
// callers indexing through `gArenaBase + slot->offset` see the new
|
// resolving the slot see the new value on their next read.
|
||||||
// value on their next read.
|
|
||||||
void codegenArenaCompact(void);
|
void codegenArenaCompact(void);
|
||||||
|
|
||||||
// Used for jlSpriteDraw's address computation. The base pointer is
|
// Where a slot's bytes are: as a pointer for the C side, and as the
|
||||||
// stable for the lifetime of the arena; only slot->offset moves.
|
// 24-bit absolute address the IIgs JSLs to. The bank tables are read-only
|
||||||
//
|
// after codegenArenaInit and exposed directly so the per-draw hot paths
|
||||||
// Direct extern access (instead of a getter function) so per-frame
|
// pay an indexed load, not a call.
|
||||||
// hot paths in jlSpriteCompile.c skip the JSL/PHB/RTL/PLB the wrapper
|
extern uint8_t *gCodegenBankBase[CODEGEN_ARENA_BANKS_MAX];
|
||||||
// would impose. Both globals are read-only after codegenArenaInit;
|
extern uint32_t gCodegenBankAddr[CODEGEN_ARENA_BANKS_MAX];
|
||||||
// the function-form getters below are kept as a back-compat shim.
|
#define codegenArenaSlotBase(slot) (gCodegenBankBase[(slot)->offset >> CODEGEN_ARENA_BANK_SHIFT] + ((slot)->offset & CODEGEN_ARENA_BANK_MASK))
|
||||||
extern uint8_t *gCodegenArenaBase;
|
#define codegenArenaSlotAddr(slot) (gCodegenBankAddr[(slot)->offset >> CODEGEN_ARENA_BANK_SHIFT] + ((slot)->offset & CODEGEN_ARENA_BANK_MASK))
|
||||||
extern uint32_t gCodegenArenaBaseAddr;
|
|
||||||
#define codegenArenaBase() ((uint8_t *)gCodegenArenaBase)
|
|
||||||
#define codegenArenaBaseAddr() ((uint32_t)gCodegenArenaBaseAddr)
|
|
||||||
|
|
||||||
// Public-API support: sum of live slot sizes, total arena size.
|
// Occupancy across all banks.
|
||||||
// Difference is free space (which may be fragmented across holes
|
|
||||||
// until codegenArenaCompact runs).
|
|
||||||
uint32_t codegenArenaBytesUsed(void);
|
uint32_t codegenArenaBytesUsed(void);
|
||||||
uint32_t codegenArenaBytesTotal(void);
|
uint32_t codegenArenaBytesTotal(void);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,11 @@ void *jlAlloc(uint32_t bytes) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void jlMemCopy(void *dst, const void *src, uint32_t bytes) {
|
||||||
|
jlpMemCopy(dst, src, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void jlFree(void *p) {
|
void jlFree(void *p) {
|
||||||
jlpBigFree(p);
|
jlpBigFree(p);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -238,10 +238,11 @@ void musicPump(void) {
|
||||||
now = jlMillisElapsed();
|
now = jlMillisElapsed();
|
||||||
if ((int32_t)(now - gMusicNextMs) < 0) {
|
if ((int32_t)(now - gMusicNextMs) < 0) {
|
||||||
#if defined(JOEYLIB_PLATFORM_IIGS)
|
#if defined(JOEYLIB_PLATFORM_IIGS)
|
||||||
// The IIgs backends are ~80 ms one-shots retriggered per call
|
// The IIgs backends are finite one-shots (DOC oscillators have
|
||||||
// (DOC oscillators have no gate to hold); re-assert the active
|
// no gate to hold); re-assert the active voices every frame so
|
||||||
// voices every frame so held notes sustain. The other ports'
|
// held notes sustain -- the HAL restarts a voice only when its
|
||||||
// tone/noise hardware holds a programmed voice on its own.
|
// one-shot is about to end. The other ports' tone/noise
|
||||||
|
// hardware holds a programmed voice on its own.
|
||||||
for (voice = 0; voice < MUSIC_VOICE_SLOTS; voice++) {
|
for (voice = 0; voice < MUSIC_VOICE_SLOTS; voice++) {
|
||||||
if (gMusicOn[voice]) {
|
if (gMusicOn[voice]) {
|
||||||
musicApplyVoice(voice);
|
musicApplyVoice(voice);
|
||||||
|
|
@ -254,9 +255,12 @@ void musicPump(void) {
|
||||||
gMusicNextMs = now;
|
gMusicNextMs = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The event budget counts DOWN at the end of the body: as a `> 0u`
|
||||||
|
// guard llvm816 compiled it to `cmp #0 / inc a / bne`, and the inc
|
||||||
|
// clobbers the compare's flags (the same backend hole that broke
|
||||||
|
// Space Taxi's collision rows on the IIgs, 2026-09-21).
|
||||||
eventsLeft = MUSIC_MAX_EVENTS_PER_PUMP;
|
eventsLeft = MUSIC_MAX_EVENTS_PER_PUMP;
|
||||||
while (gMusicPlaying && eventsLeft > 0u && (int32_t)(now - gMusicNextMs) >= 0) {
|
while (gMusicPlaying && (int32_t)(now - gMusicNextMs) >= 0) {
|
||||||
eventsLeft--;
|
|
||||||
if (gMusicPos >= gMusicLength) {
|
if (gMusicPos >= gMusicLength) {
|
||||||
musicHalt();
|
musicHalt();
|
||||||
return;
|
return;
|
||||||
|
|
@ -304,5 +308,9 @@ void musicPump(void) {
|
||||||
musicHalt(); // malformed opcode: hard stop
|
musicHalt(); // malformed opcode: hard stop
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
eventsLeft--;
|
||||||
|
if (eventsLeft == 0u) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,7 @@ void jlPaletteSet(jlSurfaceT *s, uint8_t paletteIndex, const uint16_t *colors16)
|
||||||
row[14] = colors16[14];
|
row[14] = colors16[14];
|
||||||
row[15] = colors16[15];
|
row[15] = colors16[15];
|
||||||
if (s == gStage) {
|
if (s == gStage) {
|
||||||
gStagePaletteDirty = true;
|
gStagePaletteDirty = true;
|
||||||
|
gStagePaletteDirtyMask |= (uint16_t)(1u << paletteIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -282,13 +282,24 @@ extern jlSurfaceT *gStage;
|
||||||
#define jlpSpriteSavePlanes(_s, _x, _y, _w, _h, _dst) ((void)0)
|
#define jlpSpriteSavePlanes(_s, _x, _y, _w, _h, _dst) ((void)0)
|
||||||
#define jlpSpriteRestorePlanes(_s, _x, _y, _w, _h, _src) ((void)0)
|
#define jlpSpriteRestorePlanes(_s, _x, _y, _w, _h, _src) ((void)0)
|
||||||
|
|
||||||
// frameCount: straight to the GetTick asm wrapper (peislam.s). The hal.c
|
// frameCount: straight to the heartbeat tick read in peislam.s (a VBL
|
||||||
// C wrapper this replaces was one of two pure forwarding layers between
|
// interrupt task keeps the count; the read is a same-bank word load).
|
||||||
// jlFrameCount and the Misc Toolset GetTick call -- ~60-80 cycles each on
|
// The hal.c C wrapper this replaces was a pure forwarding layer on a
|
||||||
// a clock that game loops (and the UBER bench loop) poll constantly.
|
// clock that game loops (and the UBER bench loop) poll constantly.
|
||||||
extern uint16_t iigsGetTickWord(void);
|
extern uint16_t iigsGetTickWord(void);
|
||||||
#define jlpFrameCount() iigsGetTickWord()
|
#define jlpFrameCount() iigsGetTickWord()
|
||||||
|
|
||||||
|
// jlMemCopy: MVN block moves, split at bank boundaries in hal.c.
|
||||||
|
void iigsMemCopy(void *dst, const void *src, uint32_t bytes);
|
||||||
|
#define jlpMemCopy(_d, _s, _n) iigsMemCopy((_d), (_s), (_n))
|
||||||
|
// The one Memory Manager owner ID every IIgs allocation uses (hal.c).
|
||||||
|
uint16_t iigsMemUserId(void);
|
||||||
|
|
||||||
|
// SFX streams go to NTPstreamsound as unsigned bytes with 0x00 forbidden
|
||||||
|
// (joey/audio.h JL_SFX_RAW_UNSIGNED8_NOZERO): a raw producer hands
|
||||||
|
// audio_full.c chunks it can stream without the per-byte conversion.
|
||||||
|
#define jlpAudioSfxRawFormat() 1u
|
||||||
|
|
||||||
// =====================================================================
|
// =====================================================================
|
||||||
// JL_HAS registry consistency checks (PERF-AUDIT #26).
|
// JL_HAS registry consistency checks (PERF-AUDIT #26).
|
||||||
//
|
//
|
||||||
|
|
@ -767,6 +778,9 @@ uint16_t jlpGenericFrameHz(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uint16_t jlpGenericFrameCount(void);
|
uint16_t jlpGenericFrameCount(void);
|
||||||
|
#if !defined(jlpMemCopy)
|
||||||
|
#define jlpMemCopy(_d, _s, _n) memcpy((_d), (_s), (size_t)(_n))
|
||||||
|
#endif
|
||||||
#if !defined(jlpFrameCount)
|
#if !defined(jlpFrameCount)
|
||||||
#if defined(JL_HAS_FRAME_COUNT)
|
#if defined(JL_HAS_FRAME_COUNT)
|
||||||
uint16_t jlpFrameCount(void);
|
uint16_t jlpFrameCount(void);
|
||||||
|
|
@ -851,6 +865,27 @@ void jlpGenericAudioPlaySfxStream(uint8_t slot, jlAudioStreamFillT fill, void *c
|
||||||
#define jlpAudioPlaySfxStream(_a, _b, _c, _d) jlpGenericAudioPlaySfxStream((_a), (_b), (_c), (_d))
|
#define jlpAudioPlaySfxStream(_a, _b, _c, _d) jlpGenericAudioPlaySfxStream((_a), (_b), (_c), (_d))
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
// Raw-format stream (joey/audio.h jlAudioPlaySfxStreamRaw): a port whose
|
||||||
|
// stream bytes are not signed PCM overrides both; everyone else streams
|
||||||
|
// signed PCM, so raw IS the plain stream and the format is 0 (signed).
|
||||||
|
#if !defined(jlpAudioPlaySfxStreamRaw)
|
||||||
|
#if defined(JL_HAS_AUDIO_PLAY_SFX_STREAM_RAW)
|
||||||
|
void jlpAudioPlaySfxStreamRaw(uint8_t slot, jlAudioStreamFillT fill, void *ctx, uint16_t rateHz);
|
||||||
|
#else
|
||||||
|
#define jlpAudioPlaySfxStreamRaw(_a, _b, _c, _d) jlpAudioPlaySfxStream((_a), (_b), (_c), (_d))
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#if !defined(jlpAudioSfxRawFormat)
|
||||||
|
#define jlpAudioSfxRawFormat() 0u
|
||||||
|
#endif
|
||||||
|
void jlpGenericAudioSetPan(uint8_t slot, uint8_t pan);
|
||||||
|
#if !defined(jlpAudioSetPan)
|
||||||
|
#if defined(JL_HAS_AUDIO_SET_PAN)
|
||||||
|
void jlpAudioSetPan(uint8_t slot, uint8_t pan);
|
||||||
|
#else
|
||||||
|
#define jlpAudioSetPan(_s, _p) jlpGenericAudioSetPan((_s), (_p))
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
void jlpGenericAudioStopSfx(uint8_t slot);
|
void jlpGenericAudioStopSfx(uint8_t slot);
|
||||||
#if !defined(jlpAudioStopSfx)
|
#if !defined(jlpAudioStopSfx)
|
||||||
#if defined(JL_HAS_AUDIO_STOP_SFX)
|
#if defined(JL_HAS_AUDIO_STOP_SFX)
|
||||||
|
|
|
||||||
|
|
@ -55,8 +55,7 @@ static inline __attribute__((always_inline)) void spriteCompiledDraw(jlSurfaceT
|
||||||
// sets up its own DBR/Y and restores DBR on exit. routeOffset is the
|
// sets up its own DBR/Y and restores DBR on exit. routeOffset is the
|
||||||
// gate-derived DRAW offset for this shift (#29).
|
// gate-derived DRAW offset for this shift (#29).
|
||||||
dstRow0 = &dst->pixels[SURFACE_ROW_OFFSET(y) + ((uint16_t)x >> 1)];
|
dstRow0 = &dst->pixels[SURFACE_ROW_OFFSET(y) + ((uint16_t)x >> 1)];
|
||||||
fnAddr = codegenArenaBaseAddr()
|
fnAddr = codegenArenaSlotAddr(sp->slot)
|
||||||
+ sp->slot->offset
|
|
||||||
+ (uint32_t)routeOffset;
|
+ (uint32_t)routeOffset;
|
||||||
drawFn = (void (*)(uint8_t *))fnAddr;
|
drawFn = (void (*)(uint8_t *))fnAddr;
|
||||||
drawFn(dstRow0);
|
drawFn(dstRow0);
|
||||||
|
|
@ -131,9 +130,8 @@ extern void iigsSpriteSaveStage(uint32_t fnAddr, uint16_t backupLo, uint16_t x,
|
||||||
static inline __attribute__((always_inline)) void spriteCompiledDrawMark(const jlSpriteT *sp, int16_t x, int16_t y, uint16_t routeOffset) {
|
static inline __attribute__((always_inline)) void spriteCompiledDrawMark(const jlSpriteT *sp, int16_t x, int16_t y, uint16_t routeOffset) {
|
||||||
uint32_t fnAddr;
|
uint32_t fnAddr;
|
||||||
|
|
||||||
fnAddr = codegenArenaBaseAddr()
|
fnAddr = codegenArenaSlotAddr(sp->slot)
|
||||||
+ sp->slot->offset
|
+ (uint32_t)routeOffset;
|
||||||
+ (uint32_t)routeOffset;
|
|
||||||
iigsSpriteDrawMark(fnAddr, (uint16_t)x, (uint16_t)y, sp->widthPx, sp->heightPx);
|
iigsSpriteDrawMark(fnAddr, (uint16_t)x, (uint16_t)y, sp->widthPx, sp->heightPx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -188,9 +186,8 @@ static inline __attribute__((always_inline)) void spriteCompiledSaveUnder(const
|
||||||
cachedDst = (uint8_t *)sp->cachedDstBank + cacheIdx;
|
cachedDst = (uint8_t *)sp->cachedDstBank + cacheIdx;
|
||||||
cachedSrc = (uint8_t *)sp->cachedSrcBank + cacheIdx;
|
cachedSrc = (uint8_t *)sp->cachedSrcBank + cacheIdx;
|
||||||
|
|
||||||
fnAddr = codegenArenaBaseAddr()
|
fnAddr = codegenArenaSlotAddr(sp->slot)
|
||||||
+ sp->slot->offset
|
+ (uint32_t)routeOffset;
|
||||||
+ (uint32_t)routeOffset;
|
|
||||||
|
|
||||||
// Patch the MVN bank operands only if the dst/src bank pair changed since
|
// Patch the MVN bank operands only if the dst/src bank pair changed since
|
||||||
// last call (the routine's only dynamic bytes). SAVE copies screen ->
|
// last call (the routine's only dynamic bytes). SAVE copies screen ->
|
||||||
|
|
@ -259,9 +256,8 @@ static inline __attribute__((always_inline)) void spriteCompiledSaveUnderStage(j
|
||||||
cachedDst = (uint8_t *)sp->cachedDstBank + cacheIdx;
|
cachedDst = (uint8_t *)sp->cachedDstBank + cacheIdx;
|
||||||
cachedSrc = (uint8_t *)sp->cachedSrcBank + cacheIdx;
|
cachedSrc = (uint8_t *)sp->cachedSrcBank + cacheIdx;
|
||||||
|
|
||||||
fnAddr = codegenArenaBaseAddr()
|
fnAddr = codegenArenaSlotAddr(sp->slot)
|
||||||
+ sp->slot->offset
|
+ (uint32_t)routeOffset;
|
||||||
+ (uint32_t)routeOffset;
|
|
||||||
|
|
||||||
if (*cachedDst != backupBank || *cachedSrc != screenBank) {
|
if (*cachedDst != backupBank || *cachedSrc != screenBank) {
|
||||||
patchMvnBanks((uint8_t *)fnAddr, heightPx, /*dst*/backupBank, /*src*/screenBank);
|
patchMvnBanks((uint8_t *)fnAddr, heightPx, /*dst*/backupBank, /*src*/screenBank);
|
||||||
|
|
@ -301,9 +297,8 @@ static inline __attribute__((always_inline)) void spriteCompiledRestoreUnder(jlS
|
||||||
cachedDst = (uint8_t *)sp->cachedDstBank + cacheIdx;
|
cachedDst = (uint8_t *)sp->cachedDstBank + cacheIdx;
|
||||||
cachedSrc = (uint8_t *)sp->cachedSrcBank + cacheIdx;
|
cachedSrc = (uint8_t *)sp->cachedSrcBank + cacheIdx;
|
||||||
|
|
||||||
fnAddr = codegenArenaBaseAddr()
|
fnAddr = codegenArenaSlotAddr(sp->slot)
|
||||||
+ sp->slot->offset
|
+ (uint32_t)routeOffset;
|
||||||
+ (uint32_t)routeOffset;
|
|
||||||
|
|
||||||
// Patch the MVN bank operands only if they changed (the routine's only
|
// Patch the MVN bank operands only if they changed (the routine's only
|
||||||
// dynamic bytes). RESTORE copies backup -> screen: dst = screen bank,
|
// dynamic bytes). RESTORE copies backup -> screen: dst = screen bank,
|
||||||
|
|
@ -344,9 +339,8 @@ static inline __attribute__((always_inline)) void spriteCompiledRestoreMark(jlSp
|
||||||
SPLIT_POINTER(backup->bytes, &backupLo, &backupBank);
|
SPLIT_POINTER(backup->bytes, &backupLo, &backupBank);
|
||||||
screenBank = ((const uint8_t *)&gStage->pixels)[2];
|
screenBank = ((const uint8_t *)&gStage->pixels)[2];
|
||||||
|
|
||||||
fnAddr = codegenArenaBaseAddr()
|
fnAddr = codegenArenaSlotAddr(sp->slot)
|
||||||
+ sp->slot->offset
|
+ (uint32_t)routeOffset;
|
||||||
+ (uint32_t)routeOffset;
|
|
||||||
|
|
||||||
cacheIdx = SPRITE_ROUTINE_INDEX(shift, SPRITE_OP_RESTORE);
|
cacheIdx = SPRITE_ROUTINE_INDEX(shift, SPRITE_OP_RESTORE);
|
||||||
cachedDst = (uint8_t *)sp->cachedDstBank + cacheIdx;
|
cachedDst = (uint8_t *)sp->cachedDstBank + cacheIdx;
|
||||||
|
|
@ -413,7 +407,7 @@ static inline __attribute__((always_inline)) void spriteCompiledDraw(jlSurfaceT
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
byteOff = (uint16_t)((uint16_t)y * JL_PLANAR_BYTES_PER_ROW + ((uint16_t)x >> 3));
|
byteOff = (uint16_t)((uint16_t)y * JL_PLANAR_BYTES_PER_ROW + ((uint16_t)x >> 3));
|
||||||
fn = (DrawFn)(codegenArenaBase() + sp->slot->offset + routeOffset);
|
fn = (DrawFn)(codegenArenaSlotBase(sp->slot) + routeOffset);
|
||||||
fn(pd->planes[0] + byteOff, pd->planes[1] + byteOff, pd->planes[2] + byteOff, pd->planes[3] + byteOff);
|
fn(pd->planes[0] + byteOff, pd->planes[1] + byteOff, pd->planes[2] + byteOff, pd->planes[3] + byteOff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -448,7 +442,7 @@ static inline __attribute__((always_inline)) void spriteCompiledSaveUnder(const
|
||||||
backup->height = sp->heightPx;
|
backup->height = sp->heightPx;
|
||||||
backup->sizeBytes = (uint16_t)((uint16_t)(nGroups << 3) * sp->heightPx);
|
backup->sizeBytes = (uint16_t)((uint16_t)(nGroups << 3) * sp->heightPx);
|
||||||
|
|
||||||
fn = (CopyFn)(codegenArenaBase() + sp->slot->offset + routeOffset);
|
fn = (CopyFn)(codegenArenaSlotBase(sp->slot) + routeOffset);
|
||||||
fn(pd->planes[0] + byteOff, pd->planes[1] + byteOff, pd->planes[2] + byteOff, pd->planes[3] + byteOff, backup->bytes);
|
fn(pd->planes[0] + byteOff, pd->planes[1] + byteOff, pd->planes[2] + byteOff, pd->planes[3] + byteOff, backup->bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -465,7 +459,7 @@ static inline __attribute__((always_inline)) void spriteCompiledRestoreUnder(jlS
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
byteOff = (uint16_t)((uint16_t)backup->y * JL_PLANAR_BYTES_PER_ROW + ((uint16_t)backup->x >> 3));
|
byteOff = (uint16_t)((uint16_t)backup->y * JL_PLANAR_BYTES_PER_ROW + ((uint16_t)backup->x >> 3));
|
||||||
fn = (CopyFn)(codegenArenaBase() + backup->sprite->slot->offset + routeOffset);
|
fn = (CopyFn)(codegenArenaSlotBase(backup->sprite->slot) + routeOffset);
|
||||||
fn(pd->planes[0] + byteOff, pd->planes[1] + byteOff, pd->planes[2] + byteOff, pd->planes[3] + byteOff, backup->bytes);
|
fn(pd->planes[0] + byteOff, pd->planes[1] + byteOff, pd->planes[2] + byteOff, pd->planes[3] + byteOff, backup->bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -516,7 +510,7 @@ static inline __attribute__((always_inline)) void spriteCompiledDraw(jlSurfaceT
|
||||||
groupBase = pd->base
|
groupBase = pd->base
|
||||||
+ (uint16_t)y * ST_GROUP_BYTES_PER_ROW
|
+ (uint16_t)y * ST_GROUP_BYTES_PER_ROW
|
||||||
+ (uint16_t)((uint16_t)x >> 4) * ST_BYTES_PER_PLANE_GROUP;
|
+ (uint16_t)((uint16_t)x >> 4) * ST_BYTES_PER_PLANE_GROUP;
|
||||||
fn = (DrawFn)(codegenArenaBase() + sp->slot->offset + routeOffset);
|
fn = (DrawFn)(codegenArenaSlotBase(sp->slot) + routeOffset);
|
||||||
fn(groupBase);
|
fn(groupBase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -553,7 +547,7 @@ static inline __attribute__((always_inline)) void spriteCompiledSaveUnder(const
|
||||||
backup->height = sp->heightPx;
|
backup->height = sp->heightPx;
|
||||||
backup->sizeBytes = (uint16_t)((uint16_t)(nGroups << 3) * sp->heightPx);
|
backup->sizeBytes = (uint16_t)((uint16_t)(nGroups << 3) * sp->heightPx);
|
||||||
|
|
||||||
fn = (CopyFn)(codegenArenaBase() + sp->slot->offset + routeOffset);
|
fn = (CopyFn)(codegenArenaSlotBase(sp->slot) + routeOffset);
|
||||||
fn(rowStart, backup->bytes);
|
fn(rowStart, backup->bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -572,7 +566,7 @@ static inline __attribute__((always_inline)) void spriteCompiledRestoreUnder(jlS
|
||||||
rowStart = pd->base
|
rowStart = pd->base
|
||||||
+ (uint16_t)backup->y * ST_GROUP_BYTES_PER_ROW
|
+ (uint16_t)backup->y * ST_GROUP_BYTES_PER_ROW
|
||||||
+ (uint16_t)((uint16_t)backup->x >> 4) * ST_BYTES_PER_PLANE_GROUP;
|
+ (uint16_t)((uint16_t)backup->x >> 4) * ST_BYTES_PER_PLANE_GROUP;
|
||||||
fn = (CopyFn)(codegenArenaBase() + backup->sprite->slot->offset + routeOffset);
|
fn = (CopyFn)(codegenArenaSlotBase(backup->sprite->slot) + routeOffset);
|
||||||
fn(backup->bytes, rowStart);
|
fn(backup->bytes, rowStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -584,7 +578,7 @@ static inline __attribute__((always_inline)) void spriteCompiledDraw(jlSurfaceT
|
||||||
DrawFn fn;
|
DrawFn fn;
|
||||||
|
|
||||||
destRow = &dst->pixels[(uint16_t)y * SURFACE_BYTES_PER_ROW + ((uint16_t)x >> 1)];
|
destRow = &dst->pixels[(uint16_t)y * SURFACE_BYTES_PER_ROW + ((uint16_t)x >> 1)];
|
||||||
fn = (DrawFn)(codegenArenaBase() + sp->slot->offset + routeOffset);
|
fn = (DrawFn)(codegenArenaSlotBase(sp->slot) + routeOffset);
|
||||||
fn(destRow);
|
fn(destRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -615,7 +609,7 @@ static inline __attribute__((always_inline)) void spriteCompiledSaveUnder(const
|
||||||
backup->height = heightPx;
|
backup->height = heightPx;
|
||||||
backup->sizeBytes = (uint16_t)(copyBytes * heightPx);
|
backup->sizeBytes = (uint16_t)(copyBytes * heightPx);
|
||||||
|
|
||||||
fn = (CopyFn)(codegenArenaBase() + sp->slot->offset + routeOffset);
|
fn = (CopyFn)(codegenArenaSlotBase(sp->slot) + routeOffset);
|
||||||
fn(screenPtr, backup->bytes);
|
fn(screenPtr, backup->bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -635,7 +629,7 @@ static inline __attribute__((always_inline)) void spriteCompiledRestoreUnder(jlS
|
||||||
(void)shift;
|
(void)shift;
|
||||||
sp = backup->sprite;
|
sp = backup->sprite;
|
||||||
screenPtr = (uint8_t *)&dst->pixels[(uint16_t)backup->y * SURFACE_BYTES_PER_ROW + ((uint16_t)backup->x >> 1)];
|
screenPtr = (uint8_t *)&dst->pixels[(uint16_t)backup->y * SURFACE_BYTES_PER_ROW + ((uint16_t)backup->x >> 1)];
|
||||||
fn = (CopyFn)(codegenArenaBase() + sp->slot->offset + routeOffset);
|
fn = (CopyFn)(codegenArenaSlotBase(sp->slot) + routeOffset);
|
||||||
fn(backup->bytes, screenPtr);
|
fn(backup->bytes, screenPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@ struct jlSpriteT {
|
||||||
// Compiled-path state. slot==NULL means not yet compiled (or
|
// Compiled-path state. slot==NULL means not yet compiled (or
|
||||||
// compile failed); jlSpriteDraw falls back to the interpreter.
|
// compile failed); jlSpriteDraw falls back to the interpreter.
|
||||||
// The fn-call address for (shift, op) is computed at draw time:
|
// The fn-call address for (shift, op) is computed at draw time:
|
||||||
// (codegenArenaBase() + slot->offset + routineOffsets[shift][op])
|
// (codegenArenaSlotAddr(slot) + routineOffsets[shift][op])
|
||||||
// so a codegenArenaCompact that moves the slot's bytes is
|
// so a codegenArenaCompact that moves the slot's bytes is
|
||||||
// transparent to the caller.
|
// transparent to the caller.
|
||||||
ArenaSlotT *slot;
|
ArenaSlotT *slot;
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ uint32_t gPresentScannedRows = 0;
|
||||||
// code reads and clears the flags after uploading.
|
// code reads and clears the flags after uploading.
|
||||||
bool gStageScbDirty = true;
|
bool gStageScbDirty = true;
|
||||||
bool gStagePaletteDirty = true;
|
bool gStagePaletteDirty = true;
|
||||||
|
uint16_t gStagePaletteDirtyMask = 0xFFFFu;
|
||||||
|
|
||||||
// ----- Internal helpers (alphabetical) -----
|
// ----- Internal helpers (alphabetical) -----
|
||||||
|
|
||||||
|
|
@ -85,7 +86,8 @@ void jlSurfaceCopy(jlSurfaceT *dst, const jlSurfaceT *src) {
|
||||||
// widens the pixel bands. Matches the jlScbSet / jlPaletteSet path.
|
// widens the pixel bands. Matches the jlScbSet / jlPaletteSet path.
|
||||||
if (dst == gStage) {
|
if (dst == gStage) {
|
||||||
gStageScbDirty = true;
|
gStageScbDirty = true;
|
||||||
gStagePaletteDirty = true;
|
gStagePaletteDirty = true;
|
||||||
|
gStagePaletteDirtyMask = 0xFFFFu;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -242,7 +244,8 @@ bool jlSurfaceLoadFile(jlSurfaceT *dst, const char *path) {
|
||||||
* them dirty so the next present re-uploads them. */
|
* them dirty so the next present re-uploads them. */
|
||||||
if (dst == gStage) {
|
if (dst == gStage) {
|
||||||
gStageScbDirty = true;
|
gStageScbDirty = true;
|
||||||
gStagePaletteDirty = true;
|
gStagePaletteDirty = true;
|
||||||
|
gStagePaletteDirtyMask = 0xFFFFu;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -428,7 +431,8 @@ bool stageAlloc(void) {
|
||||||
// (NATIVE-PERF Phase 2 R9 -- ports now trust these flags instead
|
// (NATIVE-PERF Phase 2 R9 -- ports now trust these flags instead
|
||||||
// of memcmp'ing cached SCB/palette mirrors every present).
|
// of memcmp'ing cached SCB/palette mirrors every present).
|
||||||
gStageScbDirty = true;
|
gStageScbDirty = true;
|
||||||
gStagePaletteDirty = true;
|
gStagePaletteDirty = true;
|
||||||
|
gStagePaletteDirtyMask = 0xFFFFu;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,11 @@ extern jlSurfaceT *gStage;
|
||||||
// 712-byte memcmp pair the IIgs port used to run unconditionally.
|
// 712-byte memcmp pair the IIgs port used to run unconditionally.
|
||||||
extern bool gStageScbDirty;
|
extern bool gStageScbDirty;
|
||||||
extern bool gStagePaletteDirty;
|
extern bool gStagePaletteDirty;
|
||||||
|
// Companion to gStagePaletteDirty: bit N set = palette N changed since the
|
||||||
|
// last upload. A port that can upload per palette (IIgs: 32 bytes each,
|
||||||
|
// not 512) consults this; the bool stays the coarse "anything" flag the
|
||||||
|
// other ports read. Both are set together and cleared together.
|
||||||
|
extern uint16_t gStagePaletteDirtyMask;
|
||||||
|
|
||||||
// Coarse dirty map: one byte per GROUP of 8 stage rows, non-zero when
|
// Coarse dirty map: one byte per GROUP of 8 stage rows, non-zero when
|
||||||
// any row in the group may be dirty. It is a conservative hint, never
|
// any row in the group may be dirty. It is a conservative hint, never
|
||||||
|
|
|
||||||
|
|
@ -134,6 +134,13 @@ void jlpGenericAudioPlaySfxStream(uint8_t slot, jlAudioStreamFillT fill, void *c
|
||||||
(void)rateHz;
|
(void)rateHz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mono ports (DOS, ST) have nowhere to put a pan; the header says so.
|
||||||
|
void jlpGenericAudioSetPan(uint8_t slot, uint8_t pan) {
|
||||||
|
(void)slot;
|
||||||
|
(void)pan;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void jlpGenericAudioStopSfx(uint8_t slot) {
|
void jlpGenericAudioStopSfx(uint8_t slot) {
|
||||||
(void)slot;
|
(void)slot;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
146
src/iigs/hal.c
146
src/iigs/hal.c
|
|
@ -34,11 +34,17 @@
|
||||||
|
|
||||||
// Memory Manager surface for jlpBigAlloc / jlpBigFree (jlAlloc backing).
|
// Memory Manager surface for jlpBigAlloc / jlpBigFree (jlAlloc backing).
|
||||||
// Same idiom as src/iigs/audio_full.c: Handles/pointers are void*,
|
// Same idiom as src/iigs/audio_full.c: Handles/pointers are void*,
|
||||||
// the owner ID comes from MMStartUp(), and the attribute bit values are
|
// the owner ID comes from iigsMemUserId(), and the attribute bit values
|
||||||
// Apple's Memory Manager constants. Only the bits we use are defined.
|
// are Apple's Memory Manager constants. Only the bits we use are defined.
|
||||||
|
//
|
||||||
|
// _ownerid used to expand to MMStartUp() at EVERY call site. MMStartUp
|
||||||
|
// hands out a NEW memory ID each time it is called, so every allocation
|
||||||
|
// burned one of the Memory Manager's application IDs; once the pool ran
|
||||||
|
// dry the next NewHandle went wild (the UBER probe build BRK'd into the
|
||||||
|
// ROM monitor inside jlSurfaceCreate, 2026-09-22). One ID, fetched once.
|
||||||
#include <iigs/toolbox.h>
|
#include <iigs/toolbox.h>
|
||||||
typedef void *Handle;
|
typedef void *Handle;
|
||||||
#define _ownerid (MMStartUp())
|
#define _ownerid (iigsMemUserId())
|
||||||
#define attrAddr 0x0002
|
#define attrAddr 0x0002
|
||||||
#define attrNoSpec 0x0020
|
#define attrNoSpec 0x0020
|
||||||
#define attrFixed 0x4000
|
#define attrFixed 0x4000
|
||||||
|
|
@ -48,14 +54,25 @@ typedef void *Handle;
|
||||||
// stash it just before the pointer we return.
|
// stash it just before the pointer we return.
|
||||||
#define BIG_ALLOC_HDR ((uint32_t)sizeof(Handle))
|
#define BIG_ALLOC_HDR ((uint32_t)sizeof(Handle))
|
||||||
|
|
||||||
/* GetTick wrapper in peislam.s: invokes the Misc Toolset GetTick
|
// VBL tick counter in peislam.s: a Misc Toolset heartbeat task that the
|
||||||
* ($2503) and returns the low 16 bits of the system's tick counter
|
// firmware VBL interrupt handler bumps every field, read by
|
||||||
* (firmware VBL ISR-driven). Polling $C019 from C user code missed
|
// iigsGetTickWord as a same-bank word load. It replaced a _GetTick
|
||||||
* transitions for any op over ~1 ms; the system's tick counter is
|
// toolbox call per read (hundreds of cycles, ten-odd reads per Space
|
||||||
* updated by the actual interrupt handler so it stays accurate
|
// Taxi frame). Polling $C019 from C missed transitions for any op over
|
||||||
* regardless of caller polling rate. Tick rate matches the video
|
// ~1 ms; the interrupt-driven count stays accurate regardless of the
|
||||||
* field rate -- 60 Hz on NTSC, 50 Hz on PAL. */
|
// caller's polling rate. Tick rate matches the video field rate -- 60 Hz
|
||||||
|
// on NTSC, 50 Hz on PAL. iigsVblInstall/iigsVblRemove wrap SetHeartBeat/
|
||||||
|
// DelHeartBeat for that task and return the Misc Tool Set error (0 = ok).
|
||||||
extern uint16_t iigsGetTickWord(void);
|
extern uint16_t iigsGetTickWord(void);
|
||||||
|
extern uint16_t iigsVblInstall(void);
|
||||||
|
extern uint16_t iigsVblRemove(void);
|
||||||
|
// MVN block copy (peislam.s): one run that crosses no bank on either side.
|
||||||
|
typedef struct {
|
||||||
|
uint32_t dst;
|
||||||
|
uint32_t src;
|
||||||
|
uint16_t len;
|
||||||
|
} IigsMvnArgsT;
|
||||||
|
extern void iigsMvnCopy(const IigsMvnArgsT *args);
|
||||||
/* Reads battery RAM hrtz50or60: 0 = NTSC, 1 = PAL. */
|
/* Reads battery RAM hrtz50or60: 0 = NTSC, 1 = PAL. */
|
||||||
extern uint16_t iigsReadHzParam(void);
|
extern uint16_t iigsReadHzParam(void);
|
||||||
static uint16_t gFrameHz = 60u;
|
static uint16_t gFrameHz = 60u;
|
||||||
|
|
@ -217,14 +234,40 @@ volatile uint16_t gPeiSkipTmp; // scan: rows skipped by a clean group
|
||||||
// start true (forces the very first present to upload), get set true
|
// start true (forces the very first present to upload), get set true
|
||||||
// again whenever jlScbSet* / jlPaletteSet mutate the stage's data, and
|
// again whenever jlScbSet* / jlPaletteSet mutate the stage's data, and
|
||||||
// get cleared here after upload.
|
// get cleared here after upload.
|
||||||
|
// Word stores, NOT memcpy: the clang IIgs libc memcpy is a ~30 cyc/byte
|
||||||
|
// byte loop (finding #79 in palette.c), so the 712 bytes here cost ~21k
|
||||||
|
// cycles -- 7.6 ms, a third of a frame -- every time both flags fire.
|
||||||
|
// 16-bit stores land at ~8 cyc/word, ~2.8k cycles for the pair.
|
||||||
|
static void copyWords(volatile uint16_t *dst, const uint16_t *src, uint16_t words) {
|
||||||
|
uint16_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < words; i++) {
|
||||||
|
dst[i] = src[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void uploadScbAndPaletteIfNeeded(const jlSurfaceT *src) {
|
static void uploadScbAndPaletteIfNeeded(const jlSurfaceT *src) {
|
||||||
if (gStageScbDirty) {
|
if (gStageScbDirty) {
|
||||||
memcpy(IIGS_SHR_SCB, src->scb, SURFACE_HEIGHT);
|
copyWords((volatile uint16_t *)IIGS_SHR_SCB, (const uint16_t *)src->scb, SURFACE_HEIGHT / 2u);
|
||||||
gStageScbDirty = false;
|
gStageScbDirty = false;
|
||||||
}
|
}
|
||||||
if (gStagePaletteDirty) {
|
if (gStagePaletteDirty) {
|
||||||
memcpy(IIGS_SHR_PALETTE, src->palette, sizeof(src->palette));
|
// Only the palettes that changed: a colour-cycling effect touches
|
||||||
gStagePaletteDirty = false;
|
// one palette a frame, and 32 bytes beats 512 sixteen times over.
|
||||||
|
uint16_t mask = gStagePaletteDirtyMask;
|
||||||
|
uint8_t pal;
|
||||||
|
|
||||||
|
for (pal = 0; mask != 0u && pal < SURFACE_PALETTE_COUNT; pal++) {
|
||||||
|
if ((mask & 1u) != 0u) {
|
||||||
|
copyWords((volatile uint16_t *)IIGS_SHR_PALETTE + (uint16_t)pal * SURFACE_COLORS_PER_PALETTE,
|
||||||
|
(const uint16_t *)src->palette + (uint16_t)pal * SURFACE_COLORS_PER_PALETTE,
|
||||||
|
SURFACE_COLORS_PER_PALETTE);
|
||||||
|
}
|
||||||
|
mask >>= 1;
|
||||||
|
}
|
||||||
|
gStagePaletteDirty = false;
|
||||||
|
gStagePaletteDirtyMask = 0u;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -274,7 +317,26 @@ void jlpBigFree(void *p) {
|
||||||
static void claimStagePixels(void);
|
static void claimStagePixels(void);
|
||||||
|
|
||||||
bool jlpInit(const jlConfigT *config) {
|
bool jlpInit(const jlConfigT *config) {
|
||||||
|
uint16_t tick;
|
||||||
|
uint16_t spin = 0u;
|
||||||
|
|
||||||
(void)config;
|
(void)config;
|
||||||
|
// The heartbeat task is the frame clock; nothing else can wait for a
|
||||||
|
// VBL, so prove it advances before touching anything else (a task
|
||||||
|
// that never runs would spin jlpWaitVBL forever).
|
||||||
|
if (iigsVblInstall() != 0u) {
|
||||||
|
jlLog("iigs: SetHeartBeat failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
tick = iigsGetTickWord();
|
||||||
|
while (iigsGetTickWord() == tick) {
|
||||||
|
spin++;
|
||||||
|
if (spin == 0u) {
|
||||||
|
iigsVblRemove();
|
||||||
|
jlLog("iigs: VBL heartbeat task never ran");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
claimStagePixels();
|
claimStagePixels();
|
||||||
gPreviousNewVideo = *IIGS_NEWVIDEO_REG;
|
gPreviousNewVideo = *IIGS_NEWVIDEO_REG;
|
||||||
gPreviousBorder = *IIGS_BORDER_REG;
|
gPreviousBorder = *IIGS_BORDER_REG;
|
||||||
|
|
@ -355,7 +417,59 @@ void jlpPresent(const jlSurfaceT *src) {
|
||||||
#define IIGS_QUIT_MAILBOX ((volatile uint8_t *)0xE19DC8L)
|
#define IIGS_QUIT_MAILBOX ((volatile uint8_t *)0xE19DC8L)
|
||||||
|
|
||||||
|
|
||||||
|
// jlMemCopy: MVN moves of up to a bank at a time. MVN's X and Y wrap
|
||||||
|
// inside their banks, so a run ends where either side would cross one;
|
||||||
|
// Memory Manager blocks are free to straddle banks.
|
||||||
|
void iigsMemCopy(void *dst, const void *src, uint32_t bytes) {
|
||||||
|
static IigsMvnArgsT args;
|
||||||
|
uint32_t d = (uint32_t)dst;
|
||||||
|
uint32_t s = (uint32_t)src;
|
||||||
|
|
||||||
|
while (bytes != 0u) {
|
||||||
|
uint32_t run = bytes;
|
||||||
|
uint32_t toD = 0x10000UL - (d & 0xFFFFu);
|
||||||
|
uint32_t toS = 0x10000UL - (s & 0xFFFFu);
|
||||||
|
|
||||||
|
if (run > toD) {
|
||||||
|
run = toD;
|
||||||
|
}
|
||||||
|
if (run > toS) {
|
||||||
|
run = toS;
|
||||||
|
}
|
||||||
|
if (run > 0xFFFFu) {
|
||||||
|
run = 0xFFFFu;
|
||||||
|
}
|
||||||
|
args.dst = d;
|
||||||
|
args.src = s;
|
||||||
|
args.len = (uint16_t)run;
|
||||||
|
iigsMvnCopy(&args);
|
||||||
|
d += run;
|
||||||
|
s += run;
|
||||||
|
bytes -= run;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The Memory Manager owner ID for every JoeyLib allocation on the IIgs
|
||||||
|
// (jlpBigAlloc, the stage pixels, the audio buffers). MMStartUp creates
|
||||||
|
// a fresh application ID per call, so it is called exactly once here
|
||||||
|
// and the result cached for the life of the program.
|
||||||
|
uint16_t iigsMemUserId(void) {
|
||||||
|
static uint16_t gMemUserId = 0u;
|
||||||
|
static bool gMemUserIdReady = false;
|
||||||
|
|
||||||
|
if (!gMemUserIdReady) {
|
||||||
|
gMemUserId = (uint16_t)MMStartUp();
|
||||||
|
gMemUserIdReady = true;
|
||||||
|
}
|
||||||
|
return gMemUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void jlpShutdown(void) {
|
void jlpShutdown(void) {
|
||||||
|
// The task lives in this program's memory: pull it from the heartbeat
|
||||||
|
// queue before GS/OS frees the segments.
|
||||||
|
iigsVblRemove();
|
||||||
if (gModeSet) {
|
if (gModeSet) {
|
||||||
*IIGS_NEWVIDEO_REG = gPreviousNewVideo;
|
*IIGS_NEWVIDEO_REG = gPreviousNewVideo;
|
||||||
*IIGS_BORDER_REG = gPreviousBorder;
|
*IIGS_BORDER_REG = gPreviousBorder;
|
||||||
|
|
@ -514,8 +628,8 @@ void jlpSurfaceCopyRect(jlSurfaceT *dst, const jlSurfaceT *src, uint16_t x, uint
|
||||||
* the legacy paths did. Same logic as the DOS port. */
|
* the legacy paths did. Same logic as the DOS port. */
|
||||||
|
|
||||||
|
|
||||||
// Block until the next VBL boundary by waiting for the firmware tick
|
// Block until the next VBL boundary by waiting for the heartbeat tick
|
||||||
// (GetTick, incremented by the VBL interrupt handler) to advance. The
|
// (incremented by the VBL interrupt handler) to advance. The
|
||||||
// old form polled $C019's VBL status bit, which never reads high under
|
// old form polled $C019's VBL status bit, which never reads high under
|
||||||
// MAME's apple2gs (PERF-AUDIT.md finding #76): the wait-for-scan loop
|
// MAME's apple2gs (PERF-AUDIT.md finding #76): the wait-for-scan loop
|
||||||
// spun forever, hanging every program that paces with jlWaitVBL. UBER
|
// spun forever, hanging every program that paces with jlWaitVBL. UBER
|
||||||
|
|
@ -537,7 +651,7 @@ void jlpWaitVBL(void) {
|
||||||
|
|
||||||
|
|
||||||
// (jlpFrameCount is a port.h macro straight to iigsGetTickWord -- the
|
// (jlpFrameCount is a port.h macro straight to iigsGetTickWord -- the
|
||||||
// GetTick asm wrapper in peislam.s -- so no C wrapper lives here.)
|
// heartbeat counter read in peislam.s -- so no C wrapper lives here.)
|
||||||
|
|
||||||
|
|
||||||
uint16_t jlpFrameHz(void) {
|
uint16_t jlpFrameHz(void) {
|
||||||
|
|
|
||||||
|
|
@ -1321,6 +1321,23 @@ iigsTilePasteGlyphMark:
|
||||||
and #0x00FF
|
and #0x00FF
|
||||||
sta tpgBitsW+14
|
sta tpgBitsW+14
|
||||||
|
|
||||||
|
; The 16-word colour table depends only on (fg, bg), and consecutive
|
||||||
|
; cells almost always share them (a level is painted in colour runs),
|
||||||
|
; so it is kept across calls: rebuild only when the pair changes. The
|
||||||
|
; key is fg << 4 | bg; a zeroed bss key with zeroed tables IS the
|
||||||
|
; fg = bg = 0 table, so no initialisation is needed.
|
||||||
|
lda 16,s ; fg
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
ora 18,s ; | bg
|
||||||
|
cmp tpgLastKey
|
||||||
|
bne tpgTableBuild
|
||||||
|
brl tpgTableReady ; the build is past an 8-bit branch's reach
|
||||||
|
tpgTableBuild:
|
||||||
|
sta tpgLastKey
|
||||||
|
|
||||||
; pb[0..3]: the output byte for each 2-bit pixel pair,
|
; pb[0..3]: the output byte for each 2-bit pixel pair,
|
||||||
; and the same four values pre-shifted into a high byte.
|
; and the same four values pre-shifted into a high byte.
|
||||||
lda 16,s ; fg
|
lda 16,s ; fg
|
||||||
|
|
@ -1405,6 +1422,7 @@ iigsTilePasteGlyphMark:
|
||||||
lda tpgPbHi+6
|
lda tpgPbHi+6
|
||||||
ora tpgPb+6
|
ora tpgPb+6
|
||||||
sta tpgWord+30
|
sta tpgWord+30
|
||||||
|
tpgTableReady:
|
||||||
|
|
||||||
; X = stage byte offset of the tile's row 0
|
; X = stage byte offset of the tile's row 0
|
||||||
lda 14,s ; by
|
lda 14,s ; by
|
||||||
|
|
@ -1579,6 +1597,11 @@ tpgPb:
|
||||||
tpgPbHi:
|
tpgPbHi:
|
||||||
.zero 8
|
.zero 8
|
||||||
|
|
||||||
|
.section .bss.tpgLastKey,"aw"
|
||||||
|
.globl tpgLastKey
|
||||||
|
tpgLastKey:
|
||||||
|
.zero 2
|
||||||
|
|
||||||
.section .bss.tpgFgHi,"aw"
|
.section .bss.tpgFgHi,"aw"
|
||||||
.globl tpgFgHi
|
.globl tpgFgHi
|
||||||
tpgFgHi:
|
tpgFgHi:
|
||||||
|
|
@ -4738,6 +4761,14 @@ peiRowClean:
|
||||||
; The index maths runs in M=16 throughout: TAX/TAY there move a clean
|
; The index maths runs in M=16 throughout: TAX/TAY there move a clean
|
||||||
; 16-bit value, whereas in M=8 they would carry whatever the hidden
|
; 16-bit value, whereas in M=8 they would carry whatever the hidden
|
||||||
; high byte B happens to hold.
|
; high byte B happens to hold.
|
||||||
|
; The flag is consulted only on a group's FIRST row: a clean row inside
|
||||||
|
; a group that has already been entered just steps (the flag could only
|
||||||
|
; have said "may be dirty", which the band tests are settling row by
|
||||||
|
; row). Measured 2026-09-22: ~90 of the ~160 clean rows per present sat
|
||||||
|
; inside dirty groups and paid the full index dance for nothing.
|
||||||
|
txa ; M=8: low byte of the row
|
||||||
|
and #7
|
||||||
|
bne peiRowStep ; mid-group: step
|
||||||
phx ; park the row
|
phx ; park the row
|
||||||
rep #0x20
|
rep #0x20
|
||||||
.a16
|
.a16
|
||||||
|
|
@ -4752,6 +4783,7 @@ peiRowClean:
|
||||||
plx ; row back in X (PLX sets flags on X)
|
plx ; row back in X (PLX sets flags on X)
|
||||||
cmp #0
|
cmp #0
|
||||||
beq peiRowSkipGroup
|
beq peiRowSkipGroup
|
||||||
|
peiRowStep:
|
||||||
inx
|
inx
|
||||||
iny
|
iny
|
||||||
bra peiRowBounds
|
bra peiRowBounds
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
; peislam.s - GetTick and ReadBParam trampolines for the IIgs.
|
; peislam.s - VBL tick counter and ReadBParam trampoline for the IIgs.
|
||||||
;
|
;
|
||||||
; GAS syntax for the llvm-mos w65816 toolchain. The old PEI-slam present
|
; GAS syntax for the llvm-mos w65816 toolchain. The old PEI-slam present
|
||||||
; logic was rolled into iigsBlitStageToShr in joeyDraw; only the two
|
; logic was rolled into iigsBlitStageToShr in joeyDraw; what remains is
|
||||||
; toolbox trampolines remain.
|
; the heartbeat-driven tick counter and one toolbox trampoline.
|
||||||
;
|
;
|
||||||
; C ABI (llvm-mos w65816): caller has M=I=16. A uint16_t return value
|
; C ABI (llvm-mos w65816): caller has M=I=16. A uint16_t return value
|
||||||
; is left in A, which matches the toolbox dispatcher's word result, so
|
; is left in A, which matches the toolbox dispatcher's word result, so
|
||||||
|
|
@ -11,27 +11,94 @@
|
||||||
.text
|
.text
|
||||||
|
|
||||||
; ----------------------------------------------------------------
|
; ----------------------------------------------------------------
|
||||||
; uint16_t iigsGetTickWord(void)
|
; VBL tick counter.
|
||||||
;
|
;
|
||||||
; Calls Misc Toolset GetTick ($2503) and returns the low 16 bits of
|
; A Misc Toolset heartbeat task (SetHeartBeat, $1203) that the firmware
|
||||||
; the 32-bit tick counter. The system increments this counter from the
|
; VBL interrupt handler runs once per video field. It bumps a 16-bit
|
||||||
; actual VBL hardware interrupt, so it stays accurate regardless of
|
; counter that iigsGetTickWord returns. The old wrapper went through
|
||||||
; caller polling rate -- C-side polling of $C019 missed transitions
|
; the toolbox dispatcher to _GetTick ($2503) on every read -- hundreds
|
||||||
; for any op over ~1 ms.
|
; of cycles, ten-odd times per Space Taxi frame (~5% of a frame). The
|
||||||
|
; rate is unchanged: both GetTick and the heartbeat queue are driven
|
||||||
|
; by the same VBL interrupt (60 Hz NTSC, 50 Hz PAL), so jlpFrameHz and
|
||||||
|
; every tick-based hold in audio_full.c keep their meaning. As before,
|
||||||
|
; a caller holding SEI freezes the count.
|
||||||
;
|
;
|
||||||
; GetTick output convention: caller pushes 4 bytes of output space, the
|
; The header, the task code, the counter and the accessors share ONE
|
||||||
; tool dispatcher writes the LongWord into them. We pull the low 16
|
; section, so every reference between them is a same-bank 16-bit
|
||||||
; bits into A (the return value) and discard the high 16 into X.
|
; absolute (K is the bank; B is set from K around each access -- the
|
||||||
|
; heartbeat handler's B and D are undefined, and C globals live in a
|
||||||
|
; different bank from this code).
|
||||||
|
;
|
||||||
|
; Heartbeat task header (Toolbox Reference, Misc Tool Set):
|
||||||
|
; +0 long link owned by the heartbeat handler
|
||||||
|
; +4 word count decremented every tick; the task runs when it
|
||||||
|
; reaches 0 and must re-arm it
|
||||||
|
; +6 word signature $A55A
|
||||||
|
; +8 code, ended by rtl
|
||||||
; ----------------------------------------------------------------
|
; ----------------------------------------------------------------
|
||||||
.section .text.iigsGetTickWord,"ax"
|
.section .text.iigsVblTask,"ax"
|
||||||
.globl iigsGetTickWord
|
.globl iigsGetTickWord
|
||||||
|
.globl iigsVblInstall
|
||||||
|
.globl iigsVblRemove
|
||||||
|
iigsVblTask:
|
||||||
|
.long 0
|
||||||
|
.word 1
|
||||||
|
.word 0xA55A
|
||||||
|
php
|
||||||
|
rep #0x30
|
||||||
|
pha
|
||||||
|
phb
|
||||||
|
phk
|
||||||
|
plb
|
||||||
|
inc vblTick
|
||||||
|
lda #1
|
||||||
|
sta iigsVblTask+4 ; re-arm for the next tick
|
||||||
|
plb
|
||||||
|
pla
|
||||||
|
plp
|
||||||
|
rtl
|
||||||
|
vblTick:
|
||||||
|
.word 0
|
||||||
|
|
||||||
|
; uint16_t iigsGetTickWord(void) -- the current VBL count.
|
||||||
iigsGetTickWord:
|
iigsGetTickWord:
|
||||||
pha ; output space high word
|
phb
|
||||||
pha ; output space low word
|
phk
|
||||||
ldx #0x2503 ; _GetTick
|
plb
|
||||||
jsl 0xe10000
|
lda vblTick
|
||||||
pla ; A = low 16 bits (return value)
|
plb
|
||||||
plx ; discard high 16 bits
|
rtl
|
||||||
|
|
||||||
|
; uint16_t iigsVblInstall(void) -- SetHeartBeat(iigsVblTask).
|
||||||
|
; uint16_t iigsVblRemove(void) -- DelHeartBeat(iigsVblTask).
|
||||||
|
; Both return the Misc Tool Set error code, 0 = success. The long
|
||||||
|
; pointer is pushed high word first ($00, K) then the low word (a
|
||||||
|
; same-section 16-bit relocation). The high word is built without
|
||||||
|
; leaving M=16: a 16-bit zero push plus phk leaves ONE stray zero byte
|
||||||
|
; under the parameter block, dropped by bumping S after the call
|
||||||
|
; (tsc/inc/tcs preserve the carry that flags a tool error). An 8-bit
|
||||||
|
; push after sep #0x20 is what the first cut did, and llvm-mc kept
|
||||||
|
; assembling `lda #0` as a 16-bit immediate (no .a8 directive) -- the
|
||||||
|
; CPU ran the extra byte as BRK inside the call.
|
||||||
|
iigsVblInstall:
|
||||||
|
ldx #0x1203 ; _SetHeartBeat
|
||||||
|
bra vblTool
|
||||||
|
iigsVblRemove:
|
||||||
|
ldx #0x1303 ; _DelHeartBeat
|
||||||
|
vblTool:
|
||||||
|
lda #0
|
||||||
|
pha ; 00 00
|
||||||
|
phk ; K -> stack holds K 00 00 (one zero too many)
|
||||||
|
pea iigsVblTask ; low word
|
||||||
|
jsl 0xe10000 ; consumes lo hi K 00
|
||||||
|
tay ; A = error code (carry set on error)
|
||||||
|
tsc
|
||||||
|
inc
|
||||||
|
tcs ; drop the stray zero byte
|
||||||
|
tya
|
||||||
|
bcs vblToolDone
|
||||||
|
lda #0
|
||||||
|
vblToolDone:
|
||||||
rtl
|
rtl
|
||||||
|
|
||||||
; ----------------------------------------------------------------
|
; ----------------------------------------------------------------
|
||||||
|
|
@ -40,8 +107,8 @@ iigsGetTickWord:
|
||||||
; Reads battery RAM parameter hrtz50or60 ($1D) via _ReadBParam ($0C03)
|
; Reads battery RAM parameter hrtz50or60 ($1D) via _ReadBParam ($0C03)
|
||||||
; and returns the raw value: 0 = NTSC (60 Hz), 1 = PAL (50 Hz).
|
; and returns the raw value: 0 = NTSC (60 Hz), 1 = PAL (50 Hz).
|
||||||
;
|
;
|
||||||
; GetTick fires from the hardware VBL ISR, so its rate matches the
|
; The heartbeat tick fires from the hardware VBL ISR, so its rate
|
||||||
; video field rate -- 60 Hz on NTSC, 50 Hz on PAL. jlpFrameHz must
|
; matches the video field rate -- 60 Hz on NTSC, 50 Hz on PAL. jlpFrameHz must
|
||||||
; report whichever this machine actually runs so wall-clock math
|
; report whichever this machine actually runs so wall-clock math
|
||||||
; (frames * 1000 / jlpFrameHz) is correct on both.
|
; (frames * 1000 / jlpFrameHz) is correct on both.
|
||||||
; ----------------------------------------------------------------
|
; ----------------------------------------------------------------
|
||||||
|
|
@ -54,3 +121,57 @@ iigsReadHzParam:
|
||||||
jsl 0xe10000
|
jsl 0xe10000
|
||||||
pla ; A = result (return value)
|
pla ; A = result (return value)
|
||||||
rtl
|
rtl
|
||||||
|
|
||||||
|
; ----------------------------------------------------------------
|
||||||
|
; void iigsMvnCopy(const IigsMvnArgsT *args) A = args low, X = bank
|
||||||
|
;
|
||||||
|
; +0 uint32_t dst 24-bit destination
|
||||||
|
; +4 uint32_t src 24-bit source
|
||||||
|
; +8 uint16_t len bytes, 1..65535, and hal.c guarantees neither
|
||||||
|
; [dst, dst+len) nor [src, src+len) crosses a bank
|
||||||
|
; (MVN's X/Y wrap inside one bank).
|
||||||
|
;
|
||||||
|
; One MVN: 7 cycles a byte against libc memcpy's byte loop of ~40. The
|
||||||
|
; two bank operands are patched into the instruction itself (this
|
||||||
|
; segment is RAM; the write goes through B = K, same section), then
|
||||||
|
; B is restored -- MVN leaves B pointing at the destination bank.
|
||||||
|
; ----------------------------------------------------------------
|
||||||
|
.section .text.iigsMvnCopy,"ax"
|
||||||
|
.globl iigsMvnCopy
|
||||||
|
iigsMvnCopy:
|
||||||
|
php
|
||||||
|
rep #0x30
|
||||||
|
.a16
|
||||||
|
.i16
|
||||||
|
phb
|
||||||
|
sta 0xe0 ; args low
|
||||||
|
txa
|
||||||
|
sta 0xe2 ; args bank
|
||||||
|
phk
|
||||||
|
plb ; B = K for the operand patch
|
||||||
|
ldy #6
|
||||||
|
lda [0xe0],y ; src bank (byte at +6, junk high)
|
||||||
|
sep #0x20
|
||||||
|
.a8
|
||||||
|
sta mvnOp+2 ; MVN encodes source bank second
|
||||||
|
ldy #2
|
||||||
|
lda [0xe0],y ; dst bank
|
||||||
|
sta mvnOp+1 ; destination bank first
|
||||||
|
rep #0x20
|
||||||
|
.a16
|
||||||
|
ldy #8
|
||||||
|
lda [0xe0],y
|
||||||
|
dec a ; A = len - 1
|
||||||
|
pha
|
||||||
|
ldy #4
|
||||||
|
lda [0xe0],y ; src low
|
||||||
|
tax
|
||||||
|
ldy #0
|
||||||
|
lda [0xe0],y ; dst low
|
||||||
|
tay
|
||||||
|
pla
|
||||||
|
mvnOp:
|
||||||
|
mvn 0, 0 ; patched: dst bank, src bank (54 dd ss)
|
||||||
|
plb
|
||||||
|
plp
|
||||||
|
rtl
|
||||||
|
|
|
||||||
121
tools/iigsStart/iigsStart.c
Normal file
121
tools/iigsStart/iigsStart.c
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
// iigsStart -- a GS/OS boot launcher for the headless IIgs harnesses.
|
||||||
|
//
|
||||||
|
// Installed as /System/Start on a copy of the GS/OS system disk, this runs
|
||||||
|
// at boot INSTEAD of the Finder and hands off to one JoeyLib example. The
|
||||||
|
// point is that the Finder cannot be driven from a script: measured under
|
||||||
|
// gssquared, injected keystrokes reach the $C000 latch but never reach the
|
||||||
|
// Finder -- Open-Apple-A, Tab, arrows and bare letters all left the desktop
|
||||||
|
// untouched. (The one redraw seen after the first key of a run is the
|
||||||
|
// Finder's own settling: byte-identical for three different keys.) Reaching
|
||||||
|
// the desktop also costs ~61s of boot that a probe must sit through; this
|
||||||
|
// route has the example on screen in ~52s.
|
||||||
|
//
|
||||||
|
// Two GS/OS calls, in this order:
|
||||||
|
// SetPrefixGS -- point prefix 0 at the example's DIRECTORY. GS/OS does
|
||||||
|
// NOT do this for a QuitGS launch; prefix 0 was measured still pointing
|
||||||
|
// at the boot volume. That is not cosmetic: JoeyLib opens its NTP
|
||||||
|
// replayer as bare "ntpplayer", and when that read fails jlpAudioInit
|
||||||
|
// returns before ntpArm(), so the sound interrupt vector stays at its
|
||||||
|
// ROM default ($E1002C = 5C xxxx FF) and the example is SILENT. The
|
||||||
|
// Finder sets prefix 0 itself, which is why this only bites here.
|
||||||
|
// QuitGS with pCount = 2 and a pathname -- "quit, and launch this".
|
||||||
|
//
|
||||||
|
// Build: clang-build.sh -DJOEY_START_PATH='"/JOEYLIB/STAXI"' \
|
||||||
|
// -o START iigsStart.c iigsStartQuit.s
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// The program to launch. Overridden per-disk from the build.
|
||||||
|
#ifndef JOEY_START_PATH
|
||||||
|
#define JOEY_START_PATH "/JOEYLIB/STAXI"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Fixed scratch in bank $E1: the $E1:9DC8-$9DFD window that survives a
|
||||||
|
// present (SCB proper ends at $9DC7). The asm stubs hard-code the two
|
||||||
|
// parameter-block addresses, so these must agree with iigsStartQuit.s.
|
||||||
|
// 9DC8 mailbox 9DCC QuitGS parm (8)
|
||||||
|
// 9DD4 pathname string 9DE4 SetPrefix parm (8)
|
||||||
|
// 9DEC prefix string
|
||||||
|
#define QUIT_MAILBOX ((volatile uint8_t *)0xE19DC8L)
|
||||||
|
#define QUIT_PARM ((volatile uint8_t *)0xE19DCCL)
|
||||||
|
#define QUIT_PATH ((volatile uint8_t *)0xE19DD4L)
|
||||||
|
#define PREFIX_PARM ((volatile uint8_t *)0xE19DE4L)
|
||||||
|
#define PREFIX_PATH ((volatile uint8_t *)0xE19DECL)
|
||||||
|
#define QUIT_PATH_ADDR 0x00E19DD4UL
|
||||||
|
#define PREFIX_PATH_ADDR 0x00E19DECUL
|
||||||
|
|
||||||
|
// A GS/OS class-1 input string is a length WORD followed by the characters.
|
||||||
|
#define GSOS_STRING_HEADER 2u
|
||||||
|
|
||||||
|
// Markers for the harness.
|
||||||
|
#define QUIT_REACHED 0xAAu
|
||||||
|
#define QUIT_RETURNED 0xEEu
|
||||||
|
|
||||||
|
|
||||||
|
void iigsStartQuit(void);
|
||||||
|
void iigsStartSetPrefix(void);
|
||||||
|
|
||||||
|
|
||||||
|
// Write a GS/OS input string (length word + characters) and return its length.
|
||||||
|
static uint16_t gsosString(volatile uint8_t *dst, const char *src, uint16_t len) {
|
||||||
|
uint16_t i;
|
||||||
|
|
||||||
|
dst[0] = (uint8_t)(len & 0xFFu);
|
||||||
|
dst[1] = (uint8_t)((len >> 8) & 0xFFu);
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
dst[GSOS_STRING_HEADER + i] = (uint8_t)src[i];
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void putLong(volatile uint8_t *dst, uint32_t v) {
|
||||||
|
dst[0] = (uint8_t)(v & 0xFFu);
|
||||||
|
dst[1] = (uint8_t)((v >> 8) & 0xFFu);
|
||||||
|
dst[2] = (uint8_t)((v >> 16) & 0xFFu);
|
||||||
|
dst[3] = (uint8_t)((v >> 24) & 0xFFu);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
const char *path = JOEY_START_PATH;
|
||||||
|
uint16_t len = 0;
|
||||||
|
uint16_t dir = 0;
|
||||||
|
uint16_t i;
|
||||||
|
|
||||||
|
while (path[len] != '\0') {
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
// Directory part, including the trailing '/': everything up to the last
|
||||||
|
// separator. "/JOEYLIB/STAXI" -> "/JOEYLIB/".
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
if (path[i] == '/') {
|
||||||
|
dir = (uint16_t)(i + 1u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gsosString(PREFIX_PATH, path, dir);
|
||||||
|
PREFIX_PARM[0] = 2u; // pCount
|
||||||
|
PREFIX_PARM[1] = 0u;
|
||||||
|
PREFIX_PARM[2] = 0u; // prefixNum 0 = current directory
|
||||||
|
PREFIX_PARM[3] = 0u;
|
||||||
|
putLong(PREFIX_PARM + 4, PREFIX_PATH_ADDR);
|
||||||
|
iigsStartSetPrefix();
|
||||||
|
|
||||||
|
gsosString(QUIT_PATH, path, len);
|
||||||
|
QUIT_PARM[0] = 2u; // pCount
|
||||||
|
QUIT_PARM[1] = 0u;
|
||||||
|
putLong(QUIT_PARM + 2, QUIT_PATH_ADDR); // pathname
|
||||||
|
QUIT_PARM[6] = 0u; // flags
|
||||||
|
QUIT_PARM[7] = 0u;
|
||||||
|
|
||||||
|
*QUIT_MAILBOX = QUIT_REACHED;
|
||||||
|
iigsStartQuit();
|
||||||
|
*QUIT_MAILBOX = QUIT_RETURNED;
|
||||||
|
|
||||||
|
// QuitGS only returns on failure. Nothing useful is left to do and the
|
||||||
|
// llvm-mos post-main exit path is broken on GS/OS, so spin rather than
|
||||||
|
// crash: the harness reads the mailbox to see what happened.
|
||||||
|
for (;;) {
|
||||||
|
}
|
||||||
|
}
|
||||||
36
tools/iigsStart/iigsStartQuit.s
Normal file
36
tools/iigsStart/iigsStartQuit.s
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
; GS/OS inline-call stubs for iigsStart.
|
||||||
|
;
|
||||||
|
; Same shape as iigsQuitGS in src/iigs/joeyDraw.s and for the same reason:
|
||||||
|
; pure constants, no relocations, so the cross-bank DBR trap that bit the
|
||||||
|
; first attempt at that routine cannot apply here either. Each parameter
|
||||||
|
; block lives at a FIXED address in the $E1:9DC8-$9DFD scratch window and
|
||||||
|
; the C caller fills it in before calling.
|
||||||
|
|
||||||
|
; iigsStartSetPrefix -- SetPrefixGS ($2009), parm block at $E1:9DE4.
|
||||||
|
;
|
||||||
|
; GS/OS does NOT point prefix 0 at the program named in a QuitGS launch: it
|
||||||
|
; was measured still pointing at the BOOT volume, so the launched example's
|
||||||
|
; own relative fopen()s missed. That is not cosmetic -- JoeyLib loads the
|
||||||
|
; NTP replayer as bare "ntpplayer", and when that read fails jlpAudioInit
|
||||||
|
; bails before ntpArm(), leaving the sound vector at its ROM default and
|
||||||
|
; the application completely silent.
|
||||||
|
.section .text.iigsStartSetPrefix,"ax"
|
||||||
|
.globl iigsStartSetPrefix
|
||||||
|
iigsStartSetPrefix:
|
||||||
|
jsl 0xE100A8
|
||||||
|
.word 0x2009
|
||||||
|
.long 0x00E19DE4
|
||||||
|
rtl
|
||||||
|
|
||||||
|
; iigsStartQuit -- QuitGS ($2029), parm block at $E1:9DCC.
|
||||||
|
;
|
||||||
|
; Unlike jlpShutdown's pCount=0 "quit to my parent", this is called with
|
||||||
|
; pCount=2 and a pathname, which tells GS/OS to LAUNCH that program in our
|
||||||
|
; place. On success it never returns.
|
||||||
|
.section .text.iigsStartQuit,"ax"
|
||||||
|
.globl iigsStartQuit
|
||||||
|
iigsStartQuit:
|
||||||
|
jsl 0xE100A8
|
||||||
|
.word 0x2029
|
||||||
|
.long 0x00E19DCC
|
||||||
|
rtl
|
||||||
|
|
@ -40,6 +40,8 @@
|
||||||
#define SPC_TARGET 3
|
#define SPC_TARGET 3
|
||||||
#elif defined(JOEYLIB_PLATFORM_IIGS)
|
#elif defined(JOEYLIB_PLATFORM_IIGS)
|
||||||
#define SPC_TARGET 4
|
#define SPC_TARGET 4
|
||||||
|
#elif defined(JOEYLIB_PLATFORM_X68000)
|
||||||
|
#define SPC_TARGET 5
|
||||||
#else
|
#else
|
||||||
#error "spritebake: build with -DJOEYLIB_PLATFORM_<port>"
|
#error "spritebake: build with -DJOEYLIB_PLATFORM_<port>"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,12 @@
|
||||||
// boot with jlSpriteBankLoadPrecompiled, so no cel is JIT-compiled on
|
// boot with jlSpriteBankLoadPrecompiled, so no cel is JIT-compiled on
|
||||||
// the 65816 (which costs ~0.4 s each).
|
// the 65816 (which costs ~0.4 s each).
|
||||||
//
|
//
|
||||||
// Usage: staxibake OUTPUT.spr
|
// Usage: staxibake OUTPUT.spr the cab/passenger/exhaust bank
|
||||||
|
// staxibake --level LEVEL.dat OUTPUT.spr one level's hook-sprite cels
|
||||||
|
//
|
||||||
|
// The per-level bank holds the cels stLevelCelList names for that level
|
||||||
|
// (its own VIC bitmaps in the colours its hook program uses), in list
|
||||||
|
// order -- the game rebuilds the same list at level entry to key them.
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
@ -26,39 +31,87 @@
|
||||||
#define SPR_OFF_HEIGHT 7u
|
#define SPR_OFF_HEIGHT 7u
|
||||||
#define SPR_OFF_CELLS 8u
|
#define SPR_OFF_CELLS 8u
|
||||||
|
|
||||||
|
static const uint8_t *levelBitmap(const StLevelT *level, uint8_t ptr);
|
||||||
|
static bool writeHeader(FILE *out, uint16_t count);
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
uint8_t header[SPR_HEADER_BYTES];
|
|
||||||
FILE *out;
|
|
||||||
uint16_t i;
|
|
||||||
|
|
||||||
if (argc < 2) {
|
// A level's own sprite block, or NULL when the level does not ship it.
|
||||||
fprintf(stderr, "usage: staxibake OUTPUT.spr\n");
|
static const uint8_t *levelBitmap(const StLevelT *level, uint8_t ptr) {
|
||||||
return 2;
|
uint8_t k;
|
||||||
}
|
|
||||||
out = fopen(argv[1], "wb");
|
for (k = 0u; k < level->spriteCount; k++) {
|
||||||
if (out == NULL) {
|
if (level->sprites[k].ptr == ptr) {
|
||||||
fprintf(stderr, "staxibake: cannot write %s\n", argv[1]);
|
return level->sprites[k].bitmap;
|
||||||
return 1;
|
}
|
||||||
}
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool writeHeader(FILE *out, uint16_t count) {
|
||||||
|
uint8_t header[SPR_HEADER_BYTES];
|
||||||
|
|
||||||
memset(header, 0, sizeof(header));
|
memset(header, 0, sizeof(header));
|
||||||
memcpy(header, "JSP1", 4);
|
memcpy(header, "JSP1", 4);
|
||||||
header[SPR_OFF_TARGET] = 0u; // cross-platform chunky
|
header[SPR_OFF_TARGET] = 0u; // cross-platform chunky
|
||||||
header[SPR_OFF_HAS_PAL] = 0u; // the game owns its palette
|
header[SPR_OFF_HAS_PAL] = 0u; // the game owns its palette
|
||||||
header[SPR_OFF_WIDTH] = (uint8_t)ST_CEL_TILES;
|
header[SPR_OFF_WIDTH] = (uint8_t)ST_CEL_TILES;
|
||||||
header[SPR_OFF_HEIGHT] = (uint8_t)ST_CEL_TILES;
|
header[SPR_OFF_HEIGHT] = (uint8_t)ST_CEL_TILES;
|
||||||
header[SPR_OFF_CELLS] = (uint8_t)(kStCelCount & 0xFFu);
|
header[SPR_OFF_CELLS] = (uint8_t)(count & 0xFFu);
|
||||||
header[SPR_OFF_CELLS + 1u] = (uint8_t)(kStCelCount >> 8);
|
header[SPR_OFF_CELLS + 1u] = (uint8_t)(count >> 8);
|
||||||
if (fwrite(header, 1, SPR_HEADER_BYTES, out) != SPR_HEADER_BYTES) {
|
return fwrite(header, 1, SPR_HEADER_BYTES, out) == SPR_HEADER_BYTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
static StLevelT level;
|
||||||
|
static StCelDefT levelCels[ST_LEVEL_CELS_MAX];
|
||||||
|
const StCelDefT *cels = kStCels;
|
||||||
|
uint16_t count = kStCelCount;
|
||||||
|
const char *outPath;
|
||||||
|
FILE *out;
|
||||||
|
uint16_t i;
|
||||||
|
|
||||||
|
if (argc == 4 && strcmp(argv[1], "--level") == 0) {
|
||||||
|
FILE *fp = fopen(argv[2], "rb");
|
||||||
|
if (fp == NULL || !stLevelParse(&level, fp)) {
|
||||||
|
fprintf(stderr, "staxibake: cannot load level %s\n", argv[2]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
cels = levelCels;
|
||||||
|
count = stLevelCelList(&level, levelCels, ST_LEVEL_CELS_MAX);
|
||||||
|
outPath = argv[3];
|
||||||
|
} else if (argc == 2) {
|
||||||
|
outPath = argv[1];
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "usage: staxibake OUTPUT.spr\n staxibake --level LEVEL.dat OUTPUT.spr\n");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
out = fopen(outPath, "wb");
|
||||||
|
if (out == NULL) {
|
||||||
|
fprintf(stderr, "staxibake: cannot write %s\n", outPath);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!writeHeader(out, count)) {
|
||||||
fprintf(stderr, "staxibake: header write failed\n");
|
fprintf(stderr, "staxibake: header write failed\n");
|
||||||
fclose(out);
|
fclose(out);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
for (i = 0u; i < kStCelCount; i++) {
|
for (i = 0u; i < count; i++) {
|
||||||
uint8_t blob[ST_CEL_BYTES];
|
uint8_t blob[ST_CEL_BYTES];
|
||||||
const uint8_t *bm = stC64SpriteBitmap((uint8_t)(kStCels[i].ptr - ST_SPRITE_PTR_FIRST));
|
const uint8_t *bm;
|
||||||
|
if (cels == levelCels) {
|
||||||
stCelBlob(bm, kStCels[i].multi, kStCels[i].color, kStCels[i].mc0, kStCels[i].mc1, blob);
|
bm = levelBitmap(&level, cels[i].ptr);
|
||||||
|
if (bm == NULL) {
|
||||||
|
fprintf(stderr, "staxibake: level %s has no sprite block $%02X\n", argv[2], (unsigned)cels[i].ptr);
|
||||||
|
fclose(out);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bm = stC64SpriteBitmap((uint8_t)(cels[i].ptr - ST_SPRITE_PTR_FIRST));
|
||||||
|
}
|
||||||
|
stCelBlob(bm, cels[i].multi, cels[i].color, cels[i].mc0, cels[i].mc1, blob);
|
||||||
if (fwrite(blob, 1, ST_CEL_BYTES, out) != ST_CEL_BYTES) {
|
if (fwrite(blob, 1, ST_CEL_BYTES, out) != ST_CEL_BYTES) {
|
||||||
fprintf(stderr, "staxibake: cel %u write failed\n", (unsigned)i);
|
fprintf(stderr, "staxibake: cel %u write failed\n", (unsigned)i);
|
||||||
fclose(out);
|
fclose(out);
|
||||||
|
|
@ -66,6 +119,6 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fclose(out);
|
fclose(out);
|
||||||
fprintf(stderr, "staxibake: wrote %u cels (%ux%u tiles) -> %s\n", (unsigned)kStCelCount, (unsigned)ST_CEL_TILES, (unsigned)ST_CEL_TILES, argv[1]);
|
fprintf(stderr, "staxibake: wrote %u cels (%ux%u tiles) -> %s\n", (unsigned)count, (unsigned)ST_CEL_TILES, (unsigned)ST_CEL_TILES, outPath);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
36
tools/staxiharness/Makefile
Normal file
36
tools/staxiharness/Makefile
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
# Host-side checks of Space Taxi's front-end screens (the parts tools/stsim
|
||||||
|
# cannot reach: they live in spacetaxi.c, above the simulation). Builds the
|
||||||
|
# game on the BLANK port with spacetaxi.c INCLUDED into the harness so its
|
||||||
|
# statics are reachable, and wraps the keyboard queue and the save file
|
||||||
|
# with scripted stand-ins.
|
||||||
|
#
|
||||||
|
# make -C tools/staxiharness build + run
|
||||||
|
|
||||||
|
REPO := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))/../..)
|
||||||
|
ST := $(REPO)/examples/spacetaxi
|
||||||
|
OUT := $(REPO)/build/tools/staxiharness
|
||||||
|
SRCS := $(filter-out $(ST)/spacetaxi.c,$(wildcard $(ST)/*.c)) \
|
||||||
|
$(wildcard $(REPO)/src/core/*.c) $(wildcard $(REPO)/src/generic/*.c) \
|
||||||
|
$(REPO)/src/blank/blank.c
|
||||||
|
CFLAGS := -O0 -g -w -DJOEYLIB_PLATFORM_BLANK -I$(REPO)/include -I$(REPO)/src/core \
|
||||||
|
-I$(REPO)/src/codegen -I$(ST)
|
||||||
|
WRAPS := -Wl,--wrap=jlInputGetChar -Wl,--wrap=jlSaveWrite -Wl,--wrap=jlSaveRead
|
||||||
|
|
||||||
|
.PHONY: all run clean
|
||||||
|
|
||||||
|
all: run
|
||||||
|
|
||||||
|
$(OUT)/harness: harness.c $(ST)/spacetaxi.c $(SRCS)
|
||||||
|
@mkdir -p $(OUT)
|
||||||
|
$(CC) $(CFLAGS) harness.c $(SRCS) $(WRAPS) -o $@
|
||||||
|
|
||||||
|
# The title screen's level file is read through DATA/levels (the DOS bake
|
||||||
|
# is the cross-platform STL4 form), so run from a directory that has one.
|
||||||
|
run: $(OUT)/harness
|
||||||
|
@mkdir -p $(OUT)/DATA
|
||||||
|
@ln -sfn $(ST)/generated/dos/levels $(OUT)/DATA/levels
|
||||||
|
@ln -sfn $(ST)/generated/dos/speech.bin $(OUT)/DATA/speech.bin
|
||||||
|
cd $(OUT) && ./harness
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(OUT)
|
||||||
183
tools/staxiharness/harness.c
Normal file
183
tools/staxiharness/harness.c
Normal file
|
|
@ -0,0 +1,183 @@
|
||||||
|
// Space Taxi front-end harness: the immortal-cabbies table and the name
|
||||||
|
// entry screen, driven with scripted keys on the host (BLANK port).
|
||||||
|
//
|
||||||
|
// spacetaxi.c is INCLUDED so its static state and routines are visible;
|
||||||
|
// its main() is renamed away. jlInputGetChar, jlSaveWrite and jlSaveRead
|
||||||
|
// are replaced through the linker's --wrap with a key script and an
|
||||||
|
// in-memory save image (see the Makefile).
|
||||||
|
#define main staxiMain
|
||||||
|
#include "spacetaxi.c"
|
||||||
|
#undef main
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define SAVE_IMAGE_MAX 512u
|
||||||
|
|
||||||
|
bool __wrap_jlSaveWrite(const char *name, const void *buf, uint32_t len);
|
||||||
|
uint32_t __wrap_jlSaveRead(const char *name, void *buf, uint32_t max);
|
||||||
|
int __wrap_jlInputGetChar(void);
|
||||||
|
|
||||||
|
static void check(bool ok, const char *what);
|
||||||
|
static void rowText(uint8_t row, uint8_t col, uint8_t n, char *out);
|
||||||
|
static void setScore(uint8_t player, const uint8_t *digits);
|
||||||
|
|
||||||
|
static const char *gKeys = "";
|
||||||
|
static uint8_t gSaveImage[SAVE_IMAGE_MAX];
|
||||||
|
static uint32_t gSaveLen = 0u;
|
||||||
|
static int gFails = 0;
|
||||||
|
|
||||||
|
|
||||||
|
int __wrap_jlInputGetChar(void) {
|
||||||
|
if (*gKeys == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return (unsigned char)*gKeys++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t __wrap_jlSaveRead(const char *name, void *buf, uint32_t max) {
|
||||||
|
(void)name;
|
||||||
|
if (gSaveLen == 0u || gSaveLen > max) {
|
||||||
|
return 0u;
|
||||||
|
}
|
||||||
|
memcpy(buf, gSaveImage, gSaveLen);
|
||||||
|
return gSaveLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool __wrap_jlSaveWrite(const char *name, const void *buf, uint32_t len) {
|
||||||
|
(void)name;
|
||||||
|
if (len > sizeof(gSaveImage)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memcpy(gSaveImage, buf, len);
|
||||||
|
gSaveLen = len;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void check(bool ok, const char *what) {
|
||||||
|
printf("%s %s\n", ok ? "ok " : "FAIL", what);
|
||||||
|
if (!ok) {
|
||||||
|
gFails++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// `n` screen cells of one row as a C string.
|
||||||
|
static void rowText(uint8_t row, uint8_t col, uint8_t n, char *out) {
|
||||||
|
uint8_t k;
|
||||||
|
|
||||||
|
for (k = 0u; k < n; k++) {
|
||||||
|
out[k] = (char)gSim.screen[ST_CELL(row, col + k)];
|
||||||
|
}
|
||||||
|
out[n] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// A player's score as the sim keeps it: seven screen-code digits with
|
||||||
|
// the point at ST_HS_POINT_AT.
|
||||||
|
static void setScore(uint8_t player, const uint8_t *digits) {
|
||||||
|
uint8_t k;
|
||||||
|
|
||||||
|
for (k = 0u; k < ST_NUMBER_CHARS; k++) {
|
||||||
|
gSim.scoreBackup[player][k] = (uint8_t)(ST_CHAR_DIGIT_BASE + digits[k]);
|
||||||
|
}
|
||||||
|
gSim.scoreBackup[player][ST_HS_POINT_AT] = ST_CHAR_POINT;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
static const uint8_t kDigits[ST_NUMBER_CHARS] = { 1u, 2u, 3u, 4u, 0u, 5u, 6u };
|
||||||
|
jlConfigT config;
|
||||||
|
jlSurfaceT *stage;
|
||||||
|
char buf[48];
|
||||||
|
uint8_t k;
|
||||||
|
|
||||||
|
config.codegenBytes = 160UL * 1024;
|
||||||
|
config.audioBytes = 32UL * 1024;
|
||||||
|
if (!jlInit(&config)) {
|
||||||
|
printf("jlInit failed\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
stage = jlStageGet();
|
||||||
|
memset(&gGame, 0, sizeof(gGame));
|
||||||
|
memset(&gSim, 0, sizeof(gSim));
|
||||||
|
stRenderInit(stage);
|
||||||
|
stAudioInit();
|
||||||
|
stSimNewGame(&gSim, 2u, false);
|
||||||
|
|
||||||
|
// ---- ranking ($4D92) ----
|
||||||
|
check(highScoreInsert((const uint8_t *)" 49.99") == ST_HS_ENTRIES, "49.99 is below the $50 floor");
|
||||||
|
check(highScoreInsert((const uint8_t *)" 50.00") == ST_HS_ENTRIES, "50.00 passes the floor but beats nobody");
|
||||||
|
check(highScoreInsert((const uint8_t *)" 500.00") == 8u, "500.00 lands in slot 8 (under 504.97, above 498.77)");
|
||||||
|
check(memcmp(gHighScores[9].score, " 498.77", 7) == 0 && memcmp(gHighScores[7].score, " 504.97", 7) == 0, "older entries shifted down, the last one dropped");
|
||||||
|
check(highScoreInsert((const uint8_t *)"3877.56") == 0u, "a tie goes above the older entry");
|
||||||
|
check(memcmp(gHighScores[1].score, "3877.56", 7) == 0 && memcmp(gHighScores[1].name, "MICHAEL PLATE ", 15) == 0, "the tied old entry is now second");
|
||||||
|
|
||||||
|
// ---- the entry screen for cabbie 2 with $1234.56 on shift 3 ($4D52 / $4E35) ----
|
||||||
|
gSim.player = 1u;
|
||||||
|
gGame.variation = 3u;
|
||||||
|
setScore(1u, kDigits);
|
||||||
|
gKeys = "zz"; // typed before the screen: flushed like $0277
|
||||||
|
check(nameEntryStart(), "1234.56 ranks");
|
||||||
|
check(gGame.state == ST_STATE_NAME_ENTRY && gGame.hsSlot == 3u, "slot 3 (under the two 3877.56 and 1809.51)");
|
||||||
|
check(gSim.borderColor == 2u && gSim.bgColor == 15u, "red border, grey field");
|
||||||
|
rowText(3u, 7u, 25u, buf);
|
||||||
|
check(strcmp(buf, "CONGRATULATIONS CABBIE 2,") == 0, buf);
|
||||||
|
rowText(0u, 15u, 8u, buf);
|
||||||
|
check(strcmp(buf, "$1234.56") == 0, buf);
|
||||||
|
rowText(9u, 9u, 21u, buf);
|
||||||
|
check(strcmp(buf, "ENTER YOUR NAME BELOW") == 0, buf);
|
||||||
|
check(*gKeys == 0, "earlier keystrokes discarded");
|
||||||
|
|
||||||
|
// ---- typing ($4ED8): s c o 9 t t <backspace> T ----
|
||||||
|
gKeys = "sco9tt\bT";
|
||||||
|
runNameEntry(1u);
|
||||||
|
rowText(ST_HS_NAME_ROW, ST_HS_NAME_COL, 5u, buf);
|
||||||
|
check(strcmp(buf, "SCOTT") == 0, buf);
|
||||||
|
check(gGame.hsLen == 5u, "five characters kept ('9' ignored, one deleted, one retyped)");
|
||||||
|
check(gGame.state == ST_STATE_NAME_ENTRY, "still entering");
|
||||||
|
for (k = 0u; k < 2u; k++) {
|
||||||
|
runNameEntry(1u);
|
||||||
|
}
|
||||||
|
check(gSim.screen[ST_CELL(ST_HS_NAME_ROW, ST_HS_NAME_COL + 5u)] == ' ', "first flip blanks the cursor ($4F4D)");
|
||||||
|
for (k = 0u; k < 3u; k++) {
|
||||||
|
runNameEntry(1u);
|
||||||
|
}
|
||||||
|
check(gSim.screen[ST_CELL(ST_HS_NAME_ROW, ST_HS_NAME_COL + 5u)] == '%', "second flip shows it");
|
||||||
|
gKeys = "\r";
|
||||||
|
runNameEntry(1u);
|
||||||
|
check(gGame.state != ST_STATE_NAME_ENTRY, "RETURN leaves the screen");
|
||||||
|
check(memcmp(gHighScores[3].name, "SCOTT ", 15) == 0 && gHighScores[3].shift == 3u, "name and shift stored");
|
||||||
|
check(gSaveLen == 240u && gSaveImage[3 * 24 + 7] == 0 && memcmp(&gSaveImage[3 * 24 + 8], "SCOTT ", 6) == 0 && gSaveImage[3 * 24 + 23] == 3u, "saved in the C64 image layout");
|
||||||
|
check(gSim.playersDone == 1u, "game-over bookkeeping continued");
|
||||||
|
|
||||||
|
// ---- reload from the image ($23E5) ----
|
||||||
|
memset(gHighScores, 0, sizeof(gHighScores));
|
||||||
|
highScoreLoad();
|
||||||
|
check(memcmp(gHighScores[3].name, "SCOTT ", 15) == 0 && memcmp(gHighScores[0].score, "3877.56", 7) == 0, "table restored from the save");
|
||||||
|
|
||||||
|
// ---- an empty name becomes "*" ----
|
||||||
|
gSim.player = 0u;
|
||||||
|
setScore(0u, kDigits);
|
||||||
|
check(nameEntryStart() && gGame.hsSlot == 3u, "second entry, a tie goes above");
|
||||||
|
gKeys = "\r";
|
||||||
|
runNameEntry(1u);
|
||||||
|
check(gHighScores[3].name[0] == '*' && memcmp(gHighScores[4].name, "SCOTT", 5) == 0, "an empty name is stored as *");
|
||||||
|
|
||||||
|
// ---- the table screen ($4C1F) ----
|
||||||
|
highScoreDraw();
|
||||||
|
rowText(1u, 9u, 20u, buf);
|
||||||
|
check(strcmp(buf, "THE IMMORTAL CABBIES") == 0, buf);
|
||||||
|
rowText(12u, 4u, 26u, buf);
|
||||||
|
check(strcmp(buf, "$1234.56 SCOTT ") == 0, buf);
|
||||||
|
check(gSim.screen[ST_CELL(12, 34)] == '3' && gSim.screen[ST_CELL(10, 15)] == '*', "shift digit in the SHIFT column, * name above");
|
||||||
|
rowText(24u, 3u, 34u, buf);
|
||||||
|
check(strcmp(buf, "PRESS FIRE TO RETURN TO TITLE PAGE") == 0, buf);
|
||||||
|
check(jlMusicIsPlaying(), "song 7 under the table");
|
||||||
|
|
||||||
|
printf("%s (%d failures)\n", gFails ? "staxiharness: FAIL" : "staxiharness: PASS", gFails);
|
||||||
|
jlShutdown();
|
||||||
|
return gFails ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
@ -2,10 +2,16 @@
|
||||||
ST := ../../examples/spacetaxi
|
ST := ../../examples/spacetaxi
|
||||||
CC ?= gcc
|
CC ?= gcc
|
||||||
CFLAGS := -O1 -g -Wall -Wextra -std=c99 -I$(ST)
|
CFLAGS := -O1 -g -Wall -Wextra -std=c99 -I$(ST)
|
||||||
SRCS := stsim.c $(ST)/stSim.c $(ST)/stFare.c $(ST)/stHooks.c $(ST)/stLevelFile.c $(ST)/stTitle.c $(ST)/stC64Data.c
|
SRCS := stsim.c $(ST)/stSim.c $(ST)/stFare.c $(ST)/stHooks.c $(ST)/stLevelFile.c $(ST)/stTitle.c $(ST)/stC64Data.c $(ST)/stLevelCels.c
|
||||||
|
|
||||||
stsim: $(SRCS) $(ST)/stSim.h $(ST)/stC64Data.h $(ST)/stDemoStreams.h
|
stsim: $(SRCS) $(ST)/stSim.h $(ST)/stC64Data.h $(ST)/stDemoStreams.h gateStreams.h
|
||||||
$(CC) $(CFLAGS) $(SRCS) -o $@
|
$(CC) $(CFLAGS) $(SRCS) -o $@
|
||||||
|
|
||||||
|
# The fixture generator for gateStreams.h. Not built by default: it runs
|
||||||
|
# a long search and is only needed when a level's stream is (re)made.
|
||||||
|
recorder: recorder.c $(SRCS:stsim.c=) $(ST)/stSim.h
|
||||||
|
$(CC) $(CFLAGS) recorder.c $(ST)/stSim.c $(ST)/stFare.c $(ST)/stHooks.c \
|
||||||
|
$(ST)/stLevelFile.c $(ST)/stTitle.c $(ST)/stC64Data.c -o $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f stsim
|
rm -f stsim recorder
|
||||||
|
|
|
||||||
34
tools/stsim/celGate.sh
Executable file
34
tools/stsim/celGate.sh
Executable file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Cel gate for Space Taxi: every level cel a recorded ride DISPLAYS must be
|
||||||
|
# in the level's baked manifest (stLevelCelList in stLevelCels.c), or the
|
||||||
|
# real ports build it mid-ride and draw it interpreted -- the "compiling
|
||||||
|
# sprites on the fly" hitch. Runs the same rides gate.sh does with the
|
||||||
|
# stsim census (STSIM_CELS=1) and fails on any MISSING line.
|
||||||
|
#
|
||||||
|
# celGate.sh check every hook level
|
||||||
|
set -u
|
||||||
|
|
||||||
|
here=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
repo=$(cd "$here/../.." && pwd)
|
||||||
|
levels=$repo/examples/spacetaxi/generated/iigs/levels
|
||||||
|
ticks=4000
|
||||||
|
pairs="H:08 T:20 W:23 X:24 G:07 I:09 K:11 O:15 Q:17 R:18 U:21 V:22 E:05 J:10 L:12 P:16 S:19"
|
||||||
|
|
||||||
|
make -C "$here" >/dev/null || { echo "celGate: build failed" >&2; exit 2; }
|
||||||
|
|
||||||
|
fail=0
|
||||||
|
for p in $pairs; do
|
||||||
|
l=${p%%:*}; n=${p##*:}
|
||||||
|
out=$(STSIM_CELS=1 "$here/stsim" "$levels/level$n.dat" "$l" "$ticks" 2>&1 >/dev/null | grep -E '^(CEL|CENSUS)')
|
||||||
|
missing=$(echo "$out" | grep -c '^CEL MISSING')
|
||||||
|
census=$(echo "$out" | grep '^CENSUS' | sed 's/^CENSUS //')
|
||||||
|
if [ "$missing" -ne 0 ]; then
|
||||||
|
printf ' %s: *** %s cel(s) shown but not baked *** (%s)\n' "$l" "$missing" "$census"
|
||||||
|
echo "$out" | grep '^CEL MISSING' | sed 's/^/ /'
|
||||||
|
fail=1
|
||||||
|
else
|
||||||
|
printf ' %s: ok (%s)\n' "$l" "$census"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
[ $fail -eq 0 ] && echo "celGate: PASS" || echo "celGate: FAIL"
|
||||||
|
exit $fail
|
||||||
75
tools/stsim/gate.sh
Executable file
75
tools/stsim/gate.sh
Executable file
|
|
@ -0,0 +1,75 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Behaviour gate for the Space Taxi simulation.
|
||||||
|
#
|
||||||
|
# Runs every hook level through stsim and compares the per-tick trace
|
||||||
|
# against a golden capture. A refactor that changes nothing observable
|
||||||
|
# leaves all seventeen traces byte-identical; anything else is a regression.
|
||||||
|
#
|
||||||
|
# H, W, T and X ride the C64's own attract recordings (the ones checked
|
||||||
|
# against VICE traces). The other thirteen ride synthesised fixtures from
|
||||||
|
# gateStreams.h -- a regression gate only, not evidence of C64 fidelity.
|
||||||
|
#
|
||||||
|
# gate.sh capture [dir] record goldens from the CURRENT tree
|
||||||
|
# gate.sh check [dir] compare the current tree against them
|
||||||
|
# gate.sh hashes print one md5 per level (compact reference)
|
||||||
|
#
|
||||||
|
# Capture from a tree you trust, refactor, then check.
|
||||||
|
set -u
|
||||||
|
|
||||||
|
here=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
repo=$(cd "$here/../.." && pwd)
|
||||||
|
levels=$repo/examples/spacetaxi/generated/iigs/levels
|
||||||
|
mode=${1:-check}
|
||||||
|
dir=${2:-$here/golden}
|
||||||
|
ticks=4000
|
||||||
|
|
||||||
|
# level letter -> level file number
|
||||||
|
pairs="H:08 T:20 W:23 X:24 G:07 I:09 K:11 O:15 Q:17 R:18 U:21 V:22 E:05 J:10 L:12 P:16 S:19"
|
||||||
|
|
||||||
|
make -C "$here" >/dev/null || { echo "gate: build failed" >&2; exit 2; }
|
||||||
|
|
||||||
|
case "$mode" in
|
||||||
|
capture)
|
||||||
|
mkdir -p "$dir"
|
||||||
|
for p in $pairs; do
|
||||||
|
l=${p%%:*}; n=${p##*:}
|
||||||
|
"$here/stsim" "$levels/level$n.dat" "$l" "$ticks" > "$dir/demo$l.jsonl" 2>/dev/null
|
||||||
|
printf ' %s: %s ticks\n' "$l" "$(wc -l < "$dir/demo$l.jsonl")"
|
||||||
|
done
|
||||||
|
echo "gate: captured to $dir"
|
||||||
|
;;
|
||||||
|
check)
|
||||||
|
fail=0
|
||||||
|
for p in $pairs; do
|
||||||
|
l=${p%%:*}; n=${p##*:}
|
||||||
|
got=$(mktemp)
|
||||||
|
"$here/stsim" "$levels/level$n.dat" "$l" "$ticks" > "$got" 2>/dev/null
|
||||||
|
if [ ! -f "$dir/demo$l.jsonl" ]; then
|
||||||
|
echo " $l: no golden (run: gate.sh capture)"
|
||||||
|
elif cmp -s "$dir/demo$l.jsonl" "$got"; then
|
||||||
|
printf ' %s: IDENTICAL (%s ticks)\n' "$l" "$(wc -l < "$got")"
|
||||||
|
else
|
||||||
|
echo " $l: *** DIFFERS ***"
|
||||||
|
diff "$dir/demo$l.jsonl" "$got" | head -3
|
||||||
|
fail=1
|
||||||
|
fi
|
||||||
|
rm -f "$got"
|
||||||
|
done
|
||||||
|
[ $fail -eq 0 ] && echo "gate: PASS" || echo "gate: FAIL"
|
||||||
|
# The cel census rides the same recordings: a cel a ride displays that
|
||||||
|
# its manifest does not bake is a mid-ride interpreted sprite.
|
||||||
|
"$here/celGate.sh" || fail=1
|
||||||
|
exit $fail
|
||||||
|
;;
|
||||||
|
hashes)
|
||||||
|
for p in $pairs; do
|
||||||
|
l=${p%%:*}; n=${p##*:}
|
||||||
|
h=$("$here/stsim" "$levels/level$n.dat" "$l" "$ticks" 2>/dev/null | md5sum | cut -d' ' -f1)
|
||||||
|
printf '%s %s\n' "$l" "$h"
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
sed -n '2,20p' "$0"
|
||||||
|
exit 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
21
tools/stsim/gateHashes.txt
Normal file
21
tools/stsim/gateHashes.txt
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Space Taxi behaviour gate -- md5 of each level's 4000-tick trace.
|
||||||
|
# Regenerate with: tools/stsim/gate.sh hashes
|
||||||
|
# H/W/T/X ride the C64's attract recordings; the rest ride
|
||||||
|
# synthesised fixtures from gateStreams.h (regression gate only).
|
||||||
|
H 760501b0e48f3922bfb0f23fd2831935
|
||||||
|
T c0b3f9e9a74e0d526703cf9b1875557b
|
||||||
|
W 92226455989e8326577a6ea20df8c8ea
|
||||||
|
X 87251d616a1be6e0c9c9317987b0fe20
|
||||||
|
G 73530651372c60042f1f46b5c0cbada0
|
||||||
|
I 7fe4415429d2b72a50884424269cb685
|
||||||
|
K 567be2ed37219356a055866459971345
|
||||||
|
O 02455280933e915e57803a4059aaefd9
|
||||||
|
Q a7732866d54ed06d52e5dc7406b5b229
|
||||||
|
R 066a9be80107446893b4571d247ee5cc
|
||||||
|
U 02dd2f26245442ab016ef02e10fde611
|
||||||
|
V cc2cb13c5e393d07fbece9c0c465e387
|
||||||
|
E 21ff29d69d8318bcb2e870b9602e910e
|
||||||
|
J 4c22a7515949b1701ed4fedabb7823dd
|
||||||
|
L a0383cbde2800bb09353df15214934ef
|
||||||
|
P f345d0adc9e011662d582c4277ee663e
|
||||||
|
S daa3cd12320e32d3e9b6b97b19b0a6d0
|
||||||
460
tools/stsim/gateStreams.h
Normal file
460
tools/stsim/gateStreams.h
Normal file
|
|
@ -0,0 +1,460 @@
|
||||||
|
// Synthesised demo input streams -- a TEST FIXTURE, not game content.
|
||||||
|
//
|
||||||
|
// The C64 ships attract recordings for four screens only (H, W, T and X);
|
||||||
|
// those live in examples/spacetaxi/stDemoStreams.h and are the streams
|
||||||
|
// checked against VICE traces. The thirteen streams below were searched for
|
||||||
|
// by tools/stsim's recorder against this port's own simulation, scored on
|
||||||
|
// how long the cab survives and how much of the level's hook state, fare
|
||||||
|
// machine and screen animation the ride reaches.
|
||||||
|
//
|
||||||
|
// So they prove one thing and not another: they are a REGRESSION gate --
|
||||||
|
// refactor the hooks and the tick-by-tick trace must not move -- but they
|
||||||
|
// are NOT evidence that a hook matches the original, because no C64 trace
|
||||||
|
// exists for these screens. Treat a change to these bytes as invalidating
|
||||||
|
// the goldens, and never put them in the attract rotation.
|
||||||
|
|
||||||
|
#ifndef ST_GATE_STREAMS_H
|
||||||
|
#define ST_GATE_STREAMS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// level G -- 1400 ticks, 5 hook bytes, 10 fare stages, 256 pictures, 13 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level??.dat G 20260921
|
||||||
|
static const uint8_t kGateStreamG[320] = {
|
||||||
|
0x88, 0x05, 0x81, 0x09, 0x81, 0x05, 0x81, 0x04, 0x81, 0x06, 0x84, 0x0C,
|
||||||
|
0x81, 0x01, 0x89, 0x02, 0x81, 0x09, 0x82, 0x0D, 0x88, 0x05, 0x81, 0x03,
|
||||||
|
0x80, 0x09, 0x81, 0x01, 0x90, 0x02, 0x81, 0x04, 0x90, 0x08, 0x81, 0x03,
|
||||||
|
0x90, 0x0A, 0x88, 0x0E, 0x88, 0x0C, 0x90, 0x0C, 0x81, 0x04, 0x81, 0x0D,
|
||||||
|
0x90, 0x0A, 0x81, 0x0D, 0x84, 0x0E, 0x81, 0x09, 0x81, 0x08, 0x81, 0x0C,
|
||||||
|
0x82, 0x04, 0x81, 0x02, 0x81, 0x0E, 0x81, 0x02, 0x84, 0x01, 0x88, 0x0A,
|
||||||
|
0x88, 0x05, 0x90, 0x09, 0x81, 0x02, 0x84, 0x09, 0x84, 0x05, 0x81, 0x0D,
|
||||||
|
0x84, 0x09, 0x88, 0x03, 0x84, 0x06, 0x84, 0x0D, 0x81, 0x05, 0x84, 0x0C,
|
||||||
|
0x88, 0x0C, 0x88, 0x0C, 0x88, 0x03, 0x88, 0x09, 0x81, 0x0A, 0x81, 0x09,
|
||||||
|
0x81, 0x06, 0x81, 0x07, 0x81, 0x05, 0x88, 0x09, 0x88, 0x02, 0x81, 0x0B,
|
||||||
|
0x84, 0x0A, 0x84, 0x05, 0x88, 0x03, 0x81, 0x05, 0x81, 0x0B, 0x81, 0x05,
|
||||||
|
0x90, 0x05, 0x84, 0x09, 0x81, 0x06, 0x81, 0x07, 0x90, 0x0D, 0x81, 0x04,
|
||||||
|
0x81, 0x0E, 0x88, 0x0D, 0x88, 0x0D, 0x88, 0x06, 0x82, 0x02, 0x88, 0x0D,
|
||||||
|
0x90, 0x0E, 0x81, 0x04, 0x81, 0x0B, 0x84, 0x0B, 0x81, 0x05, 0x84, 0x07,
|
||||||
|
0x88, 0x0A, 0x80, 0x04, 0x84, 0x07, 0x84, 0x0B, 0x88, 0x0D, 0x90, 0x0E,
|
||||||
|
0x90, 0x06, 0x88, 0x08, 0x82, 0x0C, 0x81, 0x02, 0x81, 0x0E, 0x88, 0x0A,
|
||||||
|
0x90, 0x0B, 0x81, 0x04, 0x88, 0x08, 0x90, 0x07, 0x84, 0x03, 0x82, 0x08,
|
||||||
|
0x81, 0x05, 0x81, 0x03, 0x81, 0x03, 0x81, 0x06, 0x90, 0x0B, 0x84, 0x0E,
|
||||||
|
0x82, 0x0E, 0x88, 0x08, 0x88, 0x04, 0x81, 0x0D, 0x88, 0x04, 0x81, 0x08,
|
||||||
|
0x88, 0x0C, 0x81, 0x06, 0x81, 0x04, 0x90, 0x0B, 0x81, 0x08, 0x84, 0x04,
|
||||||
|
0x90, 0x08, 0x84, 0x09, 0x88, 0x09, 0x81, 0x0B, 0x81, 0x07, 0x84, 0x0D,
|
||||||
|
0x88, 0x0A, 0x88, 0x09, 0x90, 0x0B, 0x90, 0x02, 0x84, 0x0D, 0x81, 0x0E,
|
||||||
|
0x90, 0x0D, 0x84, 0x07, 0x82, 0x0C, 0x81, 0x0E, 0x81, 0x06, 0x88, 0x09,
|
||||||
|
0x90, 0x02, 0x90, 0x04, 0x81, 0x09, 0x84, 0x0A, 0x90, 0x06, 0x81, 0x04,
|
||||||
|
0x90, 0x03, 0x90, 0x08, 0x84, 0x0B, 0x81, 0x02, 0x90, 0x04, 0x84, 0x03,
|
||||||
|
0x81, 0x03, 0x84, 0x05, 0x90, 0x0C, 0x81, 0x01, 0x90, 0x0D, 0x81, 0x02,
|
||||||
|
0x90, 0x05, 0x90, 0x0E, 0x81, 0x03, 0x90, 0x0A,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level I -- 1400 ticks, 20 hook bytes, 10 fare stages, 256 pictures, 22 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level??.dat I 20260918
|
||||||
|
static const uint8_t kGateStreamI[320] = {
|
||||||
|
0x84, 0x0E, 0x81, 0x06, 0x84, 0x0E, 0x88, 0x0B, 0x88, 0x05, 0x81, 0x03,
|
||||||
|
0x82, 0x07, 0x90, 0x0A, 0x81, 0x08, 0x88, 0x0E, 0x88, 0x0E, 0x90, 0x0B,
|
||||||
|
0x82, 0x0D, 0x81, 0x01, 0x81, 0x0A, 0x90, 0x0A, 0x82, 0x03, 0x81, 0x07,
|
||||||
|
0x82, 0x05, 0x81, 0x0B, 0x81, 0x05, 0x82, 0x05, 0x82, 0x0C, 0x81, 0x08,
|
||||||
|
0x88, 0x0C, 0x90, 0x04, 0x81, 0x02, 0x88, 0x08, 0x81, 0x03, 0x81, 0x05,
|
||||||
|
0x81, 0x0A, 0x88, 0x0E, 0x81, 0x06, 0x84, 0x0E, 0x82, 0x07, 0x82, 0x01,
|
||||||
|
0x81, 0x04, 0x82, 0x08, 0x90, 0x0C, 0x90, 0x05, 0x81, 0x08, 0x82, 0x0D,
|
||||||
|
0x90, 0x06, 0x81, 0x0A, 0x84, 0x0B, 0x84, 0x0C, 0x84, 0x0C, 0x88, 0x09,
|
||||||
|
0x81, 0x06, 0x81, 0x0C, 0x81, 0x04, 0x82, 0x09, 0x88, 0x05, 0x82, 0x0E,
|
||||||
|
0x81, 0x0C, 0x81, 0x07, 0x88, 0x01, 0x90, 0x0E, 0x81, 0x02, 0x90, 0x0E,
|
||||||
|
0x81, 0x02, 0x90, 0x07, 0x84, 0x05, 0x84, 0x06, 0x88, 0x06, 0x88, 0x0C,
|
||||||
|
0x81, 0x0C, 0x84, 0x0B, 0x82, 0x09, 0x81, 0x05, 0x81, 0x0B, 0x82, 0x09,
|
||||||
|
0x90, 0x0E, 0x90, 0x06, 0x88, 0x0D, 0x84, 0x03, 0x82, 0x0B, 0x84, 0x0D,
|
||||||
|
0x90, 0x0B, 0x90, 0x04, 0x81, 0x0B, 0x81, 0x08, 0x88, 0x08, 0x90, 0x0A,
|
||||||
|
0x81, 0x03, 0x81, 0x01, 0x81, 0x0E, 0x81, 0x04, 0x88, 0x02, 0x90, 0x05,
|
||||||
|
0x81, 0x07, 0x81, 0x01, 0x82, 0x09, 0x90, 0x06, 0x81, 0x09, 0x90, 0x0E,
|
||||||
|
0x88, 0x05, 0x82, 0x0D, 0x88, 0x03, 0x81, 0x06, 0x84, 0x0C, 0x82, 0x01,
|
||||||
|
0x81, 0x09, 0x82, 0x0E, 0x90, 0x01, 0x81, 0x01, 0x84, 0x05, 0x82, 0x06,
|
||||||
|
0x88, 0x0A, 0x81, 0x05, 0x82, 0x03, 0x90, 0x07, 0x90, 0x08, 0x81, 0x02,
|
||||||
|
0x90, 0x0D, 0x88, 0x0D, 0x81, 0x08, 0x90, 0x0E, 0x82, 0x0D, 0x88, 0x04,
|
||||||
|
0x81, 0x0C, 0x82, 0x09, 0x90, 0x04, 0x81, 0x0C, 0x82, 0x06, 0x90, 0x0E,
|
||||||
|
0x90, 0x0D, 0x90, 0x07, 0x90, 0x03, 0x82, 0x03, 0x88, 0x06, 0x81, 0x07,
|
||||||
|
0x90, 0x08, 0x81, 0x02, 0x90, 0x09, 0x81, 0x04, 0x90, 0x06, 0x82, 0x0A,
|
||||||
|
0x90, 0x05, 0x88, 0x05, 0x88, 0x06, 0x90, 0x0B, 0x81, 0x03, 0x82, 0x02,
|
||||||
|
0x90, 0x0B, 0x81, 0x02, 0x90, 0x0C, 0x90, 0x07, 0x81, 0x01, 0x84, 0x08,
|
||||||
|
0x90, 0x0C, 0x81, 0x0E, 0x88, 0x08, 0x88, 0x0C, 0x81, 0x0C, 0x90, 0x04,
|
||||||
|
0x84, 0x0D, 0x81, 0x01, 0x88, 0x08, 0x88, 0x09,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level K -- 1400 ticks, 1 hook bytes, 10 fare stages, 256 pictures, 16 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level??.dat K 11
|
||||||
|
static const uint8_t kGateStreamK[320] = {
|
||||||
|
0x84, 0x06, 0x82, 0x0B, 0x90, 0x0C, 0x82, 0x0E, 0x90, 0x05, 0x88, 0x03,
|
||||||
|
0x88, 0x02, 0x82, 0x0E, 0x88, 0x07, 0x84, 0x02, 0x84, 0x0B, 0x84, 0x08,
|
||||||
|
0x90, 0x0A, 0x88, 0x07, 0x84, 0x07, 0x81, 0x03, 0x84, 0x08, 0x82, 0x0E,
|
||||||
|
0x82, 0x0D, 0x84, 0x01, 0x81, 0x09, 0x81, 0x0E, 0x81, 0x0C, 0x81, 0x0B,
|
||||||
|
0x82, 0x0C, 0x82, 0x06, 0x81, 0x01, 0x84, 0x01, 0x90, 0x06, 0x82, 0x0D,
|
||||||
|
0x84, 0x0B, 0x82, 0x09, 0x88, 0x09, 0x88, 0x05, 0x82, 0x02, 0x84, 0x04,
|
||||||
|
0x84, 0x09, 0x88, 0x08, 0x90, 0x09, 0x82, 0x08, 0x81, 0x01, 0x88, 0x01,
|
||||||
|
0x81, 0x06, 0x90, 0x04, 0x82, 0x0A, 0x82, 0x09, 0x82, 0x09, 0x84, 0x0C,
|
||||||
|
0x82, 0x07, 0x90, 0x0E, 0x90, 0x0E, 0x90, 0x07, 0x82, 0x0D, 0x90, 0x02,
|
||||||
|
0x84, 0x04, 0x84, 0x09, 0x88, 0x05, 0x88, 0x03, 0x81, 0x05, 0x82, 0x0E,
|
||||||
|
0x90, 0x04, 0x90, 0x08, 0x88, 0x0C, 0x90, 0x08, 0x81, 0x05, 0x82, 0x0B,
|
||||||
|
0x90, 0x0B, 0x84, 0x05, 0x82, 0x0D, 0x82, 0x08, 0x84, 0x0D, 0x81, 0x01,
|
||||||
|
0x90, 0x08, 0x82, 0x0C, 0x81, 0x09, 0x82, 0x05, 0x84, 0x01, 0x82, 0x0A,
|
||||||
|
0x82, 0x0E, 0x88, 0x0A, 0x84, 0x04, 0x90, 0x01, 0x90, 0x0D, 0x82, 0x08,
|
||||||
|
0x90, 0x0B, 0x84, 0x03, 0x81, 0x01, 0x90, 0x0D, 0x82, 0x0C, 0x82, 0x05,
|
||||||
|
0x81, 0x02, 0x81, 0x06, 0x84, 0x08, 0x81, 0x01, 0x82, 0x06, 0x88, 0x0E,
|
||||||
|
0x88, 0x02, 0x82, 0x0A, 0x90, 0x0E, 0x82, 0x0B, 0x81, 0x0E, 0x81, 0x0A,
|
||||||
|
0x82, 0x08, 0x90, 0x01, 0x81, 0x04, 0x81, 0x0D, 0x90, 0x0C, 0x88, 0x0D,
|
||||||
|
0x88, 0x07, 0x81, 0x05, 0x90, 0x02, 0x82, 0x07, 0x88, 0x02, 0x84, 0x0E,
|
||||||
|
0x82, 0x0D, 0x90, 0x02, 0x88, 0x04, 0x90, 0x0D, 0x82, 0x0A, 0x82, 0x0D,
|
||||||
|
0x88, 0x09, 0x81, 0x04, 0x84, 0x08, 0x90, 0x0B, 0x81, 0x01, 0x90, 0x0C,
|
||||||
|
0x82, 0x0E, 0x84, 0x04, 0x82, 0x06, 0x90, 0x09, 0x84, 0x03, 0x90, 0x01,
|
||||||
|
0x82, 0x0C, 0x81, 0x03, 0x82, 0x05, 0x90, 0x0E, 0x82, 0x0E, 0x84, 0x07,
|
||||||
|
0x90, 0x06, 0x81, 0x02, 0x90, 0x03, 0x82, 0x09, 0x82, 0x0A, 0x81, 0x05,
|
||||||
|
0x82, 0x0D, 0x84, 0x01, 0x90, 0x0A, 0x82, 0x09, 0x81, 0x04, 0x82, 0x02,
|
||||||
|
0x82, 0x08, 0x90, 0x06, 0x90, 0x03, 0x84, 0x09, 0x82, 0x01, 0x82, 0x08,
|
||||||
|
0x82, 0x09, 0x90, 0x01, 0x88, 0x07, 0x88, 0x08,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level O -- 1400 ticks, 1 hook bytes, 3 fare stages, 256 pictures, 0 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level??.dat O 20260918
|
||||||
|
static const uint8_t kGateStreamO[320] = {
|
||||||
|
0x84, 0x0E, 0x90, 0x08, 0x82, 0x07, 0x81, 0x0B, 0x84, 0x02, 0x81, 0x09,
|
||||||
|
0x82, 0x06, 0x90, 0x0C, 0x81, 0x08, 0x88, 0x01, 0x88, 0x02, 0x90, 0x04,
|
||||||
|
0x88, 0x06, 0x81, 0x04, 0x82, 0x0A, 0x81, 0x06, 0x81, 0x0E, 0x82, 0x08,
|
||||||
|
0x88, 0x06, 0x82, 0x03, 0x81, 0x06, 0x81, 0x02, 0x88, 0x0D, 0x82, 0x04,
|
||||||
|
0x84, 0x0B, 0x84, 0x03, 0x81, 0x04, 0x84, 0x06, 0x81, 0x04, 0x81, 0x02,
|
||||||
|
0x84, 0x07, 0x82, 0x06, 0x81, 0x06, 0x90, 0x05, 0x88, 0x09, 0x81, 0x0A,
|
||||||
|
0x81, 0x0C, 0x81, 0x02, 0x82, 0x09, 0x82, 0x0D, 0x81, 0x04, 0x81, 0x0B,
|
||||||
|
0x84, 0x03, 0x88, 0x0A, 0x90, 0x0A, 0x81, 0x08, 0x82, 0x08, 0x81, 0x06,
|
||||||
|
0x81, 0x03, 0x82, 0x0A, 0x81, 0x0C, 0x82, 0x08, 0x81, 0x0C, 0x82, 0x0E,
|
||||||
|
0x81, 0x06, 0x81, 0x04, 0x81, 0x0A, 0x84, 0x05, 0x90, 0x0E, 0x84, 0x06,
|
||||||
|
0x82, 0x0B, 0x81, 0x0E, 0x81, 0x03, 0x81, 0x05, 0x90, 0x08, 0x81, 0x01,
|
||||||
|
0x90, 0x0E, 0x84, 0x0D, 0x88, 0x01, 0x90, 0x05, 0x84, 0x0B, 0x81, 0x04,
|
||||||
|
0x81, 0x04, 0x82, 0x01, 0x90, 0x04, 0x82, 0x0D, 0x88, 0x07, 0x81, 0x0B,
|
||||||
|
0x81, 0x07, 0x82, 0x01, 0x88, 0x0A, 0x81, 0x06, 0x90, 0x02, 0x82, 0x08,
|
||||||
|
0x81, 0x01, 0x81, 0x0A, 0x88, 0x0E, 0x88, 0x0D, 0x81, 0x03, 0x88, 0x0D,
|
||||||
|
0x81, 0x02, 0x88, 0x04, 0x84, 0x0E, 0x90, 0x01, 0x82, 0x0C, 0x81, 0x0B,
|
||||||
|
0x81, 0x04, 0x88, 0x03, 0x81, 0x03, 0x88, 0x09, 0x84, 0x0E, 0x81, 0x02,
|
||||||
|
0x81, 0x02, 0x81, 0x08, 0x84, 0x07, 0x84, 0x02, 0x88, 0x0C, 0x84, 0x08,
|
||||||
|
0x90, 0x09, 0x90, 0x0D, 0x81, 0x02, 0x88, 0x0D, 0x84, 0x0C, 0x84, 0x0D,
|
||||||
|
0x84, 0x01, 0x81, 0x07, 0x81, 0x0A, 0x81, 0x01, 0x84, 0x01, 0x84, 0x0D,
|
||||||
|
0x82, 0x0E, 0x81, 0x02, 0x81, 0x06, 0x81, 0x09, 0x88, 0x0D, 0x88, 0x09,
|
||||||
|
0x82, 0x0B, 0x81, 0x0B, 0x88, 0x02, 0x88, 0x0C, 0x81, 0x04, 0x84, 0x0B,
|
||||||
|
0x81, 0x06, 0x88, 0x09, 0x84, 0x0C, 0x81, 0x03, 0x84, 0x0D, 0x84, 0x0D,
|
||||||
|
0x88, 0x0E, 0x84, 0x04, 0x90, 0x0E, 0x81, 0x02, 0x88, 0x01, 0x88, 0x06,
|
||||||
|
0x81, 0x0E, 0x82, 0x08, 0x81, 0x0D, 0x90, 0x0D, 0x88, 0x0E, 0x81, 0x03,
|
||||||
|
0x8A, 0x09, 0x90, 0x0B, 0x84, 0x07, 0x88, 0x03, 0x81, 0x0C, 0x82, 0x07,
|
||||||
|
0x81, 0x0E, 0x81, 0x08, 0x90, 0x0D, 0x84, 0x0D,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level Q -- 1400 ticks, 5 hook bytes, 10 fare stages, 256 pictures, 10 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level??.dat Q 20260918
|
||||||
|
static const uint8_t kGateStreamQ[320] = {
|
||||||
|
0x84, 0x0E, 0x81, 0x04, 0x81, 0x03, 0x84, 0x09, 0x88, 0x0A, 0x81, 0x09,
|
||||||
|
0x82, 0x06, 0x88, 0x0C, 0x81, 0x08, 0x82, 0x06, 0x88, 0x0E, 0x84, 0x0B,
|
||||||
|
0x88, 0x08, 0x81, 0x09, 0x81, 0x0A, 0x84, 0x05, 0x84, 0x01, 0x88, 0x01,
|
||||||
|
0x81, 0x08, 0x84, 0x0B, 0x81, 0x05, 0x82, 0x05, 0x80, 0x0D, 0x81, 0x08,
|
||||||
|
0x88, 0x0A, 0x88, 0x04, 0x84, 0x08, 0x81, 0x06, 0x81, 0x0C, 0x81, 0x01,
|
||||||
|
0x90, 0x0A, 0x84, 0x08, 0x81, 0x0A, 0x81, 0x0A, 0x82, 0x08, 0x81, 0x06,
|
||||||
|
0x81, 0x09, 0x90, 0x06, 0x81, 0x09, 0x90, 0x0D, 0x88, 0x04, 0x82, 0x05,
|
||||||
|
0x81, 0x0B, 0x84, 0x08, 0x81, 0x08, 0x81, 0x03, 0x82, 0x0D, 0x82, 0x0A,
|
||||||
|
0x84, 0x0E, 0x81, 0x05, 0x81, 0x0E, 0x81, 0x08, 0x84, 0x0A, 0x82, 0x0C,
|
||||||
|
0x84, 0x09, 0x84, 0x01, 0x82, 0x06, 0x88, 0x0E, 0x81, 0x09, 0x90, 0x07,
|
||||||
|
0x81, 0x09, 0x81, 0x07, 0x84, 0x03, 0x81, 0x0B, 0x84, 0x01, 0x90, 0x08,
|
||||||
|
0x90, 0x06, 0x82, 0x03, 0x88, 0x06, 0x88, 0x0C, 0x84, 0x0A, 0x81, 0x03,
|
||||||
|
0x82, 0x0E, 0x81, 0x07, 0x81, 0x04, 0x81, 0x0D, 0x84, 0x02, 0x81, 0x09,
|
||||||
|
0x82, 0x08, 0x88, 0x04, 0x81, 0x09, 0x84, 0x0C, 0x81, 0x0B, 0x84, 0x0D,
|
||||||
|
0x81, 0x02, 0x82, 0x03, 0x81, 0x05, 0x88, 0x0E, 0x84, 0x0D, 0x81, 0x0B,
|
||||||
|
0x88, 0x08, 0x84, 0x0C, 0x90, 0x07, 0x82, 0x0B, 0x81, 0x03, 0x90, 0x05,
|
||||||
|
0x84, 0x05, 0x84, 0x09, 0x81, 0x04, 0x81, 0x0B, 0x81, 0x08, 0x82, 0x0E,
|
||||||
|
0x81, 0x0A, 0x81, 0x03, 0x81, 0x06, 0x88, 0x09, 0x84, 0x0A, 0x88, 0x0B,
|
||||||
|
0x81, 0x05, 0x81, 0x0D, 0x88, 0x03, 0x88, 0x0D, 0x81, 0x01, 0x90, 0x06,
|
||||||
|
0x88, 0x07, 0x82, 0x02, 0x81, 0x04, 0x84, 0x09, 0x81, 0x07, 0x90, 0x06,
|
||||||
|
0x82, 0x03, 0x84, 0x07, 0x81, 0x05, 0x90, 0x0B, 0x81, 0x0B, 0x88, 0x05,
|
||||||
|
0x81, 0x0D, 0x81, 0x0B, 0x92, 0x0E, 0x90, 0x05, 0x82, 0x02, 0x88, 0x0C,
|
||||||
|
0x84, 0x08, 0x81, 0x0A, 0x81, 0x0D, 0x81, 0x05, 0x81, 0x07, 0x81, 0x01,
|
||||||
|
0x81, 0x04, 0x88, 0x01, 0x81, 0x0E, 0x84, 0x04, 0x90, 0x03, 0x81, 0x0D,
|
||||||
|
0x90, 0x09, 0x81, 0x02, 0x81, 0x08, 0x90, 0x07, 0x82, 0x0B, 0x90, 0x08,
|
||||||
|
0x81, 0x03, 0x90, 0x0C, 0x81, 0x0B, 0x88, 0x08, 0x90, 0x04, 0x81, 0x02,
|
||||||
|
0x90, 0x0C, 0x88, 0x05, 0x81, 0x01, 0x90, 0x03,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level R -- 1400 ticks, 0 hook bytes, 3 fare stages, 256 pictures, 0 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level??.dat R 20260918
|
||||||
|
static const uint8_t kGateStreamR[320] = {
|
||||||
|
0x90, 0x0E, 0x81, 0x02, 0x82, 0x07, 0x81, 0x05, 0x88, 0x0B, 0x88, 0x09,
|
||||||
|
0x90, 0x0D, 0x90, 0x06, 0x81, 0x08, 0x81, 0x01, 0x90, 0x02, 0x88, 0x04,
|
||||||
|
0x82, 0x07, 0x90, 0x06, 0x88, 0x07, 0x81, 0x06, 0x81, 0x06, 0x82, 0x05,
|
||||||
|
0x84, 0x07, 0x82, 0x05, 0x81, 0x03, 0x84, 0x0E, 0x88, 0x05, 0x88, 0x09,
|
||||||
|
0x88, 0x04, 0x81, 0x09, 0x81, 0x02, 0x82, 0x04, 0x84, 0x0B, 0x81, 0x04,
|
||||||
|
0x82, 0x0C, 0x81, 0x03, 0x88, 0x06, 0x84, 0x08, 0x81, 0x0D, 0x88, 0x09,
|
||||||
|
0x90, 0x0E, 0x82, 0x07, 0x82, 0x02, 0x84, 0x0D, 0x88, 0x0B, 0x81, 0x0B,
|
||||||
|
0x81, 0x03, 0x82, 0x02, 0x90, 0x0B, 0x84, 0x03, 0x88, 0x02, 0x88, 0x09,
|
||||||
|
0x81, 0x02, 0x82, 0x0A, 0x81, 0x06, 0x81, 0x02, 0x90, 0x0A, 0x82, 0x0E,
|
||||||
|
0x81, 0x09, 0x88, 0x03, 0x81, 0x07, 0x84, 0x0D, 0x84, 0x0A, 0x81, 0x03,
|
||||||
|
0x82, 0x07, 0x84, 0x0D, 0x81, 0x03, 0x81, 0x08, 0x84, 0x0E, 0x81, 0x02,
|
||||||
|
0x82, 0x0E, 0x88, 0x03, 0x81, 0x01, 0x81, 0x05, 0x81, 0x06, 0x90, 0x0A,
|
||||||
|
0x84, 0x0A, 0x81, 0x02, 0x82, 0x05, 0x81, 0x03, 0x88, 0x07, 0x84, 0x0C,
|
||||||
|
0x81, 0x04, 0x88, 0x09, 0x90, 0x0E, 0x82, 0x0A, 0x88, 0x09, 0x82, 0x01,
|
||||||
|
0x81, 0x06, 0x81, 0x0D, 0x84, 0x0A, 0x82, 0x0D, 0x88, 0x0A, 0x84, 0x0A,
|
||||||
|
0x90, 0x02, 0x84, 0x02, 0x81, 0x09, 0x82, 0x0A, 0x81, 0x0B, 0x81, 0x0D,
|
||||||
|
0x82, 0x05, 0x84, 0x09, 0x84, 0x04, 0x88, 0x06, 0x84, 0x04, 0x88, 0x0D,
|
||||||
|
0x81, 0x02, 0x82, 0x0B, 0x81, 0x0E, 0x82, 0x0D, 0x82, 0x0A, 0x81, 0x02,
|
||||||
|
0x81, 0x09, 0x81, 0x0D, 0x84, 0x04, 0x82, 0x09, 0x88, 0x04, 0x81, 0x07,
|
||||||
|
0x90, 0x0A, 0x90, 0x0B, 0x81, 0x07, 0x84, 0x0D, 0x82, 0x0A, 0x81, 0x0C,
|
||||||
|
0x88, 0x07, 0x84, 0x09, 0x82, 0x08, 0x90, 0x0A, 0x84, 0x06, 0x82, 0x0D,
|
||||||
|
0x88, 0x05, 0x81, 0x06, 0x81, 0x05, 0x90, 0x0D, 0x90, 0x0B, 0x81, 0x0C,
|
||||||
|
0x81, 0x04, 0x84, 0x06, 0x90, 0x08, 0x90, 0x08, 0x90, 0x08, 0x84, 0x0E,
|
||||||
|
0x88, 0x0E, 0x81, 0x0E, 0x82, 0x0A, 0x90, 0x04, 0x81, 0x06, 0x82, 0x09,
|
||||||
|
0x84, 0x08, 0x90, 0x07, 0x84, 0x0C, 0x90, 0x05, 0x88, 0x0C, 0x84, 0x06,
|
||||||
|
0x84, 0x0A, 0x81, 0x08, 0x81, 0x01, 0x81, 0x01, 0x82, 0x08, 0x88, 0x0A,
|
||||||
|
0x90, 0x0A, 0x90, 0x01, 0x84, 0x0A, 0x81, 0x0B,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level U -- 896 ticks, 21 hook bytes, 3 fare stages, 256 pictures, 0 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level??.dat U 44
|
||||||
|
static const uint8_t kGateStreamU[320] = {
|
||||||
|
0x90, 0x01, 0x90, 0x0B, 0x88, 0x08, 0x90, 0x07, 0x88, 0x0D, 0x82, 0x0A,
|
||||||
|
0x82, 0x06, 0x88, 0x0C, 0x90, 0x03, 0x81, 0x04, 0x84, 0x0B, 0x81, 0x0C,
|
||||||
|
0x88, 0x02, 0x84, 0x08, 0x84, 0x01, 0x81, 0x04, 0x90, 0x05, 0x84, 0x06,
|
||||||
|
0x84, 0x08, 0x84, 0x05, 0x81, 0x03, 0x81, 0x09, 0x81, 0x08, 0x88, 0x07,
|
||||||
|
0x81, 0x08, 0x84, 0x06, 0x81, 0x0B, 0x88, 0x0D, 0x90, 0x08, 0x81, 0x01,
|
||||||
|
0x82, 0x01, 0x88, 0x03, 0x81, 0x0E, 0x82, 0x01, 0x81, 0x0D, 0x81, 0x07,
|
||||||
|
0x88, 0x05, 0x88, 0x07, 0x88, 0x0E, 0x84, 0x07, 0x81, 0x03, 0x81, 0x01,
|
||||||
|
0x84, 0x03, 0x81, 0x04, 0x81, 0x08, 0x90, 0x06, 0x81, 0x0C, 0x82, 0x05,
|
||||||
|
0x81, 0x08, 0x82, 0x09, 0x84, 0x06, 0x81, 0x06, 0x90, 0x0C, 0x84, 0x0E,
|
||||||
|
0x82, 0x0C, 0x81, 0x03, 0x81, 0x06, 0x84, 0x0D, 0x88, 0x03, 0x84, 0x0C,
|
||||||
|
0x81, 0x02, 0x81, 0x09, 0x81, 0x0E, 0x88, 0x01, 0x90, 0x06, 0x88, 0x08,
|
||||||
|
0x81, 0x08, 0x81, 0x0C, 0x81, 0x0C, 0x82, 0x06, 0x82, 0x08, 0x88, 0x01,
|
||||||
|
0x81, 0x07, 0x88, 0x0D, 0x84, 0x03, 0x81, 0x02, 0x82, 0x06, 0x81, 0x05,
|
||||||
|
0x81, 0x0E, 0x81, 0x04, 0x90, 0x01, 0x81, 0x02, 0x90, 0x03, 0x81, 0x0A,
|
||||||
|
0x81, 0x04, 0x84, 0x02, 0x81, 0x06, 0x81, 0x0E, 0x81, 0x06, 0x84, 0x07,
|
||||||
|
0x84, 0x04, 0x81, 0x07, 0x81, 0x0E, 0x81, 0x0D, 0x90, 0x0D, 0x90, 0x05,
|
||||||
|
0x88, 0x0C, 0x88, 0x02, 0x81, 0x0B, 0x90, 0x07, 0x88, 0x06, 0x82, 0x0D,
|
||||||
|
0x84, 0x08, 0x84, 0x08, 0x81, 0x01, 0x81, 0x06, 0x82, 0x05, 0x84, 0x06,
|
||||||
|
0x81, 0x06, 0x84, 0x0D, 0x81, 0x04, 0x90, 0x07, 0x88, 0x0D, 0x81, 0x09,
|
||||||
|
0x88, 0x0D, 0x81, 0x0E, 0x81, 0x03, 0x84, 0x0E, 0x81, 0x03, 0x81, 0x0C,
|
||||||
|
0x84, 0x0A, 0x81, 0x06, 0x90, 0x02, 0x81, 0x01, 0x88, 0x02, 0x90, 0x02,
|
||||||
|
0x81, 0x08, 0x84, 0x01, 0x88, 0x0B, 0x88, 0x0B, 0x81, 0x04, 0x81, 0x0C,
|
||||||
|
0x81, 0x07, 0x82, 0x06, 0x88, 0x0C, 0x81, 0x03, 0x90, 0x02, 0x88, 0x0A,
|
||||||
|
0x84, 0x08, 0x81, 0x0E, 0x81, 0x05, 0x88, 0x0C, 0x81, 0x0D, 0x81, 0x05,
|
||||||
|
0x81, 0x04, 0x90, 0x0D, 0x88, 0x03, 0x88, 0x06, 0x81, 0x0B, 0x81, 0x0D,
|
||||||
|
0x84, 0x0D, 0x81, 0x01, 0x81, 0x0D, 0x81, 0x01, 0x82, 0x07, 0x88, 0x02,
|
||||||
|
0x81, 0x09, 0x90, 0x0B, 0x88, 0x0D, 0x82, 0x02,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level V -- 1400 ticks, 0 hook bytes, 6 fare stages, 256 pictures, 23 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level??.dat V 20260918
|
||||||
|
static const uint8_t kGateStreamV[320] = {
|
||||||
|
0x84, 0x0E, 0x81, 0x04, 0x81, 0x05, 0x88, 0x0B, 0x84, 0x01, 0x81, 0x03,
|
||||||
|
0x82, 0x06, 0x84, 0x0C, 0x81, 0x08, 0x84, 0x07, 0x82, 0x03, 0x88, 0x0A,
|
||||||
|
0x90, 0x03, 0x81, 0x0D, 0x81, 0x0D, 0x82, 0x05, 0x81, 0x02, 0x81, 0x0E,
|
||||||
|
0x90, 0x06, 0x81, 0x05, 0x90, 0x05, 0x81, 0x0E, 0x82, 0x0D, 0x90, 0x0E,
|
||||||
|
0x88, 0x0E, 0x81, 0x03, 0x90, 0x07, 0x84, 0x0A, 0x81, 0x02, 0x90, 0x07,
|
||||||
|
0x82, 0x07, 0x81, 0x02, 0x81, 0x06, 0x82, 0x08, 0x81, 0x04, 0x88, 0x0C,
|
||||||
|
0x81, 0x06, 0x88, 0x0D, 0x90, 0x0D, 0x81, 0x05, 0x90, 0x03, 0x84, 0x0D,
|
||||||
|
0x84, 0x0A, 0x88, 0x0A, 0x88, 0x0D, 0x81, 0x08, 0x90, 0x08, 0x90, 0x01,
|
||||||
|
0x84, 0x06, 0x82, 0x0B, 0x81, 0x0D, 0x82, 0x07, 0x82, 0x0C, 0x81, 0x01,
|
||||||
|
0x90, 0x08, 0x81, 0x02, 0x81, 0x04, 0x90, 0x0C, 0x84, 0x04, 0x88, 0x04,
|
||||||
|
0x82, 0x03, 0x88, 0x07, 0x81, 0x03, 0x90, 0x05, 0x81, 0x02, 0x81, 0x03,
|
||||||
|
0x88, 0x01, 0x90, 0x05, 0x81, 0x08, 0x81, 0x06, 0x81, 0x07, 0x81, 0x08,
|
||||||
|
0x81, 0x06, 0x81, 0x02, 0x84, 0x0B, 0x88, 0x0D, 0x81, 0x06, 0x90, 0x02,
|
||||||
|
0x81, 0x07, 0x82, 0x0B, 0x88, 0x04, 0x84, 0x04, 0x84, 0x05, 0x81, 0x05,
|
||||||
|
0x81, 0x02, 0x82, 0x09, 0x81, 0x03, 0x90, 0x0C, 0x88, 0x0B, 0x82, 0x01,
|
||||||
|
0x88, 0x0D, 0x88, 0x05, 0x81, 0x02, 0x90, 0x0B, 0x81, 0x02, 0x90, 0x0A,
|
||||||
|
0x88, 0x0C, 0x81, 0x01, 0x90, 0x0A, 0x88, 0x0E, 0x90, 0x0A, 0x84, 0x0B,
|
||||||
|
0x81, 0x03, 0x90, 0x08, 0x88, 0x03, 0x82, 0x0E, 0x84, 0x06, 0x90, 0x0E,
|
||||||
|
0x81, 0x03, 0x88, 0x0A, 0x90, 0x0A, 0x82, 0x09, 0x81, 0x04, 0x88, 0x0C,
|
||||||
|
0x84, 0x04, 0x81, 0x07, 0x90, 0x03, 0x81, 0x01, 0x81, 0x01, 0x81, 0x01,
|
||||||
|
0x82, 0x0E, 0x81, 0x05, 0x81, 0x06, 0x81, 0x08, 0x84, 0x08, 0x88, 0x0C,
|
||||||
|
0x82, 0x09, 0x88, 0x05, 0x81, 0x02, 0x88, 0x04, 0x81, 0x04, 0x88, 0x0C,
|
||||||
|
0x90, 0x07, 0x88, 0x0E, 0x81, 0x03, 0x81, 0x0A, 0x82, 0x0B, 0x81, 0x02,
|
||||||
|
0x88, 0x02, 0x90, 0x0E, 0x88, 0x0D, 0x81, 0x03, 0x88, 0x09, 0x90, 0x04,
|
||||||
|
0x84, 0x01, 0x88, 0x04, 0x90, 0x04, 0x84, 0x04, 0x81, 0x03, 0x90, 0x0C,
|
||||||
|
0x88, 0x07, 0x84, 0x05, 0x81, 0x02, 0x90, 0x0B, 0x90, 0x04, 0x90, 0x09,
|
||||||
|
0x81, 0x02, 0x81, 0x01, 0x90, 0x0B, 0x88, 0x04,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level E -- 1400 ticks, 4 hook bytes, 8 fare stages, 256 pictures, 11 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level??.dat E 20260921
|
||||||
|
static const uint8_t kGateStreamE[320] = {
|
||||||
|
0x84, 0x05, 0x82, 0x03, 0x82, 0x05, 0x81, 0x0B, 0x81, 0x07, 0x81, 0x02,
|
||||||
|
0x81, 0x01, 0x89, 0x02, 0x90, 0x03, 0x81, 0x0A, 0x81, 0x05, 0x84, 0x0C,
|
||||||
|
0x81, 0x02, 0x88, 0x07, 0x81, 0x0B, 0x90, 0x08, 0x90, 0x0A, 0x88, 0x02,
|
||||||
|
0x81, 0x05, 0x84, 0x08, 0x81, 0x05, 0x90, 0x03, 0x84, 0x01, 0x84, 0x08,
|
||||||
|
0x88, 0x0A, 0x84, 0x07, 0x81, 0x04, 0x84, 0x07, 0x90, 0x0C, 0x80, 0x08,
|
||||||
|
0x90, 0x07, 0x88, 0x03, 0x90, 0x0B, 0x88, 0x01, 0x90, 0x07, 0x88, 0x04,
|
||||||
|
0x81, 0x02, 0x88, 0x08, 0x84, 0x05, 0x81, 0x05, 0x81, 0x02, 0x81, 0x09,
|
||||||
|
0x88, 0x05, 0x90, 0x0E, 0x81, 0x05, 0x81, 0x01, 0x90, 0x04, 0x90, 0x08,
|
||||||
|
0x81, 0x06, 0x84, 0x0E, 0x81, 0x09, 0x81, 0x01, 0x84, 0x05, 0x81, 0x01,
|
||||||
|
0x81, 0x02, 0x88, 0x0B, 0x90, 0x08, 0x88, 0x0C, 0x88, 0x05, 0x88, 0x08,
|
||||||
|
0x84, 0x09, 0x88, 0x05, 0x90, 0x05, 0x82, 0x0A, 0x81, 0x07, 0x90, 0x0A,
|
||||||
|
0x81, 0x07, 0x81, 0x03, 0x84, 0x0B, 0x84, 0x0E, 0x88, 0x09, 0x90, 0x02,
|
||||||
|
0x81, 0x0E, 0x81, 0x06, 0x90, 0x0E, 0x81, 0x02, 0x90, 0x08, 0x84, 0x08,
|
||||||
|
0x81, 0x0B, 0x88, 0x0D, 0x84, 0x0C, 0x81, 0x09, 0x90, 0x08, 0x82, 0x0B,
|
||||||
|
0x88, 0x08, 0x81, 0x07, 0x82, 0x02, 0x84, 0x0B, 0x88, 0x0B, 0x81, 0x0D,
|
||||||
|
0x84, 0x0E, 0x82, 0x01, 0x81, 0x03, 0x90, 0x0C, 0x81, 0x05, 0x90, 0x04,
|
||||||
|
0x82, 0x0C, 0x81, 0x06, 0x90, 0x0E, 0x88, 0x0B, 0x88, 0x09, 0x81, 0x0E,
|
||||||
|
0x82, 0x0E, 0x90, 0x0C, 0x81, 0x06, 0x81, 0x05, 0x84, 0x0E, 0x81, 0x0C,
|
||||||
|
0x88, 0x0B, 0x90, 0x06, 0x82, 0x05, 0x81, 0x06, 0x88, 0x0D, 0x88, 0x07,
|
||||||
|
0x81, 0x07, 0x84, 0x07, 0x88, 0x08, 0x81, 0x07, 0x90, 0x0C, 0x84, 0x04,
|
||||||
|
0x84, 0x0A, 0x88, 0x0B, 0x80, 0x0A, 0x81, 0x0C, 0x81, 0x07, 0x88, 0x06,
|
||||||
|
0x88, 0x09, 0x88, 0x09, 0x81, 0x0C, 0x80, 0x06, 0x81, 0x06, 0x81, 0x07,
|
||||||
|
0x84, 0x08, 0x82, 0x09, 0x84, 0x0C, 0x90, 0x09, 0x90, 0x03, 0x90, 0x0D,
|
||||||
|
0x81, 0x08, 0x84, 0x05, 0x84, 0x09, 0x88, 0x0B, 0x81, 0x01, 0x81, 0x0A,
|
||||||
|
0x90, 0x0D, 0x88, 0x0E, 0x88, 0x0D, 0x88, 0x05, 0x84, 0x06, 0x81, 0x0E,
|
||||||
|
0x81, 0x0A, 0x81, 0x08, 0x84, 0x0C, 0x88, 0x05, 0x81, 0x02, 0x90, 0x0E,
|
||||||
|
0x84, 0x0D, 0x84, 0x0A, 0x81, 0x05, 0x90, 0x09,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level J -- 1400 ticks, 10 hook bytes, 10 fare stages, 256 pictures, 8 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level??.dat J 20260921
|
||||||
|
static const uint8_t kGateStreamJ[320] = {
|
||||||
|
0x88, 0x05, 0x82, 0x09, 0x81, 0x01, 0x81, 0x04, 0x90, 0x06, 0x81, 0x05,
|
||||||
|
0x81, 0x01, 0x90, 0x09, 0x88, 0x05, 0x82, 0x08, 0x81, 0x0A, 0x81, 0x03,
|
||||||
|
0x84, 0x07, 0x88, 0x0D, 0x81, 0x06, 0x81, 0x0A, 0x84, 0x05, 0x90, 0x08,
|
||||||
|
0x81, 0x0B, 0x90, 0x0E, 0x84, 0x0E, 0x81, 0x03, 0x90, 0x09, 0x81, 0x02,
|
||||||
|
0x88, 0x04, 0x88, 0x08, 0x84, 0x0E, 0x81, 0x04, 0x88, 0x0D, 0x90, 0x0E,
|
||||||
|
0x84, 0x09, 0x90, 0x05, 0x81, 0x01, 0x81, 0x03, 0x88, 0x03, 0x88, 0x07,
|
||||||
|
0x81, 0x0B, 0x90, 0x0B, 0x88, 0x08, 0x82, 0x03, 0x90, 0x09, 0x88, 0x03,
|
||||||
|
0x84, 0x06, 0x88, 0x0A, 0x88, 0x0B, 0x84, 0x04, 0x81, 0x05, 0x88, 0x0E,
|
||||||
|
0x81, 0x0A, 0x90, 0x0C, 0x85, 0x04, 0x81, 0x0E, 0x90, 0x02, 0x82, 0x0D,
|
||||||
|
0x84, 0x01, 0x81, 0x02, 0x88, 0x09, 0x81, 0x0B, 0x81, 0x02, 0x82, 0x0C,
|
||||||
|
0x84, 0x04, 0x81, 0x05, 0x81, 0x04, 0x81, 0x04, 0x81, 0x05, 0x90, 0x05,
|
||||||
|
0x88, 0x09, 0x81, 0x02, 0x88, 0x01, 0x81, 0x05, 0x90, 0x04, 0x81, 0x0B,
|
||||||
|
0x81, 0x05, 0x82, 0x0D, 0x88, 0x06, 0x88, 0x07, 0x88, 0x02, 0x84, 0x02,
|
||||||
|
0x90, 0x07, 0x88, 0x09, 0x84, 0x0C, 0x81, 0x01, 0x88, 0x0B, 0x81, 0x0C,
|
||||||
|
0x82, 0x0A, 0x81, 0x08, 0x82, 0x01, 0x88, 0x09, 0x84, 0x05, 0x81, 0x08,
|
||||||
|
0x84, 0x0A, 0x84, 0x09, 0x88, 0x0E, 0x81, 0x01, 0x84, 0x03, 0x82, 0x0C,
|
||||||
|
0x81, 0x0E, 0x84, 0x0A, 0x88, 0x02, 0x84, 0x0D, 0x81, 0x0A, 0x90, 0x0E,
|
||||||
|
0x88, 0x09, 0x90, 0x09, 0x81, 0x05, 0x90, 0x03, 0x88, 0x09, 0x84, 0x06,
|
||||||
|
0x81, 0x01, 0x88, 0x06, 0x81, 0x05, 0x81, 0x09, 0x81, 0x06, 0x90, 0x04,
|
||||||
|
0x82, 0x0A, 0x90, 0x07, 0x81, 0x07, 0x81, 0x0A, 0x84, 0x0E, 0x84, 0x07,
|
||||||
|
0x81, 0x0A, 0x90, 0x0C, 0x81, 0x01, 0x90, 0x01, 0x88, 0x0D, 0x81, 0x03,
|
||||||
|
0x88, 0x05, 0x81, 0x03, 0x88, 0x01, 0x81, 0x01, 0x82, 0x02, 0x88, 0x07,
|
||||||
|
0x83, 0x02, 0x90, 0x07, 0x81, 0x05, 0x81, 0x04, 0x81, 0x0E, 0x88, 0x09,
|
||||||
|
0x81, 0x07, 0x90, 0x03, 0x82, 0x0B, 0x82, 0x0B, 0x90, 0x04, 0x81, 0x05,
|
||||||
|
0x81, 0x03, 0x90, 0x0C, 0x82, 0x07, 0x88, 0x0E, 0x90, 0x04, 0x81, 0x0A,
|
||||||
|
0x88, 0x02, 0x88, 0x0D, 0x82, 0x07, 0x90, 0x01, 0x81, 0x02, 0x81, 0x01,
|
||||||
|
0x82, 0x07, 0x81, 0x08, 0x84, 0x0E, 0x81, 0x03,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level L -- 1400 ticks, 0 hook bytes, 10 fare stages, 256 pictures, 8 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level??.dat L 20260921
|
||||||
|
static const uint8_t kGateStreamL[320] = {
|
||||||
|
0x84, 0x05, 0x84, 0x09, 0x81, 0x06, 0x81, 0x04, 0x81, 0x08, 0x81, 0x05,
|
||||||
|
0x81, 0x01, 0x84, 0x09, 0x90, 0x09, 0x82, 0x0D, 0x88, 0x07, 0x81, 0x06,
|
||||||
|
0x81, 0x09, 0x82, 0x01, 0x81, 0x08, 0x88, 0x0B, 0x84, 0x0E, 0x81, 0x03,
|
||||||
|
0x82, 0x02, 0x82, 0x06, 0x82, 0x03, 0x88, 0x0E, 0x84, 0x0C, 0x81, 0x06,
|
||||||
|
0x88, 0x02, 0x81, 0x04, 0x88, 0x0E, 0x81, 0x04, 0x90, 0x0B, 0x81, 0x08,
|
||||||
|
0x84, 0x09, 0x82, 0x09, 0x81, 0x01, 0x90, 0x03, 0x81, 0x05, 0x84, 0x07,
|
||||||
|
0x81, 0x07, 0x90, 0x0B, 0x81, 0x07, 0x88, 0x09, 0x81, 0x0E, 0x88, 0x03,
|
||||||
|
0x81, 0x01, 0x88, 0x09, 0x81, 0x07, 0x81, 0x0C, 0x88, 0x0C, 0x90, 0x0E,
|
||||||
|
0x81, 0x0E, 0x90, 0x05, 0x84, 0x0E, 0x82, 0x0E, 0x82, 0x01, 0x84, 0x0A,
|
||||||
|
0x88, 0x03, 0x90, 0x09, 0x82, 0x05, 0x81, 0x0E, 0x81, 0x05, 0x81, 0x07,
|
||||||
|
0x81, 0x0D, 0x81, 0x0E, 0x90, 0x04, 0x90, 0x04, 0x81, 0x06, 0x81, 0x01,
|
||||||
|
0x88, 0x0B, 0x81, 0x01, 0x90, 0x0B, 0x81, 0x05, 0x84, 0x0D, 0x81, 0x01,
|
||||||
|
0x81, 0x0A, 0x84, 0x0D, 0x82, 0x09, 0x88, 0x06, 0x88, 0x04, 0x84, 0x0C,
|
||||||
|
0x88, 0x0C, 0x84, 0x02, 0x81, 0x07, 0x82, 0x07, 0x84, 0x08, 0x88, 0x09,
|
||||||
|
0x81, 0x01, 0x88, 0x01, 0x81, 0x0C, 0x81, 0x04, 0x84, 0x06, 0x82, 0x06,
|
||||||
|
0x90, 0x0D, 0x81, 0x06, 0x81, 0x0D, 0x82, 0x0D, 0x82, 0x08, 0x81, 0x0C,
|
||||||
|
0x84, 0x05, 0x82, 0x09, 0x81, 0x05, 0x84, 0x07, 0x82, 0x02, 0x84, 0x01,
|
||||||
|
0x81, 0x02, 0x90, 0x02, 0x82, 0x06, 0x88, 0x03, 0x84, 0x0B, 0x81, 0x0E,
|
||||||
|
0x88, 0x0E, 0x90, 0x07, 0x82, 0x08, 0x82, 0x02, 0x90, 0x02, 0x82, 0x03,
|
||||||
|
0x84, 0x07, 0x82, 0x05, 0x84, 0x07, 0x81, 0x0B, 0x82, 0x0E, 0x90, 0x05,
|
||||||
|
0x88, 0x04, 0x90, 0x05, 0x84, 0x0A, 0x82, 0x0A, 0x88, 0x0C, 0x81, 0x08,
|
||||||
|
0x82, 0x0C, 0x81, 0x06, 0x84, 0x09, 0x81, 0x04, 0x84, 0x0B, 0x88, 0x0B,
|
||||||
|
0x81, 0x0C, 0x81, 0x0C, 0x82, 0x0E, 0x81, 0x03, 0x82, 0x09, 0x88, 0x04,
|
||||||
|
0x81, 0x05, 0x88, 0x04, 0x80, 0x0B, 0x84, 0x06, 0x81, 0x0E, 0x84, 0x0B,
|
||||||
|
0x90, 0x0B, 0x90, 0x07, 0x82, 0x0D, 0x84, 0x0D, 0x81, 0x08, 0x81, 0x09,
|
||||||
|
0x84, 0x0E, 0x81, 0x0B, 0x90, 0x04, 0x84, 0x0B, 0x84, 0x03, 0x81, 0x03,
|
||||||
|
0x88, 0x0A, 0x81, 0x07, 0x84, 0x0B, 0x90, 0x0E,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level P -- 1400 ticks, 11 hook bytes, 10 fare stages, 256 pictures, 4 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level??.dat P 20260921
|
||||||
|
static const uint8_t kGateStreamP[320] = {
|
||||||
|
0x88, 0x05, 0x82, 0x09, 0x81, 0x0B, 0x81, 0x04, 0x81, 0x04, 0x84, 0x05,
|
||||||
|
0x82, 0x01, 0x90, 0x0C, 0x81, 0x03, 0x81, 0x0D, 0x81, 0x01, 0x84, 0x0C,
|
||||||
|
0x84, 0x0E, 0x88, 0x07, 0x81, 0x0B, 0x88, 0x0D, 0x90, 0x0A, 0x88, 0x02,
|
||||||
|
0x81, 0x05, 0x84, 0x08, 0x81, 0x05, 0x90, 0x0C, 0x84, 0x01, 0x88, 0x08,
|
||||||
|
0x88, 0x0A, 0x84, 0x0C, 0x81, 0x04, 0x84, 0x07, 0x90, 0x0C, 0x80, 0x09,
|
||||||
|
0x90, 0x06, 0x81, 0x03, 0x81, 0x0C, 0x88, 0x01, 0x82, 0x03, 0x81, 0x04,
|
||||||
|
0x84, 0x0D, 0x88, 0x09, 0x84, 0x05, 0x81, 0x05, 0x81, 0x02, 0x81, 0x09,
|
||||||
|
0x81, 0x02, 0x90, 0x0E, 0x81, 0x05, 0x81, 0x01, 0x81, 0x03, 0x88, 0x0A,
|
||||||
|
0x81, 0x06, 0x88, 0x05, 0x90, 0x0C, 0x90, 0x01, 0x84, 0x0E, 0x88, 0x01,
|
||||||
|
0x81, 0x02, 0x82, 0x05, 0x90, 0x08, 0x88, 0x0A, 0x84, 0x0C, 0x90, 0x08,
|
||||||
|
0x84, 0x0C, 0x81, 0x06, 0x81, 0x03, 0x88, 0x0A, 0x84, 0x08, 0x90, 0x0E,
|
||||||
|
0x81, 0x02, 0x88, 0x0C, 0x81, 0x06, 0x84, 0x0C, 0x81, 0x09, 0x80, 0x07,
|
||||||
|
0x88, 0x04, 0x81, 0x07, 0x81, 0x03, 0x81, 0x09, 0x81, 0x05, 0x86, 0x05,
|
||||||
|
0x90, 0x09, 0x84, 0x06, 0x81, 0x03, 0x84, 0x09, 0x81, 0x0E, 0x88, 0x0B,
|
||||||
|
0x90, 0x08, 0x88, 0x0A, 0x81, 0x04, 0x90, 0x0D, 0x88, 0x0E, 0x88, 0x09,
|
||||||
|
0x81, 0x0B, 0x90, 0x0C, 0x84, 0x05, 0x81, 0x08, 0x84, 0x01, 0x82, 0x02,
|
||||||
|
0x81, 0x03, 0x82, 0x0E, 0x88, 0x02, 0x88, 0x05, 0x81, 0x01, 0x81, 0x08,
|
||||||
|
0x82, 0x03, 0x84, 0x0E, 0x81, 0x01, 0x81, 0x08, 0x84, 0x03, 0x82, 0x03,
|
||||||
|
0x82, 0x05, 0x81, 0x0A, 0x81, 0x02, 0x90, 0x01, 0x81, 0x0E, 0x81, 0x0C,
|
||||||
|
0x90, 0x0C, 0x88, 0x0A, 0x84, 0x03, 0x81, 0x03, 0x84, 0x0A, 0x84, 0x05,
|
||||||
|
0x82, 0x09, 0x88, 0x0A, 0x81, 0x06, 0x84, 0x07, 0x84, 0x0C, 0x81, 0x0C,
|
||||||
|
0x82, 0x07, 0x81, 0x06, 0x81, 0x03, 0x88, 0x09, 0x90, 0x01, 0x84, 0x0E,
|
||||||
|
0x81, 0x01, 0x88, 0x06, 0x81, 0x0E, 0x81, 0x0A, 0x88, 0x0A, 0x81, 0x08,
|
||||||
|
0x84, 0x08, 0x88, 0x0E, 0x81, 0x02, 0x84, 0x0E, 0x82, 0x05, 0x81, 0x06,
|
||||||
|
0x88, 0x0C, 0x81, 0x0B, 0x81, 0x0D, 0x84, 0x02, 0x90, 0x0E, 0x84, 0x0E,
|
||||||
|
0x81, 0x02, 0x90, 0x07, 0x88, 0x0C, 0x90, 0x0A, 0x81, 0x0E, 0x81, 0x0A,
|
||||||
|
0x81, 0x0D, 0x81, 0x0B, 0x81, 0x08, 0x81, 0x03,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level S -- 1400 ticks, 0 hook bytes, 10 fare stages, 256 pictures, 23 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level??.dat S 20260921
|
||||||
|
static const uint8_t kGateStreamS[320] = {
|
||||||
|
0x90, 0x04, 0x90, 0x0E, 0x88, 0x08, 0x82, 0x0D, 0x88, 0x0C, 0x81, 0x03,
|
||||||
|
0x81, 0x0A, 0x81, 0x0D, 0x88, 0x09, 0x88, 0x0C, 0x82, 0x0E, 0x81, 0x07,
|
||||||
|
0x81, 0x0D, 0x81, 0x0A, 0x90, 0x0D, 0x84, 0x01, 0x90, 0x05, 0x88, 0x0E,
|
||||||
|
0x81, 0x0A, 0x81, 0x0A, 0x90, 0x06, 0x88, 0x03, 0x82, 0x0B, 0x82, 0x0B,
|
||||||
|
0x88, 0x03, 0x88, 0x0A, 0x90, 0x0C, 0x81, 0x0E, 0x81, 0x0E, 0x90, 0x06,
|
||||||
|
0x88, 0x02, 0x82, 0x08, 0x81, 0x0A, 0x90, 0x0D, 0x90, 0x05, 0x81, 0x05,
|
||||||
|
0x88, 0x0B, 0x81, 0x05, 0x90, 0x0B, 0x88, 0x0E, 0x82, 0x09, 0x90, 0x07,
|
||||||
|
0x81, 0x03, 0x81, 0x0D, 0x88, 0x09, 0x88, 0x0E, 0x84, 0x0A, 0x88, 0x0A,
|
||||||
|
0x82, 0x01, 0x90, 0x0C, 0x82, 0x01, 0x90, 0x0A, 0x82, 0x0A, 0x84, 0x03,
|
||||||
|
0x81, 0x02, 0x90, 0x04, 0x84, 0x0E, 0x90, 0x09, 0x81, 0x0D, 0x90, 0x06,
|
||||||
|
0x81, 0x02, 0x90, 0x0E, 0x90, 0x07, 0x82, 0x05, 0x81, 0x06, 0x90, 0x07,
|
||||||
|
0x82, 0x03, 0x90, 0x07, 0x90, 0x0B, 0x82, 0x06, 0x88, 0x0E, 0x90, 0x08,
|
||||||
|
0x84, 0x0A, 0x82, 0x07, 0x81, 0x04, 0x81, 0x03, 0x84, 0x05, 0x90, 0x08,
|
||||||
|
0x81, 0x0C, 0x82, 0x01, 0x90, 0x0B, 0x81, 0x0A, 0x84, 0x05, 0x81, 0x02,
|
||||||
|
0x82, 0x09, 0x90, 0x0E, 0x81, 0x02, 0x84, 0x03, 0x81, 0x0C, 0x81, 0x0C,
|
||||||
|
0x81, 0x0D, 0x82, 0x08, 0x81, 0x03, 0x81, 0x0D, 0x81, 0x0A, 0x81, 0x0A,
|
||||||
|
0x81, 0x0C, 0x82, 0x06, 0x90, 0x0D, 0x88, 0x0C, 0x84, 0x04, 0x81, 0x01,
|
||||||
|
0x81, 0x0A, 0x81, 0x03, 0x90, 0x06, 0x88, 0x07, 0x88, 0x0E, 0x84, 0x0E,
|
||||||
|
0x81, 0x0E, 0x82, 0x03, 0x90, 0x0E, 0x82, 0x05, 0x90, 0x02, 0x88, 0x03,
|
||||||
|
0x81, 0x07, 0x81, 0x0A, 0x81, 0x0B, 0x81, 0x05, 0x84, 0x0D, 0x81, 0x01,
|
||||||
|
0x82, 0x02, 0x90, 0x0C, 0x82, 0x03, 0x90, 0x08, 0x81, 0x05, 0x84, 0x08,
|
||||||
|
0x84, 0x06, 0x88, 0x08, 0x81, 0x0E, 0x82, 0x01, 0x81, 0x01, 0x90, 0x08,
|
||||||
|
0x82, 0x03, 0x90, 0x0C, 0x84, 0x02, 0x81, 0x09, 0x82, 0x04, 0x88, 0x0D,
|
||||||
|
0x90, 0x07, 0x88, 0x0E, 0x82, 0x05, 0x81, 0x0C, 0x90, 0x0A, 0x81, 0x04,
|
||||||
|
0x84, 0x06, 0x82, 0x02, 0x81, 0x09, 0x90, 0x0D, 0x82, 0x06, 0x82, 0x01,
|
||||||
|
0x90, 0x09, 0x81, 0x0B, 0x84, 0x04, 0x88, 0x07, 0x81, 0x0B, 0x81, 0x05,
|
||||||
|
0x82, 0x03, 0x81, 0x07, 0x90, 0x06, 0x82, 0x07,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t level; // level letter the ride was searched for
|
||||||
|
const uint8_t *stream;
|
||||||
|
uint16_t length;
|
||||||
|
} StGateStreamT;
|
||||||
|
|
||||||
|
static const StGateStreamT kGateStreams[] = {
|
||||||
|
{ 'G', kGateStreamG, (uint16_t)sizeof(kGateStreamG) },
|
||||||
|
{ 'I', kGateStreamI, (uint16_t)sizeof(kGateStreamI) },
|
||||||
|
{ 'K', kGateStreamK, (uint16_t)sizeof(kGateStreamK) },
|
||||||
|
{ 'O', kGateStreamO, (uint16_t)sizeof(kGateStreamO) },
|
||||||
|
{ 'Q', kGateStreamQ, (uint16_t)sizeof(kGateStreamQ) },
|
||||||
|
{ 'R', kGateStreamR, (uint16_t)sizeof(kGateStreamR) },
|
||||||
|
{ 'U', kGateStreamU, (uint16_t)sizeof(kGateStreamU) },
|
||||||
|
{ 'V', kGateStreamV, (uint16_t)sizeof(kGateStreamV) },
|
||||||
|
{ 'E', kGateStreamE, (uint16_t)sizeof(kGateStreamE) },
|
||||||
|
{ 'J', kGateStreamJ, (uint16_t)sizeof(kGateStreamJ) },
|
||||||
|
{ 'L', kGateStreamL, (uint16_t)sizeof(kGateStreamL) },
|
||||||
|
{ 'P', kGateStreamP, (uint16_t)sizeof(kGateStreamP) },
|
||||||
|
{ 'S', kGateStreamS, (uint16_t)sizeof(kGateStreamS) },
|
||||||
|
};
|
||||||
|
#define ST_GATE_STREAMS (sizeof(kGateStreams) / sizeof(kGateStreams[0]))
|
||||||
|
|
||||||
|
#endif
|
||||||
195
tools/stsim/recorder.c
Normal file
195
tools/stsim/recorder.c
Normal file
|
|
@ -0,0 +1,195 @@
|
||||||
|
// recorder -- search for a demo input stream that exercises a level.
|
||||||
|
//
|
||||||
|
// The C64 ships attract recordings for four screens (H, W, T, X). The
|
||||||
|
// other levels have none, so a refactor of their hooks had nothing to be
|
||||||
|
// checked against. This searches for a synthetic ride instead: random
|
||||||
|
// restarts plus hill climbing over (joystick mask, duration) pairs,
|
||||||
|
// scored on how long the cab survives and how much of the level it
|
||||||
|
// reaches -- hook state bytes written, fare-machine stages entered,
|
||||||
|
// distinct pictures drawn, pads landed on.
|
||||||
|
//
|
||||||
|
// The result is a REGRESSION fixture, not a recording: it pins current
|
||||||
|
// behaviour so a refactor that changes nothing observable leaves the
|
||||||
|
// trace identical. It says nothing about matching the real C64.
|
||||||
|
//
|
||||||
|
// Usage: recorder <level.dat> <letter> <seed> > stream.h
|
||||||
|
// Then assemble the per-level outputs into gateStreams.h.
|
||||||
|
//
|
||||||
|
// Determinism: a candidate is only useful if it replays identically from
|
||||||
|
// a cold start, so every evaluation clears the whole StSimT (see the note
|
||||||
|
// in evaluate) -- stSimNewGame deliberately preserves some cross-screen
|
||||||
|
// state, which would otherwise leak between candidates.
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "stSim.h"
|
||||||
|
#include "stDemoStreams.h"
|
||||||
|
|
||||||
|
static StLevelT gLevel;
|
||||||
|
static StSimT gSim;
|
||||||
|
|
||||||
|
void stAudioSfx(const uint8_t *p){(void)p;}
|
||||||
|
void stAudioNoise(bool o){(void)o;}
|
||||||
|
void stAudioThrustSweep(uint8_t v){(void)v;}
|
||||||
|
void stAudioSpeech(uint8_t c){(void)c;}
|
||||||
|
void stAudioSpeechPhrase(const uint8_t *s){(void)s;}
|
||||||
|
void stAudioSilence(void){}
|
||||||
|
void stAudioVoice2(uint8_t a,uint8_t b,uint8_t c){(void)a;(void)b;(void)c;}
|
||||||
|
void stAudioVoice1Freq(uint8_t v){(void)v;}
|
||||||
|
|
||||||
|
#define PAIRS 160
|
||||||
|
#define STREAMLEN (PAIRS * 2)
|
||||||
|
#define MAXTICKS 1400
|
||||||
|
|
||||||
|
typedef struct { uint32_t score; uint32_t ticks; uint32_t cover; uint32_t lands; uint32_t stages; uint32_t scr; uint32_t near; } ResT;
|
||||||
|
|
||||||
|
static uint32_t rs = 1;
|
||||||
|
static uint32_t rnd(void){ rs = rs*1664525u + 1013904223u; return rs>>8; }
|
||||||
|
|
||||||
|
static ResT evaluate(const uint8_t *stream, uint8_t *touchOut)
|
||||||
|
{
|
||||||
|
uint8_t touched[sizeof(StHookScratchT)];
|
||||||
|
uint8_t base[sizeof(StHookScratchT)];
|
||||||
|
uint8_t stageSeen[16];
|
||||||
|
uint32_t t, cover = 0, lands = 0, stages = 0, scrSeen = 0;
|
||||||
|
uint32_t nearest = 255u;
|
||||||
|
uint32_t scrHash[256];
|
||||||
|
uint8_t lastPad = 0;
|
||||||
|
ResT r;
|
||||||
|
|
||||||
|
memset(stageSeen, 0, sizeof(stageSeen));
|
||||||
|
memset(touched, 0, sizeof(touched));
|
||||||
|
// stSimNewGame deliberately PRESERVES flameParity, waveIdx and
|
||||||
|
// walkParity (the C64 carries them from screen to screen), so a
|
||||||
|
// search that reuses one StSimT would score each candidate against
|
||||||
|
// whatever the previous ride left behind -- and the winning stream
|
||||||
|
// would then not reproduce from a cold start. Clear the whole struct
|
||||||
|
// so every evaluation begins exactly where the gate will.
|
||||||
|
memset(&gSim, 0, sizeof(gSim));
|
||||||
|
stSimNewGame(&gSim, 1u, true);
|
||||||
|
stSimSeedDemoBuffer(&gSim, kDemoBufferInit, ST_DEMO_BUFFER_BYTES);
|
||||||
|
stSimEnterLevel(&gSim, &gLevel);
|
||||||
|
stSimSetDemoStream(&gSim, stream, STREAMLEN);
|
||||||
|
memcpy(base, &gSim.hookScratch, sizeof(base));
|
||||||
|
for (t = 0; t < MAXTICKS; t++) {
|
||||||
|
uint16_t k;
|
||||||
|
if (stSimTick(&gSim) != ST_TICK_CONTINUE) { break; }
|
||||||
|
{ const uint8_t *cur = (const uint8_t *)&gSim.hookScratch;
|
||||||
|
for (k = 0; k < (uint16_t)sizeof(base); k++) {
|
||||||
|
if (cur[k] != base[k]) { touched[k] = 1; } } }
|
||||||
|
if (gSim.activePad != 0u && lastPad == 0u) { lands++; }
|
||||||
|
lastPad = gSim.activePad;
|
||||||
|
if (gSim.stage < 16u) { stageSeen[gSim.stage] = 1u; }
|
||||||
|
/* Shaping: how close the ride came to an actual touchdown. Some
|
||||||
|
levels (R) only run their hook once a passenger has boarded,
|
||||||
|
which needs a landing, and a landing is far too precise for a
|
||||||
|
blind search to stumble on -- so reward getting near one. */
|
||||||
|
if (gSim.activePad == 0u && (gSim.spr[0].ptr & 1u) != 0u) {
|
||||||
|
int16_t tx = (int16_t)(((uint16_t)gSim.posXmsb << 8) | gSim.posXcol);
|
||||||
|
uint8_t pi;
|
||||||
|
for (pi = 0u; pi < gLevel.padCount && pi < ST_MAX_PADS; pi++) {
|
||||||
|
int16_t x1 = (int16_t)(((uint16_t)gSim.pads[pi].x1Hi << 8) | gSim.pads[pi].x1Lo);
|
||||||
|
int16_t x2 = (int16_t)(((uint16_t)gSim.pads[pi].x2Hi << 8) | gSim.pads[pi].x2Lo);
|
||||||
|
int16_t dr = (int16_t)((int16_t)gSim.spr[0].row - (int16_t)gSim.pads[pi].row);
|
||||||
|
uint32_t ar = (uint32_t)(dr < 0 ? -dr : dr);
|
||||||
|
uint32_t ax = 0u;
|
||||||
|
uint32_t d;
|
||||||
|
if (tx < x1) { ax = (uint32_t)(x1 - tx); }
|
||||||
|
else if (tx > x2) { ax = (uint32_t)(tx - x2); }
|
||||||
|
/* Both axes matter: the row alone is easy to hit while
|
||||||
|
the cab sits at the far side of the screen. */
|
||||||
|
d = ar * 4u + ax;
|
||||||
|
if (d < nearest) { nearest = d; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* distinct pictures the hook has produced (screen + colour) */
|
||||||
|
{ uint32_t h = 2166136261u; uint16_t c; uint32_t i2; int dup = 0;
|
||||||
|
for (c = 0; c < ST_SCREEN_CELLS; c++) { h = (h ^ gSim.screen[c]) * 16777619u; }
|
||||||
|
for (c = 0; c < ST_SCREEN_CELLS; c++) { h = (h ^ gSim.color[c]) * 16777619u; }
|
||||||
|
for (i2 = 0; i2 < scrSeen; i2++) { if (scrHash[i2] == h) { dup = 1; break; } }
|
||||||
|
if (!dup && scrSeen < 256u) { scrHash[scrSeen++] = h; } }
|
||||||
|
}
|
||||||
|
{ uint16_t k; for (k = 0; k < (uint16_t)sizeof(touched); k++) { if (touched[k]) { cover++; } } }
|
||||||
|
{ int k; for (k = 0; k < 16; k++) { if (stageSeen[k]) { stages++; } } }
|
||||||
|
if (touchOut) { memcpy(touchOut, touched, sizeof(touched)); }
|
||||||
|
r.ticks = t; r.cover = cover; r.lands = lands; r.stages = stages; r.scr = scrSeen;
|
||||||
|
r.near = nearest;
|
||||||
|
r.score = cover * 30000u + stages * 30000u + lands * 4000u + t * 100u + scrSeen * 200u
|
||||||
|
+ (255u - nearest) * 60u;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void randomise(uint8_t *s, int from)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = from; i < PAIRS; i++) {
|
||||||
|
uint32_t d = rnd() % 100u;
|
||||||
|
uint8_t m = 0x80u;
|
||||||
|
if (d < 45u) { m |= 0x01u; } /* thrust up a lot */
|
||||||
|
else if (d < 60u) { m |= 0x04u; }
|
||||||
|
else if (d < 75u) { m |= 0x08u; }
|
||||||
|
else if (d < 82u) { m |= 0x02u; }
|
||||||
|
else if (d < 88u) { m |= 0x10u; }
|
||||||
|
if (rnd() % 5u == 0u) { m |= (uint8_t)(1u << (rnd() % 4u)); }
|
||||||
|
s[i*2] = m;
|
||||||
|
s[i*2 + 1] = (uint8_t)(1u + rnd() % 14u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
uint8_t best[STREAMLEN], cur[STREAMLEN], touched[ST_HOOK_BYTES];
|
||||||
|
ResT bestR, curR;
|
||||||
|
FILE *fp;
|
||||||
|
int restart, iter;
|
||||||
|
|
||||||
|
if (argc < 4) { fprintf(stderr, "usage: recorder <level.dat> <name> <seed>\n"); return 2; }
|
||||||
|
fp = fopen(argv[1], "rb");
|
||||||
|
if (!fp || !stLevelParse(&gLevel, fp)) { fprintf(stderr, "cannot load %s\n", argv[1]); return 1; }
|
||||||
|
fclose(fp);
|
||||||
|
rs = (uint32_t)strtoul(argv[3], NULL, 0);
|
||||||
|
|
||||||
|
randomise(best, 0);
|
||||||
|
bestR = evaluate(best, NULL);
|
||||||
|
for (restart = 0; restart < 120; restart++) {
|
||||||
|
memcpy(cur, best, STREAMLEN);
|
||||||
|
if (restart > 0) { randomise(cur, (int)(rnd() % PAIRS)); }
|
||||||
|
curR = evaluate(cur, NULL);
|
||||||
|
for (iter = 0; iter < 3000; iter++) {
|
||||||
|
uint8_t trial[STREAMLEN];
|
||||||
|
ResT tr;
|
||||||
|
int j, nmut = 1 + (int)(rnd() % 3u);
|
||||||
|
memcpy(trial, cur, STREAMLEN);
|
||||||
|
for (j = 0; j < nmut; j++) {
|
||||||
|
int i = (int)(rnd() % PAIRS);
|
||||||
|
if (rnd() % 2u) {
|
||||||
|
uint8_t m = 0x80u;
|
||||||
|
uint32_t d = rnd() % 100u;
|
||||||
|
if (d < 45u) { m |= 0x01u; }
|
||||||
|
else if (d < 62u) { m |= 0x04u; }
|
||||||
|
else if (d < 79u) { m |= 0x08u; }
|
||||||
|
else if (d < 88u) { m |= 0x02u; }
|
||||||
|
else { m |= 0x10u; }
|
||||||
|
trial[i*2] = m;
|
||||||
|
} else {
|
||||||
|
trial[i*2 + 1] = (uint8_t)(1u + rnd() % 14u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tr = evaluate(trial, NULL);
|
||||||
|
if (tr.score >= curR.score) { memcpy(cur, trial, STREAMLEN); curR = tr; }
|
||||||
|
}
|
||||||
|
if (curR.score > bestR.score) { memcpy(best, cur, STREAMLEN); bestR = curR; }
|
||||||
|
}
|
||||||
|
bestR = evaluate(best, touched);
|
||||||
|
fprintf(stderr, "%s: ticks=%u hookBytes=%u stages=%u pictures=%u lands=%u nearPad=%u\n",
|
||||||
|
argv[2], bestR.ticks, bestR.cover, bestR.stages, bestR.scr, bestR.lands, bestR.near);
|
||||||
|
printf("// %s: %u ticks, %u hook bytes, %u fare stages, %u pictures, %u landings\n",
|
||||||
|
argv[2], bestR.ticks, bestR.cover, bestR.stages, bestR.scr, bestR.lands);
|
||||||
|
printf("static const uint8_t kGateStream%s[%d] = {", argv[2], STREAMLEN);
|
||||||
|
{ int i; for (i = 0; i < STREAMLEN; i++) {
|
||||||
|
if (i % 12 == 0) { printf("\n "); }
|
||||||
|
printf("0x%02X,%s", best[i], (i % 12 == 11) ? "" : " "); } }
|
||||||
|
printf("\n};\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -5,14 +5,19 @@
|
||||||
// the VICE tracer records (see stuff/spacetaxi and the compare script),
|
// the VICE tracer records (see stuff/spacetaxi and the compare script),
|
||||||
// so the port's engine can be diffed against the real machine.
|
// so the port's engine can be diffed against the real machine.
|
||||||
//
|
//
|
||||||
// stsim <level.dat> <H|W|T|X> <ticks>
|
// stsim <level.dat> <level letter> <ticks>
|
||||||
|
//
|
||||||
|
// H, W, T and X use the C64's own attract recordings; every other
|
||||||
|
// letter uses a synthesised gate fixture from gateStreams.h.
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "../../examples/spacetaxi/stSim.h"
|
#include "../../examples/spacetaxi/stSim.h"
|
||||||
|
#include "../../examples/spacetaxi/stCels.h"
|
||||||
#include "../../examples/spacetaxi/stDemoStreams.h"
|
#include "../../examples/spacetaxi/stDemoStreams.h"
|
||||||
|
#include "gateStreams.h"
|
||||||
|
|
||||||
|
|
||||||
static StLevelT gLevel;
|
static StLevelT gLevel;
|
||||||
|
|
@ -23,11 +28,105 @@ void stAudioSfx(const uint8_t *program9) { (void)program9; }
|
||||||
void stAudioNoise(bool on) { (void)on; }
|
void stAudioNoise(bool on) { (void)on; }
|
||||||
void stAudioThrustSweep(uint8_t value) { (void)value; }
|
void stAudioThrustSweep(uint8_t value) { (void)value; }
|
||||||
void stAudioSpeech(uint8_t ch) { (void)ch; }
|
void stAudioSpeech(uint8_t ch) { (void)ch; }
|
||||||
|
void stAudioSpeechPhrase(const uint8_t *speech) { (void)speech; }
|
||||||
void stAudioSilence(void) { }
|
void stAudioSilence(void) { }
|
||||||
void stAudioVoice2(uint8_t freqLo, uint8_t freqHi, uint8_t ctrl) { (void)freqLo; (void)freqHi; (void)ctrl; }
|
void stAudioVoice2(uint8_t freqLo, uint8_t freqHi, uint8_t ctrl) { (void)freqLo; (void)freqHi; (void)ctrl; }
|
||||||
void stAudioVoice1Freq(uint8_t value) { (void)value; }
|
void stAudioVoice1Freq(uint8_t value) { (void)value; }
|
||||||
|
|
||||||
|
|
||||||
|
// FNV-1a over a byte span: the picture the player actually sees. The
|
||||||
|
// hooks that animate the screen (O, R, V, W, X) change screen, colour or
|
||||||
|
// charset bytes and nothing else this line used to capture.
|
||||||
|
static uint32_t hashBytes(const uint8_t *p, size_t n) {
|
||||||
|
uint32_t h = 2166136261u;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0u; i < n; i++) {
|
||||||
|
h = (h ^ p[i]) * 16777619u;
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The charset the game would be showing. The sim no longer holds a
|
||||||
|
// contiguous 2 KB copy -- edited glyphs live in an override table -- so
|
||||||
|
// rebuild the effective image here. Hashing this keeps the trace value
|
||||||
|
// identical to the pre-override captures, which is what lets the goldens
|
||||||
|
// prove the change is behaviour-preserving.
|
||||||
|
static uint32_t charsetHash(const StSimT *sim) {
|
||||||
|
uint8_t image[ST_CHARSET_CHARS * 8u];
|
||||||
|
uint16_t ch;
|
||||||
|
|
||||||
|
for (ch = 0u; ch < ST_CHARSET_CHARS; ch++) {
|
||||||
|
memcpy(&image[ch * 8u], stSimGlyph(sim, (uint8_t)ch), 8u);
|
||||||
|
}
|
||||||
|
return hashBytes(image, sizeof(image));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// STSIM_CELS=1: every level cel (ptr below the baked set) a ride
|
||||||
|
// displays, as (ptr, colour, multi, mc0, mc1), against what
|
||||||
|
// stLevelCelList bakes for the level -- a cel shown but not listed is
|
||||||
|
// built and drawn interpreted mid-ride on the real ports.
|
||||||
|
#define CENSUS_MAX 64u
|
||||||
|
static StCelDefT gCensus[CENSUS_MAX];
|
||||||
|
static uint8_t gCensusCount;
|
||||||
|
|
||||||
|
|
||||||
|
static void celCensusTick(const StSimT *sim) {
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
for (i = 1u; i < ST_HW_SPRITES; i++) {
|
||||||
|
StCelDefT d;
|
||||||
|
uint8_t k;
|
||||||
|
|
||||||
|
if ((sim->frame.enableMask & kStBit[i]) == 0u || sim->frame.ptr[i] >= ST_SPRITE_PTR_FIRST) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// A pointer with no bitmap (a hook enables a sprite a tick before
|
||||||
|
// aiming it) draws nothing on every port -- not a cel.
|
||||||
|
if (stSimSpriteBitmap(sim, sim->frame.ptr[i]) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
d.ptr = sim->frame.ptr[i];
|
||||||
|
d.color = sim->frame.color[i];
|
||||||
|
d.multi = (sim->frame.multiMask & kStBit[i]) != 0u ? 1u : 0u;
|
||||||
|
d.mc0 = d.multi ? sim->spriteMc0 : 0u;
|
||||||
|
d.mc1 = d.multi ? sim->spriteMc1 : 0u;
|
||||||
|
for (k = 0u; k < gCensusCount; k++) {
|
||||||
|
if (memcmp(&gCensus[k], &d, sizeof(d)) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (k == gCensusCount && gCensusCount < CENSUS_MAX) {
|
||||||
|
gCensus[gCensusCount++] = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void celCensusReport(const StLevelT *level) {
|
||||||
|
StCelDefT listed[ST_LEVEL_CELS_MAX];
|
||||||
|
uint8_t n = stLevelCelList(level, listed, ST_LEVEL_CELS_MAX);
|
||||||
|
uint8_t k;
|
||||||
|
uint8_t j;
|
||||||
|
|
||||||
|
for (k = 0u; k < gCensusCount; k++) {
|
||||||
|
for (j = 0u; j < n; j++) {
|
||||||
|
if (memcmp(&listed[j], &gCensus[k], sizeof(StCelDefT)) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(stderr, "CEL %s ptr=%02X color=%u multi=%u mc=%u/%u\n", (j < n) ? "listed " : "MISSING", gCensus[k].ptr, gCensus[k].color, gCensus[k].multi, gCensus[k].mc0, gCensus[k].mc1);
|
||||||
|
}
|
||||||
|
fprintf(stderr, "CENSUS shown=%u listed=%u\n", gCensusCount, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void celCensusReport(const StLevelT *level);
|
||||||
|
static void celCensusTick(const StSimT *sim);
|
||||||
|
|
||||||
|
|
||||||
static void printState(const StSimT *sim, uint32_t tick) {
|
static void printState(const StSimT *sim, uint32_t tick) {
|
||||||
uint8_t k;
|
uint8_t k;
|
||||||
|
|
||||||
|
|
@ -55,7 +154,11 @@ static void printState(const StSimT *sim, uint32_t tick) {
|
||||||
for (k = 0u; k < ST_HW_SPRITES; k++) {
|
for (k = 0u; k < ST_HW_SPRITES; k++) {
|
||||||
printf("%03X%02X", sim->frame.x[k], sim->frame.y[k]);
|
printf("%03X%02X", sim->frame.x[k], sim->frame.y[k]);
|
||||||
}
|
}
|
||||||
printf("\"}\n");
|
printf("\",\"scr\":\"%08X\",\"col\":\"%08X\",\"chr\":\"%08X\",\"bd\":\"%02X\",\"bg\":\"%02X\"}\n",
|
||||||
|
hashBytes(sim->screen, ST_SCREEN_CELLS),
|
||||||
|
hashBytes(sim->color, ST_SCREEN_CELLS),
|
||||||
|
charsetHash(sim),
|
||||||
|
sim->borderColor, sim->bgColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -63,11 +166,12 @@ int main(int argc, char **argv) {
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
const uint8_t *stream = kDemoStreamH;
|
const uint8_t *stream = kDemoStreamH;
|
||||||
uint16_t len = (uint16_t)sizeof(kDemoStreamH);
|
uint16_t len = (uint16_t)sizeof(kDemoStreamH);
|
||||||
|
uint8_t want;
|
||||||
uint32_t ticks;
|
uint32_t ticks;
|
||||||
uint32_t t;
|
uint32_t t;
|
||||||
|
|
||||||
if (argc < 4) {
|
if (argc < 4) {
|
||||||
fprintf(stderr, "usage: stsim <level.dat> <H|W|T|X> <ticks>\n");
|
fprintf(stderr, "usage: stsim <level.dat> <level letter> <ticks>\n");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
fp = fopen(argv[1], "rb");
|
fp = fopen(argv[1], "rb");
|
||||||
|
|
@ -76,11 +180,29 @@ int main(int argc, char **argv) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
switch (argv[2][0]) {
|
want = (uint8_t)argv[2][0];
|
||||||
|
switch (want) {
|
||||||
case 'W': stream = kDemoStreamW; len = (uint16_t)sizeof(kDemoStreamW); break;
|
case 'W': stream = kDemoStreamW; len = (uint16_t)sizeof(kDemoStreamW); break;
|
||||||
case 'T': stream = kDemoStreamT; len = (uint16_t)sizeof(kDemoStreamT); break;
|
case 'T': stream = kDemoStreamT; len = (uint16_t)sizeof(kDemoStreamT); break;
|
||||||
case 'X': stream = kDemoStreamX; len = (uint16_t)sizeof(kDemoStreamX); break;
|
case 'X': stream = kDemoStreamX; len = (uint16_t)sizeof(kDemoStreamX); break;
|
||||||
default: break;
|
case 'H': break;
|
||||||
|
default: {
|
||||||
|
// A synthesised gate fixture for a level the C64 has no
|
||||||
|
// attract recording of (see gateStreams.h).
|
||||||
|
uint8_t g;
|
||||||
|
for (g = 0u; g < ST_GATE_STREAMS; g++) {
|
||||||
|
if (kGateStreams[g].level == want) {
|
||||||
|
stream = kGateStreams[g].stream;
|
||||||
|
len = kGateStreams[g].length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (g == ST_GATE_STREAMS) {
|
||||||
|
fprintf(stderr, "stsim: no stream for '%c'\n", (char)want);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ticks = (uint32_t)strtoul(argv[3], NULL, 0);
|
ticks = (uint32_t)strtoul(argv[3], NULL, 0);
|
||||||
stSimNewGame(&gSim, 1u, true);
|
stSimNewGame(&gSim, 1u, true);
|
||||||
|
|
@ -101,10 +223,16 @@ int main(int argc, char **argv) {
|
||||||
StTickResultE r;
|
StTickResultE r;
|
||||||
printState(&gSim, t);
|
printState(&gSim, t);
|
||||||
r = stSimTick(&gSim);
|
r = stSimTick(&gSim);
|
||||||
|
if (getenv("STSIM_CELS") != NULL) {
|
||||||
|
celCensusTick(&gSim);
|
||||||
|
}
|
||||||
if (r != ST_TICK_CONTINUE) {
|
if (r != ST_TICK_CONTINUE) {
|
||||||
printf("{\"tick\":%u,\"end\":%d}\n", t, (int)r);
|
printf("{\"tick\":%u,\"end\":%d}\n", t, (int)r);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (getenv("STSIM_CELS") != NULL) {
|
||||||
|
celCensusReport(&gLevel);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue