tweaks to analysis, fixed verilog ball reset bug

This commit is contained in:
Steven Hugg 2023-11-06 10:35:50 -06:00
parent 3a08c24869
commit d31a8c4efe
8 changed files with 154 additions and 58 deletions

View File

@ -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);
@ -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
@ -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

View File

@ -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;
}
@ -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;
@ -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;
@ -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:
@ -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;
}
}

View File

@ -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
@ -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);
}
}

View File

@ -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)

View File

@ -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;

View File

@ -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);
});
});

View File

@ -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,
@ -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}`);
}
});

View File

@ -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');