Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,26 @@ jobs:
- name: Run tests
shell: bash
run: dub test

singlestep:
name: Single Step Tests
runs-on: ubuntu-latest
steps:
- name: Checkout xebin
uses: actions/checkout@v7
with:
submodules: recursive

- name: Install D compiler
uses: dlang-community/setup-dlang@v2
with:
compiler: ldc

- name: Checkout SingleStepTests/65x02
uses: actions/checkout@v7
with:
repository: SingleStepTests/65x02
path: ext/65x02

- run: dub test :singlestep
- run: make singlestep
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@
/*.obj
/libxebin.a
/cputest/bin/
dub.selections.json
/test/singlestep/singlestep
/test/singlestep/xebin-singlestep-test-application
/ext/65x02/
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
VERSION = 1.1.0
SOURCES = $(addprefix source/xebin/,flashpack.d binary.d disasm.d vm.d xasm.d) \
source/app.d
SOURCES = $(wildcard source/xebin/*.d) source/app.d
ASCIIDOC = asciidoc -o $@ -a doctime
ASCIIDOC_POSTPROCESS =
ZIP = 7z a -mx=9 -tzip $@
Expand Down Expand Up @@ -50,6 +49,10 @@ install:
test:
dub test

singlestep:
dub run -b release :singlestep


.PHONY: all doc debug dist windist srcdist clean install test

.DELETE_ON_ERROR:
22 changes: 14 additions & 8 deletions README.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ Disassembling binary file
~~~~~~~~~~~~~~~~~~~~~~~~~

----------------------------
$ xebin d[isasm] [-o=fn] [input_files]
$ xebin d[isasm] [-c=cpu] [-o=fn] [input_files]
----------------------------

Each block is disassembled as code, preceded by an +ORG+ directive with its load
Expand All @@ -131,10 +131,13 @@ run and init addresses. Addresses outside the loaded blocks are defined with +EQ
the beginning of the output, and a reference pointing into the middle of an instruction
is printed as an offset from that instruction's label, e.g. +L2103+1+.

Opcode names and argument syntax are taken from http://atari800.sourceforge.net/[Atari 800]
emulator. Bytes that do not decode into an official 6502 instruction, as well as an
instruction truncated by the end of a block, are emitted as +DTA+, so that the whole
output can be fed back to http://atariarea.krap.pl/x-asm/[xasm] and assembled into
Option +-c=cpu+ or +--cpu=cpu+ selects the instruction set: +6502+ (NMOS, the default),
+65c02+ (WDC 65C02), +r65c02+ (Rockwell R65C02, adding +RMB+/+SMB+/+BBR+/+BBS+) or
+w65c02s+ (adding +WAI+ and +STP+).

Bytes that do not decode into a documented instruction of the selected CPU, as well as
an instruction truncated by the end of a block, are emitted as +DTA+, so that the whole
output can be fed back to https://github.com/pfusik/xasm[xasm] and assembled into
the original file.

Example output:
Expand All @@ -161,17 +164,20 @@ Running binary file
~~~~~~~~~~~~~~~~~~~

----------------------------
$ xebin r[un] [--trace=what] [input_file]
$ xebin r[un] [-c=cpu] [--trace=what] [input_file]
----------------------------

The file is loaded into memory of a simple built-in emulator and run: each +INIT+
routine is called as soon as the block setting it is loaded, and the +RUN+ routine
is called once the whole file is loaded. Only the first input file is used.

Only the CPU (NMOS 6502) and a few OS entry points are emulated. There is neither OS
Only the CPU and a few OS entry points are emulated. There is neither OS
ROM nor any hardware, so this is useful for programs that compute and do character
I/O, and not for anything that touches ANTIC, POKEY or GTIA.

Option +-c=cpu+ or +--cpu=cpu+ selects the emulated CPU, with the same choices as
for +disasm+: +6502+ (NMOS, the default), +65c02+, +r65c02+ or +w65c02s+.

+CIOV+ ($E456) is served by the host:

- IOCB #0 is the console: get record reads a line from +stdin+, put record and put
Expand Down Expand Up @@ -267,7 +273,7 @@ unreleased::
Added +run+ command, which loads and executes a binary file in a built-in emulator,
with OS calls served by the host.
The emulator implements all 65x02 variants (NMOS 6502, 65C02, Rockwell R65C02 and W65C02S)
with cycle-exact timing; the +run+ command uses the NMOS 6502.
with cycle-exact timing.
Added +--trace+ option, which reports what the +run+ command loads, executes
and asks CIO for.
Disassembler now creates labels for addresses referenced inside the disassembled code.
Expand Down
2 changes: 2 additions & 0 deletions dub.sdl
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ copyright "Copyright © 2010-2014, 2017, 2023, 2026 Adrian Matoga"
license "Zlib"

dependency "xasm" path="ext/xasm"

subPackage "test/singlestep"
48 changes: 41 additions & 7 deletions source/app.d
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,29 @@ bool verbose;
string outputFile;
bool disableOs;
bool removeHeader;
Cpu cpu = Cpu.mos_6502;

void parseCpu(string option, string value)
{
switch (value.toLower())
{
case "6502":
cpu = Cpu.mos_6502;
break;
case "65c02":
cpu = Cpu.wdc_65c02;
break;
case "r65c02":
cpu = Cpu.rockwell_r65c02;
break;
case "w65c02s":
cpu = Cpu.wdc_w65c02s;
break;
default:
throw new Exception("Unknown CPU `" ~ value
~ "'. Available CPUs: 6502, 65c02, r65c02, w65c02s");
}
}

immutable VERSION_STRING = "1.1.0";

Expand Down Expand Up @@ -205,7 +228,7 @@ void disassembly(string[] args)
if (args.length > 3)
of.writeln("; ", file.name, ":");

foreach (line; BinaryFileReader(file).readFile().disassemble)
foreach (line; BinaryFileReader(file).readFile().disassemble(cpu))
{
of.writeln(line);
}
Expand All @@ -224,10 +247,18 @@ private void runEmulator(E)(BinaryBlock[] blocks)
void run(string[] args)
{
auto blocks = BinaryFileReader(InputFiles(args).front).readFile();
if (tracing(Trace.cpu))
runEmulator!(Emulator!(CpuVariant.mos_6502, CpuTracer))(blocks);
else
runEmulator!(Emulator!())(blocks);
final switch (cpu)
{
static foreach (c; EnumMembers!Cpu)
{
case c:
if (tracing(Trace.cpu))
runEmulator!(Emulator!(c, CpuTracer))(blocks);
else
runEmulator!(Emulator!(c))(blocks);
return;
}
}
}

void printHelp(string[] args)
Expand All @@ -244,8 +275,8 @@ void printHelp(string[] args)
" r[emove] [-n=pos] [-o=fn] [-v] remove block from file\n" ~
" i[nsert] [-n=pos] [-a=ad] [-o=fn] [-v] insert block into file\n" ~
" o[ptimize] [-o=fn] [-i] optimize file\n +/
" d[isasm] [-o=fn] disassemble blocks\n" ~
" r[un] [--trace=what] run in a simple emulator\n" ~
" d[isasm] [-c=cpu] [-o=fn] disassemble blocks\n" ~
" r[un] [-c=cpu] [--trace=what] run in a simple emulator\n" ~
" p[ack] [-a=ad] [-s] [-o=fn] [-v] pack using FlashPack algorithm\n" ~
" u[npack] [-o=fn] [-v] unpack FlashPack'd file\n" ~
" h[elp] print this message\n" ~
Expand All @@ -256,6 +287,8 @@ void printHelp(string[] args)
" (use '-' for end address)\n" ~
" -n|--position=pos which block to extract (indexed from 0)\n" ~
" -r|--raw remove header from extracted block\n" ~
" -c|--cpu=type disassemble or run for the given CPU: 6502\n" ~
" (default), 65c02, r65c02 or w65c02s\n" ~
// " -i|--ignore-order do not preserve order of block if it makes\n" ~
// " optimized file shorter\n" ~
// " -n|--position=pos set block position for extract, delete, insert\n" ~
Expand Down Expand Up @@ -370,6 +403,7 @@ int main(string[] args)
config.caseSensitive,
config.noBundling,
"s|disable-os", &disableOs,
"c|cpu", &parseCpu,
"trace", &parseTraces,
"a|address", &strAddr,
"n|position", &position,
Expand Down
42 changes: 42 additions & 0 deletions source/xebin/cpu.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/** CPU versions and features

Author: Adrian Matoga [email protected]

zlib License:

This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not
be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
module xebin.cpu;

///
enum Cpu {
mos_6502, /// NMOS
wdc_65c02, /// original CMOS (also Synertek, GTE, etc.), part of Lynx's Mikey
rockwell_r65c02, /// Rockwell (bit ops)
wdc_w65c02s, /// modern W65C02S (bit ops + WAI/STP)
}

/// Basic CMOS instruction set + BCD and JMP (abs) fixes.
enum bool isCmos(Cpu v) = v != Cpu.mos_6502;

/// RMBn/SMBn/BBRn/BBSn. Rockwell's addition, carried over into the W65C02S.
enum bool hasBitOps(Cpu v) =
v == Cpu.rockwell_r65c02 || v == Cpu.wdc_w65c02s;

/// WAI and STP, added by the W65C02S; NOPs everywhere else.
enum bool hasWaiStp(Cpu v) = v == Cpu.wdc_w65c02s;
Loading