mirror of
https://github.com/RevCurtisP/C02.git
synced 2024-11-22 01:31:33 +00:00
Initial commit of lib6502
This commit is contained in:
parent
501944f525
commit
88fd3c7dda
9
lib6502/config.h
Normal file
9
lib6502/config.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef __config_h
|
||||
#define __config_h
|
||||
|
||||
#define PACKAGE_NAME "lib6502"
|
||||
#define PACKAGE_VERSION "1.0"
|
||||
#define PACKAGE_BUGREPORT "firstName (at) lastName (dot) com"
|
||||
#define PACKAGE_COPYRIGHT "Copyright (c) 2005 Ian Piumarta"
|
||||
|
||||
#endif /* __config_h */
|
107
lib6502/examples/lib1.c
Normal file
107
lib6502/examples/lib1.c
Normal file
@ -0,0 +1,107 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lib6502.h"
|
||||
|
||||
/* Emulated OS functions. */
|
||||
|
||||
#define WRCH 0xFFEE /* Write accumulator to stdout. */
|
||||
|
||||
/* Write the accumulator to stdout. This function will be invoked
|
||||
* when the emulated program calls 0xFFEE.
|
||||
*/
|
||||
int wrch(M6502 *mpu, uint16_t address, uint8_t data)
|
||||
{
|
||||
int pc;
|
||||
|
||||
/* Write the character.
|
||||
*/
|
||||
putchar(mpu->registers->a);
|
||||
|
||||
/* We arrived here from a JSR instruction. The stack contains the
|
||||
* saved PC. Pop it off the stack.
|
||||
*/
|
||||
pc = mpu->memory[++mpu->registers->s + 0x100];
|
||||
pc |= mpu->memory[++mpu->registers->s + 0x100] << 8;
|
||||
|
||||
/* The JSR instruction pushes the value of PC before it has been
|
||||
* incremented to point to the instruction after the JSR. Return PC
|
||||
* + 1 as the address for the next insn. Returning non-zero
|
||||
* indicates that we handled the 'subroutine' ourselves, and the
|
||||
* emulator should pretend the original 'JSR' neveer happened at
|
||||
* all.
|
||||
*/
|
||||
return pc + 1; /* JSR pushes next insn addr - 1 */
|
||||
}
|
||||
|
||||
|
||||
/* Exit gracefully. We arrange for this function to be called when
|
||||
* the emulator tries to transfer control to address 0.
|
||||
*/
|
||||
int done(M6502 *mpu, uint16_t address, uint8_t data)
|
||||
{
|
||||
char buffer[64];
|
||||
|
||||
/* Dump the internal state of the processor.
|
||||
*/
|
||||
M6502_dump(mpu, buffer);
|
||||
|
||||
/* Print a cute message and quit.
|
||||
*/
|
||||
printf("\nBRK instruction\n%s\n", buffer);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
M6502 *mpu = M6502_new(0, 0, 0); /* Make a 6502 */
|
||||
unsigned pc = 0x1000; /* PC for 'assembly' */
|
||||
|
||||
/* Install the two callback functions defined above.
|
||||
*/
|
||||
M6502_setCallback(mpu, call, WRCH, wrch); /* Calling FFEE -> wrch() */
|
||||
M6502_setCallback(mpu, call, 0, done); /* Calling 0 -> done() */
|
||||
|
||||
/* A few macros that dump bytes into the 6502's memory.
|
||||
*/
|
||||
# define gen1(X) (mpu->memory[pc++]= (uint8_t)(X))
|
||||
# define gen2(X,Y) gen1(X); gen1(Y)
|
||||
# define gen3(X,Y,Z) gen1(X); gen2(Y,Z)
|
||||
|
||||
/* Hand-assemble the program.
|
||||
*/
|
||||
gen2(0xA2, 'A' ); // LDX #'A'
|
||||
gen1(0x8A ); // TXA
|
||||
gen3(0x20,0xEE,0xFF); // JSR FFEE
|
||||
gen1(0xE8 ); // INX
|
||||
gen2(0xE0, 'Z'+1 ); // CPX #'Z'+1
|
||||
gen2(0xD0, -9 ); // BNE 0x1002
|
||||
gen2(0xA9, '\n' ); // LDA #'\n'
|
||||
gen3(0x20,0xEE,0xFF); // JSR FFEE
|
||||
gen2(0x00,0x00 ); // BRK
|
||||
|
||||
/* Just for fun: disssemble the program.
|
||||
*/
|
||||
{
|
||||
char insn[64];
|
||||
uint16_t ip= 0x1000;
|
||||
while (ip < pc)
|
||||
{
|
||||
ip += M6502_disassemble(mpu, ip, insn);
|
||||
printf("%04X %s\n", ip, insn);
|
||||
}
|
||||
}
|
||||
|
||||
/* Point the RESET vector at the first instruction in the assembled
|
||||
* program.
|
||||
*/
|
||||
M6502_setVector(mpu, RST, 0x1000);
|
||||
|
||||
/* Reset the 6502 and run the program.
|
||||
*/
|
||||
M6502_reset(mpu);
|
||||
M6502_run(mpu);
|
||||
M6502_delete(mpu); /* We never reach here, but what the hey. */
|
||||
|
||||
return 0;
|
||||
}
|
350
lib6502/html/lib6502.html
Normal file
350
lib6502/html/lib6502.html
Normal file
@ -0,0 +1,350 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<!-- saved from url=(0056)https://www.piumarta.com/software/lib6502/lib6502.3.html -->
|
||||
<html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
|
||||
|
||||
<meta name="generator" content="tty2html http://piumarta.com/software">
|
||||
<title>lib6502(3)</title></head>
|
||||
<body>
|
||||
<pre>LIB6502(3) BSD Library Functions Manual LIB6502(3)
|
||||
|
||||
<b>NAME</b>
|
||||
<b>lib6502</b> - 6502 microprocessor emulator
|
||||
|
||||
<b>SYNOPSIS</b>
|
||||
<b>#include</b> <b><stdint.h></b>
|
||||
<b>#include</b> <b><lib6502.h></b>
|
||||
|
||||
<u>M6502</u> <u>*</u>
|
||||
<b>M6502_new</b>(<u>M6502</u><b>_</b><u>Registers</u> <u>*registers</u>, <u>M6502</u><b>_</b>M<u>emory</u> <u>memory</u>,
|
||||
<u>M6502</u><b>_</b><u>Callbacks</u> <u>*callbacks</u>);
|
||||
|
||||
<u>void</u>
|
||||
<b>M6502_reset</b>(<u>M6502</u> <u>*mpu</u>);
|
||||
|
||||
<u>void</u>
|
||||
<b>M6502_nmi</b>(<u>M6502</u> <u>*mpu</u>);
|
||||
|
||||
<u>void</u>
|
||||
<b>M6502_irq</b>(<u>M6502</u> <u>*mpu</u>);
|
||||
|
||||
<u>uint16</u><b>_</b><u>t</u>
|
||||
<b>M6502_getVector</b>(<u>M6502</u> <u>*mpu</u>, <u>vector</u>);
|
||||
|
||||
<u>uint16</u><b>_</b><u>t</u>
|
||||
<b>M6502_setVector</b>(<u>M6502</u> <u>*mpu</u>, <u>vector</u>, <u>uint16</u><b>_</b><u>t</u> <u>a</u>d<u>dress</u>);
|
||||
|
||||
<u>M6502</u><b>_</b><u>Callback</u>
|
||||
<b>M6502_getCallback</b>(<u>M6502</u> <u>*mpu</u>, <u>type</u>, <u>uint16</u><b>_</b><u>t</u> <u>a</u>d<u>dress</u>);
|
||||
|
||||
<u>M6502</u><b>_</b><u>Callback</u>
|
||||
<b>M6502_setCallback</b>(<u>M6502</u> <u>*mpu</u>, <u>type</u>, <u>uint16</u><b>_</b><u>t</u> <u>a</u>d<u>dress</u>,
|
||||
<u>M6502</u><b>_</b><u>Callback</u> <u>callback</u>);
|
||||
|
||||
<u>void</u>
|
||||
<b>M6502_run</b>(<u>M6502</u> <u>*mpu</u>);
|
||||
|
||||
<u>int</u>
|
||||
<b>M6502_disassemble</b>(<u>M6502</u> <u>*mpu</u>, <u>uint16</u><b>_</b><u>t</u> <u>addres</u>_s, <u>char</u> <u>buffer[64]</u>);
|
||||
|
||||
<u>void</u>
|
||||
<b>M6502_dump</b>(<u>M6502</u> <u>*mpu</u>, <u>char</u> <u>buffer[64]</u>);
|
||||
|
||||
<u>void</u>
|
||||
<b>M6502_delete</b>(<u>M6502</u> <u>*mpu</u>);
|
||||
|
||||
<b>DESCRIPTION</b>
|
||||
<b>M6502_new</b>() creates an instance of a 6502 microprocessor. <b>M6502_reset</b>(),
|
||||
<b>M6502_nmi</b>() and <b>M6502_irq</b>() place it into the states associated with the
|
||||
hardware signals for reset, non-maskable interrupt and interrupt request,
|
||||
respectively. The macros <b>M6502_getVector</b>() and <b>M6502_setVector</b>() read
|
||||
and write the vectors through which the processor jumps in response to
|
||||
the above signals. The macros <b>M6502_getCallback</b>() and <b>M6502_setVec</b>tt<b>or</b>()
|
||||
read and write client-supplied functions that intercept accesses to mem-
|
||||
ory. <b>M6502_run</b>() begins emulated execution. <b>M6502_dump</b>() and
|
||||
<b>M6502_disassemble</b>() create human-readable representations of processor or
|
||||
memory state. <b>M6502_delete</b>() frees all resources associated with a pro-
|
||||
cessor instance. Each of these functions and macros is described in more
|
||||
detail below.
|
||||
|
||||
<b>M6502_new</b>() returns a pointer to a <u>M6502</u> structure containing at least
|
||||
the following members:
|
||||
|
||||
struct _M6502
|
||||
{
|
||||
M6502_Registers *registers; /* processor state */
|
||||
uint8_t *memory; /* memory image */
|
||||
M6502_Callbacks *callbacks; /* r/w/x callbacks */
|
||||
};
|
||||
|
||||
These members are initialised according to the supplied <u>registers</u>, <u>memory</u>
|
||||
and <u>callbacks</u> arguments. If a given argument is NULL, the corresponding
|
||||
member is initialised automatically with a suitable (non-NULL) value.
|
||||
|
||||
The members of <u>M6502</u> are as follows:
|
||||
|
||||
<u>registers</u> the processor state, containing all registers and condition
|
||||
codes.
|
||||
|
||||
<u>memory</u> a block of at least 64 kilobytes of storage containing the
|
||||
processor's memory. (An array type <u>M6502</u><b>_</b><u>Memory,</u> suitable for
|
||||
defining values to pass as the <u>memory</u> argument, is defined in
|
||||
the <b>#include</b> <b><lib6502.h></b>
|
||||
include file.)
|
||||
|
||||
<u>callbacks</u> a structure mapping processor memory accesses to client call-
|
||||
back functions.
|
||||
|
||||
Access to the contents of the <u>registers</u> and <u>memory</u> members can be made
|
||||
directly. The <u>registers</u> member is a <u>M6502</u><b>_</b><u>Registers</u> containing the fol-
|
||||
lowing members:
|
||||
|
||||
struct _M6502_Registers
|
||||
{
|
||||
uint8_t a; /* accumulator */
|
||||
uint8_t x; /* X index register */
|
||||
uint8_t y; /* Y index register */
|
||||
uint8_t p; /* processor status register */
|
||||
uint8_t s; /* stack pointer */
|
||||
uint16_t pc; /* program counter */
|
||||
};
|
||||
|
||||
The <u>memory</u> member is an array of <u>unsigned</u> <u>char</u> and can be indexed
|
||||
directly. In addition, two convenience macros <b>M6502_getVector</b>() and
|
||||
<b>M6502_setVector</b>() provide access to the reset and interrupt vectors
|
||||
within <u>memory</u>. <b>M6502_getVector</b>() returns the address stored in the named
|
||||
<u>vector</u> which must be precisely one of the following:
|
||||
|
||||
RST the reset vector.
|
||||
|
||||
NMI the non-maskable interrupt vector.
|
||||
|
||||
IRQ the interrupt request vector.
|
||||
|
||||
<b>M6502_setVector</b>() stores its <u>address</u> argument in the named <u>vector</u> and
|
||||
returns the new value.
|
||||
|
||||
The <u>callbacks</u> member contains an opaque structure mapping processor mem-
|
||||
ory accesses to client callback functions. Whenever the processor per-
|
||||
forms an access for which a corresponding entry exists in the the
|
||||
<u>callbacks</u> structure, the emulator suspends execution and invokes the
|
||||
callback to complete the operation. Each callback function should have a
|
||||
signature equivalent to:
|
||||
|
||||
int <u>callback</u> (M6502 *mpu, uint16_t address, uint8_t data);
|
||||
|
||||
The macros <b>M6502_getCallback</b>() and <b>M6502_setCallback</b>() read and write
|
||||
entries in the <u>callbacks</u> structure. These macros identify a unique mem-
|
||||
ory access operation from the specified <u>address</u> on which it operates and
|
||||
<u>type</u> of access involved. The <u>type</u> argument must be one of the following:
|
||||
|
||||
read the <u>callback</u> is invoked when the processor attempts to read from
|
||||
the given address. The emulator passes the effective address of
|
||||
the operation to the callback in its <u>address</u> argument. (The <u>data</u>
|
||||
argument is undefined.) The value returned by the callback will
|
||||
be used by the emulator as the result of the read operation.
|
||||
|
||||
write the <u>callback</u> is invoked when the processor attempts to write to
|
||||
the given address. The emulator passes the effective address of
|
||||
the operation to the callback in its <u>address</u> argument and the byte
|
||||
being written in the <u>data</u> argument. The emulator will not perform
|
||||
the write operation before invoking the callback; if the write
|
||||
should complete, the callback must modify the processor's <u>memory</u>
|
||||
explicitly. The valued returned from the callback is ignored.
|
||||
|
||||
call the <u>callback</u> is invoked when the processor attempts to transfer
|
||||
control to the given address by any instruction other than a rela-
|
||||
tive branch. The emulator passes the destination address to the
|
||||
callback in its <u>address</u> argument and the instruction that initi-
|
||||
ated the control transfer in its <u>data</u> argument (one of JMP, JSR,
|
||||
BRK, RTS or RTI). If the callback returns zero (the callback
|
||||
refuses to handle the operation) the emulator will allow the oper-
|
||||
ation to complete as normal. If the callback returns a non-zero
|
||||
address (indicating that the callback has handled the operation
|
||||
internally) the emulator will transfer control to that address.
|
||||
|
||||
<b>M6502_getCallback</b>() returns zero if there is no callback associated with
|
||||
the given <u>type</u> and <u>address</u>. Passing zero as the <u>callback</u> argument of
|
||||
<b>M6502_setCallback</b>() removes any callback that might have been associated
|
||||
with <u>type</u> and <u>address</u>.
|
||||
|
||||
<b>M6502_run</b>() emulates processor execution in the given <u>mpu</u> by repeatedly
|
||||
fetching the instruction addressed by <u>pc</u> and dispatching to it. This
|
||||
function normally never returns.
|
||||
|
||||
<b>M6502_dump</b>() writes a (NUL-terminated) symbolic representation of the
|
||||
processor's internal state into the supplied <u>buffer</u>. Typical output
|
||||
resembles:
|
||||
|
||||
PC=1010 SP=01FE A=0A X=5B Y=00 P=D1 NV-B---C
|
||||
|
||||
<b>M6502_disassemble</b>() writes a (NUL-terminated) symbolic representation of
|
||||
the instruction in the processor's memory at the given <u>address</u> into the
|
||||
supplied <u>buffer</u>. It returns the size (in bytes) of the instruction. (In
|
||||
other words, the amount by which <u>address</u> should be incremented to arrive
|
||||
at the next instruction.) Typical output resembles:
|
||||
|
||||
1009 cpx #5B
|
||||
|
||||
(The <u>buffer</u> arguments are oversized to allow for future expansion.)
|
||||
|
||||
<b>M6502_delete</b>() frees the resources associated with the given <u>mpu.</u> Any
|
||||
members that were allocated implicitly (passed as NULL to <b>M6502_new</b>())
|
||||
are deallocated. Members that were initialised from non-NULL arguments
|
||||
are not deallocated.
|
||||
|
||||
<b>IMPLEMENTATION</b> <b>NOTES</b>
|
||||
You can share the <u>memory</u> and <u>callbacks</u> members of <u>M6502</u> between multiple
|
||||
instances to simulate multiprocessor hardware.
|
||||
|
||||
<b>RETURN</b> <b>VALUES</b>
|
||||
<b>M6502_new</b>() returns a pointer to a <u>M6502</u> structure. <b>M6502_getVector</b>()
|
||||
and <b>M6502_setVector</b>() return the contents of the given <u>vector</u>.
|
||||
<b>M6502_getCallback</b>() and <b>M6502_setCallback</b>() return the <u>M</u><u>6502</u><b>_</b><u>Callback</u>
|
||||
function associated with the given <u>address</u> and access <u>type</u>.
|
||||
<b>M6502_disassemble</b>() returns the size (in bytes) of the instruction at the
|
||||
given <u>address</u>. <b>M6502_reset</b>(), <b>M6502_nmi</b>(), <b>M6502_irq</b>(), <b>M6502_run</b>(),
|
||||
<b>M6502_dump</b>() and <b>M6502_delete</b>() don't return anything (unless you forgot
|
||||
to include
|
||||
|
||||
<b>EXAMPLES</b>
|
||||
The following program creates a 6502 processor, sets up callbacks for
|
||||
printing characters and halting after a BRK instruction, stores a program
|
||||
into memory that prints the alphabet, disassembles the program on stdout,
|
||||
and then executes the program.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "lib6502.h"
|
||||
|
||||
#define WRCH 0xFFEE
|
||||
|
||||
int wrch(M6502 *mpu, uint16_t address, uint8_t data)
|
||||
{
|
||||
int pc;
|
||||
putchar(mpu->registers->a);
|
||||
pc = mpu->memory[++mpu->registers->s + 0x100];
|
||||
pc |= mpu->memory[++mpu->registers->s + 0x100] << 8;
|
||||
return pc + 1; /* JSR pushes next insn addr - 1 */
|
||||
}
|
||||
|
||||
int done(M6502 *mpu, uint16_t address, uint8_t data)
|
||||
{
|
||||
char buffer[64];
|
||||
M6502_dump(mpu, buffer);
|
||||
printf("\nBRK instruction\n%s\n", buffer);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
M6502 *mpu = M6502_new(0, 0, 0);
|
||||
unsigned pc = 0x1000;
|
||||
|
||||
mpu->callbacks->call[WRCH] = wrch; /* write character */
|
||||
mpu->callbacks->call[0000] = done; /* reached after BRK */
|
||||
|
||||
# define gen1(X) (mpu->memory[pc++] = (uint8_t)(X))
|
||||
# define gen2(X,Y) gen1(X); gen1(Y)
|
||||
# define gen3(X,Y,Z) gen1(X); gen2(Y,Z)
|
||||
|
||||
gen2(0xA2, 'A' ); /* LDX #'A' */
|
||||
gen1(0x8A ); /* TXA */
|
||||
gen3(0x20,0xEE,0xFF); /* JSR FFEE */
|
||||
gen1(0xE8 ); /* INX */
|
||||
gen2(0xE0, 'Z'+1 ); /* CPX #'Z'+1 */
|
||||
gen2(0xD0, -9 ); /* BNE 1002 */
|
||||
gen2(0xA9, '\n' ); /* LDA #'\n' */
|
||||
gen3(0x20,0xEE,0xFF); /* JSR FFEE */
|
||||
gen2(0x00,0x00 ); /* BRK */
|
||||
|
||||
{
|
||||
uint16_t ip = 0x1000;
|
||||
while (ip < pc)
|
||||
{
|
||||
char insn[64];
|
||||
ip += M6502_disassemble(mpu, ip, insn);
|
||||
printf("%04X %s\n", ip, insn);
|
||||
}
|
||||
}
|
||||
|
||||
M6502_setVector(mpu, RST, 0x1000);
|
||||
|
||||
M6502_reset(mpu);
|
||||
M6502_run(mpu);
|
||||
M6502_delete(mpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
<b>DIAGNOSTICS</b>
|
||||
If <b>M6502_new</b>() cannot allocate sufficient memory it prints "out of mem-
|
||||
ory" to stderr and exits with a non-zero status.
|
||||
|
||||
If <b>M6502_run</b>() encounters an illegal or undefined instruction, it prints
|
||||
"undefined instruction" and the processor's state to stderr, then exits
|
||||
with a non-zero status.
|
||||
|
||||
<b>COMPATIBILITY</b>
|
||||
M6502 is a generic name. The initial letter is mandated by C naming con-
|
||||
ventions and chosen in deference to MOS Technology, the original design-
|
||||
ers of the processor. To the best of my knowledge the 'M' prefix was
|
||||
never stamped on a physical 6502.
|
||||
|
||||
The emulator implements the CMOS version of the processor (NMOS bugs in
|
||||
effective address calculations involving page boundaries are corrected)
|
||||
but does not tolerate the execution of undefined instructions (which were
|
||||
all no-ops in the first-generation CMOS hardware). It would be nice to
|
||||
support the several alternative instruction sets (model-specific undocu-
|
||||
mented instructions in NMOS models, and various documented extensions in
|
||||
the later CMOS models) but there are currently no plans to do so.
|
||||
|
||||
The emulated 6502 will run much faster than real hardware on any modern
|
||||
computer. The fastest 6502 hardware available at the time of writing has
|
||||
a clock speed of 14 MHz. On a 2 GHz PowerPC, the emulated 6502 runs at
|
||||
almost 300 MHz.
|
||||
|
||||
<b>SEE</b> <b>ALSO</b>
|
||||
run6502(1)
|
||||
|
||||
For development tools, documentation and source code: <u>http://6502.org</u>
|
||||
|
||||
<b>AUTHORS</b>
|
||||
The software and manual pages were written by Ian Piumarta.
|
||||
|
||||
The software is provided as-is, with absolutely no warranty, in the hope
|
||||
that you will enjoy and benefit from it. You may use (entirely at your
|
||||
own risk) and redistribute it under the terms of a very liberal license
|
||||
that does not seek to restrict your rights in any way (unlike certain so-
|
||||
called 'open source' licenses that significantly limit your freedom in
|
||||
the name of 'free' software that is, ultimately, anything but free). See
|
||||
the file COPYING for details.
|
||||
|
||||
<b>BUGS</b>
|
||||
<b>M6502_getVector</b>() and <b>M6502_setVector</b>() evaluate their arguments more
|
||||
than once.
|
||||
|
||||
The out-of-memory condition and attempted execution of illegal/undefined
|
||||
instructions should not be fatal errors.
|
||||
|
||||
There is no way to limit the duration of execution within <b>M6502_run</b>() to
|
||||
a certain number of instructions or cycles.
|
||||
|
||||
The emulator should support some means of implicit interrupt generation,
|
||||
either by polling or in response to (Unix) signals.
|
||||
|
||||
The <u>COMPATIBILITY</u> section in this manual page has been diverted from its
|
||||
legitimate purpose.
|
||||
|
||||
The plural of 'callback' really aught to be 'callsback'.
|
||||
|
||||
Please send bug reports (and feature requests) to the author at: first-
|
||||
Name (at) lastName (dot) com. (See <u>AUTHORS</u> above for suitable values of
|
||||
firstName and lastName.)
|
||||
|
||||
BSD October 31, 2005 BSD
|
||||
</pre>
|
||||
|
||||
|
||||
</body></html>
|
275
lib6502/html/run6502.html
Normal file
275
lib6502/html/run6502.html
Normal file
@ -0,0 +1,275 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<!-- saved from url=(0056)https://www.piumarta.com/software/lib6502/run6502.1.html -->
|
||||
<html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
|
||||
|
||||
<meta name="generator" content="tty2html http://piumarta.com/software">
|
||||
<title>run6502(1)</title></head>
|
||||
<body>
|
||||
<pre>RUN6502(1) BSD General Commands Manual RUN6502(1)
|
||||
|
||||
<b>NAME</b>
|
||||
<b>run6502</b> - execute a 6502 microprocessor program
|
||||
|
||||
<b>SYNOPSIS</b>
|
||||
<b>run6502</b> [<u>option</u> <u>...</u>]
|
||||
<b>run6502</b> [<u>option</u> <u>...</u>] <b>-B</b> [<u>file</u> <u>...</u>]
|
||||
|
||||
<b>DESCRIPTION</b>
|
||||
The <b>run6502</b> command emulates the execution of a 6502 microprocessor. It
|
||||
creates a memory image from the contents of one or more files on the com-
|
||||
mand line and then simulates a power-on hardware reset to begin execu-
|
||||
tion.
|
||||
|
||||
In its first form, <b>run6502</b> emulates an embedded 6502 processor with 64
|
||||
kilobytes of RAM, no memory-mapped hardware, and no input-output capabil-
|
||||
ities. Limited interaction with the machine is possible only through the
|
||||
<b>-G</b>, <b>-M</b> and <b>-P</b> options.
|
||||
|
||||
In its second form (with the <b>-B</b> option) <b>run6502</b> provides minimal emula-
|
||||
tion of Acorn 'BBC Model B' hardware with 32 kilobytes of RAM, 16 kilo-
|
||||
bytes of paged language ROMs, and 16 kilobytes of operating system ROM.
|
||||
A few MOS calls are intercepted to provide keyboard input and screen out-
|
||||
put via stdin and stdout. Switching between the sixteen paged read-only
|
||||
memory banks is also supported by the usual memory-mapped control regis-
|
||||
ter. Any <u>file</u> arguments after the <b>-B</b> are loaded into successive paged
|
||||
ROM banks (starting at 15 and working down towards 0) before execution
|
||||
begins.
|
||||
|
||||
<b>Options</b>
|
||||
<b>-B</b> enable minimal Acorn 'BBC Model B' hardware emulation:
|
||||
|
||||
<b>o</b> the contents of memory between addresses 0x8000 and 0xBFFF
|
||||
are copied into paged ROM number 0;
|
||||
|
||||
<b>o</b> memory between 0x8000 and 0xBFFF becomes bank-switchable
|
||||
between sixteen different ROM images;
|
||||
|
||||
<b>o</b> the memory-mapped pages ('FRED', 'JIM' and 'SHEILA') between
|
||||
0xFC00 and 0xFEFF are initialised to harmless values;
|
||||
|
||||
<b>o</b> the upper half of the address space is write-protected; and
|
||||
|
||||
<b>o</b> callbacks are installed on several OS entry points to provide
|
||||
input-output via stdin and stdout.
|
||||
|
||||
Any remaining non-option arguments on the command line will name
|
||||
files to be loaded successively into paged ROMs, starting at 15
|
||||
and working downwards towards 0.
|
||||
|
||||
<b>-d</b> <u>addr</u> <u>end</u>
|
||||
dump memory from the address <u>addr</u> (given in hexadecimal) up to
|
||||
(but not including) <u>end</u>. The <u>end</u> argument is either an absolute
|
||||
address or a relative address specified as a '+' character fol-
|
||||
lowed by the number (in hexadecimal) of bytes to dump. In other
|
||||
words, the following two options dump the same region of memory:
|
||||
|
||||
<b>-d</b> 8000 C000
|
||||
<b>-d</b> 8000 +4000
|
||||
|
||||
The format of the dump cannot currently be modified and consists
|
||||
of the current address followed by one, two or three hexadecimal
|
||||
bytes, and a symbolic representation of the instruction at that
|
||||
address.
|
||||
|
||||
<b>-G</b> <u>addr</u>
|
||||
arrange that subroutine calls to <u>addr</u> will behave as if there
|
||||
were an implementation of getchar(3) at that address, reading a
|
||||
character from stdin and returning it in the accumulator.
|
||||
|
||||
<b>-h</b> print a summary of the available options and then exit.
|
||||
|
||||
<b>-I</b> <u>addr</u>
|
||||
set the IRQ (interrupt request) vector (the address to which the
|
||||
processor will transfer control upon execution of a BRK instruc-
|
||||
tion). Setting this address to zero will cause execution to halt
|
||||
(and the emulator to exit) when a BRK instruction is encountered.
|
||||
|
||||
<b>-i</b> <u>addr</u> <u>file</u>
|
||||
Load <u>file</u> into the memory image at the address <u>addr</u> (in hexadeci-
|
||||
mal), skipping over any initial '#!' interpreter line.
|
||||
|
||||
<b>-l</b> <u>addr</u> <u>file</u>
|
||||
Load <u>file</u> into the memory image at the address <u>addr</u> (in hexadeci-
|
||||
mal).
|
||||
|
||||
<b>-M</b> <u>addrio</u>
|
||||
arrange that memory reads from address <u>addrio</u> will return the
|
||||
next character on stdin (blocking if necessary), and memory
|
||||
writes to <u>addrio</u> will send the value written to stdout.
|
||||
|
||||
<b>-N</b> <u>addr</u>
|
||||
set the NMI (non-maskable interrupt) vector to <u>addr</u>.
|
||||
|
||||
<b>-P</b> <u>addr</u>
|
||||
arrange that subroutine calls to <u>addr</u> will behave as if there
|
||||
were an implementation of putchar(3) at that address, writing the
|
||||
contents of the accumulator to stdout.
|
||||
|
||||
<b>-R</b> <u>addr</u>
|
||||
set the RST (hardware reset) vector. The processor will transfer
|
||||
control to this address when emulated execution begins.
|
||||
|
||||
<b>-s</b> <u>addr</u> <u>end</u> <u>file</u>
|
||||
save the contents of memory from the address <u>addr</u> up to <u>end</u>
|
||||
(exclusive) to the given <u>file</u>. As with the <b>-d</b> option, <u>end</u> can be
|
||||
absolute or '+' followed by a byte count.
|
||||
|
||||
<b>-v</b> print version information and then exit.
|
||||
|
||||
<b>-X</b> <u>addr</u>
|
||||
arrange that any transfer of control to the address <u>addr</u> will
|
||||
cause an immediate exit with zero exit status.
|
||||
|
||||
<b>-x</b> exit immediately. (Useful after <b>-d</b> or when <b>run6502</b> is being used
|
||||
as a trivial 'image editor', with several <b>-l</b> options followed by
|
||||
<b>-s</b> and <b>-x</b>.)
|
||||
|
||||
<u>file</u> <u>...</u>
|
||||
following a <b>-B</b> option, load one or more ROM image files into suc-
|
||||
cessive paged ROM slots. Other than the paging aspect, this is
|
||||
equivalent to:
|
||||
|
||||
<b>-l</b> <u>8000</u> <u>image</u>
|
||||
|
||||
<b>EXAMPLES</b>
|
||||
<b>A</b> <b>Very</b> <b>Simple</b> <b>Program</b>
|
||||
The perl(1) command can be used to create a binary file from hexadecimal
|
||||
input:
|
||||
|
||||
echo a2418a20eeffe8e05bd0f7a90a20eeff00 |
|
||||
perl -e 'print pack "H*",<STDIN>' > temp.img
|
||||
|
||||
The file can be loaded and executed with:
|
||||
|
||||
run6502 -l 1000 temp.img -R 1000 -P FFEE -X 0
|
||||
|
||||
The contents of the file can be inspected symbolically with:
|
||||
|
||||
run6502 -l 1000 temp.img -d 1000 +12
|
||||
|
||||
The options passed to <b>run6502</b> in the above examples have the following
|
||||
effects:
|
||||
|
||||
-l 1000 temp.img
|
||||
loads the file <u>temp.img</u> into memory at address 0x8000.
|
||||
|
||||
-R 1000
|
||||
sets the reset vector (the address of first instruction to be
|
||||
executed after 'power on') to 0x1000.
|
||||
|
||||
-P FFEE
|
||||
arranges for calls to address 0xFFEE to behave as if there were
|
||||
an implementation of putchar(3) at that address.
|
||||
|
||||
-X 0 arranges for transfers of control to address 0 to exit from the
|
||||
emulator. This works in the above example because the final
|
||||
'BRK' instruction causes an implicit subroutine call through an
|
||||
uninitialised interrupt vector to location 0. To see this
|
||||
instruction...
|
||||
|
||||
-d 1000 +12
|
||||
disassembles 18 bytes of memory at address 0x8000.
|
||||
|
||||
<b>Standalone</b> <b>Images</b>
|
||||
The <b>-i</b> option is designed for use in the 'interpreter command' appearing
|
||||
on the first line of an executable script. Adding the line
|
||||
|
||||
#!run6502 -R 1000 -P FFEE -X 0 -i 1000
|
||||
|
||||
(with no leading spaces and a single trailing newline character) to the
|
||||
<u>temp.img</u> file from the first example turns it into a script. If the file
|
||||
is made executable with
|
||||
|
||||
chmod +x temp.img
|
||||
|
||||
it can be run like a standalone program:
|
||||
|
||||
./temp.img
|
||||
|
||||
<b>A</b> <b>Very</b> <b>Complex</b> <b>Program</b>
|
||||
Consider a pair of files named <u>os1.2</u> and <u>basic2</u> containing (legally-
|
||||
acquired, of course) ROM images of Acorn MOS 1.2 and BBC Basic 2. The
|
||||
following command loads each of the images into memory at the appropriate
|
||||
address, cleans up the regions of memory containing memory-mapped i/o on
|
||||
the BBC computer, saves a snapshot of the entire memory to the file <u>image</u>
|
||||
and then exits:
|
||||
|
||||
run6502 -l C000 os1.2 -l 8000 basic2 -B -s0 +10000 image -x
|
||||
|
||||
Running the generated image with
|
||||
|
||||
run6502 image
|
||||
|
||||
will cold-start the emulated hardware, run the OS for a while, and then
|
||||
drop into the language ROM. Basic programs can then be entered, edited
|
||||
and run from the terminal.
|
||||
|
||||
More details are given in the <u>README</u> file available in the <u>examples</u>
|
||||
directory of the distribution.
|
||||
|
||||
<b>Exercises</b>
|
||||
Create a standalone image (one that can be run as a program, with a '#!'
|
||||
interpreter line at the beginning) that contains Basic2 and OS1.2 (as
|
||||
described above). This image should be no larger than 32K (memory below
|
||||
0x8000, which would be full of zeroes, should not appear in the image
|
||||
file).
|
||||
|
||||
<b>DIAGNOSTICS</b>
|
||||
If nothing goes wrong, none. Otherwise lots. They should be self-
|
||||
explanatory. I'm too lazy to enumerate them.
|
||||
|
||||
<b>COMPATIBILITY</b>
|
||||
See lib6502(3) for a discussion of the emulated instruction set.
|
||||
|
||||
<b>SEE</b> <b>ALSO</b>
|
||||
lib6502(3)
|
||||
|
||||
The file <u>examples/README</u> in the lib6502 distribution. (Depending on your
|
||||
system this may be installed in <u>/usr/doc/lib6502</u>, <u>/usr/local/doc</u>/<u>lib6502</u>,
|
||||
<u>/usr/share/doc/lib6502</u>, or similar.)
|
||||
|
||||
<u>http://piumarta.com/software/lib6502</u> for updates and documentation.
|
||||
|
||||
<u>http://6502.org</u> for lots of 6502-related resources.
|
||||
|
||||
<b>AUTHORS</b>
|
||||
The software and manual pages were written by Ian Piumarta.
|
||||
|
||||
The software is provided as-is, with absolutely no warranty, in the hope
|
||||
that you will enjoy and benefit from it. You may use (entirely at your
|
||||
own risk) and redistribute it under the terms of a very liberal license
|
||||
that does not seek to restrict your rights in any way (unlike certain so-
|
||||
called 'open source' licenses that significantly limit your freedom in
|
||||
the name of 'free' software that is, ultimately, anything but free). See
|
||||
the file COPYING for details.
|
||||
|
||||
<b>BUGS</b>
|
||||
<b>o</b> Options must appear one at a time.
|
||||
|
||||
<b>o</b> Any attempt (in a load or save operation) to transfer data beyond
|
||||
0xFFFF is silently truncated at the end of memory.
|
||||
|
||||
<b>o</b> There is no way to specify the slot into which a ROM image should be
|
||||
loaded, other than implicitly according to the order of arguments on
|
||||
the command line.
|
||||
|
||||
<b>o</b> Execution can only be started via the emulated power-up reset. There
|
||||
is no support for 'warm-starting' execution in an image at an arbi-
|
||||
trary address.
|
||||
|
||||
<b>o</b> Even though the emulator fully supports them, there is no way to
|
||||
artificially generate a hardware interrupt request, non-maskable
|
||||
interrupt, or reset condition. If you need these, read lib6502(3)
|
||||
and write your own shell.
|
||||
|
||||
<b>o</b> The Acorn 'BBC Model B' hardware emulation is totally lame.
|
||||
|
||||
Please send bug reports (and feature requests) to the author at: first-
|
||||
Name (at) lastName (dot) com. (See <u>AUTHORS</u> above for suitable values of
|
||||
firstName and lastName.)
|
||||
|
||||
BSD October 31, 2005 BSD
|
||||
</pre>
|
||||
|
||||
|
||||
</body></html>
|
893
lib6502/lib6502.c
Normal file
893
lib6502/lib6502.c
Normal file
@ -0,0 +1,893 @@
|
||||
/* lib6502.c -- MOS Technology 6502 emulator -*- C -*- */
|
||||
|
||||
/* Copyright (c) 2005 Ian Piumarta
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the 'Software'),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, provided that the above copyright notice(s) and this
|
||||
* permission notice appear in all copies of the Software and that both the
|
||||
* above copyright notice(s) and this permission notice appear in supporting
|
||||
* documentation.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK.
|
||||
*/
|
||||
|
||||
/* Last edited: 2013-06-07 23:03:39 by piumarta on emilia.local
|
||||
*
|
||||
* BUGS:
|
||||
* - RTS and RTI do not check the return address for a callback
|
||||
* - the disassembler cannot be configured to read two bytes for BRK
|
||||
* - architectural variations (unimplemented/extended instructions) not implemented
|
||||
* - ANSI versions (from from gcc extensions) of the dispatch macros are missing
|
||||
* - emulator+disassembler in same object file (library is kind of pointless)
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lib6502.h"
|
||||
|
||||
typedef uint8_t byte;
|
||||
typedef uint16_t word;
|
||||
|
||||
enum {
|
||||
flagN= (1<<7), /* negative */
|
||||
flagV= (1<<6), /* overflow */
|
||||
flagX= (1<<5), /* unused */
|
||||
flagB= (1<<4), /* irq from brk */
|
||||
flagD= (1<<3), /* decimal mode */
|
||||
flagI= (1<<2), /* irq disable */
|
||||
flagZ= (1<<1), /* zero */
|
||||
flagC= (1<<0) /* carry */
|
||||
};
|
||||
|
||||
#define getN() (P & flagN)
|
||||
#define getV() (P & flagV)
|
||||
#define getB() (P & flagB)
|
||||
#define getD() (P & flagD)
|
||||
#define getI() (P & flagI)
|
||||
#define getZ() (P & flagZ)
|
||||
#define getC() (P & flagC)
|
||||
|
||||
#define setNVZC(N,V,Z,C) (P= (P & ~(flagN | flagV | flagZ | flagC)) | (N) | ((V)<<6) | ((Z)<<1) | (C))
|
||||
#define setNZC(N,Z,C) (P= (P & ~(flagN | flagZ | flagC)) | (N) | ((Z)<<1) | (C))
|
||||
#define setNZ(N,Z) (P= (P & ~(flagN | flagZ )) | (N) | ((Z)<<1) )
|
||||
#define setZ(Z) (P= (P & ~( flagZ )) | ((Z)<<1) )
|
||||
#define setC(C) (P= (P & ~( flagC)) | (C))
|
||||
|
||||
#define NAND(P, Q) (!((P) & (Q)))
|
||||
|
||||
#define tick(n)
|
||||
#define tickIf(p)
|
||||
|
||||
/* memory access (indirect if callback installed) -- ARGUMENTS ARE EVALUATED MORE THAN ONCE! */
|
||||
|
||||
#define putMemory(ADDR, BYTE) \
|
||||
( writeCallback[ADDR] \
|
||||
? writeCallback[ADDR](mpu, ADDR, BYTE) \
|
||||
: (memory[ADDR]= BYTE) )
|
||||
|
||||
#define getMemory(ADDR) \
|
||||
( readCallback[ADDR] \
|
||||
? readCallback[ADDR](mpu, ADDR, 0) \
|
||||
: memory[ADDR] )
|
||||
|
||||
/* stack access (always direct) */
|
||||
|
||||
#define push(BYTE) (memory[0x0100 + S--]= (BYTE))
|
||||
#define pop() (memory[++S + 0x0100])
|
||||
|
||||
/* adressing modes (memory access direct) */
|
||||
|
||||
#define implied(ticks) \
|
||||
tick(ticks);
|
||||
|
||||
#define immediate(ticks) \
|
||||
tick(ticks); \
|
||||
ea= PC++;
|
||||
|
||||
#define abs(ticks) \
|
||||
tick(ticks); \
|
||||
ea= memory[PC] + (memory[PC + 1] << 8); \
|
||||
PC += 2;
|
||||
|
||||
#define relative(ticks) \
|
||||
tick(ticks); \
|
||||
ea= memory[PC++]; \
|
||||
if (ea & 0x80) ea -= 0x100; \
|
||||
tickIf((ea >> 8) != (PC >> 8));
|
||||
|
||||
#define indirect(ticks) \
|
||||
tick(ticks); \
|
||||
{ \
|
||||
word tmp; \
|
||||
tmp= memory[PC] + (memory[PC + 1] << 8); \
|
||||
ea = memory[tmp] + (memory[tmp + 1] << 8); \
|
||||
PC += 2; \
|
||||
}
|
||||
|
||||
#define absx(ticks) \
|
||||
tick(ticks); \
|
||||
ea= memory[PC] + (memory[PC + 1] << 8); \
|
||||
PC += 2; \
|
||||
tickIf((ticks == 4) && ((ea >> 8) != ((ea + X) >> 8))); \
|
||||
ea += X;
|
||||
|
||||
#define absy(ticks) \
|
||||
tick(ticks); \
|
||||
ea= memory[PC] + (memory[PC + 1] << 8); \
|
||||
PC += 2; \
|
||||
tickIf((ticks == 4) && ((ea >> 8) != ((ea + Y) >> 8))); \
|
||||
ea += Y
|
||||
|
||||
#define zp(ticks) \
|
||||
tick(ticks); \
|
||||
ea= memory[PC++];
|
||||
|
||||
#define zpx(ticks) \
|
||||
tick(ticks); \
|
||||
ea= memory[PC++] + X; \
|
||||
ea &= 0x00ff;
|
||||
|
||||
#define zpy(ticks) \
|
||||
tick(ticks); \
|
||||
ea= memory[PC++] + Y; \
|
||||
ea &= 0x00ff;
|
||||
|
||||
#define indx(ticks) \
|
||||
tick(ticks); \
|
||||
{ \
|
||||
byte tmp= memory[PC++] + X; \
|
||||
ea= memory[tmp] + (memory[tmp + 1] << 8); \
|
||||
}
|
||||
|
||||
#define indy(ticks) \
|
||||
tick(ticks); \
|
||||
{ \
|
||||
byte tmp= memory[PC++]; \
|
||||
ea= memory[tmp] + (memory[tmp + 1] << 8); \
|
||||
tickIf((ticks == 5) && ((ea >> 8) != ((ea + Y) >> 8))); \
|
||||
ea += Y; \
|
||||
}
|
||||
|
||||
#define indabsx(ticks) \
|
||||
tick(ticks); \
|
||||
{ \
|
||||
word tmp; \
|
||||
tmp= memory[PC ] + (memory[PC + 1] << 8) + X; \
|
||||
ea = memory[tmp] + (memory[tmp + 1] << 8); \
|
||||
}
|
||||
|
||||
#define indzp(ticks) \
|
||||
tick(ticks); \
|
||||
{ \
|
||||
byte tmp; \
|
||||
tmp= memory[PC++]; \
|
||||
ea = memory[tmp] + (memory[tmp + 1] << 8); \
|
||||
}
|
||||
|
||||
/* insns */
|
||||
|
||||
#define adc(ticks, adrmode) \
|
||||
adrmode(ticks); \
|
||||
{ \
|
||||
byte B= getMemory(ea); \
|
||||
if (!getD()) \
|
||||
{ \
|
||||
int c= A + B + getC(); \
|
||||
int v= (int8_t)A + (int8_t)B + getC(); \
|
||||
fetch(); \
|
||||
A= c; \
|
||||
setNVZC((A & 0x80), (((A & 0x80) > 0) ^ (v < 0)), (A == 0), ((c & 0x100) > 0)); \
|
||||
next(); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
int l, h, s; \
|
||||
/* inelegant & slow, but consistent with the hw for illegal digits */ \
|
||||
l= (A & 0x0F) + (B & 0x0F) + getC(); \
|
||||
h= (A & 0xF0) + (B & 0xF0); \
|
||||
if (l >= 0x0A) { l -= 0x0A; h += 0x10; } \
|
||||
if (h >= 0xA0) { h -= 0xA0; } \
|
||||
fetch(); \
|
||||
s= h | (l & 0x0F); \
|
||||
/* only C is valid on NMOS 6502 */ \
|
||||
setNVZC(s & 0x80, !(((A ^ B) & 0x80) && ((A ^ s) & 0x80)), !s, !!(h & 0x80)); \
|
||||
A= s; \
|
||||
tick(1); \
|
||||
next(); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define sbc(ticks, adrmode) \
|
||||
adrmode(ticks); \
|
||||
{ \
|
||||
byte B= getMemory(ea); \
|
||||
if (!getD()) \
|
||||
{ \
|
||||
int b= 1 - (P &0x01); \
|
||||
int c= A - B - b; \
|
||||
int v= (int8_t)A - (int8_t) B - b; \
|
||||
fetch(); \
|
||||
A= c; \
|
||||
setNVZC(A & 0x80, ((A & 0x80) > 0) ^ ((v & 0x100) != 0), A == 0, c >= 0); \
|
||||
next(); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
/* this is verbatim ADC, with a 10's complemented operand */ \
|
||||
int l, h, s; \
|
||||
B= 0x99 - B; \
|
||||
l= (A & 0x0F) + (B & 0x0F) + getC(); \
|
||||
h= (A & 0xF0) + (B & 0xF0); \
|
||||
if (l >= 0x0A) { l -= 0x0A; h += 0x10; } \
|
||||
if (h >= 0xA0) { h -= 0xA0; } \
|
||||
fetch(); \
|
||||
s= h | (l & 0x0F); \
|
||||
/* only C is valid on NMOS 6502 */ \
|
||||
setNVZC(s & 0x80, !(((A ^ B) & 0x80) && ((A ^ s) & 0x80)), !s, !!(h & 0x80)); \
|
||||
A= s; \
|
||||
tick(1); \
|
||||
next(); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define cmpR(ticks, adrmode, R) \
|
||||
adrmode(ticks); \
|
||||
fetch(); \
|
||||
{ \
|
||||
byte B= getMemory(ea); \
|
||||
byte d= R - B; \
|
||||
setNZC(d & 0x80, !d, R >= B); \
|
||||
} \
|
||||
next();
|
||||
|
||||
#define cmp(ticks, adrmode) cmpR(ticks, adrmode, A)
|
||||
#define cpx(ticks, adrmode) cmpR(ticks, adrmode, X)
|
||||
#define cpy(ticks, adrmode) cmpR(ticks, adrmode, Y)
|
||||
|
||||
#define dec(ticks, adrmode) \
|
||||
adrmode(ticks); \
|
||||
fetch(); \
|
||||
{ \
|
||||
byte B= getMemory(ea); \
|
||||
--B; \
|
||||
putMemory(ea, B); \
|
||||
setNZ(B & 0x80, !B); \
|
||||
} \
|
||||
next();
|
||||
|
||||
#define decR(ticks, adrmode, R) \
|
||||
fetch(); \
|
||||
tick(ticks); \
|
||||
--R; \
|
||||
setNZ(R & 0x80, !R); \
|
||||
next();
|
||||
|
||||
#define dea(ticks, adrmode) decR(ticks, adrmode, A)
|
||||
#define dex(ticks, adrmode) decR(ticks, adrmode, X)
|
||||
#define dey(ticks, adrmode) decR(ticks, adrmode, Y)
|
||||
|
||||
#define inc(ticks, adrmode) \
|
||||
adrmode(ticks); \
|
||||
fetch(); \
|
||||
{ \
|
||||
byte B= getMemory(ea); \
|
||||
++B; \
|
||||
putMemory(ea, B); \
|
||||
setNZ(B & 0x80, !B); \
|
||||
} \
|
||||
next();
|
||||
|
||||
#define incR(ticks, adrmode, R) \
|
||||
fetch(); \
|
||||
tick(ticks); \
|
||||
++R; \
|
||||
setNZ(R & 0x80, !R); \
|
||||
next();
|
||||
|
||||
#define ina(ticks, adrmode) incR(ticks, adrmode, A)
|
||||
#define inx(ticks, adrmode) incR(ticks, adrmode, X)
|
||||
#define iny(ticks, adrmode) incR(ticks, adrmode, Y)
|
||||
|
||||
#define bit(ticks, adrmode) \
|
||||
adrmode(ticks); \
|
||||
fetch(); \
|
||||
{ \
|
||||
byte B= getMemory(ea); \
|
||||
P= (P & ~(flagN | flagV | flagZ)) \
|
||||
| (B & (0xC0)) | (((A & B) == 0) << 1); \
|
||||
} \
|
||||
next();
|
||||
|
||||
#define tsb(ticks, adrmode) \
|
||||
adrmode(ticks); \
|
||||
fetch(); \
|
||||
{ \
|
||||
byte b= getMemory(ea); \
|
||||
b |= A; \
|
||||
putMemory(ea, b); \
|
||||
setZ(!b); \
|
||||
} \
|
||||
next();
|
||||
|
||||
#define trb(ticks, adrmode) \
|
||||
adrmode(ticks); \
|
||||
fetch(); \
|
||||
{ \
|
||||
byte b= getMemory(ea); \
|
||||
b |= (A ^ 0xFF); \
|
||||
putMemory(ea, b); \
|
||||
setZ(!b); \
|
||||
} \
|
||||
next();
|
||||
|
||||
#define bitwise(ticks, adrmode, op) \
|
||||
adrmode(ticks); \
|
||||
fetch(); \
|
||||
A op##= getMemory(ea); \
|
||||
setNZ(A & 0x80, !A); \
|
||||
next();
|
||||
|
||||
#define and(ticks, adrmode) bitwise(ticks, adrmode, &)
|
||||
#define eor(ticks, adrmode) bitwise(ticks, adrmode, ^)
|
||||
#define ora(ticks, adrmode) bitwise(ticks, adrmode, |)
|
||||
|
||||
#define asl(ticks, adrmode) \
|
||||
adrmode(ticks); \
|
||||
{ \
|
||||
unsigned int i= getMemory(ea) << 1; \
|
||||
putMemory(ea, i); \
|
||||
fetch(); \
|
||||
setNZC(i & 0x80, !i, i >> 8); \
|
||||
} \
|
||||
next();
|
||||
|
||||
#define asla(ticks, adrmode) \
|
||||
tick(ticks); \
|
||||
fetch(); \
|
||||
{ \
|
||||
int c= A >> 7; \
|
||||
A <<= 1; \
|
||||
setNZC(A & 0x80, !A, c); \
|
||||
} \
|
||||
next();
|
||||
|
||||
#define lsr(ticks, adrmode) \
|
||||
adrmode(ticks); \
|
||||
{ \
|
||||
byte b= getMemory(ea); \
|
||||
int c= b & 1; \
|
||||
fetch(); \
|
||||
b >>= 1; \
|
||||
putMemory(ea, b); \
|
||||
setNZC(0, !b, c); \
|
||||
} \
|
||||
next();
|
||||
|
||||
#define lsra(ticks, adrmode) \
|
||||
tick(ticks); \
|
||||
fetch(); \
|
||||
{ \
|
||||
int c= A & 1; \
|
||||
A >>= 1; \
|
||||
setNZC(0, !A, c); \
|
||||
} \
|
||||
next();
|
||||
|
||||
#define rol(ticks, adrmode) \
|
||||
adrmode(ticks); \
|
||||
{ \
|
||||
word b= (getMemory(ea) << 1) | getC(); \
|
||||
fetch(); \
|
||||
putMemory(ea, b); \
|
||||
setNZC(b & 0x80, !(b & 0xFF), b >> 8); \
|
||||
} \
|
||||
next();
|
||||
|
||||
#define rola(ticks, adrmode) \
|
||||
tick(ticks); \
|
||||
fetch(); \
|
||||
{ \
|
||||
word b= (A << 1) | getC(); \
|
||||
A= b; \
|
||||
setNZC(A & 0x80, !A, b >> 8); \
|
||||
} \
|
||||
next();
|
||||
|
||||
#define ror(ticks, adrmode) \
|
||||
adrmode(ticks); \
|
||||
{ \
|
||||
int c= getC(); \
|
||||
byte m= getMemory(ea); \
|
||||
byte b= (c << 7) | (m >> 1); \
|
||||
fetch(); \
|
||||
putMemory(ea, b); \
|
||||
setNZC(b & 0x80, !b, m & 1); \
|
||||
} \
|
||||
next();
|
||||
|
||||
#define rora(ticks, adrmode) \
|
||||
adrmode(ticks); \
|
||||
{ \
|
||||
int ci= getC(); \
|
||||
int co= A & 1; \
|
||||
fetch(); \
|
||||
A= (ci << 7) | (A >> 1); \
|
||||
setNZC(A & 0x80, !A, co); \
|
||||
} \
|
||||
next();
|
||||
|
||||
#define tRS(ticks, adrmode, R, S) \
|
||||
fetch(); \
|
||||
tick(ticks); \
|
||||
S= R; \
|
||||
setNZ(S & 0x80, !S); \
|
||||
next();
|
||||
|
||||
#define tax(ticks, adrmode) tRS(ticks, adrmode, A, X)
|
||||
#define txa(ticks, adrmode) tRS(ticks, adrmode, X, A)
|
||||
#define tay(ticks, adrmode) tRS(ticks, adrmode, A, Y)
|
||||
#define tya(ticks, adrmode) tRS(ticks, adrmode, Y, A)
|
||||
#define tsx(ticks, adrmode) tRS(ticks, adrmode, S, X)
|
||||
|
||||
#define txs(ticks, adrmode) \
|
||||
fetch(); \
|
||||
tick(ticks); \
|
||||
S= X; \
|
||||
next();
|
||||
|
||||
#define ldR(ticks, adrmode, R) \
|
||||
adrmode(ticks); \
|
||||
fetch(); \
|
||||
R= getMemory(ea); \
|
||||
setNZ(R & 0x80, !R); \
|
||||
next();
|
||||
|
||||
#define lda(ticks, adrmode) ldR(ticks, adrmode, A)
|
||||
#define ldx(ticks, adrmode) ldR(ticks, adrmode, X)
|
||||
#define ldy(ticks, adrmode) ldR(ticks, adrmode, Y)
|
||||
|
||||
#define stR(ticks, adrmode, R) \
|
||||
adrmode(ticks); \
|
||||
fetch(); \
|
||||
putMemory(ea, R); \
|
||||
next();
|
||||
|
||||
#define sta(ticks, adrmode) stR(ticks, adrmode, A)
|
||||
#define stx(ticks, adrmode) stR(ticks, adrmode, X)
|
||||
#define sty(ticks, adrmode) stR(ticks, adrmode, Y)
|
||||
#define stz(ticks, adrmode) stR(ticks, adrmode, 0)
|
||||
|
||||
#define branch(ticks, adrmode, cond) \
|
||||
if (cond) \
|
||||
{ \
|
||||
adrmode(ticks); \
|
||||
PC += ea; \
|
||||
tick(1); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
tick(ticks); \
|
||||
PC++; \
|
||||
} \
|
||||
fetch(); \
|
||||
next();
|
||||
|
||||
#define bcc(ticks, adrmode) branch(ticks, adrmode, !getC())
|
||||
#define bcs(ticks, adrmode) branch(ticks, adrmode, getC())
|
||||
#define bne(ticks, adrmode) branch(ticks, adrmode, !getZ())
|
||||
#define beq(ticks, adrmode) branch(ticks, adrmode, getZ())
|
||||
#define bpl(ticks, adrmode) branch(ticks, adrmode, !getN())
|
||||
#define bmi(ticks, adrmode) branch(ticks, adrmode, getN())
|
||||
#define bvc(ticks, adrmode) branch(ticks, adrmode, !getV())
|
||||
#define bvs(ticks, adrmode) branch(ticks, adrmode, getV())
|
||||
|
||||
#define bra(ticks, adrmode) \
|
||||
adrmode(ticks); \
|
||||
PC += ea; \
|
||||
fetch(); \
|
||||
tick(1); \
|
||||
next();
|
||||
|
||||
#define jmp(ticks, adrmode) \
|
||||
adrmode(ticks); \
|
||||
PC= ea; \
|
||||
if (mpu->callbacks->call[ea]) \
|
||||
{ \
|
||||
word addr; \
|
||||
externalise(); \
|
||||
if ((addr= mpu->callbacks->call[ea](mpu, ea, 0))) \
|
||||
{ \
|
||||
internalise(); \
|
||||
PC= addr; \
|
||||
} \
|
||||
} \
|
||||
fetch(); \
|
||||
next();
|
||||
|
||||
#define jsr(ticks, adrmode) \
|
||||
PC++; \
|
||||
push(PC >> 8); \
|
||||
push(PC & 0xff); \
|
||||
PC--; \
|
||||
adrmode(ticks); \
|
||||
if (mpu->callbacks->call[ea]) \
|
||||
{ \
|
||||
word addr; \
|
||||
externalise(); \
|
||||
if ((addr= mpu->callbacks->call[ea](mpu, ea, 0))) \
|
||||
{ \
|
||||
internalise(); \
|
||||
PC= addr; \
|
||||
fetch(); \
|
||||
next(); \
|
||||
} \
|
||||
} \
|
||||
PC=ea; \
|
||||
fetch(); \
|
||||
next();
|
||||
|
||||
#define rts(ticks, adrmode) \
|
||||
tick(ticks); \
|
||||
PC = pop(); \
|
||||
PC |= (pop() << 8); \
|
||||
PC++; \
|
||||
fetch(); \
|
||||
next();
|
||||
|
||||
#define brk(ticks, adrmode) \
|
||||
tick(ticks); \
|
||||
PC++; \
|
||||
push(PC >> 8); \
|
||||
push(PC & 0xff); \
|
||||
P |= flagB; \
|
||||
push(P | flagX); \
|
||||
P |= flagI; \
|
||||
{ \
|
||||
word hdlr= getMemory(0xfffe) + (getMemory(0xffff) << 8); \
|
||||
if (mpu->callbacks->call[hdlr]) \
|
||||
{ \
|
||||
word addr; \
|
||||
externalise(); \
|
||||
if ((addr= mpu->callbacks->call[hdlr](mpu, PC - 2, 0))) \
|
||||
{ \
|
||||
internalise(); \
|
||||
hdlr= addr; \
|
||||
} \
|
||||
} \
|
||||
PC= hdlr; \
|
||||
} \
|
||||
fetch(); \
|
||||
next();
|
||||
|
||||
#define rti(ticks, adrmode) \
|
||||
tick(ticks); \
|
||||
P= pop(); \
|
||||
PC= pop(); \
|
||||
PC |= (pop() << 8); \
|
||||
fetch(); \
|
||||
next();
|
||||
|
||||
#define nop(ticks, adrmode) \
|
||||
fetch(); \
|
||||
tick(ticks); \
|
||||
next();
|
||||
|
||||
#define ill(ticks, adrmode) \
|
||||
fetch(); \
|
||||
tick(ticks); \
|
||||
fflush(stdout); \
|
||||
fprintf(stderr, "\nundefined instruction %02X\n", memory[PC-1]); \
|
||||
return;
|
||||
|
||||
#define phR(ticks, adrmode, R) \
|
||||
fetch(); \
|
||||
tick(ticks); \
|
||||
push(R); \
|
||||
next();
|
||||
|
||||
#define pha(ticks, adrmode) phR(ticks, adrmode, A)
|
||||
#define phx(ticks, adrmode) phR(ticks, adrmode, X)
|
||||
#define phy(ticks, adrmode) phR(ticks, adrmode, Y)
|
||||
#define php(ticks, adrmode) phR(ticks, adrmode, P | flagX | flagB)
|
||||
|
||||
#define plR(ticks, adrmode, R) \
|
||||
fetch(); \
|
||||
tick(ticks); \
|
||||
R= pop(); \
|
||||
setNZ(R & 0x80, !R); \
|
||||
next();
|
||||
|
||||
#define pla(ticks, adrmode) plR(ticks, adrmode, A)
|
||||
#define plx(ticks, adrmode) plR(ticks, adrmode, X)
|
||||
#define ply(ticks, adrmode) plR(ticks, adrmode, Y)
|
||||
|
||||
#define plp(ticks, adrmode) \
|
||||
fetch(); \
|
||||
tick(ticks); \
|
||||
P= pop(); \
|
||||
next();
|
||||
|
||||
#define clF(ticks, adrmode, F) \
|
||||
fetch(); \
|
||||
tick(ticks); \
|
||||
P &= ~F; \
|
||||
next();
|
||||
|
||||
#define clc(ticks, adrmode) clF(ticks, adrmode, flagC)
|
||||
#define cld(ticks, adrmode) clF(ticks, adrmode, flagD)
|
||||
#define cli(ticks, adrmode) clF(ticks, adrmode, flagI)
|
||||
#define clv(ticks, adrmode) clF(ticks, adrmode, flagV)
|
||||
|
||||
#define seF(ticks, adrmode, F) \
|
||||
fetch(); \
|
||||
tick(ticks); \
|
||||
P |= F; \
|
||||
next();
|
||||
|
||||
#define sec(ticks, adrmode) seF(ticks, adrmode, flagC)
|
||||
#define sed(ticks, adrmode) seF(ticks, adrmode, flagD)
|
||||
#define sei(ticks, adrmode) seF(ticks, adrmode, flagI)
|
||||
|
||||
#define do_insns(_) \
|
||||
_(00, brk, implied, 7); _(01, ora, indx, 6); _(02, ill, implied, 2); _(03, ill, implied, 2); \
|
||||
_(04, tsb, zp, 3); _(05, ora, zp, 3); _(06, asl, zp, 5); _(07, ill, implied, 2); \
|
||||
_(08, php, implied, 3); _(09, ora, immediate, 3); _(0a, asla,implied, 2); _(0b, ill, implied, 2); \
|
||||
_(0c, tsb, abs, 4); _(0d, ora, abs, 4); _(0e, asl, abs, 6); _(0f, ill, implied, 2); \
|
||||
_(10, bpl, relative, 2); _(11, ora, indy, 5); _(12, ora, indzp, 3); _(13, ill, implied, 2); \
|
||||
_(14, trb, zp, 3); _(15, ora, zpx, 4); _(16, asl, zpx, 6); _(17, ill, implied, 2); \
|
||||
_(18, clc, implied, 2); _(19, ora, absy, 4); _(1a, ina, implied, 2); _(1b, ill, implied, 2); \
|
||||
_(1c, trb, abs, 4); _(1d, ora, absx, 4); _(1e, asl, absx, 7); _(1f, ill, implied, 2); \
|
||||
_(20, jsr, abs, 6); _(21, and, indx, 6); _(22, ill, implied, 2); _(23, ill, implied, 2); \
|
||||
_(24, bit, zp, 3); _(25, and, zp, 3); _(26, rol, zp, 5); _(27, ill, implied, 2); \
|
||||
_(28, plp, implied, 4); _(29, and, immediate, 3); _(2a, rola,implied, 2); _(2b, ill, implied, 2); \
|
||||
_(2c, bit, abs, 4); _(2d, and, abs, 4); _(2e, rol, abs, 6); _(2f, ill, implied, 2); \
|
||||
_(30, bmi, relative, 2); _(31, and, indy, 5); _(32, and, indzp, 3); _(33, ill, implied, 2); \
|
||||
_(34, bit, zpx, 4); _(35, and, zpx, 4); _(36, rol, zpx, 6); _(37, ill, implied, 2); \
|
||||
_(38, sec, implied, 2); _(39, and, absy, 4); _(3a, dea, implied, 2); _(3b, ill, implied, 2); \
|
||||
_(3c, bit, absx, 4); _(3d, and, absx, 4); _(3e, rol, absx, 7); _(3f, ill, implied, 2); \
|
||||
_(40, rti, implied, 6); _(41, eor, indx, 6); _(42, ill, implied, 2); _(43, ill, implied, 2); \
|
||||
_(44, ill, implied, 2); _(45, eor, zp, 3); _(46, lsr, zp, 5); _(47, ill, implied, 2); \
|
||||
_(48, pha, implied, 3); _(49, eor, immediate, 3); _(4a, lsra,implied, 2); _(4b, ill, implied, 2); \
|
||||
_(4c, jmp, abs, 3); _(4d, eor, abs, 4); _(4e, lsr, abs, 6); _(4f, ill, implied, 2); \
|
||||
_(50, bvc, relative, 2); _(51, eor, indy, 5); _(52, eor, indzp, 3); _(53, ill, implied, 2); \
|
||||
_(54, ill, implied, 2); _(55, eor, zpx, 4); _(56, lsr, zpx, 6); _(57, ill, implied, 2); \
|
||||
_(58, cli, implied, 2); _(59, eor, absy, 4); _(5a, phy, implied, 3); _(5b, ill, implied, 2); \
|
||||
_(5c, ill, implied, 2); _(5d, eor, absx, 4); _(5e, lsr, absx, 7); _(5f, ill, implied, 2); \
|
||||
_(60, rts, implied, 6); _(61, adc, indx, 6); _(62, ill, implied, 2); _(63, ill, implied, 2); \
|
||||
_(64, stz, zp, 3); _(65, adc, zp, 3); _(66, ror, zp, 5); _(67, ill, implied, 2); \
|
||||
_(68, pla, implied, 4); _(69, adc, immediate, 3); _(6a, rora,implied, 2); _(6b, ill, implied, 2); \
|
||||
_(6c, jmp, indirect, 5); _(6d, adc, abs, 4); _(6e, ror, abs, 6); _(6f, ill, implied, 2); \
|
||||
_(70, bvs, relative, 2); _(71, adc, indy, 5); _(72, adc, indzp, 3); _(73, ill, implied, 2); \
|
||||
_(74, stz, zpx, 4); _(75, adc, zpx, 4); _(76, ror, zpx, 6); _(77, ill, implied, 2); \
|
||||
_(78, sei, implied, 2); _(79, adc, absy, 4); _(7a, ply, implied, 4); _(7b, ill, implied, 2); \
|
||||
_(7c, jmp, indabsx, 6); _(7d, adc, absx, 4); _(7e, ror, absx, 7); _(7f, ill, implied, 2); \
|
||||
_(80, bra, relative, 2); _(81, sta, indx, 6); _(82, ill, implied, 2); _(83, ill, implied, 2); \
|
||||
_(84, sty, zp, 2); _(85, sta, zp, 2); _(86, stx, zp, 2); _(87, ill, implied, 2); \
|
||||
_(88, dey, implied, 2); _(89, bit, immediate, 2); _(8a, txa, implied, 2); _(8b, ill, implied, 2); \
|
||||
_(8c, sty, abs, 4); _(8d, sta, abs, 4); _(8e, stx, abs, 4); _(8f, ill, implied, 2); \
|
||||
_(90, bcc, relative, 2); _(91, sta, indy, 6); _(92, sta, indzp, 3); _(93, ill, implied, 2); \
|
||||
_(94, sty, zpx, 4); _(95, sta, zpx, 4); _(96, stx, zpy, 4); _(97, ill, implied, 2); \
|
||||
_(98, tya, implied, 2); _(99, sta, absy, 5); _(9a, txs, implied, 2); _(9b, ill, implied, 2); \
|
||||
_(9c, stz, abs, 4); _(9d, sta, absx, 5); _(9e, stz, absx, 5); _(9f, ill, implied, 2); \
|
||||
_(a0, ldy, immediate, 3); _(a1, lda, indx, 6); _(a2, ldx, immediate, 3); _(a3, ill, implied, 2); \
|
||||
_(a4, ldy, zp, 3); _(a5, lda, zp, 3); _(a6, ldx, zp, 3); _(a7, ill, implied, 2); \
|
||||
_(a8, tay, implied, 2); _(a9, lda, immediate, 3); _(aa, tax, implied, 2); _(ab, ill, implied, 2); \
|
||||
_(ac, ldy, abs, 4); _(ad, lda, abs, 4); _(ae, ldx, abs, 4); _(af, ill, implied, 2); \
|
||||
_(b0, bcs, relative, 2); _(b1, lda, indy, 5); _(b2, lda, indzp, 3); _(b3, ill, implied, 2); \
|
||||
_(b4, ldy, zpx, 4); _(b5, lda, zpx, 4); _(b6, ldx, zpy, 4); _(b7, ill, implied, 2); \
|
||||
_(b8, clv, implied, 2); _(b9, lda, absy, 4); _(ba, tsx, implied, 2); _(bb, ill, implied, 2); \
|
||||
_(bc, ldy, absx, 4); _(bd, lda, absx, 4); _(be, ldx, absy, 4); _(bf, ill, implied, 2); \
|
||||
_(c0, cpy, immediate, 3); _(c1, cmp, indx, 6); _(c2, ill, implied, 2); _(c3, ill, implied, 2); \
|
||||
_(c4, cpy, zp, 3); _(c5, cmp, zp, 3); _(c6, dec, zp, 5); _(c7, ill, implied, 2); \
|
||||
_(c8, iny, implied, 2); _(c9, cmp, immediate, 3); _(ca, dex, implied, 2); _(cb, ill, implied, 2); \
|
||||
_(cc, cpy, abs, 4); _(cd, cmp, abs, 4); _(ce, dec, abs, 6); _(cf, ill, implied, 2); \
|
||||
_(d0, bne, relative, 2); _(d1, cmp, indy, 5); _(d2, cmp, indzp, 3); _(d3, ill, implied, 2); \
|
||||
_(d4, ill, implied, 2); _(d5, cmp, zpx, 4); _(d6, dec, zpx, 6); _(d7, ill, implied, 2); \
|
||||
_(d8, cld, implied, 2); _(d9, cmp, absy, 4); _(da, phx, implied, 3); _(db, ill, implied, 2); \
|
||||
_(dc, ill, implied, 2); _(dd, cmp, absx, 4); _(de, dec, absx, 7); _(df, ill, implied, 2); \
|
||||
_(e0, cpx, immediate, 3); _(e1, sbc, indx, 6); _(e2, ill, implied, 2); _(e3, ill, implied, 2); \
|
||||
_(e4, cpx, zp, 3); _(e5, sbc, zp, 3); _(e6, inc, zp, 5); _(e7, ill, implied, 2); \
|
||||
_(e8, inx, implied, 2); _(e9, sbc, immediate, 3); _(ea, nop, implied, 2); _(eb, ill, implied, 2); \
|
||||
_(ec, cpx, abs, 4); _(ed, sbc, abs, 4); _(ee, inc, abs, 6); _(ef, ill, implied, 2); \
|
||||
_(f0, beq, relative, 2); _(f1, sbc, indy, 5); _(f2, sbc, indzp, 3); _(f3, ill, implied, 2); \
|
||||
_(f4, ill, implied, 2); _(f5, sbc, zpx, 4); _(f6, inc, zpx, 6); _(f7, ill, implied, 2); \
|
||||
_(f8, sed, implied, 2); _(f9, sbc, absy, 4); _(fa, plx, implied, 4); _(fb, ill, implied, 2); \
|
||||
_(fc, ill, implied, 2); _(fd, sbc, absx, 4); _(fe, inc, absx, 7); _(ff, ill, implied, 2);
|
||||
|
||||
|
||||
|
||||
void M6502_irq(M6502 *mpu)
|
||||
{
|
||||
if (!(mpu->registers->p & flagI))
|
||||
{
|
||||
mpu->memory[0x0100 + mpu->registers->s--] = (byte)(mpu->registers->pc >> 8);
|
||||
mpu->memory[0x0100 + mpu->registers->s--] = (byte)(mpu->registers->pc & 0xff);
|
||||
mpu->memory[0x0100 + mpu->registers->s--] = mpu->registers->p;
|
||||
mpu->registers->p &= ~flagB;
|
||||
mpu->registers->p |= flagI;
|
||||
mpu->registers->pc = M6502_getVector(mpu, IRQ);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void M6502_nmi(M6502 *mpu)
|
||||
{
|
||||
mpu->memory[0x0100 + mpu->registers->s--] = (byte)(mpu->registers->pc >> 8);
|
||||
mpu->memory[0x0100 + mpu->registers->s--] = (byte)(mpu->registers->pc & 0xff);
|
||||
mpu->memory[0x0100 + mpu->registers->s--] = mpu->registers->p;
|
||||
mpu->registers->p &= ~flagB;
|
||||
mpu->registers->p |= flagI;
|
||||
mpu->registers->pc = M6502_getVector(mpu, NMI);
|
||||
}
|
||||
|
||||
|
||||
void M6502_reset(M6502 *mpu)
|
||||
{
|
||||
mpu->registers->p &= ~flagD;
|
||||
mpu->registers->p |= flagI;
|
||||
mpu->registers->pc = M6502_getVector(mpu, RST);
|
||||
}
|
||||
|
||||
|
||||
/* the compiler should elminate all call to this function */
|
||||
|
||||
static void oops(void)
|
||||
{
|
||||
fprintf(stderr, "\noops -- instruction dispatch missing\n");
|
||||
}
|
||||
|
||||
|
||||
void M6502_run(M6502 *mpu)
|
||||
{
|
||||
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
|
||||
|
||||
static void *itab[256]= { &&_00, &&_01, &&_02, &&_03, &&_04, &&_05, &&_06, &&_07, &&_08, &&_09, &&_0a, &&_0b, &&_0c, &&_0d, &&_0e, &&_0f,
|
||||
&&_10, &&_11, &&_12, &&_13, &&_14, &&_15, &&_16, &&_17, &&_18, &&_19, &&_1a, &&_1b, &&_1c, &&_1d, &&_1e, &&_1f,
|
||||
&&_20, &&_21, &&_22, &&_23, &&_24, &&_25, &&_26, &&_27, &&_28, &&_29, &&_2a, &&_2b, &&_2c, &&_2d, &&_2e, &&_2f,
|
||||
&&_30, &&_31, &&_32, &&_33, &&_34, &&_35, &&_36, &&_37, &&_38, &&_39, &&_3a, &&_3b, &&_3c, &&_3d, &&_3e, &&_3f,
|
||||
&&_40, &&_41, &&_42, &&_43, &&_44, &&_45, &&_46, &&_47, &&_48, &&_49, &&_4a, &&_4b, &&_4c, &&_4d, &&_4e, &&_4f,
|
||||
&&_50, &&_51, &&_52, &&_53, &&_54, &&_55, &&_56, &&_57, &&_58, &&_59, &&_5a, &&_5b, &&_5c, &&_5d, &&_5e, &&_5f,
|
||||
&&_60, &&_61, &&_62, &&_63, &&_64, &&_65, &&_66, &&_67, &&_68, &&_69, &&_6a, &&_6b, &&_6c, &&_6d, &&_6e, &&_6f,
|
||||
&&_70, &&_71, &&_72, &&_73, &&_74, &&_75, &&_76, &&_77, &&_78, &&_79, &&_7a, &&_7b, &&_7c, &&_7d, &&_7e, &&_7f,
|
||||
&&_80, &&_81, &&_82, &&_83, &&_84, &&_85, &&_86, &&_87, &&_88, &&_89, &&_8a, &&_8b, &&_8c, &&_8d, &&_8e, &&_8f,
|
||||
&&_90, &&_91, &&_92, &&_93, &&_94, &&_95, &&_96, &&_97, &&_98, &&_99, &&_9a, &&_9b, &&_9c, &&_9d, &&_9e, &&_9f,
|
||||
&&_a0, &&_a1, &&_a2, &&_a3, &&_a4, &&_a5, &&_a6, &&_a7, &&_a8, &&_a9, &&_aa, &&_ab, &&_ac, &&_ad, &&_ae, &&_af,
|
||||
&&_b0, &&_b1, &&_b2, &&_b3, &&_b4, &&_b5, &&_b6, &&_b7, &&_b8, &&_b9, &&_ba, &&_bb, &&_bc, &&_bd, &&_be, &&_bf,
|
||||
&&_c0, &&_c1, &&_c2, &&_c3, &&_c4, &&_c5, &&_c6, &&_c7, &&_c8, &&_c9, &&_ca, &&_cb, &&_cc, &&_cd, &&_ce, &&_cf,
|
||||
&&_d0, &&_d1, &&_d2, &&_d3, &&_d4, &&_d5, &&_d6, &&_d7, &&_d8, &&_d9, &&_da, &&_db, &&_dc, &&_dd, &&_de, &&_df,
|
||||
&&_e0, &&_e1, &&_e2, &&_e3, &&_e4, &&_e5, &&_e6, &&_e7, &&_e8, &&_e9, &&_ea, &&_eb, &&_ec, &&_ed, &&_ee, &&_ef,
|
||||
&&_f0, &&_f1, &&_f2, &&_f3, &&_f4, &&_f5, &&_f6, &&_f7, &&_f8, &&_f9, &&_fa, &&_fb, &&_fc, &&_fd, &&_fe, &&_ff };
|
||||
|
||||
register void **itabp= &itab[0];
|
||||
register void *tpc;
|
||||
|
||||
# define begin() fetch(); next()
|
||||
# define fetch() tpc= itabp[memory[PC++]]
|
||||
# define next() goto *tpc
|
||||
# define dispatch(num, name, mode, cycles) _##num: name(cycles, mode) oops(); next()
|
||||
# define end()
|
||||
|
||||
#else /* (!__GNUC__) || (__STRICT_ANSI__) */
|
||||
|
||||
# define begin() for (;;) switch (memory[PC++]) {
|
||||
# define fetch()
|
||||
# define next() break
|
||||
# define dispatch(num, name, mode, cycles) case 0x##num: name(cycles, mode); next()
|
||||
# define end() }
|
||||
|
||||
#endif
|
||||
|
||||
register byte *memory= mpu->memory;
|
||||
register word PC;
|
||||
word ea;
|
||||
byte A, X, Y, P, S;
|
||||
M6502_Callback *readCallback= mpu->callbacks->read;
|
||||
M6502_Callback *writeCallback= mpu->callbacks->write;
|
||||
|
||||
# define internalise() A= mpu->registers->a; X= mpu->registers->x; Y= mpu->registers->y; P= mpu->registers->p; S= mpu->registers->s; PC= mpu->registers->pc
|
||||
# define externalise() mpu->registers->a= A; mpu->registers->x= X; mpu->registers->y= Y; mpu->registers->p= P; mpu->registers->s= S; mpu->registers->pc= PC
|
||||
|
||||
internalise();
|
||||
|
||||
begin();
|
||||
do_insns(dispatch);
|
||||
end();
|
||||
|
||||
# undef begin
|
||||
# undef internalise
|
||||
# undef externalise
|
||||
# undef fetch
|
||||
# undef next
|
||||
# undef dispatch
|
||||
# undef end
|
||||
|
||||
(void)oops;
|
||||
}
|
||||
|
||||
|
||||
int M6502_disassemble(M6502 *mpu, word ip, char buffer[64])
|
||||
{
|
||||
char *s= buffer;
|
||||
byte *b= mpu->memory + ip;
|
||||
|
||||
switch (b[0])
|
||||
{
|
||||
# define _implied return 1;
|
||||
# define _immediate sprintf(s, "#%02X", b[1]); return 2;
|
||||
# define _zp sprintf(s, "%02X", b[1]); return 2;
|
||||
# define _zpx sprintf(s, "%02X,X", b[1]); return 2;
|
||||
# define _zpy sprintf(s, "%02X,Y", b[1]); return 2;
|
||||
# define _abs sprintf(s, "%02X%02X", b[2], b[1]); return 3;
|
||||
# define _absx sprintf(s, "%02X%02X,X", b[2], b[1]); return 3;
|
||||
# define _absy sprintf(s, "%02X%02X,Y", b[2], b[1]); return 3;
|
||||
# define _relative sprintf(s, "%04X", ip + 2 + (int8_t)b[1]); return 2;
|
||||
# define _indirect sprintf(s, "(%02X%02X)", b[2], b[1]); return 3;
|
||||
# define _indzp sprintf(s, "(%02X)", b[1]); return 2;
|
||||
# define _indx sprintf(s, "(%02X,X)", b[1]); return 2;
|
||||
# define _indy sprintf(s, "(%02X),Y", b[1]); return 2;
|
||||
# define _indabsx sprintf(s, "(%02X%02X,X)", b[2], b[1]); return 3;
|
||||
|
||||
# define disassemble(num, name, mode, cycles) case 0x##num: s += sprintf(s, "%s ", #name); _##mode
|
||||
do_insns(disassemble);
|
||||
# undef _do
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void M6502_dump(M6502 *mpu, char buffer[64])
|
||||
{
|
||||
M6502_Registers *r= mpu->registers;
|
||||
uint8_t p= r->p;
|
||||
# define P(N,C) (p & (1 << (N)) ? (C) : '-')
|
||||
sprintf(buffer, "PC=%04X SP=%04X A=%02X X=%02X Y=%02X P=%02X %c%c%c%c%c%c%c%c",
|
||||
r->pc, 0x0100 + r->s,
|
||||
r->a, r->x, r->y, r->p,
|
||||
P(7,'N'), P(6,'V'), P(5,'?'), P(4,'B'), P(3,'D'), P(2,'I'), P(1,'Z'), P(0,'C'));
|
||||
# undef P
|
||||
}
|
||||
|
||||
|
||||
static void outOfMemory(void)
|
||||
{
|
||||
fflush(stdout);
|
||||
fprintf(stderr, "\nout of memory\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
M6502 *M6502_new(M6502_Registers *registers, M6502_Memory memory, M6502_Callbacks *callbacks)
|
||||
{
|
||||
M6502 *mpu= calloc(1, sizeof(M6502));
|
||||
if (!mpu) outOfMemory();
|
||||
|
||||
if (!registers) { registers = (M6502_Registers *)calloc(1, sizeof(M6502_Registers)); mpu->flags |= M6502_RegistersAllocated; }
|
||||
if (!memory ) { memory = (uint8_t *)calloc(1, sizeof(M6502_Memory )); mpu->flags |= M6502_MemoryAllocated; }
|
||||
if (!callbacks) { callbacks = (M6502_Callbacks *)calloc(1, sizeof(M6502_Callbacks)); mpu->flags |= M6502_CallbacksAllocated; }
|
||||
|
||||
if (!registers || !memory || !callbacks) outOfMemory();
|
||||
|
||||
mpu->registers = registers;
|
||||
mpu->memory = memory;
|
||||
mpu->callbacks = callbacks;
|
||||
|
||||
return mpu;
|
||||
}
|
||||
|
||||
|
||||
void M6502_delete(M6502 *mpu)
|
||||
{
|
||||
if (mpu->flags & M6502_CallbacksAllocated) free(mpu->callbacks);
|
||||
if (mpu->flags & M6502_MemoryAllocated ) free(mpu->memory);
|
||||
if (mpu->flags & M6502_RegistersAllocated) free(mpu->registers);
|
||||
|
||||
free(mpu);
|
||||
}
|
75
lib6502/lib6502.h
Normal file
75
lib6502/lib6502.h
Normal file
@ -0,0 +1,75 @@
|
||||
#ifndef __m6502_h
|
||||
#define __m6502_h
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct _M6502 M6502;
|
||||
typedef struct _M6502_Registers M6502_Registers;
|
||||
typedef struct _M6502_Callbacks M6502_Callbacks;
|
||||
|
||||
typedef int (*M6502_Callback)(M6502 *mpu, uint16_t address, uint8_t data);
|
||||
|
||||
typedef M6502_Callback M6502_CallbackTable[0x10000];
|
||||
typedef uint8_t M6502_Memory[0x10000];
|
||||
|
||||
enum {
|
||||
M6502_NMIVector= 0xfffa, M6502_NMIVectorLSB= 0xfffa, M6502_NMIVectorMSB= 0xfffb,
|
||||
M6502_RSTVector= 0xfffc, M6502_RSTVectorLSB= 0xfffc, M6502_RSTVectorMSB= 0xfffd,
|
||||
M6502_IRQVector= 0xfffe, M6502_IRQVectorLSB= 0xfffe, M6502_IRQVectorMSB= 0xffff
|
||||
};
|
||||
|
||||
struct _M6502_Registers
|
||||
{
|
||||
uint8_t a; /* accumulator */
|
||||
uint8_t x; /* X index register */
|
||||
uint8_t y; /* Y index register */
|
||||
uint8_t p; /* processor status register */
|
||||
uint8_t s; /* stack pointer */
|
||||
uint16_t pc; /* program counter */
|
||||
};
|
||||
|
||||
struct _M6502_Callbacks
|
||||
{
|
||||
M6502_CallbackTable read;
|
||||
M6502_CallbackTable write;
|
||||
M6502_CallbackTable call;
|
||||
};
|
||||
|
||||
struct _M6502
|
||||
{
|
||||
M6502_Registers *registers;
|
||||
uint8_t *memory;
|
||||
M6502_Callbacks *callbacks;
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
enum {
|
||||
M6502_RegistersAllocated = 1 << 0,
|
||||
M6502_MemoryAllocated = 1 << 1,
|
||||
M6502_CallbacksAllocated = 1 << 2
|
||||
};
|
||||
|
||||
extern M6502 *M6502_new(M6502_Registers *registers, M6502_Memory memory, M6502_Callbacks *callbacks);
|
||||
extern void M6502_reset(M6502 *mpu);
|
||||
extern void M6502_nmi(M6502 *mpu);
|
||||
extern void M6502_irq(M6502 *mpu);
|
||||
extern void M6502_run(M6502 *mpu);
|
||||
extern int M6502_disassemble(M6502 *mpu, uint16_t addr, char buffer[64]);
|
||||
extern void M6502_dump(M6502 *mpu, char buffer[64]);
|
||||
extern void M6502_delete(M6502 *mpu);
|
||||
|
||||
#define M6502_getVector(MPU, VEC) \
|
||||
( ( ((MPU)->memory[M6502_##VEC##VectorLSB]) ) \
|
||||
| ((MPU)->memory[M6502_##VEC##VectorMSB] << 8) )
|
||||
|
||||
#define M6502_setVector(MPU, VEC, ADDR) \
|
||||
( ( ((MPU)->memory[M6502_##VEC##VectorLSB]= ((uint8_t)(ADDR)) & 0xff) ) \
|
||||
, ((MPU)->memory[M6502_##VEC##VectorMSB]= (uint8_t)((ADDR) >> 8)) )
|
||||
|
||||
#define M6502_getCallback(MPU, TYPE, ADDR) ((MPU)->callbacks->TYPE[ADDR])
|
||||
#define M6502_setCallback(MPU, TYPE, ADDR, FN) ((MPU)->callbacks->TYPE[ADDR]= (FN))
|
||||
|
||||
|
||||
#endif __m6502_h
|
1
lib6502/man/M6502_delete.3
Normal file
1
lib6502/man/M6502_delete.3
Normal file
@ -0,0 +1 @@
|
||||
.so man3/lib6502.3
|
1
lib6502/man/M6502_disassemble.3
Normal file
1
lib6502/man/M6502_disassemble.3
Normal file
@ -0,0 +1 @@
|
||||
.so man3/lib6502.3
|
1
lib6502/man/M6502_dump.3
Normal file
1
lib6502/man/M6502_dump.3
Normal file
@ -0,0 +1 @@
|
||||
.so man3/lib6502.3
|
1
lib6502/man/M6502_getCallback.3
Normal file
1
lib6502/man/M6502_getCallback.3
Normal file
@ -0,0 +1 @@
|
||||
.so man3/lib6502.3
|
1
lib6502/man/M6502_getVector.3
Normal file
1
lib6502/man/M6502_getVector.3
Normal file
@ -0,0 +1 @@
|
||||
.so man3/lib6502.3
|
1
lib6502/man/M6502_irq.3
Normal file
1
lib6502/man/M6502_irq.3
Normal file
@ -0,0 +1 @@
|
||||
.so man3/lib6502.3
|
1
lib6502/man/M6502_new.3
Normal file
1
lib6502/man/M6502_new.3
Normal file
@ -0,0 +1 @@
|
||||
.so man3/lib6502.3
|
1
lib6502/man/M6502_nmi.3
Normal file
1
lib6502/man/M6502_nmi.3
Normal file
@ -0,0 +1 @@
|
||||
.so man3/lib6502.3
|
1
lib6502/man/M6502_reset.3
Normal file
1
lib6502/man/M6502_reset.3
Normal file
@ -0,0 +1 @@
|
||||
.so man3/lib6502.3
|
1
lib6502/man/M6502_run.3
Normal file
1
lib6502/man/M6502_run.3
Normal file
@ -0,0 +1 @@
|
||||
.so man3/lib6502.3
|
1
lib6502/man/M6502_setCallback.3
Normal file
1
lib6502/man/M6502_setCallback.3
Normal file
@ -0,0 +1 @@
|
||||
.so man3/lib6502.3
|
1
lib6502/man/M6502_setVector.3
Normal file
1
lib6502/man/M6502_setVector.3
Normal file
@ -0,0 +1 @@
|
||||
.so man3/lib6502.3
|
508
lib6502/man/lib6502.3
Normal file
508
lib6502/man/lib6502.3
Normal file
@ -0,0 +1,508 @@
|
||||
.\" Copyright (c) 2005 Ian Piumarta
|
||||
.\"
|
||||
.\" Permission is hereby granted, free of charge, to any person
|
||||
.\" obtaining a copy of this software and associated documentation
|
||||
.\" files (the 'Software'), to deal in the Software without
|
||||
.\" restriction, including without limitation the rights to use, copy,
|
||||
.\" modify, merge, publish, distribute, and/or sell copies of the
|
||||
.\" Software, and to permit persons to whom the Software is furnished
|
||||
.\" to do so, provided that the above copyright notice(s) and this
|
||||
.\" permission notice appear in all copies of the Software and that
|
||||
.\" both the above copyright notice(s) and this permission notice
|
||||
.\" appear in supporting documentation.
|
||||
.\"
|
||||
.\" THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK.
|
||||
.\"
|
||||
.\" last edited: 2005-11-02 01:18:07 by piumarta on margaux.local
|
||||
.\"
|
||||
.Dd October 31, 2005
|
||||
.Dt LIB6502 3 LOCAL
|
||||
.Os ""
|
||||
.\" ----------------------------------------------------------------
|
||||
.Sh NAME
|
||||
.\"
|
||||
.Nm lib6502
|
||||
.Nd 6502 microprocessor emulator
|
||||
.\" ----------------------------------------------------------------
|
||||
.Sh SYNOPSIS
|
||||
.\"
|
||||
.In stdint.h
|
||||
.In lib6502.h
|
||||
.Ft M6502 *
|
||||
.Fn M6502_new "M6502_Registers *registers" "M6502_Memory memory" "M6502_Callbacks *callbacks"
|
||||
.Ft void
|
||||
.Fn M6502_reset "M6502 *mpu"
|
||||
.Ft void
|
||||
.Fn M6502_nmi "M6502 *mpu"
|
||||
.Ft void
|
||||
.Fn M6502_irq "M6502 *mpu"
|
||||
.Ft uint16_t
|
||||
.Fn M6502_getVector "M6502 *mpu" "vector"
|
||||
.Ft uint16_t
|
||||
.Fn M6502_setVector "M6502 *mpu" "vector" "uint16_t address"
|
||||
.Ft M6502_Callback
|
||||
.Fn M6502_getCallback "M6502 *mpu" "type" "uint16_t address"
|
||||
.Ft M6502_Callback
|
||||
.Fn M6502_setCallback "M6502 *mpu" "type" "uint16_t address" "M6502_Callback callback"
|
||||
.Ft void
|
||||
.Fn M6502_run "M6502 *mpu"
|
||||
.Ft int
|
||||
.Fn M6502_disassemble "M6502 *mpu" "uint16_t address" "char buffer[64]"
|
||||
.Ft void
|
||||
.Fn M6502_dump "M6502 *mpu" "char buffer[64]"
|
||||
.Ft void
|
||||
.Fn M6502_delete "M6502 *mpu"
|
||||
.\" ----------------------------------------------------------------
|
||||
.Sh DESCRIPTION
|
||||
.\"
|
||||
.Fn M6502_new
|
||||
creates an instance of a 6502 microprocessor.
|
||||
.Fn M6502_reset ,
|
||||
.Fn M6502_nmi
|
||||
and
|
||||
.Fn M6502_irq
|
||||
place it into the states associated with the hardware signals for
|
||||
reset, non-maskable interrupt and interrupt request, respectively.
|
||||
The macros
|
||||
.Fn M6502_getVector
|
||||
and
|
||||
.Fn M6502_setVector
|
||||
read and write the vectors through which the processor jumps in
|
||||
response to the above signals. The macros
|
||||
.Fn M6502_getCallback
|
||||
and
|
||||
.Fn M6502_setVector
|
||||
read and write client-supplied functions that intercept accesses to
|
||||
memory.
|
||||
.Fn M6502_run
|
||||
begins emulated execution.
|
||||
.Fn M6502_dump
|
||||
and
|
||||
.Fn M6502_disassemble
|
||||
create human-readable representations of processor or memory state.
|
||||
.Fn M6502_delete
|
||||
frees all resources associated with a processor instance. Each of
|
||||
these functions and macros is described in more detail below.
|
||||
.Pp
|
||||
.Fn M6502_new
|
||||
returns a pointer to a
|
||||
.Fa M6502
|
||||
structure containing at least the following members:
|
||||
.Bd -literal
|
||||
struct _M6502
|
||||
{
|
||||
M6502_Registers *registers; /* processor state */
|
||||
uint8_t *memory; /* memory image */
|
||||
M6502_Callbacks *callbacks; /* r/w/x callbacks */
|
||||
};
|
||||
.Ed
|
||||
.Pp
|
||||
These members are initialised according to the supplied
|
||||
.Fa registers ,
|
||||
.Fa memory
|
||||
and
|
||||
.Fa callbacks
|
||||
arguments. If a given argument is NULL, the corresponding member is
|
||||
initialised automatically with a suitable (non-NULL) value.
|
||||
.Pp
|
||||
The members of
|
||||
.Fa M6502
|
||||
are as follows:
|
||||
.Bl -tag -width ".Fa callbacks"
|
||||
.It Fa registers
|
||||
the processor state, containing all registers and condition codes.
|
||||
.It Fa memory
|
||||
a block of at least 64 kilobytes of storage containing the processor's
|
||||
memory. (An array type
|
||||
.Vt M6502_Memory,
|
||||
suitable for defining values to pass as the
|
||||
.Fa memory
|
||||
argument, is defined in the
|
||||
.In lib6502.h
|
||||
include file.)
|
||||
.It Fa callbacks
|
||||
a structure mapping processor memory accesses to client callback
|
||||
functions.
|
||||
.El
|
||||
.Pp
|
||||
Access to the contents of the
|
||||
.Fa registers
|
||||
and
|
||||
.Fa memory
|
||||
members can be made directly.
|
||||
The
|
||||
.Fa registers
|
||||
member is a
|
||||
.Vt M6502_Registers
|
||||
containing the following members:
|
||||
.Bd -literal
|
||||
struct _M6502_Registers
|
||||
{
|
||||
uint8_t a; /* accumulator */
|
||||
uint8_t x; /* X index register */
|
||||
uint8_t y; /* Y index register */
|
||||
uint8_t p; /* processor status register */
|
||||
uint8_t s; /* stack pointer */
|
||||
uint16_t pc; /* program counter */
|
||||
};
|
||||
.Ed
|
||||
.Pp
|
||||
The
|
||||
.Fa memory
|
||||
member is an array of
|
||||
.Vt unsigned char
|
||||
and can be indexed directly. In addition, two convenience macros
|
||||
.Fn M6502_getVector
|
||||
and
|
||||
.Fn M6502_setVector
|
||||
provide access to the reset and interrupt vectors within
|
||||
.Fa memory .
|
||||
.Fn M6502_getVector
|
||||
returns the address stored in the named
|
||||
.Fa vector
|
||||
which must be precisely one of the following:
|
||||
.Bl -tag -width ".Dv RST" -offset indent
|
||||
.It Dv RST
|
||||
the reset vector.
|
||||
.It Dv NMI
|
||||
the non-maskable interrupt vector.
|
||||
.It Dv IRQ
|
||||
the interrupt request vector.
|
||||
.El
|
||||
.Pp
|
||||
.Fn M6502_setVector
|
||||
stores its
|
||||
.Fa address
|
||||
argument in the named
|
||||
.Fa vector
|
||||
and returns the new value.
|
||||
.Pp
|
||||
The
|
||||
.Fa callbacks
|
||||
member contains an opaque structure mapping processor memory accesses
|
||||
to client callback functions. Whenever the processor performs an
|
||||
access for which a corresponding entry exists in the the
|
||||
.Fa callbacks
|
||||
structure, the emulator suspends execution and invokes the callback to
|
||||
complete the operation. Each callback function should have a
|
||||
signature equivalent to:
|
||||
.Bd -ragged -offset indent
|
||||
int
|
||||
.Va callback
|
||||
(M6502 *mpu, uint16_t address, uint8_t data);
|
||||
.Ed
|
||||
.Pp
|
||||
The macros
|
||||
.Fn M6502_getCallback
|
||||
and
|
||||
.Fn M6502_setCallback
|
||||
read and write entries in the
|
||||
.Fa callbacks
|
||||
structure. These macros identify a unique memory access operation
|
||||
from the specified
|
||||
.Fa address
|
||||
on which it operates and
|
||||
.Fa type
|
||||
of access involved. The
|
||||
.Fa type
|
||||
argument must be one of the following:
|
||||
.Bl -tag -width ".Dv write"
|
||||
.It Dv read
|
||||
the
|
||||
.Fa callback
|
||||
is invoked when the processor attempts to read from the
|
||||
given address. The emulator passes the effective address of the
|
||||
operation to the callback in its
|
||||
.Fa address
|
||||
argument. (The
|
||||
.Fa data
|
||||
argument is undefined.) The value returned by the callback will be
|
||||
used by the emulator as the result of the read operation.
|
||||
.It Dv write
|
||||
the
|
||||
.Fa callback
|
||||
is invoked when the processor attempts to write to the
|
||||
given address. The emulator passes the effective address of the
|
||||
operation to the callback in its
|
||||
.Fa address
|
||||
argument and the byte being written in the
|
||||
.Fa data
|
||||
argument. The emulator will not perform the write operation before
|
||||
invoking the callback; if the write should complete, the callback must
|
||||
modify the processor's
|
||||
.Fa memory
|
||||
explicitly. The valued returned from the callback is ignored.
|
||||
.It Dv call
|
||||
the
|
||||
.Fa callback
|
||||
is invoked when the processor attempts to transfer control to the
|
||||
given address by any instruction other than a relative branch. The
|
||||
emulator passes the destination address to the callback in its
|
||||
.Fa address
|
||||
argument and the instruction that initiated the control transfer in
|
||||
its
|
||||
.Fa data
|
||||
argument (one of JMP, JSR, BRK, RTS or RTI). If the callback returns
|
||||
zero (the callback refuses to handle the operation) the emulator will
|
||||
allow the operation to complete as normal. If the callback returns a
|
||||
non-zero address (indicating that the callback has handled the
|
||||
operation internally) the emulator will transfer control to that
|
||||
address.
|
||||
.El
|
||||
.Pp
|
||||
.Fn M6502_getCallback
|
||||
returns zero if there is no callback associated with the given
|
||||
.Fa type
|
||||
and
|
||||
.Fa address .
|
||||
Passing zero as the
|
||||
.Fa callback
|
||||
argument of
|
||||
.Fn M6502_setCallback
|
||||
removes any callback that might have been associated with
|
||||
.Fa type
|
||||
and
|
||||
.Fa address .
|
||||
.Pp
|
||||
.Fn M6502_run
|
||||
emulates processor execution in the given
|
||||
.Fa mpu
|
||||
by repeatedly fetching the instruction addressed by
|
||||
.Fa pc
|
||||
and dispatching to it. This function normally never returns.
|
||||
.Pp
|
||||
.Fn M6502_dump
|
||||
writes a (NUL-terminated) symbolic representation of the processor's
|
||||
internal state into the supplied
|
||||
.Fa buffer .
|
||||
Typical output resembles:
|
||||
.Bd -literal -offset indent
|
||||
PC=1010 SP=01FE A=0A X=5B Y=00 P=D1 NV-B---C
|
||||
.Ed
|
||||
.Pp
|
||||
.Fn M6502_disassemble
|
||||
writes a (NUL-terminated) symbolic representation of the instruction
|
||||
in the processor's memory at the given
|
||||
.Fa address
|
||||
into the supplied
|
||||
.Fa buffer .
|
||||
It returns the size (in bytes) of the instruction. (In other words,
|
||||
the amount by which
|
||||
.Fa address
|
||||
should be incremented to arrive at the next instruction.)
|
||||
Typical output resembles:
|
||||
.Bd -literal -offset indent
|
||||
1009 cpx #5B
|
||||
.Ed
|
||||
.Pp
|
||||
(The
|
||||
.Fa buffer
|
||||
arguments are oversized to allow for future expansion.)
|
||||
.Pp
|
||||
.Fn M6502_delete
|
||||
frees the resources associated with the given
|
||||
.Fa mpu.
|
||||
Any members that were allocated implicitly (passed as NULL to
|
||||
.Fn M6502_new )
|
||||
are deallocated. Members that were initialised from non-NULL
|
||||
arguments are not deallocated.
|
||||
.\" ----------------------------------------------------------------
|
||||
.Sh IMPLEMENTATION NOTES
|
||||
.\"
|
||||
You can share the
|
||||
.Fa memory
|
||||
and
|
||||
.Fa callbacks
|
||||
members of
|
||||
.Vt M6502
|
||||
between multiple instances to simulate multiprocessor hardware.
|
||||
.\" ----------------------------------------------------------------
|
||||
.Sh RETURN VALUES
|
||||
.\"
|
||||
.Fn M6502_new
|
||||
returns a pointer to a
|
||||
.Vt M6502
|
||||
structure.
|
||||
.Fn M6502_getVector
|
||||
and
|
||||
.Fn M6502_setVector
|
||||
return the contents of the given
|
||||
.Fa vector .
|
||||
.Fn M6502_getCallback
|
||||
and
|
||||
.Fn M6502_setCallback
|
||||
return the
|
||||
.Vt M6502_Callback
|
||||
function associated with the given
|
||||
.Fa address
|
||||
and access
|
||||
.Fa type .
|
||||
.Fn M6502_disassemble
|
||||
returns the size (in bytes) of the instruction at the given
|
||||
.Fa address .
|
||||
.Fn M6502_reset ,
|
||||
.Fn M6502_nmi ,
|
||||
.Fn M6502_irq ,
|
||||
.Fn M6502_run ,
|
||||
.Fn M6502_dump
|
||||
and
|
||||
.Fn M6502_delete
|
||||
don't return anything (unless you forgot to include
|
||||
.In lib6502.h ) .
|
||||
.\" ----------------------------------------------------------------
|
||||
.Sh EXAMPLES
|
||||
.\"
|
||||
The following program creates a 6502 processor, sets up callbacks for
|
||||
printing characters and halting after a BRK instruction, stores a
|
||||
program into memory that prints the alphabet, disassembles the program
|
||||
on stdout, and then executes the program.
|
||||
.Bd -literal -offset indent -compact
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "lib6502.h"
|
||||
|
||||
#define WRCH 0xFFEE
|
||||
|
||||
int wrch(M6502 *mpu, uint16_t address, uint8_t data)
|
||||
{
|
||||
int pc;
|
||||
putchar(mpu->registers->a);
|
||||
pc = mpu->memory[++mpu->registers->s + 0x100];
|
||||
pc |= mpu->memory[++mpu->registers->s + 0x100] << 8;
|
||||
return pc + 1; /* JSR pushes next insn addr - 1 */
|
||||
}
|
||||
|
||||
int done(M6502 *mpu, uint16_t address, uint8_t data)
|
||||
{
|
||||
char buffer[64];
|
||||
M6502_dump(mpu, buffer);
|
||||
printf("\\nBRK instruction\\n%s\\n", buffer);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
M6502 *mpu = M6502_new(0, 0, 0);
|
||||
unsigned pc = 0x1000;
|
||||
|
||||
mpu->callbacks->call[WRCH] = wrch; /* write character */
|
||||
mpu->callbacks->call[0000] = done; /* reached after BRK */
|
||||
|
||||
# define gen1(X) (mpu->memory[pc++] = (uint8_t)(X))
|
||||
# define gen2(X,Y) gen1(X); gen1(Y)
|
||||
# define gen3(X,Y,Z) gen1(X); gen2(Y,Z)
|
||||
|
||||
gen2(0xA2, 'A' ); /* LDX #'A' */
|
||||
gen1(0x8A ); /* TXA */
|
||||
gen3(0x20,0xEE,0xFF); /* JSR FFEE */
|
||||
gen1(0xE8 ); /* INX */
|
||||
gen2(0xE0, 'Z'+1 ); /* CPX #'Z'+1 */
|
||||
gen2(0xD0, -9 ); /* BNE 1002 */
|
||||
gen2(0xA9, '\\n' ); /* LDA #'\\n' */
|
||||
gen3(0x20,0xEE,0xFF); /* JSR FFEE */
|
||||
gen2(0x00,0x00 ); /* BRK */
|
||||
|
||||
{
|
||||
uint16_t ip = 0x1000;
|
||||
while (ip < pc)
|
||||
{
|
||||
char insn[64];
|
||||
ip += M6502_disassemble(mpu, ip, insn);
|
||||
printf("%04X %s\\n", ip, insn);
|
||||
}
|
||||
}
|
||||
|
||||
M6502_setVector(mpu, RST, 0x1000);
|
||||
|
||||
M6502_reset(mpu);
|
||||
M6502_run(mpu);
|
||||
M6502_delete(mpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
.Ed
|
||||
.\" ----------------------------------------------------------------
|
||||
.Sh DIAGNOSTICS
|
||||
.\"
|
||||
If
|
||||
.Fn M6502_new
|
||||
cannot allocate sufficient memory it prints "out of memory" to stderr
|
||||
and exits with a non-zero status.
|
||||
.Pp
|
||||
If
|
||||
.Fn M6502_run
|
||||
encounters an illegal or undefined instruction, it prints "undefined
|
||||
instruction" and the processor's state to stderr, then exits with a
|
||||
non-zero status.
|
||||
.\" ----------------------------------------------------------------
|
||||
.Sh COMPATIBILITY
|
||||
.\"
|
||||
M6502 is a generic name. The initial letter is mandated by C naming
|
||||
conventions and chosen in deference to MOS Technology, the original
|
||||
designers of the processor. To the best of my knowledge the 'M'
|
||||
prefix was never stamped on a physical 6502.
|
||||
.Pp
|
||||
The emulator implements the CMOS version of the processor (NMOS bugs
|
||||
in effective address calculations involving page boundaries are
|
||||
corrected) but does not tolerate the execution of undefined
|
||||
instructions (which were all no-ops in the first-generation CMOS
|
||||
hardware). It would be nice to support the several alternative
|
||||
instruction sets (model-specific undocumented instructions in NMOS
|
||||
models, and various documented extensions in the later CMOS models)
|
||||
but there are currently no plans to do so.
|
||||
.Pp
|
||||
The emulated 6502 will run much faster than real hardware on any
|
||||
modern computer. The fastest 6502 hardware available at the time of
|
||||
writing has a clock speed of 14 MHz. On a 2 GHz PowerPC, the emulated
|
||||
6502 runs at almost 300 MHz.
|
||||
.\" ----------------------------------------------------------------
|
||||
.Sh SEE ALSO
|
||||
.\"
|
||||
.Xr run6502 1
|
||||
.Pp
|
||||
For development tools, documentation and source code:
|
||||
.Pa http://6502.org
|
||||
.\" ----------------------------------------------------------------
|
||||
.Sh AUTHORS
|
||||
.\"
|
||||
The software and manual pages were written by Ian Piumarta.
|
||||
.Pp
|
||||
The software is provided as-is, with absolutely no warranty, in the
|
||||
hope that you will enjoy and benefit from it. You may use (entirely
|
||||
at your own risk) and redistribute it under the terms of a very
|
||||
liberal license that does not seek to restrict your rights in any way
|
||||
(unlike certain so-called 'open source' licenses that significantly
|
||||
limit your freedom in the name of 'free' software that is, ultimately,
|
||||
anything but free). See the file COPYING for details.
|
||||
.\" ----------------------------------------------------------------
|
||||
.Sh BUGS
|
||||
.\"
|
||||
.Fn M6502_getVector
|
||||
and
|
||||
.Fn M6502_setVector
|
||||
evaluate their arguments more than once.
|
||||
.Pp
|
||||
The out-of-memory condition and attempted execution of
|
||||
illegal/undefined instructions should not be fatal errors.
|
||||
.Pp
|
||||
There is no way to limit the duration of execution within
|
||||
.Fn M6502_run
|
||||
to a certain number of instructions or cycles.
|
||||
.Pp
|
||||
The emulator should support some means of implicit interrupt
|
||||
generation, either by polling or in response to (Unix) signals.
|
||||
.Pp
|
||||
The
|
||||
.Sx COMPATIBILITY
|
||||
section in this manual page has been diverted from its legitimate
|
||||
purpose.
|
||||
.Pp
|
||||
The plural of 'callback' really aught to be 'callsback'.
|
||||
.Pp
|
||||
Please send bug reports (and feature requests) to the author at:
|
||||
firstName (at) lastName (dot) com. (See
|
||||
.Sx AUTHORS
|
||||
above for suitable values of firstName and lastName.)
|
380
lib6502/man/run6502.1
Normal file
380
lib6502/man/run6502.1
Normal file
@ -0,0 +1,380 @@
|
||||
.\" Copyright (c) 2005 Ian Piumarta
|
||||
.\"
|
||||
.\" Permission is hereby granted, free of charge, to any person
|
||||
.\" obtaining a copy of this software and associated documentation
|
||||
.\" files (the 'Software'), to deal in the Software without
|
||||
.\" restriction, including without limitation the rights to use, copy,
|
||||
.\" modify, merge, publish, distribute, and/or sell copies of the
|
||||
.\" Software, and to permit persons to whom the Software is furnished
|
||||
.\" to do so, provided that the above copyright notice(s) and this
|
||||
.\" permission notice appear in all copies of the Software and that
|
||||
.\" both the above copyright notice(s) and this permission notice
|
||||
.\" appear in supporting documentation.
|
||||
.\"
|
||||
.\" THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK.
|
||||
.\"
|
||||
.\" last edited: 2005-11-02 01:18:22 by piumarta on margaux.local
|
||||
.\"
|
||||
.Dd October 31, 2005
|
||||
.Dt RUN6502 1 LOCAL
|
||||
.Os ""
|
||||
.\" ----------------------------------------------------------------
|
||||
.Sh NAME
|
||||
.\"
|
||||
.Nm run6502
|
||||
.Nd execute a 6502 microprocessor program
|
||||
.\" ----------------------------------------------------------------
|
||||
.Sh SYNOPSIS
|
||||
.\"
|
||||
.Nm run6502
|
||||
.Op Ar option ...
|
||||
.Nm run6502
|
||||
.Op Ar option ...
|
||||
.Fl B
|
||||
.Op Ar
|
||||
.\" ----------------------------------------------------------------
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm run6502
|
||||
command emulates the execution of a 6502 microprocessor. It creates a
|
||||
memory image from the contents of one or more files on the command
|
||||
line and then simulates a power-on hardware reset to begin execution.
|
||||
.Pp
|
||||
In its first form,
|
||||
.Nm run6502
|
||||
emulates an embedded 6502 processor with 64 kilobytes of RAM, no
|
||||
memory-mapped hardware, and no input-output capabilities. Limited
|
||||
interaction with the machine is possible only through the
|
||||
.Fl G , M
|
||||
and
|
||||
.Fl P
|
||||
options.
|
||||
.Pp
|
||||
In its second form (with the
|
||||
.Fl B
|
||||
option)
|
||||
.Nm run6502
|
||||
provides minimal emulation of Acorn 'BBC Model B' hardware with 32
|
||||
kilobytes of RAM, 16 kilobytes of paged language ROMs, and 16
|
||||
kilobytes of operating system ROM. A few MOS calls are intercepted to
|
||||
provide keyboard input and screen output via stdin and stdout.
|
||||
Switching between the sixteen paged read-only memory banks is also
|
||||
supported by the usual memory-mapped control register. Any
|
||||
.Ar file
|
||||
arguments after the
|
||||
.Fl B
|
||||
are loaded into successive paged ROM banks (starting at 15 and working
|
||||
down towards 0) before execution begins.
|
||||
.\" ----------------------------------------------------------------
|
||||
.Ss Options
|
||||
.\"
|
||||
.Bl -tag -width indent
|
||||
.It Fl B
|
||||
enable minimal Acorn 'BBC Model B' hardware emulation:
|
||||
.Bl -bullet
|
||||
.It
|
||||
the contents of memory between addresses 0x8000 and 0xBFFF are copied
|
||||
into paged ROM number 0;
|
||||
.It
|
||||
memory between 0x8000 and 0xBFFF becomes bank-switchable between
|
||||
sixteen different ROM images;
|
||||
.It
|
||||
the memory-mapped pages ('FRED', 'JIM' and 'SHEILA') between 0xFC00
|
||||
and 0xFEFF are initialised to harmless values;
|
||||
.It
|
||||
the upper half of the address space is write-protected; and
|
||||
.It
|
||||
callbacks are installed on several OS entry points to provide
|
||||
input-output via stdin and stdout.
|
||||
.El
|
||||
.Pp
|
||||
Any remaining non-option arguments on the command line will name files
|
||||
to be loaded successively into paged ROMs, starting at 15 and working
|
||||
downwards towards 0.
|
||||
.It Fl d Ar addr Ar end
|
||||
dump memory from the address
|
||||
.Ar addr
|
||||
(given in hexadecimal) up to (but not including)
|
||||
.Ar end .
|
||||
The
|
||||
.Ar end
|
||||
argument is either an absolute address or a relative address specified
|
||||
as a '+' character followed by the number (in hexadecimal) of bytes to
|
||||
dump. In other words, the following two options dump the same region
|
||||
of memory:
|
||||
.Bd -ragged -offset indent
|
||||
.Fl d
|
||||
8000 C000
|
||||
.Ed
|
||||
.Bd -ragged -offset indent -compact
|
||||
.Fl d
|
||||
8000 +4000
|
||||
.Ed
|
||||
.Pp
|
||||
The format of the dump cannot currently be modified and consists of
|
||||
the current address followed by one, two or three hexadecimal bytes,
|
||||
and a symbolic representation of the instruction at that address.
|
||||
.It Fl G Ar addr
|
||||
arrange that subroutine calls to
|
||||
.Ar addr
|
||||
will behave as if there were an implementation of
|
||||
.Xr getchar 3
|
||||
at that address, reading a character from stdin and returning it in
|
||||
the accumulator.
|
||||
.It Fl h
|
||||
print a summary of the available options and then exit.
|
||||
.It Fl I Ar addr
|
||||
set the IRQ (interrupt request) vector (the address to which the
|
||||
processor will transfer control upon execution of a BRK instruction).
|
||||
Setting this address to zero will cause execution to halt (and the
|
||||
emulator to exit) when a BRK instruction is encountered.
|
||||
.It Fl i Ar addr Ar file
|
||||
Load
|
||||
.Ar file
|
||||
into the memory image at the address
|
||||
.Ar addr
|
||||
(in hexadecimal), skipping over any initial '#!' interpreter line.
|
||||
.It Fl l Ar addr Ar file
|
||||
Load
|
||||
.Ar file
|
||||
into the memory image at the address
|
||||
.Ar addr
|
||||
(in hexadecimal).
|
||||
.It Fl M Ar addrio
|
||||
arrange that memory reads from address
|
||||
.Ar addrio
|
||||
will return the next character on stdin (blocking if necessary), and
|
||||
memory writes to
|
||||
.Ar addrio
|
||||
will send the value written to stdout.
|
||||
.It Fl N Ar addr
|
||||
set the NMI (non-maskable interrupt) vector to
|
||||
.Ar addr .
|
||||
.It Fl P Ar addr
|
||||
arrange that subroutine calls to
|
||||
.Ar addr
|
||||
will behave as if there were an implementation of
|
||||
.Xr putchar 3
|
||||
at that address, writing the contents of the accumulator to stdout.
|
||||
.It Fl R Ar addr
|
||||
set the RST (hardware reset) vector. The processor will transfer
|
||||
control to this address when emulated execution begins.
|
||||
.It Fl s Ar addr Ar end Ar file
|
||||
save the contents of memory from the address
|
||||
.Ar addr
|
||||
up to
|
||||
.Ar end
|
||||
(exclusive) to the given
|
||||
.Ar file .
|
||||
As with the
|
||||
.Fl d
|
||||
option,
|
||||
.Ar end
|
||||
can be absolute or '+' followed by a byte count.
|
||||
.It Fl v
|
||||
print version information and then exit.
|
||||
.It Fl X Ar addr
|
||||
arrange that any transfer of control to the address
|
||||
.Ar addr
|
||||
will cause an immediate exit with zero exit status.
|
||||
.It Fl x
|
||||
exit immediately. (Useful after
|
||||
.Fl d
|
||||
or when
|
||||
.Nm run6502
|
||||
is being used as a trivial 'image editor', with several
|
||||
.Fl l
|
||||
options followed by
|
||||
.Fl s
|
||||
and
|
||||
.Fl x . )
|
||||
.It Ar
|
||||
following a
|
||||
.Fl B
|
||||
option, load one or more ROM image
|
||||
files
|
||||
into successive paged ROM slots. Other than the paging aspect, this
|
||||
is equivalent to:
|
||||
.Bd -ragged -offset indent
|
||||
.Fl l Ar 8000 Ar image
|
||||
.Ed
|
||||
.El
|
||||
.\" ----------------------------------------------------------------
|
||||
.Sh EXAMPLES
|
||||
.\"
|
||||
.Ss A Very Simple Program
|
||||
The
|
||||
.Xr perl 1
|
||||
command can be used to create a binary file from hexadecimal input:
|
||||
.Bd -literal
|
||||
echo a2418a20eeffe8e05bd0f7a90a20eeff00 |
|
||||
perl -e 'print pack "H*",<STDIN>' > temp.img
|
||||
.Ed
|
||||
.Pp
|
||||
The file can be loaded and executed with:
|
||||
.Bd -literal
|
||||
run6502 -l 1000 temp.img -R 1000 -P FFEE -X 0
|
||||
.Ed
|
||||
.Pp
|
||||
The contents of the file can be inspected symbolically with:
|
||||
.Bd -literal
|
||||
run6502 -l 1000 temp.img -d 1000 +12
|
||||
.Ed
|
||||
.Pp
|
||||
The options passed to
|
||||
.Nm run6502
|
||||
in the above examples have the following effects:
|
||||
.Bl -tag -width offset
|
||||
.It \-l 1000 temp.img
|
||||
loads the file
|
||||
.Pa temp.img
|
||||
into memory at address 0x8000.
|
||||
.It \-R 1000
|
||||
sets the reset vector (the address of first instruction to be executed
|
||||
after 'power on') to 0x1000.
|
||||
.It \-P FFEE
|
||||
arranges for calls to address 0xFFEE to behave as if there were an
|
||||
implementation of
|
||||
.Xr putchar 3
|
||||
at that address.
|
||||
.It \-X 0
|
||||
arranges for transfers of control to address 0 to exit from the
|
||||
emulator. This works in the above example because the final 'BRK'
|
||||
instruction causes an implicit subroutine call through an
|
||||
uninitialised interrupt vector to location 0. To see this
|
||||
instruction...
|
||||
.It \-d 1000 +12
|
||||
disassembles 18 bytes of memory at address 0x8000.
|
||||
.El
|
||||
.Ss Standalone Images
|
||||
The
|
||||
.Fl i
|
||||
option is designed for use in the 'interpreter command' appearing on
|
||||
the first line of an executable script. Adding the line
|
||||
.Bd -literal
|
||||
#!run6502 -R 1000 -P FFEE -X 0 -i 1000
|
||||
.Ed
|
||||
.Pp
|
||||
(with no leading spaces and a single trailing newline character)
|
||||
to the
|
||||
.Pa temp.img
|
||||
file from the first example turns it into a script. If the file is
|
||||
made executable with
|
||||
.Bd -literal
|
||||
chmod +x temp.img
|
||||
.Ed
|
||||
.Pp
|
||||
it can be run like a standalone program:
|
||||
.Bd -literal
|
||||
./temp.img
|
||||
.Ed
|
||||
.Ss A Very Complex Program
|
||||
Consider a pair of files named
|
||||
.Pa os1.2
|
||||
and
|
||||
.Pa basic2
|
||||
containing (legally-acquired, of course) ROM images of Acorn MOS 1.2
|
||||
and BBC Basic 2. The following command loads each of the images into
|
||||
memory at the appropriate address, cleans up the regions of memory
|
||||
containing memory-mapped i/o on the BBC computer, saves a snapshot of
|
||||
the entire memory to the file
|
||||
.Pa image
|
||||
and then exits:
|
||||
.Bd -literal
|
||||
run6502 -l C000 os1.2 -l 8000 basic2 -B -s0 +10000 image -x
|
||||
.Ed
|
||||
.Pp
|
||||
Running the generated image with
|
||||
.Bd -literal
|
||||
run6502 image
|
||||
.Ed
|
||||
.Pp
|
||||
will cold-start the emulated hardware, run the OS for a while, and
|
||||
then drop into the language ROM. Basic programs can then be entered,
|
||||
edited and run from the terminal.
|
||||
.Pp
|
||||
More details are given in the
|
||||
.Pa README
|
||||
file available in the
|
||||
.Pa examples
|
||||
directory of the distribution.
|
||||
.Ss Exercises
|
||||
Create a standalone image (one that can be run as a program, with
|
||||
a '#!' interpreter line at the beginning) that contains Basic2 and
|
||||
OS1.2 (as described above). This image should be no larger than 32K
|
||||
(memory below 0x8000, which would be full of zeroes, should not appear
|
||||
in the image file).
|
||||
.\" ----------------------------------------------------------------
|
||||
.Sh DIAGNOSTICS
|
||||
.\"
|
||||
If nothing goes wrong, none. Otherwise lots. They should be
|
||||
self-explanatory. I'm too lazy to enumerate them.
|
||||
.\" ----------------------------------------------------------------
|
||||
.Sh COMPATIBILITY
|
||||
.\"
|
||||
See
|
||||
.Xr lib6502 3
|
||||
for a discussion of the emulated instruction set.
|
||||
.\" ----------------------------------------------------------------
|
||||
.Sh SEE ALSO
|
||||
.\"
|
||||
.Xr lib6502 3
|
||||
.Pp
|
||||
The file
|
||||
.Pa examples/README
|
||||
in the lib6502 distribution. (Depending on your system this may be
|
||||
installed in
|
||||
.Pa /usr/doc/lib6502 ,
|
||||
.Pa /usr/local/doc/lib6502 ,
|
||||
.Pa /usr/share/doc/lib6502 ,
|
||||
or similar.)
|
||||
.Pp
|
||||
.Pa http://piumarta.com/software/lib6502
|
||||
for updates and documentation.
|
||||
.Pp
|
||||
.Pa http://6502.org
|
||||
for lots of 6502-related resources.
|
||||
.\" ----------------------------------------------------------------
|
||||
.Sh AUTHORS
|
||||
.\"
|
||||
The software and manual pages were written by
|
||||
.An "Ian Piumarta" .
|
||||
.Pp
|
||||
The software is provided as-is, with absolutely no warranty, in the
|
||||
hope that you will enjoy and benefit from it. You may use (entirely
|
||||
at your own risk) and redistribute it under the terms of a very
|
||||
liberal license that does not seek to restrict your rights in any way
|
||||
(unlike certain so-called 'open source' licenses that significantly
|
||||
limit your freedom in the name of 'free' software that is, ultimately,
|
||||
anything but free). See the file COPYING for details.
|
||||
.\" ----------------------------------------------------------------
|
||||
.Sh BUGS
|
||||
.\"
|
||||
.Bl -bullet
|
||||
.It
|
||||
Options must appear one at a time.
|
||||
.It
|
||||
Any attempt (in a load or save operation) to transfer data beyond
|
||||
0xFFFF is silently truncated at the end of memory.
|
||||
.It
|
||||
There is no way to specify the slot into which a ROM image should be
|
||||
loaded, other than implicitly according to the order of arguments on
|
||||
the command line.
|
||||
.It
|
||||
Execution can only be started via the emulated power-up reset. There
|
||||
is no support for 'warm-starting' execution in an image at an
|
||||
arbitrary address.
|
||||
.It
|
||||
Even though the emulator fully supports them, there is no way to
|
||||
artificially generate a hardware interrupt request, non-maskable
|
||||
interrupt, or reset condition. If you need these, read
|
||||
.Xr lib6502 3
|
||||
and write your own shell.
|
||||
.It
|
||||
The Acorn 'BBC Model B' hardware emulation is totally lame.
|
||||
.El
|
||||
.Pp
|
||||
Please send bug reports (and feature requests) to the author at:
|
||||
firstName (at) lastName (dot) com. (See
|
||||
.Sx AUTHORS
|
||||
above for suitable values of firstName and lastName.)
|
524
lib6502/run6502.c
Normal file
524
lib6502/run6502.c
Normal file
@ -0,0 +1,524 @@
|
||||
/* run6502.c -- 6502 emulator shell -*- C -*- */
|
||||
|
||||
/* Copyright (c) 2005 Ian Piumarta
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the 'Software'),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, provided that the above copyright notice(s) and this
|
||||
* permission notice appear in all copies of the Software and that both the
|
||||
* above copyright notice(s) and this permission notice appear in supporting
|
||||
* documentation.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK.
|
||||
*/
|
||||
|
||||
/* Last edited: 2005-11-02 01:18:58 by piumarta on margaux.local
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "lib6502.h"
|
||||
|
||||
#define VERSION PACKAGE_NAME " " PACKAGE_VERSION " " PACKAGE_COPYRIGHT
|
||||
|
||||
typedef uint8_t byte;
|
||||
typedef uint16_t word;
|
||||
|
||||
static char *program= 0;
|
||||
|
||||
static byte bank[0x10][0x4000];
|
||||
|
||||
|
||||
void fail(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
fflush(stdout);
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
fprintf(stderr, "\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
void pfail(const char *msg)
|
||||
{
|
||||
fflush(stdout);
|
||||
perror(msg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
#define rts \
|
||||
{ \
|
||||
word pc; \
|
||||
pc = mpu->memory[++mpu->registers->s + 0x100]; \
|
||||
pc |= mpu->memory[++mpu->registers->s + 0x100] << 8; \
|
||||
return pc + 1; \
|
||||
}
|
||||
|
||||
|
||||
int osword(M6502 *mpu, word address, byte data)
|
||||
{
|
||||
byte *params= mpu->memory + mpu->registers->x + (mpu->registers->y << 8);
|
||||
|
||||
switch (mpu->registers->a)
|
||||
{
|
||||
case 0x00: /* input line */
|
||||
/* On entry: XY+0,1=>string area,
|
||||
* XY+2=maximum line length,
|
||||
* XY+3=minimum acceptable ASCII value,
|
||||
* XY+4=maximum acceptable ASCII value.
|
||||
* On exit: Y is the line length (excluding CR),
|
||||
* C is set if Escape terminated input.
|
||||
*/
|
||||
{
|
||||
word offset= params[0] + (params[1] << 8);
|
||||
byte *buffer= mpu->memory + offset;
|
||||
byte length= params[2], minVal= params[3], maxVal= params[4], b= 0;
|
||||
if (!fgets(buffer, length, stdin))
|
||||
{
|
||||
putchar('\n');
|
||||
exit(0);
|
||||
}
|
||||
for (b= 0; b < length; ++b)
|
||||
if ((buffer[b] < minVal) || (buffer[b] > maxVal) || ('\n' == buffer[b]))
|
||||
break;
|
||||
buffer[b]= 13;
|
||||
mpu->registers->y= b;
|
||||
mpu->registers->p &= 0xFE;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
char state[64];
|
||||
M6502_dump(mpu, state);
|
||||
fflush(stdout);
|
||||
fprintf(stderr, "\nOSWORD %s\n", state);
|
||||
fail("ABORT");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
rts;
|
||||
}
|
||||
|
||||
|
||||
int osbyte(M6502 *mpu, word address, byte data)
|
||||
{
|
||||
switch (mpu->registers->a)
|
||||
{
|
||||
case 0x7A: /* perform keyboard scan */
|
||||
mpu->registers->x= 0x00;
|
||||
break;
|
||||
|
||||
case 0x7E: /* acknowledge detection of escape condition */
|
||||
return 1;
|
||||
break;
|
||||
|
||||
case 0x82: /* read machine higher order address */
|
||||
mpu->registers->y= 0x00;
|
||||
mpu->registers->x= 0x00;
|
||||
break;
|
||||
|
||||
case 0x83: /* read top of OS ram address (OSHWM) */
|
||||
mpu->registers->y= 0x0E;
|
||||
mpu->registers->x= 0x00;
|
||||
break;
|
||||
|
||||
case 0x84: /* read bottom of display ram address */
|
||||
mpu->registers->y= 0x80;
|
||||
mpu->registers->x= 0x00;
|
||||
break;
|
||||
|
||||
case 0x89: /* motor control */
|
||||
break;
|
||||
|
||||
case 0xDA: /* read/write number of items in vdu queue (stored at 0x026A) */
|
||||
return 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
char state[64];
|
||||
M6502_dump(mpu, state);
|
||||
fflush(stdout);
|
||||
fprintf(stderr, "\nOSBYTE %s\n", state);
|
||||
fail("ABORT");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
rts;
|
||||
}
|
||||
|
||||
|
||||
int oscli(M6502 *mpu, word address, byte data)
|
||||
{
|
||||
byte *params= mpu->memory + mpu->registers->x + (mpu->registers->y << 8);
|
||||
char command[1024], *ptr= command;
|
||||
while (('*' == *params) || (' ' == *params))
|
||||
++params;
|
||||
while (13 != *params)
|
||||
*ptr++= *params++;
|
||||
*ptr= '\0';
|
||||
system(command);
|
||||
rts;
|
||||
}
|
||||
|
||||
|
||||
int oswrch(M6502 *mpu, word address, byte data)
|
||||
{
|
||||
switch (mpu->registers->a)
|
||||
{
|
||||
case 0x0C:
|
||||
fputs("\033[2J\033[H", stdout);
|
||||
break;
|
||||
|
||||
default:
|
||||
putchar(mpu->registers->a);
|
||||
break;
|
||||
}
|
||||
fflush(stdout);
|
||||
rts;
|
||||
}
|
||||
|
||||
|
||||
static int writeROM(M6502 *mpu, word address, byte value)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int bankSelect(M6502 *mpu, word address, byte value)
|
||||
{
|
||||
memcpy(mpu->memory + 0x8000, bank[value & 0x0F], 0x4000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int doBtraps(int argc, char **argv, M6502 *mpu)
|
||||
{
|
||||
unsigned addr;
|
||||
|
||||
/* Acorn Model B ROM and memory-mapped IO */
|
||||
|
||||
for (addr= 0x8000; addr <= 0xFBFF; ++addr) mpu->callbacks->write[addr]= writeROM;
|
||||
for (addr= 0xFC00; addr <= 0xFEFF; ++addr) mpu->memory[addr]= 0xFF;
|
||||
for (addr= 0xFE30; addr <= 0xFE33; ++addr) mpu->callbacks->write[addr]= bankSelect;
|
||||
for (addr= 0xFE40; addr <= 0xFE4F; ++addr) mpu->memory[addr]= 0x00;
|
||||
for (addr= 0xFF00; addr <= 0xFFFF; ++addr) mpu->callbacks->write[addr]= writeROM;
|
||||
|
||||
/* anything already loaded at 0x8000 appears in bank 0 */
|
||||
|
||||
memcpy(bank[0x00], mpu->memory + 0x8000, 0x4000);
|
||||
|
||||
/* fake a few interesting OS calls */
|
||||
|
||||
# define trap(vec, addr, func) mpu->callbacks->call[addr]= (func)
|
||||
trap(0x020C, 0xFFF1, osword);
|
||||
trap(0x020A, 0xFFF4, osbyte);
|
||||
//trap(0x0208, 0xFFF7, oscli ); /* enable this to send '*COMMAND's to system(3) :-) */
|
||||
trap(0x020E, 0xFFEE, oswrch);
|
||||
trap(0x020E, 0xE0A4, oswrch); /* NVWRCH */
|
||||
#undef trap
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void usage(int status)
|
||||
{
|
||||
FILE *stream= status ? stderr : stdout;
|
||||
fprintf(stream, VERSION"\n");
|
||||
fprintf(stream, "please send bug reports to: %s\n", PACKAGE_BUGREPORT);
|
||||
fprintf(stream, "\n");
|
||||
fprintf(stream, "usage: %s [option ...]\n", program);
|
||||
fprintf(stream, " %s [option ...] -B [image ...]\n", program);
|
||||
fprintf(stream, " -B -- minimal Acorn 'BBC Model B' compatibility\n");
|
||||
fprintf(stream, " -d addr last -- dump memory between addr and last\n");
|
||||
fprintf(stream, " -G addr -- emulate getchar(3) at addr\n");
|
||||
fprintf(stream, " -h -- help (print this message)\n");
|
||||
fprintf(stream, " -I addr -- set IRQ vector\n");
|
||||
fprintf(stream, " -l addr file -- load file at addr\n");
|
||||
fprintf(stream, " -M addr -- emulate memory-mapped stdio at addr\n");
|
||||
fprintf(stream, " -N addr -- set NMI vector\n");
|
||||
fprintf(stream, " -P addr -- emulate putchar(3) at addr\n");
|
||||
fprintf(stream, " -R addr -- set RST vector\n");
|
||||
fprintf(stream, " -s addr last file -- save memory from addr to last in file\n");
|
||||
fprintf(stream, " -v -- print version number then exit\n");
|
||||
fprintf(stream, " -X addr -- terminate emulation if PC reaches addr\n");
|
||||
fprintf(stream, " -x -- exit wihout further ado\n");
|
||||
fprintf(stream, " image -- '-l 8000 image' in available ROM slot\n");
|
||||
fprintf(stream, "\n");
|
||||
fprintf(stream, "'last' can be an address (non-inclusive) or '+size' (in bytes)\n");
|
||||
exit(status);
|
||||
}
|
||||
|
||||
|
||||
static int doHelp(int argc, char **argv, M6502 *mpu)
|
||||
{
|
||||
usage(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int doVersion(int argc, char **argv, M6502 *mpu)
|
||||
{
|
||||
puts(VERSION);
|
||||
exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static unsigned long htol(char *hex)
|
||||
{
|
||||
char *end;
|
||||
unsigned long l= strtol(hex, &end, 16);
|
||||
if (*end) fail("bad hex number: %s", hex);
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
static int loadInterpreter(M6502 *mpu, word start, const char *path)
|
||||
{
|
||||
FILE *file= 0;
|
||||
int count= 0;
|
||||
byte *memory= mpu->memory + start;
|
||||
size_t max= 0x10000 - start;
|
||||
int c= 0;
|
||||
|
||||
if ((!(file= fopen(path, "r"))) || ('#' != fgetc(file)) || ('!' != fgetc(file)))
|
||||
return 0;
|
||||
while ((c= fgetc(file)) >= ' ')
|
||||
;
|
||||
while ((count= fread(memory, 1, max, file)) > 0)
|
||||
{
|
||||
memory += count;
|
||||
max -= count;
|
||||
}
|
||||
fclose(file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int save(M6502 *mpu, word address, unsigned length, const char *path)
|
||||
{
|
||||
FILE *file= 0;
|
||||
int count= 0;
|
||||
if (!(file= fopen(path, "w")))
|
||||
return 0;
|
||||
while ((count= fwrite(mpu->memory + address, 1, length, file)))
|
||||
{
|
||||
address += count;
|
||||
length -= count;
|
||||
}
|
||||
fclose(file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int load(M6502 *mpu, word address, const char *path)
|
||||
{
|
||||
FILE *file= 0;
|
||||
int count= 0;
|
||||
size_t max= 0x10000 - address;
|
||||
if (!(file= fopen(path, "r")))
|
||||
return 0;
|
||||
while ((count= fread(mpu->memory + address, 1, max, file)) > 0)
|
||||
{
|
||||
address += count;
|
||||
max -= count;
|
||||
}
|
||||
fclose(file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int doLoadInterpreter(int argc, char **argv, M6502 *mpu)
|
||||
{
|
||||
if (argc < 3) usage(1);
|
||||
if (!loadInterpreter(mpu, htol(argv[1]), argv[2])) pfail(argv[2]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
static int doLoad(int argc, char **argv, M6502 *mpu) /* -l addr file */
|
||||
{
|
||||
if (argc < 3) usage(1);
|
||||
if (!load(mpu, htol(argv[1]), argv[2])) pfail(argv[2]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
static int doSave(int argc, char **argv, M6502 *mpu) /* -l addr size file */
|
||||
{
|
||||
if (argc < 4) usage(1);
|
||||
if (!save(mpu, htol(argv[1]), htol(argv[2]), argv[3])) pfail(argv[3]);
|
||||
return 3;
|
||||
}
|
||||
|
||||
|
||||
#define doVEC(VEC) \
|
||||
static int do##VEC(int argc, char **argv, M6502 *mpu) \
|
||||
{ \
|
||||
unsigned addr= 0; \
|
||||
if (argc < 2) usage(1); \
|
||||
addr= htol(argv[1]); \
|
||||
M6502_setVector(mpu, VEC, addr); \
|
||||
return 1; \
|
||||
}
|
||||
|
||||
doVEC(IRQ);
|
||||
doVEC(NMI);
|
||||
doVEC(RST);
|
||||
|
||||
#undef doVEC
|
||||
|
||||
|
||||
/* Emulate getchar(3) at addr */
|
||||
static int gTrap(M6502 *mpu, word addr, byte data) { mpu->registers->a= getchar(); rts; }
|
||||
|
||||
static int doGtrap(int argc, char **argv, M6502 *mpu)
|
||||
{
|
||||
unsigned addr;
|
||||
if (argc < 2) usage(1);
|
||||
addr= htol(argv[1]);
|
||||
M6502_setCallback(mpu, call, addr, gTrap);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Emulate putchar(3) at addr */
|
||||
static int pTrap(M6502 *mpu, word addr, byte data) { putchar(mpu->registers->a); rts; }
|
||||
|
||||
static int doPtrap(int argc, char **argv, M6502 *mpu)
|
||||
{
|
||||
unsigned addr;
|
||||
if (argc < 2) usage(1);
|
||||
addr= htol(argv[1]);
|
||||
M6502_setCallback(mpu, call, addr, pTrap);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Emulate memory-mapped stdio at addr */
|
||||
static int mTrapRead(M6502 *mpu, word addr, byte data) { return getchar(); }
|
||||
static int mTrapWrite(M6502 *mpu, word addr, byte data) { return putchar(data); }
|
||||
|
||||
static int doMtrap(int argc, char **argv, M6502 *mpu)
|
||||
{
|
||||
unsigned addr= 0;
|
||||
if (argc < 2) usage(1);
|
||||
addr= htol(argv[1]);
|
||||
M6502_setCallback(mpu, read, addr, mTrapRead);
|
||||
M6502_setCallback(mpu, write, addr, mTrapWrite);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Terminate emulation if PC reaches addr */
|
||||
static int xTrap(M6502 *mpu, word addr, byte data) { exit(0); return 0; }
|
||||
|
||||
static int doXtrap(int argc, char **argv, M6502 *mpu)
|
||||
{
|
||||
unsigned addr= 0;
|
||||
if (argc < 2) usage(1);
|
||||
addr= htol(argv[1]);
|
||||
M6502_setCallback(mpu, call, addr, xTrap);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int doDisassemble(int argc, char **argv, M6502 *mpu)
|
||||
{
|
||||
unsigned addr= 0, last= 0;
|
||||
if (argc < 3) usage(1);
|
||||
addr= htol(argv[1]);
|
||||
last= ('+' == *argv[2]) ? addr + htol(1 + argv[2]) : htol(argv[2]);
|
||||
while (addr < last)
|
||||
{
|
||||
char insn[64];
|
||||
int i= 0, size= M6502_disassemble(mpu, addr, insn);
|
||||
printf("%04X ", addr);
|
||||
while (i++ < size) printf("%02X", mpu->memory[addr + i - 1]);
|
||||
while (i++ < 4) printf(" ");
|
||||
putchar(' ');
|
||||
i= 0;
|
||||
while (i++ < size) putchar(isgraph(mpu->memory[addr + i - 1]) ? mpu->memory[addr + i - 1] : ' ');
|
||||
while (i++ < 4) putchar(' ');
|
||||
printf(" %s\n", insn);
|
||||
addr += size;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
M6502 *mpu= M6502_new(0, 0, 0);
|
||||
int bTraps= 0;
|
||||
|
||||
program= argv[0];
|
||||
|
||||
if ((2 == argc) && ('-' != *argv[1]))
|
||||
{
|
||||
if ((!loadInterpreter(mpu, 0, argv[1])) && (!load(mpu, 0, argv[1])))
|
||||
pfail(argv[1]);
|
||||
doBtraps(0, 0, mpu);
|
||||
}
|
||||
else
|
||||
while (++argv, --argc > 0)
|
||||
{
|
||||
int n= 0;
|
||||
if (!strcmp(*argv, "-B")) bTraps= 1;
|
||||
else if (!strcmp(*argv, "-d")) n= doDisassemble(argc, argv, mpu);
|
||||
else if (!strcmp(*argv, "-G")) n= doGtrap(argc, argv, mpu);
|
||||
else if (!strcmp(*argv, "-h")) n= doHelp(argc, argv, mpu);
|
||||
else if (!strcmp(*argv, "-i")) n= doLoadInterpreter(argc, argv, mpu);
|
||||
else if (!strcmp(*argv, "-I")) n= doIRQ(argc, argv, mpu);
|
||||
else if (!strcmp(*argv, "-l")) n= doLoad(argc, argv, mpu);
|
||||
else if (!strcmp(*argv, "-M")) n= doMtrap(argc, argv, mpu);
|
||||
else if (!strcmp(*argv, "-N")) n= doNMI(argc, argv, mpu);
|
||||
else if (!strcmp(*argv, "-P")) n= doPtrap(argc, argv, mpu);
|
||||
else if (!strcmp(*argv, "-R")) n= doRST(argc, argv, mpu);
|
||||
else if (!strcmp(*argv, "-s")) n= doSave(argc, argv, mpu);
|
||||
else if (!strcmp(*argv, "-v")) n= doVersion(argc, argv, mpu);
|
||||
else if (!strcmp(*argv, "-X")) n= doXtrap(argc, argv, mpu);
|
||||
else if (!strcmp(*argv, "-x")) exit(0);
|
||||
else if ('-' == **argv) usage(1);
|
||||
else
|
||||
{
|
||||
/* doBtraps() left 0x8000+0x4000 in bank 0, so load */
|
||||
/* additional images starting at 15 and work down */
|
||||
static int bankSel= 0x0F;
|
||||
if (!bTraps) usage(1);
|
||||
if (bankSel < 0) fail("too many images");
|
||||
if (!load(mpu, 0x8000, argv[0])) pfail(argv[0]);
|
||||
memcpy(bank[bankSel--],
|
||||
0x8000 + mpu->memory,
|
||||
0x4000);
|
||||
n= 1;
|
||||
}
|
||||
argc -= n;
|
||||
argv += n;
|
||||
}
|
||||
|
||||
if (bTraps)
|
||||
doBtraps(0, 0, mpu);
|
||||
|
||||
M6502_reset(mpu);
|
||||
M6502_run(mpu);
|
||||
M6502_delete(mpu);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user