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.height = this.fmt.h || 1;
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 editview = this.createEditor(aeditor, viewer, xscale, yscale);
this.context.setCurrentEditor(aeditor, $(viewer.canvas), this);

View File

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

View File

@ -60,7 +60,9 @@ function getToolForFilename_vcs(fn: string) {
if (fn.endsWith(".wiz")) return "wiz";
if (fn.endsWith(".bb") || fn.endsWith(".bas")) return "bataribasic";
if (fn.endsWith(".ca65")) return "ca65";
//if (fn.endsWith(".inc")) return "ca65";
if (fn.endsWith(".c")) return "cc65";
//if (fn.endsWith(".h")) return "cc65";
if (fn.endsWith(".ecs")) return "ecs";
return "dasm";
}
@ -222,11 +224,13 @@ class VCSPlatform extends BasePlatform {
return state;
}
fixState(state) {
var ofs = (state.ca && state.ca.bo) || 0;
if (state.ca && state.ca.fo && (state.c.PC & 0xfff) >= 2048)
ofs = state.ca.fo; // 3E/3F fixed-slice formats
// TODO: for batari BASIC
state.c.EPC = state.c.PC + ofs; // EPC = effective PC for ROM
// TODO: DASM listing prevents us from using RORG offset
// TODO: how to handle 1000/3000/etc vs overlapping addresses?
if (state.ca.f != '3E' && state.ca.f != '3F') {
var ofs = (state.ca && state.ca.bo) || 0;
// TODO: for batari BASIC
state.c.EPC = state.c.PC + ofs; // EPC = effective PC for ROM
}
}
loadState(state) {
return Javatari.room.console.loadState(state);
@ -294,7 +298,8 @@ class VCSPlatform extends BasePlatform {
}
}
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) {
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.
# From http://wiki.cc65.org/doku.php?id=cc65:atari_2600
# Modified for TigerVision (3E) mapper
MEMORY {
RAM: start = $80, size=$80, type = rw, define = yes;
ROM: start = $F000, size=$1000, type = ro, file = %O, define = yes;
RAM: start = $80, size=$70, type = rw, define = yes;
TIA: start = $00, size=$40, 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 {
RODATA: load=ROM, type=ro, align=$100;
STARTUP: load=ROM, type=ro, optional=yes;
CODE: load=ROM, type=ro, define=yes;
DATA: load=ROM, run=RAM, type=rw, define=yes;
ROM0: load=ROM0, type=ro, align=$100, optional=yes;
ROM1: load=ROM1, type=ro, align=$100, optional=yes;
ROM2: load=ROM2, type=ro, align=$100, optional=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;
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;
TIA: load=TIA, 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
});
}
var linem = insnLineMatch.exec(line);
var topfile = linem && linem[3] == '1';
if (topfile) linenum++;
if (topfile && linem[1]) {
var offset = parseInt(linem[1], 16);
var insns = linem[4].trim();
if (insns.length) {
//console.log(curpath, linenum, offset, segofs, insns);
if (!dbg) {
lines.push({
path: curpath,
line: linenum,
offset: offset + segofs,
insns: insns,
iscode: true // TODO: can't really tell unless we parse it
});
}
} else {
var sym = linem[5];
if (sym.endsWith(':') && !sym.startsWith('@')) {
var symofs = symbols[sym.substring(0, sym.length - 1)];
if (typeof symofs === 'number') {
segofs = symofs - offset;
//console.log(sym, segofs, symofs, '-', offset);
let linem = insnLineMatch.exec(line);
let topfile = linem && linem[3] == '1';
if (topfile) {
let insns = linem[4]?.trim() || '';
// skip extra insns for macro expansions
if (!(insns != '' && linem[5] == '')) {
linenum++;
}
if (linem[1]) {
var offset = parseInt(linem[1], 16);
if (insns.length) {
//console.log(dbg, curpath, linenum, offset, segofs, insns);
if (!dbg) {
lines.push({
path: curpath,
line: linenum,
offset: offset + segofs,
insns: insns,
iscode: true // TODO: can't really tell unless we parse it
});
}
} else {
var sym = null;
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
putWorkFile(fn, lstout);
//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) {
var asmlines = [];
var srclines = parseCA65Listing(fn, lstout, symbolmap, segments, params, true, listings);