git.haldean.org x6502 / 5734a2a
Polish up vterm, improve docs, change baseaddr. The default base address has been changed to 0x1000; this is the default base address for most 6502 compilers out there. Will Haldean Brown 7 years ago
9 changed file(s) with 74 addition(s) and 35 deletion(s). Raw diff Collapse all Expand all
66
77 x6502 is an emulator for the 6502 class of processors.
88 It currently supports the full instruction set of the
9 6502 (plus a few extensions) and has an extremely
10 rudimentary simulated I/O bus. It should be able to run
11 arbitrary x6502 bytecode with ``correct'' results,
12 although most binaries for common 6502 systems (Atari,
13 C64, Apple II, etc) won't function as expected, since
14 they expect I/O devices to be mapped into memory where
15 there are currently none.
9 6502 (plus a few extensions) and has a rudimentary
10 simulated I/O bus. It should be able to run arbitrary
11 x6502 bytecode with ``correct'' results, although most
12 binaries for common 6502 systems (Atari, C64, Apple II,
13 etc) won't function as expected, since they expect I/O
14 devices to be mapped into memory where there are
15 currently none.
1616
1717 x6502 is freely available under the original 4-clause
1818 BSD license, the full text of which is included in the
3131 bytecode, see below). You can use any 6502 assembler to
3232 compile to 6502 bytecode; `xa' is one that is bundled
3333 with Debian-based distros. Note that, by default, x6502
34 loads code in at address 0x00; you therefore need to
34 loads code in at address 0x1000; you therefore need to
3535 either tell your assembler that that's the base address
3636 for the text section of your binary or override the
37 default load address using the `-b' flag of x6502.
37 default load address using the `-b' flag of x6502. Note
38 that 0x1000 is the default load address for the `xa'
39 assembler.
3840
3941 If you want to compile a version of x6502 that dumps
4042 machine state after every instruction, run `make debug'
4143 instead of `make'. This will also disable compiler
42 optimizations.
44 optimizations. This mode really does not play nice with
45 vterm mode, so be warned.
4346
4447 Extensions to the 6502 instruction set
4548
6265
6366 I/O memory map:
6467
65 There are only two I/O devices right now: a character
66 input device and a character output device. Convenience
67 constants and macros for all I/O devices are defined in
68 `stdlib/stdio.s' for use in user programs. Add stdlib to
69 your include path and then add `#include <stdio.s>' to
70 your program to use these constants.
71
68 There are three I/O devices right now: a character input
69 device, a character output device and a virtual
70 terminal. Convenience constants and macros for the
71 character I/O devices are defined in `stdlib/stdio.s'
72 for use in user programs. Add stdlib to your include
73 path and then add `#include <stdio.s>' to your program
74 to use these constants.
75
76 I/O options are controlled by setting bits on the I/O
77 flag byte at address 0xFF02. The current set of
78 supported flags are:
79
80 VTERM_ENABLE (0x01):
81 when set, activates vterm mode.
82 WAIT_HALT (0x02):
83 when set, waits for a keypress input before
84 terminating upon receiving an EXT instruction.
85 Non-vterm applications will probably want to set
86 this flag, as some implementations of ncurses
87 will clear the display when the emulator exits.
88
89 I/O devices:
90
7291 The character output device is mapped to FF00. Any
7392 character written to FF00 is immediately echoed to the
7493 terminal.
8099 interrupt; if the user types ``abc'', they will get
81100 three interrupts one after the other.
82101
83 A commented example of how to use the I/O capabilities
84 of x6502 is provided in sample_programs/echo.s.
102 The virtual terminal is activated by setting the
103 VTERM_ENABLE bit on the IO flag byte. After the flag is
104 set, the data in memory addresses 0xFB00 through 0xFEE7
105 are mapped to a 40x25 grid in the host terminal. Data in
106 this region is stored in row-major format, and any write
107 will trigger a refresh of the vterm.
108
109 Note that even in vterm-mode, the putchar-esque
110 character output device is still usable, and will put
111 the character at the position directly after the
112 position of the last write.
113
114 A commented example of how to use the character I/O
115 capabilities of x6502 is provided in
116 sample_programs/echo.s, and an example of a vterm
117 application is provided in sample_programs/spam.s
85118
86119 Reading the source
87120
90123 constants used throughout the code (mostly around CPU
91124 flags) as well as the `cpu' struct, which is used pretty
92125 much everywhere.
93
126
94127 `emu.c' is where the interesting stuff happens; this is
95128 the main loop of the emulator where opcodes are decoded
96129 as dispatched. It also handles interrupts and calls out
138171 TODO:
139172 - support buffered input, where the program can read
140173 more than one input character at once.
141 - graphics? definitely more I/O.
174 - paint options in vterm mode. Now that we're using
175 ncurses, it's way easier to do pretty output.
142176
143177 THANKS:
144178 - voltagex on Github for sending me a patch to improve
145179 the sample_programs readme.
146180 - anatoly on HN for suggesting I add a bit on source
147181 code structure to the README.
182 - shalmanese for coffee and pie.
11 #include <stdlib.h>
22 #include <string.h>
33
4 cpu * new_cpu() {
4 cpu * new_cpu(uint16_t pc_start) {
55 cpu *m = malloc(sizeof(cpu));
6 m->pc = 0x00;
6 m->pc = pc_start;
77 m->sr = FLAG_INTERRUPT;
88 m->sp = 0xFF;
99 m->interrupt_waiting = 0x00;
3838 uint8_t last_opcode;
3939 } cpu;
4040
41 cpu * new_cpu();
41 cpu * new_cpu(uint16_t pc_start);
4242
4343 #endif
1818 void finish_vterm();
1919
2020 void init_io() {
21 initscr();
2122 cbreak();
2223 noecho();
2324 nodelay(stdscr, TRUE);
2526 }
2627
2728 void finish_io() {
29 if (io_modeflags & IO_MODEFLAG_WAIT_HALT) {
30 nodelay(stdscr, FALSE);
31 printw("\nterminated, press any key to exit.\n");
32 getch();
33 }
34
2835 if (io_modeflags & IO_MODEFLAG_VTERM) {
2936 finish_vterm();
3037 }
3138 }
3239
3340 void init_vterm() {
34 initscr();
35 // reinit IO after initializing the ncurses window
36 init_io();
37
3841 window = newwin(VTERM_ROWS + 2, VTERM_COLS + 2, 0, 0);
3942 box(window, 0, 0);
4043 }
8285 wprintw(window, "%c", m->mem[addr]);
8386 wrefresh(window);
8487 } else {
85 printf("%c", m->mem[addr]);
88 addch(m->mem[addr]);
8689 }
8790 } else if (addr == IO_MODEFLAGS) {
8891 update_modeflags(io_modeflags, m->mem[IO_MODEFLAGS]);
88 #define IO_VTERM_START 0xFB00
99 #define IO_VTERM_END 0xFF00
1010
11 #define IO_MODEFLAG_VTERM 0x01
11 #define IO_MODEFLAG_VTERM 0x01
12 #define IO_MODEFLAG_WAIT_HALT 0x02
1213
1314 void init_io();
1415 void finish_io();
1515 }
1616
1717 int main(int argc, char *argv[]) {
18 int base_addr = 0;
18 int base_addr = 0x1000;
1919
2020 int c;
2121 while ((c = getopt(argc, argv, "hb:")) != -1) {
4646 FILE *in_f = fopen(argv[optind], "rb");
4747 int b;
4848 int i = base_addr;
49 cpu *m = new_cpu();
49 cpu *m = new_cpu(base_addr);
5050 while ((b = fgetc(in_f)) != EOF) {
5151 m->mem[i++] = (uint8_t) b;
5252 }
00 Test 6502 assembler programs. Generate binaries with:
11
2 xa -w -bt0 -Istdlib/ sample_programs/test.s
2 xa -w -bt 0x0100 -Istdlib/ sample_programs/test.s
33
44 xa is a cross-assembler and utility suite for 65xx series processors. It's
55 included in many package repositories as `xa65', including Homebrew on OSX in
0 lda #$01 ; set WAIT_TERMINATE flag
0 lda #$02 ; set WAIT_TERMINATE flag
11 sta $FF02
22 lda #$41 ; 41 == 'A'
33 sta $FF00
99 cli
1010
1111 ; wait for input
12 loop nop
12 loop wai
1313 jmp loop
1414
1515 ; start ISR