Skip to content

Commit

Permalink
tweaks to analysis, fixed verilog ball reset bug
Browse files Browse the repository at this point in the history
  • Loading branch information
sehugg committed Nov 6, 2023
1 parent 3a08c24 commit d31a8c4
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 58 deletions.
16 changes: 10 additions & 6 deletions presets/verilog/ball_absolute.v
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@

/*
A bouncing ball using absolute coordinates.
Note: This module uses different clock domains
and thus may be unstable on a FPGA.
See: https://github.com/sehugg/8bitworkshop/issues/23
*/

module ball_absolute_top(clk, reset, hsync, vsync, rgb);
Expand All @@ -18,8 +22,8 @@ module ball_absolute_top(clk, reset, hsync, vsync, rgb);
reg [8:0] ball_hpos; // ball current X position
reg [8:0] ball_vpos; // ball current Y position

reg [8:0] ball_horiz_move = -2; // ball current X velocity
reg [8:0] ball_vert_move = 2; // ball current Y velocity
reg [8:0] ball_horiz_move; // ball current X velocity
reg [8:0] ball_vert_move; // ball current Y velocity

localparam ball_horiz_initial = 128; // ball initial X position
localparam ball_vert_initial = 128; // ball initial Y position
Expand Down Expand Up @@ -52,15 +56,15 @@ module ball_absolute_top(clk, reset, hsync, vsync, rgb);
end

// vertical bounce
always @(posedge ball_vert_collide)
always @(posedge ball_vert_collide or posedge reset)
begin
ball_vert_move <= -ball_vert_move;
ball_vert_move <= reset ? 2 : -ball_vert_move;
end

// horizontal bounce
always @(posedge ball_horiz_collide)
always @(posedge ball_horiz_collide or posedge reset)
begin
ball_horiz_move <= -ball_horiz_move;
ball_horiz_move <= reset ? -2 : -ball_horiz_move;
end

// offset of ball position from video beam
Expand Down
89 changes: 54 additions & 35 deletions src/common/analysis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
import { hex, byte2signed } from "./util";
import { Platform } from "./baseplatform";

const debug = false;

export interface CodeAnalyzer {
showLoopTimingForPC(pc:number);
pc2minclocks : {[key:number]:number};
pc2maxclocks : {[key:number]:number};
pc2clockrange : {[key:number]:ClockRange};
MAX_CLOCKS : number;
}

Expand Down Expand Up @@ -37,13 +38,17 @@ function constraintEquals(a,b) {
return null;
}

interface ClockRange {
minclocks: number;
maxclocks: number;
}

abstract class CodeAnalyzer6502 implements CodeAnalyzer {
pc2minclocks = {};
pc2maxclocks = {};
pc2clockrange : {[key:number]:ClockRange} = {};
jsrresult : {[key:number]:ClockRange} = {};
START_CLOCKS : number;
MAX_CLOCKS : number;
WRAP_CLOCKS : boolean;
jsrresult = {};
platform : Platform;
MAX_CYCLES : number = 2000;

Expand All @@ -58,45 +63,60 @@ abstract class CodeAnalyzer6502 implements CodeAnalyzer {
}

traceInstructions(pc:number, minclocks:number, maxclocks:number, subaddr:number, constraints) {
if (this.WRAP_CLOCKS) {
if (this.pc2minclocks[pc] !== undefined)
minclocks = Math.min(minclocks, this.pc2minclocks[pc]);
if (this.pc2maxclocks[pc] !== undefined)
maxclocks = Math.max(maxclocks, this.pc2maxclocks[pc]);
}
//console.log("trace", hex(pc), minclocks, maxclocks);
if (debug) console.log("trace", hex(pc), minclocks, maxclocks);
if (!constraints) constraints = {};
var modified = true;
var abort = false;
for (var i=0; modified && !abort; i++) {
for (let i=0; modified && !abort; i++) {
if (i >= this.MAX_CYCLES) {
console.log("too many cycles @", hex(pc), "routine", hex(subaddr));
break;
}
modified = false;
if (this.WRAP_CLOCKS && minclocks >= this.MAX_CLOCKS) {
if (this.WRAP_CLOCKS) {
// wrap clocks
minclocks = minclocks % this.MAX_CLOCKS;
maxclocks = maxclocks % this.MAX_CLOCKS;
if (maxclocks == minclocks-1) {
if (debug) console.log("0-75", hex(pc), minclocks, maxclocks);
minclocks = 0;
maxclocks = this.MAX_CLOCKS-1;
}
} else {
// truncate clocks
minclocks = Math.min(this.MAX_CLOCKS, minclocks);
maxclocks = Math.min(this.MAX_CLOCKS, maxclocks);
}
var meta = this.getClockCountsAtPC(pc);
var lob = this.platform.readAddress(pc+1);
var hib = this.platform.readAddress(pc+2);
var addr = lob + (hib << 8);
var pc0 = pc;
if (!(minclocks >= this.pc2minclocks[pc0])) {
this.pc2minclocks[pc0] = minclocks;
let meta = this.getClockCountsAtPC(pc);
let lob = this.platform.readAddress(pc+1);
let hib = this.platform.readAddress(pc+2);
let addr = lob + (hib << 8);
let pc0 = pc;
let pcrange = this.pc2clockrange[pc0];
if (pcrange == null) {
this.pc2clockrange[pc0] = pcrange = {minclocks:minclocks, maxclocks:maxclocks};
if (debug) console.log("new", hex(pc), hex(pc0), hex(subaddr), minclocks, maxclocks);
modified = true;
}
if (!(maxclocks <= this.pc2maxclocks[pc0])) {
this.pc2maxclocks[pc0] = maxclocks;
modified = true;
//console.log(hex(pc),minclocks,maxclocks, pcrange);
if (pcrange.minclocks != minclocks || pcrange.maxclocks != maxclocks) {
if (this.WRAP_CLOCKS && (minclocks <= maxclocks) != (pcrange.minclocks <= pcrange.maxclocks)) {
if (debug) console.log("wrap", hex(pc), hex(pc0), hex(subaddr), minclocks, maxclocks, pcrange);
pcrange.minclocks = minclocks = 0;
pcrange.maxclocks = maxclocks = this.MAX_CLOCKS-1;
modified = true;
}
if (minclocks < pcrange.minclocks) {
if (debug) console.log("min", hex(pc), hex(pc0), hex(subaddr), minclocks, maxclocks, pcrange);
pcrange.minclocks = minclocks;
modified = true;
}
if (maxclocks > pcrange.maxclocks) {
if (debug) console.log("max", hex(pc), hex(pc0), hex(subaddr), minclocks, maxclocks, pcrange);
pcrange.maxclocks = maxclocks;
modified = true;
}
}
//console.log(hex(pc),minclocks,maxclocks,modified,meta,constraints);
if (!meta.insnlength) {
console.log("Illegal instruction!", hex(pc), hex(meta.opcode), meta);
break;
Expand All @@ -110,13 +130,11 @@ abstract class CodeAnalyzer6502 implements CodeAnalyzer {
case 0x39: case 0x3d:
case 0x59: case 0x5d:
case 0x79: case 0x7d:
case 0x99: case 0x9d:
case 0xa9: case 0xad:
case 0xb9: case 0xbd: case 0xbc: case 0xbe:
case 0xb9: case 0xbb:
case 0xbc: case 0xbd: case 0xbe: case 0xbf:
case 0xd9: case 0xdd:
case 0xf9: case 0xfd:
if (lob == 0)
meta.maxCycles -= 1; // no page boundary crossed
if (lob == 0) meta.maxCycles -= 1; // no page boundary crossed
break;
// TODO: only VCS
case 0x85:
Expand Down Expand Up @@ -208,26 +226,27 @@ abstract class CodeAnalyzer6502 implements CodeAnalyzer {
return;
}
// add min/max instruction time to min/max clocks bound
if (debug) console.log("add", hex(pc), meta.minCycles, meta.maxCycles);
minclocks += meta.minCycles;
maxclocks += meta.maxCycles;
}
}

showLoopTimingForPC(pc:number) {
this.pc2minclocks = {};
this.pc2maxclocks = {};
this.pc2clockrange = {};
this.jsrresult = {};
// recurse through all traces
this.traceInstructions(pc | this.platform.getOriginPC(), this.START_CLOCKS, this.MAX_CLOCKS, 0, {});
}
}

// 76 cycles * 2 (support two scanline kernels)
// 76 cycles
export class CodeAnalyzer_vcs extends CodeAnalyzer6502 {
constructor(platform : Platform) {
super(platform);
this.MAX_CLOCKS = this.START_CLOCKS = 76*4; // 4 scanlines
this.WRAP_CLOCKS = false;
this.MAX_CLOCKS = 76; // 1 scanline
this.START_CLOCKS = 0; // TODO?
this.WRAP_CLOCKS = true;
}
}

Expand Down
7 changes: 4 additions & 3 deletions src/common/cpu/MOS6502.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1805,7 +1805,7 @@ export var _MOS6502 = function() {
2, 6, 0, 0, 4, 4, 4, 4, 2, 5, 2, 0, 0, 5, 0, 0,
2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 0, 4, 4, 4, 4,
2, 5, 0, 5, 4, 4, 4, 4, 2, 4, 2, 0, 4, 4, 4, 4,
2, 6, 0, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 3, 6,
2, 6, 0, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6,
2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 0, 7, 4, 4, 7, 7,
2, 6, 0, 8, 3, 3, 5, 5, 2, 2, 2, 0, 4, 4, 6, 6,
2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 0, 7, 4, 4, 7, 7
Expand Down Expand Up @@ -1989,6 +1989,7 @@ export class MOS6502 implements CPU, ClockBased, SavesState<MOS6502State>, Inter
isStable() : boolean {
return this.cpu.isPCStable();
}
// TODO: metadata
// TODO: disassembler
getOpcodeMetadata(op: number) {
return this.cpu.getOpcodeMetadata(op);
}
}
7 changes: 4 additions & 3 deletions src/ide/views/editors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,9 +325,10 @@ export class SourceEditor implements ProjectView {
if (this.sourcefile == null) return;
// show the lines
for (const line of Object.keys(this.sourcefile.line2offset)) {
var pc = this.sourcefile.line2offset[line];
var minclocks = result.pc2minclocks[pc];
var maxclocks = result.pc2maxclocks[pc];
let pc = this.sourcefile.line2offset[line];
let clocks = result.pc2clockrange[pc];
var minclocks = clocks && clocks.minclocks;
var maxclocks = clocks && clocks.maxclocks;
if (minclocks>=0 && maxclocks>=0) {
var s;
if (maxclocks == minclocks)
Expand Down
2 changes: 1 addition & 1 deletion src/platform/vcs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ class VCSPlatform extends BasePlatform {
bus.oldRead = bus.read;
bus.read = function(a) {
var v = this.oldRead(a);
if (a < 0x80) probe.logIORead(a,v);
if (a > 0 && a < 0x80) probe.logIORead(a,v); // (00),x reads $00?
else if (a > 0x280 && a < 0x300) probe.logIORead(a,v);
else probe.logRead(a,v);
return v;
Expand Down
72 changes: 64 additions & 8 deletions src/test/testanalysis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,73 @@ class Test6502Platform implements Platform {
}

describe('6502 analysis', function () {
it('Should analyze 6502', function () {
it('Should analyze WSYNC', 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);
console.log(analysis.pc2clockrange);
assert.equal(analysis.pc2clockrange[0x0].minclocks, 0);
assert.equal(analysis.pc2clockrange[0x0].maxclocks, 0);
assert.equal(analysis.pc2clockrange[0x1].minclocks, 2);
assert.equal(analysis.pc2clockrange[0x1].maxclocks, 19);
assert.equal(analysis.pc2clockrange[0x3].minclocks, 0);
assert.equal(analysis.pc2clockrange[0x3].maxclocks, 0);
});
it('Should analyze loop', function () {
let platform = new Test6502Platform();
platform.ram.set([0xea,0x4c,0x00,0x00]); // 5 cycles
let analysis = new CodeAnalyzer_vcs(platform);
analysis.showLoopTimingForPC(0x0);
console.log(analysis.pc2clockrange);
assert.equal(analysis.pc2clockrange[0x0].minclocks, 0);
assert.equal(analysis.pc2clockrange[0x0].maxclocks, 75);
// TODO: should be 0-75
/*
assert.equal(analysis.pc2clockrange[0x1].minclocks, 1);
assert.equal(analysis.pc2clockrange[0x1].maxclocks, 72);
*/
});
it('Should wrap clocks', function () {
let platform = new Test6502Platform();
platform.ram.set([0xb1,0,0xb1,0,0xb1,0,0xb1,0,0xb1,0,0xb1,0,0xb1,0,0xb1,0,0xb1,0,0xb1,0,0xb1,0,0xb1,0,0xb1,0,0xb1,0,0xea,0x4c,0,0]);
let analysis = new CodeAnalyzer_vcs(platform);
analysis.showLoopTimingForPC(0x0);
console.log(analysis.pc2clockrange);
// TODO: should be 0-75
assert.equal(analysis.pc2clockrange[0x0].minclocks, 0);
assert.equal(analysis.pc2clockrange[0x0].maxclocks, 75);
assert.equal(analysis.pc2clockrange[0x2].minclocks, 5);
assert.equal(analysis.pc2clockrange[0x2].maxclocks, 6);
});
it('Should wrap RTS', function () {
let platform = new Test6502Platform();
platform.ram.set([0xb1,0x60,0x20,1,0,0x4c,0,0]);
let analysis = new CodeAnalyzer_vcs(platform);
analysis.showLoopTimingForPC(0x0);
console.log(analysis.pc2clockrange);
// TODO: should be 0-75
assert.equal(analysis.pc2clockrange[0x0].minclocks, 0);
assert.equal(analysis.pc2clockrange[0x0].maxclocks, 75);
});
it('Should not recurse', function () {
let platform = new Test6502Platform();
platform.ram.set([0xea,0x20,0x07,0x00,0x4c,0x00,0x00, 0xa4,0x88,0xea,0xd0,0xfb,0x60]);
let analysis = new CodeAnalyzer_vcs(platform);
analysis.showLoopTimingForPC(0x0);
console.log(analysis.pc2clockrange);
// TODO: should be 0-75
assert.equal(analysis.pc2clockrange[0x0].minclocks, 0);
assert.equal(analysis.pc2clockrange[0x0].maxclocks, 75);
});
it('Should not break', function () {
let platform = new Test6502Platform();
platform.ram.set([0x85,0x02,0x85,0x2a,0xa9,0x00,0x85,0x26,0x85,0x1b,0x85,0x1c,0x4c,0x00,0x00]);
let analysis = new CodeAnalyzer_vcs(platform);
analysis.showLoopTimingForPC(0x0);
console.log(analysis.pc2clockrange);
// TODO: should be 0-75
assert.equal(analysis.pc2clockrange[0x0].minclocks, 0);
assert.equal(analysis.pc2clockrange[0x0].maxclocks, 17);
});
});
17 changes: 16 additions & 1 deletion src/test/testutil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
import assert from "assert";
import { describe } from "mocha";
import { EmuHalt } from "../common/emu"
import { lzgmini, isProbablyBinary } from "../common/util";
import { lzgmini, isProbablyBinary, hex } from "../common/util";
import { Tokenizer, TokenType } from "../common/tokenizer";
import { OPS_6502 } from "../common/cpu/disasm6502";
import { MOS6502 } from "../common/cpu/MOS6502";

var NES_CONIO_ROM_LZG = [
76, 90, 71, 0, 0, 160, 16, 0, 0, 11, 158, 107, 131, 223, 83, 1, 9, 17, 21, 22, 78, 69, 83, 26, 2, 1, 3, 0, 22, 6, 120, 216,
Expand Down Expand Up @@ -184,3 +186,16 @@ describe('EmuHalt', function () {
assert.ok(err.squelchError);
}
});

describe('CPU metadata', function () {
let cpu = new MOS6502();
for (let i=0; i<256; i++) {
let meta1 = OPS_6502[i];
let meta2 = cpu.getOpcodeMetadata(i);
if (meta1.il > 0) continue;
// assert.strictEqual(meta1.mn, meta2.mnenomic, `mnemonic ${hex(i)}: ${meta1.mn} != ${meta2.mnenomic}`);
assert.strictEqual(meta1.nb, meta2.insnlength, `insnLength ${hex(i)}: ${meta1.nb} != ${meta2.insnlength}`);
assert.strictEqual(meta1.c1, meta2.minCycles, `minCycles ${hex(i)}: ${meta1.c1} != ${meta2.minCycles}`);
assert.strictEqual(meta1.c1+meta1.c2, meta2.maxCycles, `maxCycles ${hex(i)}: ${meta1.c1+meta1.c2} != ${meta2.maxCycles}`);
}
});
2 changes: 1 addition & 1 deletion test/verilog/testverilog.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ var _fs = require('fs');
var _path = require('path')
var _cproc = require('child_process');
var fs = require('fs');
var wtu = require('./workertestutils.js');
var wtu = require('../cli/workertestutils.js');
//var heapdump = require("heapdump");
var commandExists = require('command-exists');

Expand Down

0 comments on commit d31a8c4

Please sign in to comment.