vcs: changed .cfg file for TigerVision (3E) mapper

vcs: mappers 3E/3F don't correct bank offsets, so banks must not overlap
ca65: parse .proc symbols
This commit is contained in:
Steven Hugg 2023-10-25 08:10:14 -05:00
parent 2e2996bd73
commit 73c74c519e
6 changed files with 138 additions and 40 deletions

View File

@ -837,7 +837,7 @@ export class CharmapEditor extends PixNode {
chooser.width = this.fmt.w || 1; chooser.width = this.fmt.w || 1;
chooser.height = this.fmt.h || 1; chooser.height = this.fmt.h || 1;
chooser.recreate(agrid, (index, viewer) => { chooser.recreate(agrid, (index, viewer) => {
var yscale = Math.ceil(192 / this.fmt.w); var yscale = Math.ceil(256 / this.fmt.w); // TODO: variable scale?
var xscale = yscale * (this.fmt.aspect || 1.0); var xscale = yscale * (this.fmt.aspect || 1.0);
var editview = this.createEditor(aeditor, viewer, xscale, yscale); var editview = this.createEditor(aeditor, viewer, xscale, yscale);
this.context.setCurrentEditor(aeditor, $(viewer.canvas), this); this.context.setCurrentEditor(aeditor, $(viewer.canvas), this);

View File

@ -2442,8 +2442,9 @@ export function getPlatformAndRepo() {
qs.repo = repo_id; qs.repo = repo_id;
if (repo.platform_id && !qs.platform) if (repo.platform_id && !qs.platform)
qs.platform = platform_id = repo.platform_id; qs.platform = platform_id = repo.platform_id;
if (!qs.file && repo.mainPath) if (repo.mainPath && !qs.file)
qs.file = repo.mainPath; qs.file = repo.mainPath;
// TODO: update repo definition if new main file compiles successfully
//requestPersistPermission(true, true); //requestPersistPermission(true, true);
} }
} else { } else {

View File

@ -60,7 +60,9 @@ function getToolForFilename_vcs(fn: string) {
if (fn.endsWith(".wiz")) return "wiz"; if (fn.endsWith(".wiz")) return "wiz";
if (fn.endsWith(".bb") || fn.endsWith(".bas")) return "bataribasic"; if (fn.endsWith(".bb") || fn.endsWith(".bas")) return "bataribasic";
if (fn.endsWith(".ca65")) return "ca65"; if (fn.endsWith(".ca65")) return "ca65";
//if (fn.endsWith(".inc")) return "ca65";
if (fn.endsWith(".c")) return "cc65"; if (fn.endsWith(".c")) return "cc65";
//if (fn.endsWith(".h")) return "cc65";
if (fn.endsWith(".ecs")) return "ecs"; if (fn.endsWith(".ecs")) return "ecs";
return "dasm"; return "dasm";
} }
@ -222,11 +224,13 @@ class VCSPlatform extends BasePlatform {
return state; return state;
} }
fixState(state) { fixState(state) {
var ofs = (state.ca && state.ca.bo) || 0; // TODO: DASM listing prevents us from using RORG offset
if (state.ca && state.ca.fo && (state.c.PC & 0xfff) >= 2048) // TODO: how to handle 1000/3000/etc vs overlapping addresses?
ofs = state.ca.fo; // 3E/3F fixed-slice formats if (state.ca.f != '3E' && state.ca.f != '3F') {
// TODO: for batari BASIC var ofs = (state.ca && state.ca.bo) || 0;
state.c.EPC = state.c.PC + ofs; // EPC = effective PC for ROM // TODO: for batari BASIC
state.c.EPC = state.c.PC + ofs; // EPC = effective PC for ROM
}
} }
loadState(state) { loadState(state) {
return Javatari.room.console.loadState(state); return Javatari.room.console.loadState(state);
@ -294,7 +298,8 @@ class VCSPlatform extends BasePlatform {
} }
} }
bankSwitchStateToString(state) { bankSwitchStateToString(state) {
return (state.ca && state.ca.bo !== undefined) ? "BankOffset "+hex(state.ca.bo,4)+"\n" : ""; if (state.ca?.ro >= 0) return "RAMOffset "+hex(state.ca.ro,4)+"\n";
return (state.ca?.bo >= 0) ? "BankOffset "+hex(state.ca.bo,4)+"\n" : "";
} }
piaStateToLongString(p) { piaStateToLongString(p) {
return "Timer " + p.t + "/" + p.c + "\nINTIM $" + hex(p.IT,2) + " (" + p.IT + ")\nINSTAT $" + hex(p.IS,2) + "\n"; return "Timer " + p.t + "/" + p.c + "\nINTIM $" + hex(p.IT,2) + " (" + p.IT + ")\nINSTAT $" + hex(p.IS,2) + "\n";

60
src/test/testanalysis.ts Normal file
View File

@ -0,0 +1,60 @@
import { describe } from "mocha";
import { OpcodeMetadata, Platform } from "../common/baseplatform";
import { CodeAnalyzer_vcs } from "../common/analysis";
import { MOS6502 } from "../common/cpu/MOS6502";
import assert from "assert";
class Test6502Platform implements Platform {
cpu = new MOS6502();
ram = new Uint8Array(65536);
start(): void | Promise<void> {
throw new Error("Method not implemented.");
}
reset(): void {
throw new Error("Method not implemented.");
}
isRunning(): boolean {
throw new Error("Method not implemented.");
}
getToolForFilename(s: string): string {
throw new Error("Method not implemented.");
}
getDefaultExtension(): string {
throw new Error("Method not implemented.");
}
pause(): void {
throw new Error("Method not implemented.");
}
resume(): void {
throw new Error("Method not implemented.");
}
loadROM(title: string, rom: any) {
throw new Error("Method not implemented.");
}
getOpcodeMetadata?(opcode: number, offset: number): OpcodeMetadata {
return this.cpu.cpu.getOpcodeMetadata(opcode, offset);
}
getOriginPC(): number {
return 0;
}
readAddress?(addr:number) : number {
return this.ram[addr] || 0;
}
}
describe('6502 analysis', function () {
it('Should analyze 6502', function () {
let platform = new Test6502Platform();
platform.ram.set([0xea,0x85,0x02,0xa9,0x60,0x20,0x04,0x00,0xea,0x4c,0x01,0x00]);
let analysis = new CodeAnalyzer_vcs(platform);
analysis.showLoopTimingForPC(0x0);
console.log(analysis);
assert.equal(analysis.pc2minclocks[0x0], 304);
assert.equal(analysis.pc2maxclocks[0x0], 304);
assert.equal(analysis.pc2minclocks[0x1], 19);
assert.equal(analysis.pc2maxclocks[0x0], 304);
assert.equal(analysis.pc2minclocks[0x3], 0);
assert.equal(analysis.pc2maxclocks[0x3], 0);
});
});

View File

@ -1,20 +1,41 @@
# Linker config file for targeting the Atari 2600. # Linker config file for targeting the Atari 2600.
# From http://wiki.cc65.org/doku.php?id=cc65:atari_2600 # From http://wiki.cc65.org/doku.php?id=cc65:atari_2600
# Modified for TigerVision (3E) mapper
MEMORY { MEMORY {
RAM: start = $80, size=$80, type = rw, define = yes; RAM: start = $80, size=$70, type = rw, define = yes;
ROM: start = $F000, size=$1000, type = ro, file = %O, define = yes;
TIA: start = $00, size=$40, type = rw, define = yes; TIA: start = $00, size=$40, type = rw, define = yes;
RIOT: start = $280, size=$20, type = rw, define = yes; RIOT: start = $280, size=$20, type = rw, define = yes;
ROM0: start = $1000, size=$800, type=ro, file=%O, define=yes, fill=yes, bank=0;
ROM1: start = $3000, size=$800, type=ro, file=%O, define=yes, fill=yes, bank=1;
ROM2: start = $5000, size=$800, type=ro, file=%O, define=yes, fill=yes, bank=2;
ROM3: start = $7000, size=$800, type=ro, file=%O, define=yes, fill=yes, bank=3;
ROM4: start = $9000, size=$800, type=ro, file=%O, define=yes, fill=yes, bank=4;
ROM5: start = $b000, size=$800, type=ro, file=%O, define=yes, fill=yes, bank=5;
ROM6: start = $d000, size=$800, type=ro, file=%O, define=yes, fill=yes, bank=6;
ROM7: start = $f000, size=$800, type=ro, file=%O, define=yes, fill=yes, bank=7;
RAM0: start = $f000, size=$400, type=rw, define=yes;
PERM: start = $f800, size=$800, type=ro, file=%O, define=yes, fill=yes;
} }
SEGMENTS { SEGMENTS {
RODATA: load=ROM, type=ro, align=$100; ROM0: load=ROM0, type=ro, align=$100, optional=yes;
STARTUP: load=ROM, type=ro, optional=yes; ROM1: load=ROM1, type=ro, align=$100, optional=yes;
CODE: load=ROM, type=ro, define=yes; ROM2: load=ROM2, type=ro, align=$100, optional=yes;
DATA: load=ROM, run=RAM, type=rw, define=yes; ROM3: load=ROM3, type=ro, align=$100, optional=yes;
ROM4: load=ROM4, type=ro, align=$100, optional=yes;
ROM5: load=ROM5, type=ro, align=$100, optional=yes;
ROM6: load=ROM6, type=ro, align=$100, optional=yes;
ROM7: load=ROM7, type=ro, align=$100, optional=yes;
RODATA: load=PERM, type=ro, align=$100;
STARTUP: load=PERM, type=ro, optional=yes;
CODE: load=PERM, type=ro, define=yes;
DATA: load=ROM0, run=RAM, type=rw, define=yes;
BSS: load=RAM, type=bss, define=yes; BSS: load=RAM, type=bss, define=yes;
VECTORS: load=ROM, type=ro, start=$FFFA; VECTORS: load=PERM, type=ro, start=$FFFA;
XDATA: load=ROM0, run=RAM0, type=rw, define=yes, optional=yes;
ZEROPAGE: load=RAM, type=zp; ZEROPAGE: load=RAM, type=zp;
TIA: load=TIA, type=rw, define = yes, optional = yes; TIA: load=TIA, type=rw, define = yes, optional = yes;
RIOT: load=RIOT, type=rw, define = yes, optional = yes; RIOT: load=RIOT, type=rw, define = yes, optional = yes;

View File

@ -62,30 +62,41 @@ function parseCA65Listing(asmfn: string, code: string, symbols, segments, params
insns: null insns: null
}); });
} }
var linem = insnLineMatch.exec(line); let linem = insnLineMatch.exec(line);
var topfile = linem && linem[3] == '1'; let topfile = linem && linem[3] == '1';
if (topfile) linenum++; if (topfile) {
if (topfile && linem[1]) { let insns = linem[4]?.trim() || '';
var offset = parseInt(linem[1], 16); // skip extra insns for macro expansions
var insns = linem[4].trim(); if (!(insns != '' && linem[5] == '')) {
if (insns.length) { linenum++;
//console.log(curpath, linenum, offset, segofs, insns); }
if (!dbg) { if (linem[1]) {
lines.push({ var offset = parseInt(linem[1], 16);
path: curpath, if (insns.length) {
line: linenum, //console.log(dbg, curpath, linenum, offset, segofs, insns);
offset: offset + segofs, if (!dbg) {
insns: insns, lines.push({
iscode: true // TODO: can't really tell unless we parse it path: curpath,
}); line: linenum,
} offset: offset + segofs,
} else { insns: insns,
var sym = linem[5]; iscode: true // TODO: can't really tell unless we parse it
if (sym.endsWith(':') && !sym.startsWith('@')) { });
var symofs = symbols[sym.substring(0, sym.length - 1)]; }
if (typeof symofs === 'number') { } else {
segofs = symofs - offset; var sym = null;
//console.log(sym, segofs, symofs, '-', offset); var label = linem[5];
if (label?.endsWith(':')) {
sym = label.substring(0, label.length-1);
} else if (label?.toLowerCase().startsWith('.proc')) {
sym = label.split(' ')[1];
}
if (sym && !sym.startsWith('@')) {
var symofs = symbols[sym];
if (typeof symofs === 'number') {
segofs = symofs - offset;
//console.log(sym, segofs, symofs, '-', offset);
}
} }
} }
} }
@ -224,7 +235,7 @@ export function linkLD65(step: BuildStep): BuildStepResult {
lstout = lstout.split('\n\n')[1] || lstout; // remove header lstout = lstout.split('\n\n')[1] || lstout; // remove header
putWorkFile(fn, lstout); putWorkFile(fn, lstout);
//const asmpath = fn.replace(/\.lst$/, '.ca65'); // TODO! could be .s //const asmpath = fn.replace(/\.lst$/, '.ca65'); // TODO! could be .s
let isECS = step.debuginfo?.entities != null; // TODO let isECS = step.debuginfo?.systems?.Init != null; // TODO
if (isECS) { if (isECS) {
var asmlines = []; var asmlines = [];
var srclines = parseCA65Listing(fn, lstout, symbolmap, segments, params, true, listings); var srclines = parseCA65Listing(fn, lstout, symbolmap, segments, params, true, listings);