Skip to content

Commit ed30bf2

Browse files
committed
singlestep: filter and load test cases from files
1 parent 75e8f52 commit ed30bf2

3 files changed

Lines changed: 126 additions & 3 deletions

File tree

‎.github/workflows/test.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,4 @@ jobs:
4747
path: ext/65x02
4848

4949
- run: dub test :singlestep
50-
- run: dub run -b release :singlestep -- --dir="${{ github.workspace }}/ext/65x02"
50+
- run: make singlestep

‎Makefile‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ install:
5050
test:
5151
dub test
5252

53+
singlestep:
54+
dub run -b release :singlestep -- -o ea
55+
5356
.PHONY: all doc debug dist windist srcdist clean install test
5457

5558
.DELETE_ON_ERROR:

‎test/singlestep/source/app.d‎

Lines changed: 122 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,21 @@
2727
distribution.
2828
*/
2929

30+
import std.algorithm : canFind, filter, map;
3031
import std.array : appender;
31-
import std.file : exists;
32+
import std.conv : to;
33+
import std.file : exists, read;
3234
import std.format;
3335
import std.getopt;
36+
import std.parallelism : parallel;
3437
import std.path : buildPath;
38+
import std.range : array, iota, split;
3539
import std.stdio;
40+
import std.string : strip;
41+
import std.traits : EnumMembers;
3642

3743
import stdx.data.json;
44+
import xebin.emu;
3845

3946
/* Emits `TaggedAlgebraic.opEquals` into this object file.
4047
@@ -171,6 +178,78 @@ unittest
171178
assert(tests[0].cycles == [Access(59082, 177, false), Access(40, 160, true)]);
172179
}
173180

181+
struct Result
182+
{
183+
ubyte opcode;
184+
size_t total, failed;
185+
}
186+
187+
Result runOpcode(CpuVariant v)(string path, ubyte opcode)
188+
{
189+
Result r = { opcode: opcode };
190+
191+
// WAI and STP do not finish and so they have empty files
192+
const text = cast(string) read(path);
193+
if (!text.strip().length)
194+
return r;
195+
196+
foreach (ref t; parseTests(text))
197+
{}
198+
199+
return r;
200+
}
201+
202+
struct Target
203+
{
204+
string dir;
205+
CpuVariant cpu;
206+
}
207+
208+
immutable Target[] targets = [
209+
Target("6502", CpuVariant.mos_6502),
210+
Target("synertek65c02", CpuVariant.wdc_65c02),
211+
Target("rockwell65c02", CpuVariant.rockwell_r65c02),
212+
Target("wdc65c02", CpuVariant.wdc_w65c02s),
213+
];
214+
215+
size_t runTarget(CpuVariant v)(string dir, const(ubyte)[] opcodes)
216+
{
217+
auto results = new Result[opcodes.length];
218+
foreach (i, opcode; opcodes.parallel)
219+
{
220+
const path = buildPath(dir, format("%02x.json", opcode));
221+
results[i] = exists(path)
222+
? runOpcode!v(path, opcode)
223+
: Result(opcode);
224+
}
225+
226+
size_t failed;
227+
return failed;
228+
}
229+
230+
ubyte[] parseOpcodes(string spec)
231+
{
232+
if (!spec.length)
233+
return iota(256).map!(i => cast(ubyte) i).array;
234+
bool[256] set;
235+
foreach (part; spec.split(","))
236+
{
237+
const range = part.split("-").map!(a => a.to!uint(16)).array;
238+
const lo = range[0];
239+
const hi = range.length > 1 ? range[1] : lo;
240+
foreach (i; lo .. hi + 1)
241+
set[i] = true;
242+
}
243+
return iota(256).filter!(i => set[i]).map!(i => cast(ubyte) i).array;
244+
}
245+
246+
unittest
247+
{
248+
assert(parseOpcodes("a9") == [0xa9]);
249+
assert(parseOpcodes("b1-b5,00") == [0x00, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5]);
250+
assert(parseOpcodes("").length == 256);
251+
}
252+
174253
string findSuite(string dir)
175254
{
176255
if (dir.length)
@@ -184,8 +263,13 @@ string findSuite(string dir)
184263
int main(string[] args)
185264
{
186265
string dir;
266+
string cpuSpec;
267+
string opcodeSpec;
268+
187269
auto help = getopt(args,
188-
"d|dir", "root of SingleStepTests/65x02", &dir);
270+
"d|dir", "root of SingleStepTests/65x02", &dir,
271+
"c|cpu", "CPUs to test, comma separated (default: all found).", &cpuSpec,
272+
"o|opcodes", "Opcodes in hex, e.g. a9,1e,b1-b5 (default: all).", &opcodeSpec);
189273

190274
if (help.helpWanted)
191275
{
@@ -202,5 +286,41 @@ int main(string[] args)
202286
return 2;
203287
}
204288

289+
const opcodes = parseOpcodes(opcodeSpec);
290+
const cpus = cpuSpec.length ? cpuSpec.split(",") : null;
291+
292+
size_t failed;
293+
size_t ran;
294+
foreach (target; targets)
295+
{
296+
if (cpus !is null && !cpus.canFind(target.dir) &&
297+
!cpus.canFind(target.cpu.to!string))
298+
continue;
299+
const dataDir = buildPath(root, target.dir, "v1");
300+
if (!exists(dataDir))
301+
continue;
302+
++ran;
303+
writefln("%s (%s)", target.cpu, target.dir);
304+
stdout.flush();
305+
dispatch: switch (target.cpu)
306+
{
307+
static foreach (v; EnumMembers!CpuVariant)
308+
{
309+
case v:
310+
failed += runTarget!v(dataDir, opcodes);
311+
break dispatch;
312+
}
313+
default:
314+
assert(0);
315+
}
316+
}
317+
318+
if (!ran)
319+
{
320+
stderr.writefln("nothing to run -- no family member under %s matched --cpu", root);
321+
return 2;
322+
}
323+
writefln("%d opcode%s with failures", failed, failed == 1 ? "" : "s");
324+
205325
return 0;
206326
}

0 commit comments

Comments
 (0)