mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-12-21 06:29:48 +00:00
tweaks to analysis, fixed verilog ball reset bug
This commit is contained in:
parent
3a08c24869
commit
d31a8c4efe
@ -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
|
||||
|
@ -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;
|
||||
//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;
|
||||
}
|
||||
//console.log(hex(pc),minclocks,maxclocks,modified,meta,constraints);
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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}`);
|
||||
}
|
||||
});
|
||||
|
@ -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');
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user