|
|
||
|---|---|---|
| progs | progs/basic: add BASIC port | |
| tests | tests: adjust for new MMIO ranges and fix broken | |
| .gitignore | README updates | |
| 6502.sh | README updates | |
| build.sh | missing opcodes and fixes | |
| machine.sh | missing opcodes and fixes | |
| README.md | README updates | |
| shellcheck.sh | Initial commit | |
| test.sh | missing opcodes and fixes | |
| watch.sh | missing opcodes and fixes | |
./6502.sh
6502.sh is a fully-functional 6502 emulator and debugger written in busybox ash compliant shell script, using only a handful of busybox tools.
Features
6502.sh has a whopping 32k of RAM and 16k ROM in its default configuration,
however this can be easily adjusted by editing machine.sh.
It includes an interactive debugger with single-stepping, breakpoints (break on code, data access, JSR/RTS), and more. See #Debugger for detailed instructions.
STDIO is directed to an ACIA compatible serial port at 8400ドル allowing for
programs like BASIC to run.
Launching with the -d flag will make 6502.sh output additional info about the
instructions being executed to a socket (/tmp/65sh.sock). You can watch this
log by running ./watch.sh.
Requirements
The dasm compiler is required for building wozmon and the unit tests. cc65 is required for BASIC.
Usage
$ ./6502.sh ./progs/basic/basic.bin
Loading ./progs/basic/basic.bin...
Reset vector: $E836
6502 EhBASIC [C]old/[W]arm ? c
Memory size ? 32768
31999 Bytes free
Enhanced BASIC 2.22p5
Ready
10 PRINT "HI FROM 6502.SH"
20 GOTO 10
RUN
HI FROM 6502.SH
HI FROM 6502.SH
HI FROM 6502.SH
HI FROM 6502.SH
> Dropping into debug monitor
Status: 36ドル
negative : 0
overflow : 0
constant : 1
break : 1
decimal : 0
interrupt: 1
zero : 1
carry : 0
Registers:
A : 00ドル
X : $DF
Y : 02ドル
SP: $FF
PC: $C4C3
Ran 31204 instructions
65sh>
Unit tests can be run with test.sh, they live in the tests
subdirectory.
Some example programs can be found in progs:
- wozmon - A port of the Apple I monitor
- basic - Enhanced BASIC port (compile with
make, cc65 is required)
Any DASM compatible assembly can be built with ./build.sh path/to/source.asm,
the resulting binary is placed in build/, e.g.
$ ./build.sh progs/wozmon.asm
Complete. (0)
TODO
- Emulate more hardware? Disk?
- Plugin system for hardware modules
- Performance optimisations (JIT to shell?)
Serial port
An ACIA compatible serial port is emulated. Tx delay loops are NOT recommended here as they only serve to slow the emulator down.
For each opcode executed, the emulator checks for input on stdin and buffers a
single character that can be read from 8400ドル by the emulated program. Output
is available by writing a character to the same address. You can check if a
character is pending by reading 8401ドル and checking bit 3 (value 08ドル).
Additional registers
| Address | Name | R/W | Description |
|---|---|---|---|
8010ドル |
ASSERT | WO | Writes will trigger a test assert. This is used for unit tests which describe the expected state of the machine at the start of the test ROM. |
8040ドル |
HALT | WO | Writes will halt execution and drop to the debugger. |
Trap
Writes of any value to 8040ドル act as a trap or breakpoint and cause the
emulator to pause execution and drop to the monitor (described below).
Alternatively, executing any branching instruction that sets the program counter to point to itself (causing an infinite loop) will also cause a trap.
Finally, pressing the backtick key (```) will always cause the emulator to trap.
In call cases the emulator will drop to the debugger 65sh>.
Debugger
6502.sh includes a built-in monitor/debugger which you can drop to by pressing
the backtick '`' key on your keyboard or via a trap in the program (described
above). You can also halt before execution by launching 6502.sh with the -d
flag.
NOTE: Unlike in GDB, ctrl+c will cause the emulator to exit rather than trap into the debugger.
Loading ./build/fibonacci.bin...
Reset vector: 8009ドル
Registers:
A : 00ドル
X : 00ドル
Y : 00ドル
PC: 8009ドル
Ran 0 instructions
65sh>
Debug socket
Run with the -d flag to enable the debug socket, and run ./watch.sh in a
separate terminal. This will output the fetch/decode/execute internals of the
CPU.
The format is PC: opcode | addressing mode | instruction <arguments>.
C90B: 20ドル | ABS $E0FC | JSR $E0FC
E0FC: 6ドルC | IND (=207ドル ) | JMP $E865
E865: 48ドル | IMP | PHA (SP=1ドルF9)
E866: $AD | ABS 8401ドル | LDA 8401ドル <- 00ドル
E869: 68ドル | IMP | PLA (A=39ドル SP=1ドルF9)
E86A: 8ドルD | ABS 8400ドル | STA 8400ドル <- 39ドル
E86D: 60ドル | IMP | RTS $C90E
C90E: $C9 | IMM $C90F | CMP 39ドル 0ドルD = 2ドルC
C910: $D0 | REL $C911 | BNE (Z=0) 14ドル (20)
C926: 29ドル | IMM $C927 | AND $FF
C928: 60ドル | IMP | RTS $C8E4
C8E4: $C8 | IMP | INY 04ドル
C8E5: $CA | IMP | DEX 01ドル
C8E6: $D0 | REL $C8E7 | BNE (Z=0) $F7 (-9)
C8DF: $B1 | (71),4 = 7ドルFFF | LDA 7ドルFFF <- 39ドル
C8E1: 20ドル | ABS $C8EE | JSR $C8EE
C8EE: $C9 | IMM $C8EF | CMP 39ドル 20ドル = 19ドル
C8F0: 90ドル | REL $C8F1 | BCC (C=1) 19ドル (25)
C8F2: 48ドル | IMP | PHA (SP=1ドルFB)
C8F3: $A5 | ZER $F | LDA $F <- 00ドル
C8F5: $D0 | REL $C8F6 | BNE (Z=1) 0ドルA (10)
C8F7: $A5 | ZER $E | LDA $E <- 04ドル
The debug monitor uses a GDB-like syntax, the most useful commands are:
dt- toggle debug outputp- dump CPU registersc- continue executions- step a single instructionb,del ID- add and remove breakpoints (note that the PC must be set to the address in question for the breakpoint to be hit).bmem,del mem ID- add/remove breakpoints based on memory access.
Logging/self-debug
These commands configure logging or help debug the emulator itself.
v- enable verbose mode (set -x)d [CATEGORIES]- list available debug categories (no args), or set debug categories.dt- toggle standard debug output on/off (standard categories areINSTR,ADDR,OPCODE)
Breakpoints
These commands manipulate breakpoints.
b ADDR- break just before the instruction atADDRis executedbmem ADDR- break immediately after any read or write toADDRdel [mem] ID- remove breakpoint with numberID(or memory breakpointID).-
i,info- list breakpoints
Machine state
These commands allow for viewing or manipulating the machine state.
p [VAR]- print all CPU registers or a specific one (printed as hex)echo VAR-echoset VAR VAL- set a variable (this can be any global, usuallypc,a,x,y,s- program counter, CPU registers, stack pointer, etc)ps- print status flagsstack- show info about the stack and stack pointerc- continue execution
These are the available global variables (that are useful to modify), though you may find joy in manipulating some of the others:
pc- Program counters- Stack pointera,x,y- CPU registersp- status register (note that there are also variables for accessing the individual status flags, are kept in sync with the status register and it's recommended to treat them as read-only. The uppercase variants are bitmasks).
Debug categories
- INSTR - print info about the running instruction
- MEM - show memory accesses
- ADDR - show addressing modes / address decoding
- OPCODE - show opcodes
- STATUS - show changes to status bits