mirror of
https://github.com/makarcz/vm6502.git
synced 2024-12-28 05:29:47 +00:00
Multiple changes/general development.
Reset option in dbg console. RESET keyword in memory definition file. Command line arguments added. Save snapshot added. Refactoring - huge switch replaced with array of methods. IRQ support. Cycle accurate emulation. Intel HEX format support.
This commit is contained in:
parent
dce9babd36
commit
0d47565b9d
178
MKCpu.h
178
MKCpu.h
@ -29,6 +29,9 @@ struct Regs {
|
|||||||
int LastOpCode; // op-code of last instruction
|
int LastOpCode; // op-code of last instruction
|
||||||
unsigned short LastArg; // argument to the last instruction
|
unsigned short LastArg; // argument to the last instruction
|
||||||
int LastAddrMode; // addressing mode of last instruction
|
int LastAddrMode; // addressing mode of last instruction
|
||||||
|
bool IrqPending; // pending Interrupt ReQuest (IRQ)
|
||||||
|
int CyclesLeft; // # of cycles left to complete current opcode
|
||||||
|
bool PageBoundary; // true if page boundary was crossed
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -351,11 +354,16 @@ enum eOpCodes {
|
|||||||
OPCODE_ILL_FF = 0xFF // illegal opcode
|
OPCODE_ILL_FF = 0xFF // illegal opcode
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class MKCpu;
|
||||||
|
|
||||||
|
typedef void (MKCpu::*OpCodeHdlrFn)();
|
||||||
|
|
||||||
struct OpCode {
|
struct OpCode {
|
||||||
int code; // the byte value of the opcode
|
int code; // the byte value of the opcode
|
||||||
int addrmode; // addressing mode (see eAddrModes)
|
int addrmode; // addressing mode (see eAddrModes)
|
||||||
int time; // # of cycles
|
int time; // # of cycles
|
||||||
string amf; // assembler mnemonic
|
string amf; // assembler mnemonic
|
||||||
|
OpCodeHdlrFn pfun; // opcoce handler function
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef map<eOpCodes,OpCode> OpCodesMap;
|
typedef map<eOpCodes,OpCode> OpCodesMap;
|
||||||
@ -409,15 +417,20 @@ class MKCpu
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
bool mExitAtLastRTS;
|
||||||
|
|
||||||
MKCpu();
|
MKCpu();
|
||||||
MKCpu(Memory *pmem);
|
MKCpu(Memory *pmem);
|
||||||
~MKCpu();
|
~MKCpu();
|
||||||
|
|
||||||
Regs *ExecOpcode(unsigned short memaddr);
|
Regs *ExecOpcode(unsigned short memaddr);
|
||||||
Regs *GetRegs();
|
Regs *GetRegs();
|
||||||
|
void SetRegs(Regs r);
|
||||||
queue<string> GetExecHistory();
|
queue<string> GetExecHistory();
|
||||||
unsigned short Disassemble(unsigned short addr,
|
unsigned short Disassemble(unsigned short addr,
|
||||||
char *instrbuf); // Disassemble instruction in memory, return next instruction addr.
|
char *instrbuf); // Disassemble instruction in memory, return next instruction addr.
|
||||||
|
void Reset(); // reset CPU
|
||||||
|
void Interrupt(); // Interrupt ReQuest (IRQ)
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
@ -455,6 +468,163 @@ class MKCpu
|
|||||||
int mode); // Get argument from address with specified addr. mode
|
int mode); // Get argument from address with specified addr. mode
|
||||||
unsigned short Disassemble(); // Disassemble instruction and argument per addressing mode
|
unsigned short Disassemble(); // Disassemble instruction and argument per addressing mode
|
||||||
void Add2History(string s); // add entry to op-codes execute history
|
void Add2History(string s); // add entry to op-codes execute history
|
||||||
|
bool PageBoundary(unsigned short startaddr,
|
||||||
|
unsigned short endaddr); // detect if page boundary was crossed
|
||||||
|
|
||||||
|
// opcode execute methods
|
||||||
|
void OpCodeBrk();
|
||||||
|
void OpCodeNop();
|
||||||
|
void OpCodeLdaIzx();
|
||||||
|
void OpCodeLdaZp();
|
||||||
|
void OpCodeLdaImm();
|
||||||
|
void OpCodeLdaAbs();
|
||||||
|
void OpCodeLdaIzy();
|
||||||
|
void OpCodeLdaZpx();
|
||||||
|
void OpCodeLdaAby();
|
||||||
|
void OpCodeLdaAbx();
|
||||||
|
void OpCodeLdxImm();
|
||||||
|
void OpCodeLdxZp();
|
||||||
|
void OpCodeLdxAbs();
|
||||||
|
void OpCodeLdxZpy();
|
||||||
|
void OpCodeLdxAby();
|
||||||
|
void OpCodeLdyImm();
|
||||||
|
void OpCodeLdyZp();
|
||||||
|
void OpCodeLdyAbs();
|
||||||
|
void OpCodeLdyZpx();
|
||||||
|
void OpCodeLdyAbx();
|
||||||
|
void OpCodeTax();
|
||||||
|
void OpCodeTay();
|
||||||
|
void OpCodeTxa();
|
||||||
|
void OpCodeTya();
|
||||||
|
void OpCodeTsx();
|
||||||
|
void OpCodeTxs();
|
||||||
|
void OpCodeStaIzx();
|
||||||
|
void OpCodeStaZp();
|
||||||
|
void OpCodeStaAbs();
|
||||||
|
void OpCodeStaIzy();
|
||||||
|
void OpCodeStaZpx();
|
||||||
|
void OpCodeStaAby();
|
||||||
|
void OpCodeStaAbx();
|
||||||
|
void OpCodeStxZp();
|
||||||
|
void OpCodeStxAbs();
|
||||||
|
void OpCodeStxZpy();
|
||||||
|
void OpCodeStyZp();
|
||||||
|
void OpCodeStyAbs();
|
||||||
|
void OpCodeStyZpx();
|
||||||
|
void OpCodeBneRel();
|
||||||
|
void OpCodeBeqRel();
|
||||||
|
void OpCodeBplRel();
|
||||||
|
void OpCodeBmiRel();
|
||||||
|
void OpCodeBvcRel();
|
||||||
|
void OpCodeBvsRel();
|
||||||
|
void OpCodeBccRel();
|
||||||
|
void OpCodeBcsRel();
|
||||||
|
void OpCodeIncZp();
|
||||||
|
void OpCodeIncAbs();
|
||||||
|
void OpCodeIncZpx();
|
||||||
|
void OpCodeIncAbx();
|
||||||
|
void OpCodeInx();
|
||||||
|
void OpCodeDex();
|
||||||
|
void OpCodeIny();
|
||||||
|
void OpCodeDey();
|
||||||
|
void OpCodeJmpAbs();
|
||||||
|
void OpCodeJmpInd();
|
||||||
|
void OpCodeOraIzx();
|
||||||
|
void OpCodeOraZp();
|
||||||
|
void OpCodeOraImm();
|
||||||
|
void OpCodeOraAbs();
|
||||||
|
void OpCodeOraIzy();
|
||||||
|
void OpCodeOraZpx();
|
||||||
|
void OpCodeOraAby();
|
||||||
|
void OpCodeOraAbx();
|
||||||
|
void OpCodeAslZp();
|
||||||
|
void OpCodeAslAcc();
|
||||||
|
void OpCodeAslAbs();
|
||||||
|
void OpCodeAslZpx();
|
||||||
|
void OpCodeAslAbx();
|
||||||
|
void OpCodeJsrAbs();
|
||||||
|
void OpCodeAndIzx();
|
||||||
|
void OpCodeAndZp();
|
||||||
|
void OpCodeAndImm();
|
||||||
|
void OpCodeAndAbs();
|
||||||
|
void OpCodeAndIzy();
|
||||||
|
void OpCodeAndZpx();
|
||||||
|
void OpCodeAndAby();
|
||||||
|
void OpCodeAndAbx();
|
||||||
|
void OpCodeBitZp();
|
||||||
|
void OpCodeBitAbs();
|
||||||
|
void OpCodeRolZp();
|
||||||
|
void OpCodeRolAcc();
|
||||||
|
void OpCodeRolAbs();
|
||||||
|
void OpCodeRolZpx();
|
||||||
|
void OpCodeRolAbx();
|
||||||
|
void OpCodePhp();
|
||||||
|
void OpCodePha();
|
||||||
|
void OpCodePlp();
|
||||||
|
void OpCodePla();
|
||||||
|
void OpCodeClc();
|
||||||
|
void OpCodeSec();
|
||||||
|
void OpCodeCli();
|
||||||
|
void OpCodeClv();
|
||||||
|
void OpCodeCld();
|
||||||
|
void OpCodeSed();
|
||||||
|
void OpCodeSei();
|
||||||
|
void OpCodeRti();
|
||||||
|
void OpCodeRts();
|
||||||
|
void OpCodeEorIzx();
|
||||||
|
void OpCodeEorZp();
|
||||||
|
void OpCodeEorImm();
|
||||||
|
void OpCodeEorAbs();
|
||||||
|
void OpCodeEorIzy();
|
||||||
|
void OpCodeEorZpx();
|
||||||
|
void OpCodeEorAby();
|
||||||
|
void OpCodeEorAbx();
|
||||||
|
void OpCodeLsrZp();
|
||||||
|
void OpCodeLsrAcc();
|
||||||
|
void OpCodeLsrAbs();
|
||||||
|
void OpCodeLsrZpx();
|
||||||
|
void OpCodeLsrAbx();
|
||||||
|
void OpCodeAdcIzx();
|
||||||
|
void OpCodeAdcZp();
|
||||||
|
void OpCodeAdcImm();
|
||||||
|
void OpCodeAdcAbs();
|
||||||
|
void OpCodeAdcIzy();
|
||||||
|
void OpCodeAdcZpx();
|
||||||
|
void OpCodeAdcAby();
|
||||||
|
void OpCodeAdcAbx();
|
||||||
|
void OpCodeRorZp();
|
||||||
|
void OpCodeRorAcc();
|
||||||
|
void OpCodeRorAbs();
|
||||||
|
void OpCodeRorZpx();
|
||||||
|
void OpCodeRorAbx();
|
||||||
|
void OpCodeCpyImm();
|
||||||
|
void OpCodeCpyZp();
|
||||||
|
void OpCodeCpyAbs();
|
||||||
|
void OpCodeCmpIzx();
|
||||||
|
void OpCodeCmpZp();
|
||||||
|
void OpCodeCmpImm();
|
||||||
|
void OpCodeCmpAbs();
|
||||||
|
void OpCodeCmpIzy();
|
||||||
|
void OpCodeCmpZpx();
|
||||||
|
void OpCodeCmpAby();
|
||||||
|
void OpCodeCmpAbx();
|
||||||
|
void OpCodeDecZp();
|
||||||
|
void OpCodeDecAbs();
|
||||||
|
void OpCodeDecZpx();
|
||||||
|
void OpCodeDecAbx();
|
||||||
|
void OpCodeCpxImm();
|
||||||
|
void OpCodeCpxZp();
|
||||||
|
void OpCodeCpxAbs();
|
||||||
|
void OpCodeSbcZp();
|
||||||
|
void OpCodeSbcAbs();
|
||||||
|
void OpCodeSbcIzx();
|
||||||
|
void OpCodeSbcIzy();
|
||||||
|
void OpCodeSbcZpx();
|
||||||
|
void OpCodeSbcAby();
|
||||||
|
void OpCodeSbcAbx();
|
||||||
|
void OpCodeSbcImm();
|
||||||
|
void OpCodeDud();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace MKBasic
|
} // namespace MKBasic
|
||||||
|
@ -225,10 +225,7 @@ unsigned char Memory::ReadCharKb(bool nonblock)
|
|||||||
set_conio_terminal_mode();
|
set_conio_terminal_mode();
|
||||||
#endif
|
#endif
|
||||||
static int c = ' ';
|
static int c = ' ';
|
||||||
//putchar('\n');
|
|
||||||
if (mIOEcho && isprint(c)) putchar(c);
|
if (mIOEcho && isprint(c)) putchar(c);
|
||||||
//else putchar(' ');
|
|
||||||
//fputs("<-Character Input (CTRL-Y to BREAK) ?\r",stdout);
|
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
if (!nonblock) while(!kbhit());
|
if (!nonblock) while(!kbhit());
|
||||||
else c = 0;
|
else c = 0;
|
||||||
@ -239,8 +236,6 @@ unsigned char Memory::ReadCharKb(bool nonblock)
|
|||||||
kill(getpid(),SIGINT);
|
kill(getpid(),SIGINT);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
//fputs(" \r",stdout);
|
|
||||||
//fflush(stdout);
|
|
||||||
mCharIOBufIn[mInBufDataEnd] = c;
|
mCharIOBufIn[mInBufDataEnd] = c;
|
||||||
mInBufDataEnd++;
|
mInBufDataEnd++;
|
||||||
if (mInBufDataEnd >= CHARIO_BUF_SIZE) mInBufDataEnd = 0;
|
if (mInBufDataEnd >= CHARIO_BUF_SIZE) mInBufDataEnd = 0;
|
||||||
|
317
ReadMe.txt
317
ReadMe.txt
@ -49,9 +49,43 @@ Parts of this project is based on or contains 3-rd party work:
|
|||||||
|
|
||||||
2. Format of the memory image definition file.
|
2. Format of the memory image definition file.
|
||||||
|
|
||||||
|
Emulator recognizes 4 formats of memory image:
|
||||||
|
|
||||||
|
- raw binary, no header, up to 64 kB of raw data,
|
||||||
|
- binary image with a configuration/snapshot header,
|
||||||
|
- Intel HEX format,
|
||||||
|
- plain text memory image definition file.
|
||||||
|
|
||||||
|
Please see detailed description of each format below:
|
||||||
|
|
||||||
Program can load raw binary image of MOS 6502 opcodes.
|
Program can load raw binary image of MOS 6502 opcodes.
|
||||||
Binary image is always loaded from address $0000 and can be up to 64 kB long,
|
Binary image is always loaded from address $0000 and can be up to 64 kB long,
|
||||||
so the code must be properly located inside that image.
|
so the code must be properly located inside that image. Image can be shorter
|
||||||
|
than 64 kB, user will receive warning in such case, but it will be loaded.
|
||||||
|
Binary image may have header attached at the top.
|
||||||
|
It consists of magic keyword 'SNAPSHOT' followed by 15 bytes of data
|
||||||
|
(subject to change in future versions).
|
||||||
|
The header data saves the status of CPU and emulation facilities like
|
||||||
|
character I/O address and enable flag, ROM boundaries and enable flag etc.
|
||||||
|
This header is added when user saves snapshot of the VM from debug console
|
||||||
|
menu with command: Y [file_name].
|
||||||
|
Header is not mandatory, so the binary image created outside application can
|
||||||
|
also be used. User will receive warning at startup during image load if
|
||||||
|
header is missing, but image will be loaded. In this case, user may need
|
||||||
|
to configure necessary emulation facilities manually in debug console
|
||||||
|
before executing native 6502 code.
|
||||||
|
When binary image with a header is loaded, user can continue executing the
|
||||||
|
program from the place where it was saved by executing command from debug
|
||||||
|
console of the emulator:
|
||||||
|
X pc_value
|
||||||
|
Where:
|
||||||
|
pc_value - the address currently showing in CPU's PC register.
|
||||||
|
If the reset vector is set right, execute the 6502 code right from command
|
||||||
|
line:
|
||||||
|
mkbasic -r image_name
|
||||||
|
Above will execute the code set in reset vector without having to start it
|
||||||
|
from debug console. If 6502 code requires character I/O and/or ROM facilities
|
||||||
|
then image should include header with proper setup.
|
||||||
Depending on your favorite 6502 assembler, you may need to use proper command
|
Depending on your favorite 6502 assembler, you may need to use proper command
|
||||||
line arguments or configuration to achieve properly formatted binary file.
|
line arguments or configuration to achieve properly formatted binary file.
|
||||||
E.g.: if using CL65 from CC65 package, create configuration file that defines
|
E.g.: if using CL65 from CC65 package, create configuration file that defines
|
||||||
@ -62,7 +96,18 @@ Two CFG files, one for microchess and one for All Suite from hmc-6502 project
|
|||||||
are supplied with this project and assembler source code adapted to be
|
are supplied with this project and assembler source code adapted to be
|
||||||
compiled with CL65.
|
compiled with CL65.
|
||||||
Other assemblers may need a different approach or may not be able to generate
|
Other assemblers may need a different approach or may not be able to generate
|
||||||
binary images that are required for this emulator.
|
binary images in format required for this emulator. In such case you may need
|
||||||
|
to design your own custom conversion tools to generate such images.
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
Simple conversion utility is supplied with this project (bin2hex), which is
|
||||||
|
described later in this file.
|
||||||
|
|
||||||
|
Emulator recognizes Intel HEX format file. It recognizes properly data
|
||||||
|
records and end-of-file record only at this time. Similar to binary image
|
||||||
|
with no header, when Intel HEX file is loaded, user may need to configure
|
||||||
|
necessary emulation facilities manually in debug console before executing
|
||||||
|
native 6502 code.
|
||||||
|
|
||||||
Program can also load memory image definition file (plain text), which is
|
Program can also load memory image definition file (plain text), which is
|
||||||
a format developed especially for this project.
|
a format developed especially for this project.
|
||||||
@ -86,22 +131,25 @@ ENROM
|
|||||||
ENIO
|
ENIO
|
||||||
EXEC
|
EXEC
|
||||||
address
|
address
|
||||||
|
RESET
|
||||||
|
|
||||||
Where:
|
Where:
|
||||||
ADDR - label indicating that starting and run address will follow in
|
ADDR - label indicating that starting and run address will follow in
|
||||||
the next line
|
the next line
|
||||||
ORG - label indicating that the address counter will change to the
|
ORG - label indicating that the address counter will change to the
|
||||||
value provided in next line
|
value provided in next line
|
||||||
IOADDR - label indicating that character I/O emulation trap address will
|
IOADDR - label indicating that character I/O emulation trap address will
|
||||||
follow in the next line
|
follow in the next line
|
||||||
ROMBEGIN - label indicating that the emulated read-only memory start address
|
ROMBEGIN - label indicating that the emulated read-only memory start
|
||||||
will follow in the next line
|
address will follow in the next line
|
||||||
ROMEND - label indicating that the emulated read-only memory end address
|
ROMEND - label indicating that the emulated read-only memory end address
|
||||||
will follow in the next line
|
will follow in the next line
|
||||||
ENROM - enable read-only memory emulation
|
ENROM - enable read-only memory emulation
|
||||||
ENIO - enable character I/O emulation
|
ENIO - enable character I/O emulation
|
||||||
EXEC - label indicating that the auto-execute address will follow
|
EXEC - label indicating that the auto-execute address will follow
|
||||||
in the next line
|
in the next line, 6502 program will auto-execute from that
|
||||||
|
address after memory definition file is done loading
|
||||||
|
RESET - initiate CPU reset sequence after loading memory definition file
|
||||||
|
|
||||||
|
|
||||||
address - decimal or hexadecimal (prefix $) address in memory
|
address - decimal or hexadecimal (prefix $) address in memory
|
||||||
@ -275,7 +323,248 @@ If you experience unexpected character input prompt while emulating
|
|||||||
6502 code, this may be the case. Reconfigure your IOADDR to be inside ROM in
|
6502 code, this may be the case. Reconfigure your IOADDR to be inside ROM in
|
||||||
such case and try again.
|
such case and try again.
|
||||||
|
|
||||||
6. Warranty and License Agreement.
|
Emulator is "cycle accurate" but not time or speed accurate.
|
||||||
|
This means that each call to MKCpu::ExecOpcode() method is considered a single
|
||||||
|
CPU cycle, so depending on the executed opcode, multiple calls (# varies per
|
||||||
|
opcode and other conditions) are needed to complete the opcode execution and
|
||||||
|
proceed to the next one. Method returns pointer to the the virtual CPU
|
||||||
|
registers. One of the members of this structure is named CyclesLeft. When this
|
||||||
|
variable reaches 0, the opcode execution is considered complete.
|
||||||
|
|
||||||
|
The VMachine class calls the ExecOpcode() method as fast as possible, so it is
|
||||||
|
not real-time accurate, as already mentioned. To implement real-time accurate
|
||||||
|
emulation, the MKCpu::ExecOpcode() method calls would have to be synchronized
|
||||||
|
to some fairly accurate time scale (some kind of timer thread or a different
|
||||||
|
solution).
|
||||||
|
|
||||||
|
6. Debugger Console Command Reference.
|
||||||
|
|
||||||
|
S - step
|
||||||
|
Executes single opcode at current address.
|
||||||
|
C - continue
|
||||||
|
Continues code execution from current address until BRK.
|
||||||
|
M - dump memory
|
||||||
|
Usage: M [startaddr] [endaddr]
|
||||||
|
Where: startaddr,endaddr - memory addr. in hexadecimal format [0000..FFFF].
|
||||||
|
Dumps contents of memory, hexadecimal and ASCII formats."
|
||||||
|
G - go/continue from new address until BRK
|
||||||
|
Usage: G [address]
|
||||||
|
Where: address - memory addr. in hexadecimal format [0000.FFFF].
|
||||||
|
Executes code at provided address, interrupted by BRK opcode.
|
||||||
|
X - execute code from new address until RTS
|
||||||
|
Usage: X [address]
|
||||||
|
Where: address - memory addr. in hexadecimal format [0000.FFFF].
|
||||||
|
Executes code at provided address, until RTS (last one).
|
||||||
|
Q - quit
|
||||||
|
Exits from the emulator/debugger.
|
||||||
|
A - set address for next step
|
||||||
|
Usage: A [address]
|
||||||
|
Where: address - memory addr. in hexadecimal format [0000.FFFF].
|
||||||
|
Sets current address to a new value.
|
||||||
|
N - go number of steps
|
||||||
|
Usage: N [steps]
|
||||||
|
Where: steps - number of steps in decimal format
|
||||||
|
Execute number of opcodes provided in steps argument starting
|
||||||
|
from current address.
|
||||||
|
P - IRQ
|
||||||
|
Send maskable interrupt request to CPU (set the IRQ line LOW).
|
||||||
|
W - write to memory
|
||||||
|
Usage: W [address] [hexval] [hexval] ... 100
|
||||||
|
Where: address - memory addr. in hexadecimal format [0000.FFFF],
|
||||||
|
hexval - byte value in hexadecimal format [00.FF].
|
||||||
|
Writes provided values to memory starting at specified address.
|
||||||
|
I - toggle char I/O emulation
|
||||||
|
Usage: I [address]
|
||||||
|
Where: address - memory addr. in hexadecimal format [0000.FFFF],
|
||||||
|
Toggles basic character I/O emulation. When enabled, all writes
|
||||||
|
to the specified memory address also writes a character code to
|
||||||
|
to a virtual console. All reads from specified memory address
|
||||||
|
are interpreted as console character input.
|
||||||
|
R - show registers
|
||||||
|
Displays CPU registers, flags and stack.
|
||||||
|
Y - snapshot
|
||||||
|
Usage: Y [file_name]
|
||||||
|
Where: file_name - the name of the output file.
|
||||||
|
Save snapshot of current CPU and memory in a binary file.
|
||||||
|
T - show I/O console
|
||||||
|
Displays/prints the contents of the virtual console screen.
|
||||||
|
Note that in run mode (commands X, G or C), virtual screen is
|
||||||
|
displayed automatically in real-time if I/O emulation is enabled.
|
||||||
|
E - toggle I/O local echo
|
||||||
|
Toggles local echo on/off when I/O emulation is enabled.
|
||||||
|
B - blank (clear) screen
|
||||||
|
Clears the screen, useful when after exiting I/O emulation or
|
||||||
|
registers animation (long stack) your screen is messed up.
|
||||||
|
F - toggle registers animation mode
|
||||||
|
When in multi-step debug mode (command: N), displaying registers
|
||||||
|
can be suppressed or, when animation mode is enabled - they will
|
||||||
|
be continuously displayed after each executed step.
|
||||||
|
J - set registers status animation delay
|
||||||
|
Usage: J [delay]
|
||||||
|
Where: delay - time of delay in milliseconds,
|
||||||
|
Sets the time added at the end of each execution step in multi
|
||||||
|
step mode (command: N). The default value is 250 ms.
|
||||||
|
K - toggle ROM emulation
|
||||||
|
Usage: K [rombegin] [romend] - to enable,
|
||||||
|
K - to disable,
|
||||||
|
(OR just use 'K' in both cases and be prompted for arguments.)
|
||||||
|
Where:
|
||||||
|
rombegin - hexadecimal address [0200..FFFF],
|
||||||
|
romend - hexadecimal address [rombegin+1..FFFF].
|
||||||
|
Enable/disable ROM emulation and define address range to which the ROM
|
||||||
|
(read-only memory) will be mapped. Default range: $D000-$DFFF.
|
||||||
|
L - load memory image
|
||||||
|
Usage: L [image_type] [image_name]
|
||||||
|
Where:
|
||||||
|
image_type - B (binary), H (Intel HEX) OR D (definition),
|
||||||
|
image_name - name of the image file.
|
||||||
|
This function allows to load new memory image from either binary
|
||||||
|
image file, Intel HEX format file or the ASCII definition file.
|
||||||
|
The binary image is always loaded from address 0x0000 and can be up to
|
||||||
|
64kB long. The definition file format is a plain text file that can
|
||||||
|
contain following keywords and data:
|
||||||
|
|
||||||
|
ADDR This keyword defines the run address of the executable code.
|
||||||
|
It is optional, but if exists, it must be the 1-st keyword
|
||||||
|
in the definition file.
|
||||||
|
Address in decimal or hexadecimal ($xxxx) format must follow
|
||||||
|
in the next line.
|
||||||
|
|
||||||
|
ORG Changes the current address counter. The line that follows
|
||||||
|
sets the new address in decimal or hexadecimal format.
|
||||||
|
Data that follows will be put in memory starting from that
|
||||||
|
address. This keyword is optional and can be used multiple
|
||||||
|
times in the definition file.
|
||||||
|
|
||||||
|
IOADDR Defines the address of the character I/O emulation. The
|
||||||
|
next line sets the address of I/O emulation in decimal or
|
||||||
|
hexadecimal format. If the I/O emulation is enabled
|
||||||
|
(see ENIO keyword), then any character written to this
|
||||||
|
address will be sent to the virtual console. The reading
|
||||||
|
from that address will invoke character input from the
|
||||||
|
emulated console. That input procedure is of blocking
|
||||||
|
type. To invoke non-blocking character procedure, reading
|
||||||
|
should be performed from IOADDR+1.
|
||||||
|
|
||||||
|
ROMBEGIN Defines the address in memory where the beginning of the
|
||||||
|
Read Only memory is mapped. The next line that follows this
|
||||||
|
keyword sets the address in decimal or hexadecimal format.
|
||||||
|
|
||||||
|
ROMEND Defines the address in memory where the end of the Read
|
||||||
|
Only Memory is mapped. The next line that follows this
|
||||||
|
keyword sets the address in decimal or hexadecimal format.
|
||||||
|
|
||||||
|
ENIO Putting this keyword in memory definition file enables
|
||||||
|
rudimentary character I/O emulation and virtual console
|
||||||
|
emulation.
|
||||||
|
|
||||||
|
ENROM Putting this keyword in memory definition file enables
|
||||||
|
emulation of Read Only Memory, in range of addresses
|
||||||
|
defined by ROMBEGIN and ROMEND keywords.
|
||||||
|
|
||||||
|
EXEC Define starting address of code which will be automatically
|
||||||
|
executed after the memory image is loaded.
|
||||||
|
The next line that follows this keyword sets the address
|
||||||
|
in decimal or hexadecimal format.
|
||||||
|
|
||||||
|
RESET Enables auto-reset of the CPU. After loading the memory
|
||||||
|
definition file, the CPU reset sequence will be initiated.
|
||||||
|
|
||||||
|
O - display op-codes history
|
||||||
|
Show the history of last executed op-codes/instructions, full with
|
||||||
|
disassembled mnemonic and argument.
|
||||||
|
D - diassemble code in memory
|
||||||
|
Usage: D [startaddr] [endaddr]
|
||||||
|
Where: startaddr,endaddr - hexadecimal address [0000..FFFF].
|
||||||
|
Attempt to disassemble code in specified address range and display
|
||||||
|
the results (print) on the screen in symbolic form.
|
||||||
|
0 - reset
|
||||||
|
Run the processor initialization sequence, just like the real CPU
|
||||||
|
when its RTS signal is set to LOW and HIGH again. CPU will disable
|
||||||
|
interrupts, copy address from vector $FFFC to processors PC and will
|
||||||
|
start executing code. Programmer must put initialization routine
|
||||||
|
under address pointed by $FFFC vector, which will set the arithmetic
|
||||||
|
mode, initialize stack, I/O devices and enable IRQ if needed before
|
||||||
|
jumping to main loop. The reset routine disables trapping last RTS
|
||||||
|
opcode if stack is empty, so the VM will never return from opcodes
|
||||||
|
execution loop unless user interrupts with CTRL-C or CTRL-Break.
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
1. If no arguments provided, each command will prompt user to enter
|
||||||
|
missing data.
|
||||||
|
2. It is possible to exit from running program to debugger console
|
||||||
|
by pressing CTRL-C or CTRL-Pause/Break, which will generate
|
||||||
|
a "Operator Interrupt". However in the character input mode
|
||||||
|
use CTRL-Y combination or CTRL-Break (DOS), CTRL-C (Linux).
|
||||||
|
You may need to press ENTER after that in input mode (DOS)
|
||||||
|
|
||||||
|
7. Command line usage.
|
||||||
|
|
||||||
|
D:\src\wrk\mkbasic>mkbasic -h
|
||||||
|
Virtual Machine/CPU Emulator (MOS 6502) and Debugger.
|
||||||
|
Copyright (C) by Marek Karcz 2016. All rights reserved.
|
||||||
|
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
mkbasic [-h] | [ramdeffile] | [-b ramimage] | [-r ramimage]
|
||||||
|
OR
|
||||||
|
mkbasic [-x intelheximage]
|
||||||
|
|
||||||
|
Where:
|
||||||
|
|
||||||
|
ramdeffile - RAM definition file name
|
||||||
|
intelheximage - Intel HEX format file
|
||||||
|
ramimage - RAM binary image file name
|
||||||
|
|
||||||
|
|
||||||
|
When ran with no arguments, program will load default memory
|
||||||
|
definition files: default.rom, default.ram and will enter the debug
|
||||||
|
console menu.
|
||||||
|
When ramdeffile argument is provided, program will load the memory
|
||||||
|
definition from the file, set the flags and parameters depending on the
|
||||||
|
contents of the memory definition file and enter the corresponding mode
|
||||||
|
of operation as defined in that file.
|
||||||
|
If used with flag -b or -x, program will load memory from the provided image
|
||||||
|
file and enter the debug console menu.
|
||||||
|
If used with flag -r, program will load memory from the provided image
|
||||||
|
file and execute CPU reset sequence.
|
||||||
|
|
||||||
|
8. Utilities.
|
||||||
|
|
||||||
|
Utility bin2hex is supplied with the project to aid in conversion from raw
|
||||||
|
binary memory image to one of the plain text formats recognized by emulator
|
||||||
|
NOTE: In current version, emulator can load raw binary format directly, so
|
||||||
|
usefulness of this utility is somewhat deprecated.
|
||||||
|
|
||||||
|
D:\src\wrk\mkbasic>bin2hex -h
|
||||||
|
|
||||||
|
Program: bin2hex
|
||||||
|
Convert binary file to Intel HEX format.
|
||||||
|
OR
|
||||||
|
Convert binary file to memory image definition for MKBASIC (VM65) emulator.
|
||||||
|
|
||||||
|
Copyright: Marek Karcz 2016. All rights reserved.
|
||||||
|
Free for personal and educational use.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
bin2hex -f input -o output [-w addr] [-x exec] [[-s] [-z] | -i]
|
||||||
|
|
||||||
|
Where:
|
||||||
|
|
||||||
|
input - binary file name
|
||||||
|
output - output file name
|
||||||
|
addr - starting address to load data (default: 2048)
|
||||||
|
exec - address to auto-execute code from (default: 2048)
|
||||||
|
-s - suppress auto-execute statement in output
|
||||||
|
-z - suppress data blocks with 0-s only
|
||||||
|
-i - convert to Intel HEX format
|
||||||
|
NOTE: When this switch is used, addr, exec, -s, -z are ignored,
|
||||||
|
addr = 0, exec is not set and data blocks with 0-s only
|
||||||
|
are always suppressed.
|
||||||
|
|
||||||
|
9. Warranty and License Agreement.
|
||||||
|
|
||||||
This software is provided with No Warranty.
|
This software is provided with No Warranty.
|
||||||
I (The Author) will not be held responsible for any damage to computer
|
I (The Author) will not be held responsible for any damage to computer
|
||||||
|
435
VMachine.cpp
435
VMachine.cpp
@ -74,7 +74,7 @@ VMachine::~VMachine()
|
|||||||
* Purpose: Initialize class.
|
* Purpose: Initialize class.
|
||||||
* Arguments: n/a
|
* Arguments: n/a
|
||||||
* Returns: n/a
|
* Returns: n/a
|
||||||
*--------------------------------------------------------------------
|
f *--------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
void VMachine::InitVM()
|
void VMachine::InitVM()
|
||||||
{
|
{
|
||||||
@ -82,6 +82,7 @@ void VMachine::InitVM()
|
|||||||
mpRAM = new Memory();
|
mpRAM = new Memory();
|
||||||
|
|
||||||
mAutoExec = false;
|
mAutoExec = false;
|
||||||
|
mAutoReset = false;
|
||||||
mCharIOAddr = CHARIO_ADDR;
|
mCharIOAddr = CHARIO_ADDR;
|
||||||
mCharIOActive = mCharIO = false;
|
mCharIOActive = mCharIO = false;
|
||||||
if (NULL == mpRAM) {
|
if (NULL == mpRAM) {
|
||||||
@ -106,10 +107,11 @@ void VMachine::InitVM()
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
* Method:
|
* Method: ClearScreen()
|
||||||
* Purpose:
|
* Purpose: Clear the working are of the VM - DOS.
|
||||||
* Arguments:
|
* This is not a part of virtual display emulation.
|
||||||
* Returns:
|
* Arguments: n/a
|
||||||
|
* Returns: n/a
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
void VMachine::ClearScreen()
|
void VMachine::ClearScreen()
|
||||||
@ -151,10 +153,11 @@ void VMachine::ClearScreen()
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
* Method:
|
* Method: ScrHome()
|
||||||
* Purpose:
|
* Purpose: Bring the console cursor to home position - DOS.
|
||||||
* Arguments:
|
* This is not a part of virtual display emulation.
|
||||||
* Returns:
|
* Arguments: n/a
|
||||||
|
* Returns: n/a
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
void VMachine::ScrHome()
|
void VMachine::ScrHome()
|
||||||
@ -175,10 +178,11 @@ void VMachine::ScrHome()
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
* Method:
|
* Method: ClearScreen()
|
||||||
* Purpose:
|
* Purpose: Clear the working are of the VM - Linux.
|
||||||
* Arguments:
|
* This is not a part of virtual display emulation.
|
||||||
* Returns:
|
* Arguments: n/a
|
||||||
|
* Returns: n/a
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
void VMachine::ClearScreen()
|
void VMachine::ClearScreen()
|
||||||
@ -188,10 +192,11 @@ void VMachine::ClearScreen()
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
* Method:
|
* Method: ScrHome()
|
||||||
* Purpose:
|
* Purpose: Bring the console cursor to home position - Linux.
|
||||||
* Arguments:
|
* This is not a part of virtual display emulation.
|
||||||
* Returns:
|
* Arguments: n/a
|
||||||
|
* Returns: n/a
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
void VMachine::ScrHome()
|
void VMachine::ScrHome()
|
||||||
@ -203,10 +208,10 @@ void VMachine::ScrHome()
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
* Method:
|
* Method: ShowDisp()
|
||||||
* Purpose:
|
* Purpose: Show the emulated virtual text display device contents.
|
||||||
* Arguments:
|
* Arguments: n/a
|
||||||
* Returns:
|
* Returns: n/a
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
void VMachine::ShowDisp()
|
void VMachine::ShowDisp()
|
||||||
@ -234,7 +239,7 @@ Regs *VMachine::Run()
|
|||||||
ShowDisp();
|
ShowDisp();
|
||||||
while (true) {
|
while (true) {
|
||||||
cpureg = Step();
|
cpureg = Step();
|
||||||
if (mCharIO) {
|
if (cpureg->CyclesLeft == 0 && mCharIO) {
|
||||||
ShowDisp();
|
ShowDisp();
|
||||||
}
|
}
|
||||||
if (cpureg->SoftIrq || mOpInterrupt)
|
if (cpureg->SoftIrq || mOpInterrupt)
|
||||||
@ -264,8 +269,8 @@ Regs *VMachine::Run(unsigned short addr)
|
|||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
* Method: Exec()
|
* Method: Exec()
|
||||||
* Purpose: Run VM from current address until last RTS.
|
* Purpose: Run VM from current address until last RTS (if enabled).
|
||||||
* NOTE: Stack must be empty!
|
* NOTE: Stack must be empty for last RTS to be trapped.
|
||||||
* Arguments: n/a
|
* Arguments: n/a
|
||||||
* Returns: Pointer to CPU registers and flags.
|
* Returns: Pointer to CPU registers and flags.
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
@ -279,7 +284,7 @@ Regs *VMachine::Exec()
|
|||||||
ShowDisp();
|
ShowDisp();
|
||||||
while (true) {
|
while (true) {
|
||||||
cpureg = Step();
|
cpureg = Step();
|
||||||
if (mCharIO) {
|
if (cpureg->CyclesLeft == 0 && mCharIO) {
|
||||||
cout << mpDisp->GetLastChar();
|
cout << mpDisp->GetLastChar();
|
||||||
cout << flush;
|
cout << flush;
|
||||||
}
|
}
|
||||||
@ -322,7 +327,7 @@ Regs *VMachine::Step()
|
|||||||
addr = cpureg->PtrAddr;
|
addr = cpureg->PtrAddr;
|
||||||
mRunAddr = addr;
|
mRunAddr = addr;
|
||||||
|
|
||||||
if (mCharIOActive && !mOpInterrupt) {
|
if (cpureg->CyclesLeft == 0 && mCharIOActive && !mOpInterrupt) {
|
||||||
char c = -1;
|
char c = -1;
|
||||||
mCharIO = false;
|
mCharIO = false;
|
||||||
while ((c = mpRAM->GetCharOut()) != -1) {
|
while ((c = mpRAM->GetCharOut()) != -1) {
|
||||||
@ -380,35 +385,340 @@ void VMachine::LoadRAM(string ramfname)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
* Method: LoadRAMBin()
|
* Method: HasHdrData()
|
||||||
* Purpose: Load data from binary image file to the memory.
|
* Purpose: Check for header in the binary memory image.
|
||||||
* Arguments: ramfname - name of the RAM file definition
|
* Arguments: File pointer.
|
||||||
|
* Returns: true if magic keyword found at the beginning of the
|
||||||
|
* memory image file, false otherwise
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
bool VMachine::HasHdrData(FILE *fp)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
int n = 0, l = strlen(HDRMAGICKEY);
|
||||||
|
char buf[20];
|
||||||
|
|
||||||
|
memset(buf, 0, 20);
|
||||||
|
|
||||||
|
while (0 == feof(fp) && 0 == ferror(fp)) {
|
||||||
|
unsigned char val = fgetc(fp);
|
||||||
|
buf[n] = val;
|
||||||
|
n++;
|
||||||
|
if (n >= l) break;
|
||||||
|
}
|
||||||
|
ret = (0 == strncmp(buf, HDRMAGICKEY, l));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
* Method: LoadHdrData()
|
||||||
|
* Purpose: Load data from binary image header.
|
||||||
|
* Arguments: File pointer.
|
||||||
|
* Returns: bool, true if success, false if error
|
||||||
|
*
|
||||||
|
* Details:
|
||||||
|
* Header of the binary memory image consists of magic keyword
|
||||||
|
* string followed by the data (unsigned char values).
|
||||||
|
* It has following format:
|
||||||
|
*
|
||||||
|
* MAGIC_KEYWORD
|
||||||
|
* aabbccddefghijk
|
||||||
|
*
|
||||||
|
* Where:
|
||||||
|
* MAGIC_KEYWORD - text string indicating header, may vary between
|
||||||
|
* versions thus rendering headers from previous
|
||||||
|
* versions incompatible - currently: "SNAPSHOT"
|
||||||
|
* aa - low and hi bytes of execute address (PC)
|
||||||
|
* bb - low and hi bytes of char IO address
|
||||||
|
* cc - low and hi bytes of ROM begin address
|
||||||
|
* dd - low and hi bytes of ROM end address
|
||||||
|
* e - 0 if char IO is disabled, 1 if enabled
|
||||||
|
* f - 0 if ROM is disabled, 1 if enabled
|
||||||
|
* g - value in CPU Acc (accumulator) register
|
||||||
|
* h - value in CPU X (X index) register
|
||||||
|
* i - value in CPU Y (Y index) register
|
||||||
|
* j - value in CPU PS (processor status/flags)
|
||||||
|
* k - value in CPU SP (stack pointer) register
|
||||||
|
*
|
||||||
|
* NOTE:
|
||||||
|
* If magic keyword was detected, this part is already read and file
|
||||||
|
* pointer position is at the 1-st byte of data. Therefore this
|
||||||
|
* method does not have to read and skip the magic keyword.
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
bool VMachine::LoadHdrData(FILE *fp)
|
||||||
|
{
|
||||||
|
int n = 0, l = 0;
|
||||||
|
unsigned short rb = 0, re = 0;
|
||||||
|
Regs r;
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
while (0 == feof(fp) && 0 == ferror(fp) && n < HDRDATALEN) {
|
||||||
|
unsigned char val = fgetc(fp);
|
||||||
|
switch (n)
|
||||||
|
{
|
||||||
|
case 1: mRunAddr = l + 256 * val;
|
||||||
|
break;
|
||||||
|
case 3: mCharIOAddr = l + 256 * val;
|
||||||
|
break;
|
||||||
|
case 5: rb = l + 256 * val;
|
||||||
|
break;
|
||||||
|
case 7: re = l + 256 * val;
|
||||||
|
break;
|
||||||
|
case 8: mCharIOActive = (val != 0);
|
||||||
|
break;
|
||||||
|
case 9: if (val != 0) {
|
||||||
|
mpRAM->EnableROM(rb, re);
|
||||||
|
} else {
|
||||||
|
mpRAM->SetROM(rb, re);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 10: r.Acc = val;
|
||||||
|
break;
|
||||||
|
case 11: r.IndX = val;
|
||||||
|
break;
|
||||||
|
case 12: r.IndY = val;
|
||||||
|
break;
|
||||||
|
case 13: r.Flags = val;
|
||||||
|
break;
|
||||||
|
case 14: r.PtrStack = val;
|
||||||
|
ret = true;
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
l = val;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
if (ret) {
|
||||||
|
r.PtrAddr = mRunAddr;
|
||||||
|
mpCPU->SetRegs(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
* Method: SaveHdrData()
|
||||||
|
* Purpose: Save header data to binary file (memory snapshot).
|
||||||
|
* Arguments: File pointer, must be opened for writing in binary mode.
|
||||||
* Returns: n/a
|
* Returns: n/a
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
void VMachine::LoadRAMBin(string ramfname)
|
void VMachine::SaveHdrData(FILE *fp)
|
||||||
|
{
|
||||||
|
char buf[20] = {0};
|
||||||
|
strcpy(buf, HDRMAGICKEY);
|
||||||
|
for (unsigned int i = 0; i < strlen(HDRMAGICKEY); i++) {
|
||||||
|
fputc(buf[i], fp);
|
||||||
|
}
|
||||||
|
Regs *reg = mpCPU->GetRegs();
|
||||||
|
unsigned char lo = 0, hi = 0;
|
||||||
|
lo = (unsigned char) (reg->PtrAddr & 0x00FF);
|
||||||
|
hi = (unsigned char) ((reg->PtrAddr & 0xFF00) >> 8);
|
||||||
|
fputc(lo, fp);
|
||||||
|
fputc(hi, fp);
|
||||||
|
lo = (unsigned char) (mCharIOAddr & 0x00FF);
|
||||||
|
hi = (unsigned char) ((mCharIOAddr & 0xFF00) >> 8);
|
||||||
|
fputc(lo, fp);
|
||||||
|
fputc(hi, fp);
|
||||||
|
lo = (unsigned char) (GetROMBegin() & 0x00FF);
|
||||||
|
hi = (unsigned char) ((GetROMBegin() & 0xFF00) >> 8);
|
||||||
|
fputc(lo, fp);
|
||||||
|
fputc(hi, fp);
|
||||||
|
lo = (unsigned char) (GetROMEnd() & 0x00FF);
|
||||||
|
hi = (unsigned char) ((GetROMEnd() & 0xFF00) >> 8);
|
||||||
|
fputc(lo, fp);
|
||||||
|
fputc(hi, fp);
|
||||||
|
lo = (mCharIOActive ? 1 : 0);
|
||||||
|
fputc(lo, fp);
|
||||||
|
lo = (IsROMEnabled() ? 1 : 0);
|
||||||
|
fputc(lo, fp);
|
||||||
|
Regs *pregs = mpCPU->GetRegs();
|
||||||
|
if (pregs != NULL) {
|
||||||
|
fputc(pregs->Acc, fp);
|
||||||
|
fputc(pregs->IndX, fp);
|
||||||
|
fputc(pregs->IndY, fp);
|
||||||
|
fputc(pregs->Flags, fp);
|
||||||
|
fputc(pregs->PtrStack, fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
* Method: SaveSnapshot()
|
||||||
|
* Purpose: Save current state of the VM and memory image.
|
||||||
|
* Arguments: String - file name.
|
||||||
|
* Returns: int, 0 if successful, greater then 0 if not (# of bytes
|
||||||
|
* not written).
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
int VMachine::SaveSnapshot(string fname)
|
||||||
|
{
|
||||||
|
FILE *fp = NULL;
|
||||||
|
int ret = MAX_8BIT_ADDR+1;
|
||||||
|
|
||||||
|
if ((fp = fopen(fname.c_str(), "wb")) != NULL) {
|
||||||
|
SaveHdrData(fp);
|
||||||
|
for (int addr = 0; addr < MAX_8BIT_ADDR+1; addr++) {
|
||||||
|
if (addr != mCharIOAddr && addr != mCharIOAddr+1) {
|
||||||
|
unsigned char b = mpRAM->Peek8bit((unsigned short)addr);
|
||||||
|
if (EOF != fputc(b, fp)) ret--;
|
||||||
|
else break;
|
||||||
|
} else {
|
||||||
|
if (EOF != fputc(0, fp)) ret--;
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
* Method: LoadRAMBin()
|
||||||
|
* Purpose: Load data from binary image file to the memory.
|
||||||
|
* Arguments: ramfname - name of the RAM file definition
|
||||||
|
* Returns: int - error code
|
||||||
|
* 0 - OK
|
||||||
|
* 1 - WARNING: Unexpected EOF (image shorter than 64kB).
|
||||||
|
* 2 - WARNING: Unable to open memory image file.
|
||||||
|
* 3 - WARNING: Problem with binary image header.
|
||||||
|
* 4 - WARNING: No header found in binary image.
|
||||||
|
* 5 - WARNING: Problem with binary image header and
|
||||||
|
* Unexpected EOF (image shorter than 64kB).
|
||||||
|
* 6 - WARNING: No header found in binary image and
|
||||||
|
* Unexpected EOF (image shorter than 64kB).
|
||||||
|
* TO DO:
|
||||||
|
* - Add fixed size header to binary image with emulator
|
||||||
|
* configuration data. Presence of the header will be detected
|
||||||
|
* by magic key at the beginning. Header should also include
|
||||||
|
* snapshot info, so the program can continue from the place
|
||||||
|
* where it was frozen/saved.
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
int VMachine::LoadRAMBin(string ramfname)
|
||||||
{
|
{
|
||||||
FILE *fp = NULL;
|
FILE *fp = NULL;
|
||||||
unsigned short addr = 0x0000;
|
unsigned short addr = 0x0000;
|
||||||
int n = 0;
|
int n = 0;
|
||||||
Memory *pm = mpRAM;
|
Memory *pm = mpRAM;
|
||||||
|
int ret = 2;
|
||||||
|
|
||||||
if ((fp = fopen(ramfname.c_str(), "rb")) != NULL) {
|
if ((fp = fopen(ramfname.c_str(), "rb")) != NULL) {
|
||||||
|
if (HasHdrData(fp)) {
|
||||||
|
ret = (LoadHdrData(fp) ? 0 : 3);
|
||||||
|
} else {
|
||||||
|
ret = 4;
|
||||||
|
rewind(fp);
|
||||||
|
}
|
||||||
|
// temporarily disable emulation facilities to allow
|
||||||
|
// proper memory image initialization
|
||||||
|
bool tmp1 = mCharIOActive, tmp2 = mpRAM->IsROMEnabled();
|
||||||
|
DisableCharIO();
|
||||||
|
DisableROM();
|
||||||
while (0 == feof(fp) && 0 == ferror(fp)) {
|
while (0 == feof(fp) && 0 == ferror(fp)) {
|
||||||
unsigned char val = fgetc(fp);
|
unsigned char val = fgetc(fp);
|
||||||
pm->Poke8bit(addr, val);
|
pm->Poke8bit(addr, val);
|
||||||
addr++; n++;
|
addr++; n++;
|
||||||
}
|
}
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
// restore emulation facilities status
|
||||||
|
if (tmp1) SetCharIO(mCharIOAddr, false);
|
||||||
|
if (tmp2) EnableROM();
|
||||||
if (n <= 0xFFFF) {
|
if (n <= 0xFFFF) {
|
||||||
cout << "WARNING: Unexpected EOF." << endl;
|
switch (ret) {
|
||||||
|
case 0: ret = 1; break;
|
||||||
|
case 3: ret = 5; break;
|
||||||
|
case 4: ret = 6; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
cout << "WARNING: Unable to open memory image file: " << ramfname << endl;
|
return ret;
|
||||||
cout << "Press [ENTER]...";
|
}
|
||||||
getchar();
|
|
||||||
}
|
/*
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
* Method: LoadRAMHex()
|
||||||
|
* Purpose: Load data from Intel HEX file format to memory.
|
||||||
|
* Arguments: hexfname - name of the HEX file
|
||||||
|
* Returns: int, 0 if OK, >0 - error code:
|
||||||
|
* 1 - unable to open file
|
||||||
|
* 2 - syntax error
|
||||||
|
* 3 - hex format error
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
int VMachine::LoadRAMHex(string hexfname)
|
||||||
|
{
|
||||||
|
char line[256] = {0};
|
||||||
|
FILE *fp = NULL;
|
||||||
|
int ret = 0;
|
||||||
|
unsigned int addr = 0;
|
||||||
|
|
||||||
|
bool tmp1 = mCharIOActive, tmp2 = mpRAM->IsROMEnabled();
|
||||||
|
DisableCharIO();
|
||||||
|
DisableROM();
|
||||||
|
if ((fp = fopen(hexfname.c_str(), "r")) != NULL) {
|
||||||
|
while (0 == feof(fp) && 0 == ferror(fp)) {
|
||||||
|
line[0] = '\0';
|
||||||
|
fgets(line, 256, fp);
|
||||||
|
if (line[0] == ':') {
|
||||||
|
if (0 == strcmp(line, HEXEOF)) {
|
||||||
|
break; // EOF, we are done here.
|
||||||
|
}
|
||||||
|
char blen[3] = {0,0,0};
|
||||||
|
char baddr[5] = {0,0,0,0,0};
|
||||||
|
char brectype[3] = {0,0,0};
|
||||||
|
blen[0] = line[1];
|
||||||
|
blen[1] = line[2];
|
||||||
|
blen[2] = 0;
|
||||||
|
baddr[0] = line[3];
|
||||||
|
baddr[1] = line[4];
|
||||||
|
baddr[2] = line[5];
|
||||||
|
baddr[3] = line[6];
|
||||||
|
baddr[4] = 0;
|
||||||
|
brectype[0] = line[7];
|
||||||
|
brectype[1] = line[8];
|
||||||
|
brectype[2] = 0;
|
||||||
|
unsigned int reclen = 0, rectype = 0;
|
||||||
|
sscanf(blen, "%02x", &reclen);
|
||||||
|
sscanf(baddr, "%04x", &addr);
|
||||||
|
sscanf(brectype, "%02x", &rectype);
|
||||||
|
if (reclen == 0 && rectype == 1) break; // EOF, we are done here.
|
||||||
|
if (rectype != 0) continue; // not a data record, next!
|
||||||
|
for (unsigned int i=9; i<reclen*2+9; i+=2,addr++) {
|
||||||
|
if (i>=strlen(line)-3) {
|
||||||
|
ret = 3; // hex format error
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
char dbuf[3] = {0,0,0};
|
||||||
|
unsigned int byteval = 0;
|
||||||
|
Memory *pm = mpRAM;
|
||||||
|
dbuf[0] = line[i];
|
||||||
|
dbuf[1] = line[i+1];
|
||||||
|
dbuf[2] = 0;
|
||||||
|
sscanf(dbuf, "%02x", &byteval);
|
||||||
|
pm->Poke8bit(addr, (unsigned char)byteval&0x00FF);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret = 2; // syntax error
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
} else {
|
||||||
|
ret = 1; // unable to open file
|
||||||
|
}
|
||||||
|
if (tmp1) SetCharIO(mCharIOAddr, false);
|
||||||
|
if (tmp2) EnableROM();
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -506,6 +816,10 @@ void VMachine::LoadMEM(string memfname, Memory *pmem)
|
|||||||
Memory *pm = pmem;
|
Memory *pm = pmem;
|
||||||
|
|
||||||
if ((fp = fopen(memfname.c_str(), "r")) != NULL) {
|
if ((fp = fopen(memfname.c_str(), "r")) != NULL) {
|
||||||
|
// to ensure proper memory initialization, disable emulation
|
||||||
|
// of char I/O and ROM
|
||||||
|
DisableROM();
|
||||||
|
DisableCharIO();
|
||||||
while (0 == feof(fp) && 0 == ferror(fp))
|
while (0 == feof(fp) && 0 == ferror(fp))
|
||||||
{
|
{
|
||||||
line[0] = '\0';
|
line[0] = '\0';
|
||||||
@ -593,6 +907,11 @@ void VMachine::LoadMEM(string memfname, Memory *pmem)
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// auto reset
|
||||||
|
if (0 == strncmp(line, "RESET", 5)) {
|
||||||
|
mAutoReset = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// define ROM begin address
|
// define ROM begin address
|
||||||
if (0 == strncmp(line, "ROMBEGIN", 8)) {
|
if (0 == strncmp(line, "ROMBEGIN", 8)) {
|
||||||
line[0] = '\0';
|
line[0] = '\0';
|
||||||
@ -651,7 +970,7 @@ void VMachine::LoadMEM(string memfname, Memory *pmem)
|
|||||||
else
|
else
|
||||||
pm->SetROM(rombegin, romend);
|
pm->SetROM(rombegin, romend);
|
||||||
} else {
|
} else {
|
||||||
if (enrom) pm->EnableROM();
|
if (enrom) EnableROM();
|
||||||
}
|
}
|
||||||
if (enio) {
|
if (enio) {
|
||||||
SetCharIO(mCharIOAddr, false);
|
SetCharIO(mCharIOAddr, false);
|
||||||
@ -796,6 +1115,19 @@ bool VMachine::IsAutoExec()
|
|||||||
return mAutoExec;
|
return mAutoExec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
* Method: IsAutoReset()
|
||||||
|
* Purpose: Return status of auto-reset flag.
|
||||||
|
* Arguments: n/a
|
||||||
|
* Returns: bool - true if auto-exec flag is enabled.
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
bool VMachine::IsAutoReset()
|
||||||
|
{
|
||||||
|
return mAutoReset;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
* Method:
|
* Method:
|
||||||
@ -954,4 +1286,33 @@ unsigned short VMachine::Disassemble(unsigned short addr, char *buf)
|
|||||||
return mpCPU->Disassemble(addr, buf);
|
return mpCPU->Disassemble(addr, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
* Method: Reset()
|
||||||
|
* Purpose: Reset VM and CPU (should never return except operator
|
||||||
|
* induced interrupt).
|
||||||
|
* Arguments: n/a
|
||||||
|
* Returns: n/a
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
void VMachine::Reset()
|
||||||
|
{
|
||||||
|
mpCPU->Reset();
|
||||||
|
Exec(mpCPU->GetRegs()->PtrAddr);
|
||||||
|
mpCPU->mExitAtLastRTS = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
* Method: Interrupt()
|
||||||
|
* Purpose: Send Interrupt ReQuest to CPU (set the IRQ line LOW).
|
||||||
|
* Arguments: n/a
|
||||||
|
* Returns: n/a
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
void VMachine::Interrupt()
|
||||||
|
{
|
||||||
|
mpCPU->Interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace MKBasic
|
} // namespace MKBasic
|
||||||
|
14
VMachine.h
14
VMachine.h
@ -15,6 +15,9 @@
|
|||||||
|
|
||||||
#define IOREFRESH 32
|
#define IOREFRESH 32
|
||||||
#define OPINTERRUPT 25 // operator interrupt code (CTRL-Y)
|
#define OPINTERRUPT 25 // operator interrupt code (CTRL-Y)
|
||||||
|
#define HDRMAGICKEY "SNAPSHOT"
|
||||||
|
#define HDRDATALEN 15
|
||||||
|
#define HEXEOF ":00000001FF"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@ -36,7 +39,8 @@ class VMachine
|
|||||||
Regs *Step(unsigned short addr);
|
Regs *Step(unsigned short addr);
|
||||||
void LoadROM(string romfname);
|
void LoadROM(string romfname);
|
||||||
void LoadRAM(string ramfname);
|
void LoadRAM(string ramfname);
|
||||||
void LoadRAMBin(string ramfname);
|
int LoadRAMBin(string ramfname);
|
||||||
|
int LoadRAMHex(string hexfname);
|
||||||
unsigned short MemPeek8bit(unsigned short addr);
|
unsigned short MemPeek8bit(unsigned short addr);
|
||||||
void MemPoke8bit(unsigned short addr, unsigned char v);
|
void MemPoke8bit(unsigned short addr, unsigned char v);
|
||||||
Regs *GetRegs();
|
Regs *GetRegs();
|
||||||
@ -48,6 +52,7 @@ class VMachine
|
|||||||
void ClearScreen();
|
void ClearScreen();
|
||||||
void ScrHome();
|
void ScrHome();
|
||||||
bool IsAutoExec();
|
bool IsAutoExec();
|
||||||
|
bool IsAutoReset();
|
||||||
void EnableROM();
|
void EnableROM();
|
||||||
void DisableROM();
|
void DisableROM();
|
||||||
void SetROM(unsigned short start, unsigned short end);
|
void SetROM(unsigned short start, unsigned short end);
|
||||||
@ -60,6 +65,9 @@ class VMachine
|
|||||||
bool IsOpInterrupt();
|
bool IsOpInterrupt();
|
||||||
queue<string> GetExecHistory();
|
queue<string> GetExecHistory();
|
||||||
unsigned short Disassemble(unsigned short addr, char *buf);
|
unsigned short Disassemble(unsigned short addr, char *buf);
|
||||||
|
void Reset();
|
||||||
|
void Interrupt();
|
||||||
|
int SaveSnapshot(string fname);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
@ -75,9 +83,13 @@ class VMachine
|
|||||||
bool mCharIO;
|
bool mCharIO;
|
||||||
bool mOpInterrupt; // operator interrupt from console
|
bool mOpInterrupt; // operator interrupt from console
|
||||||
bool mAutoExec;
|
bool mAutoExec;
|
||||||
|
bool mAutoReset;
|
||||||
|
|
||||||
void LoadMEM(string memfname, Memory *pmem);
|
void LoadMEM(string memfname, Memory *pmem);
|
||||||
void ShowDisp();
|
void ShowDisp();
|
||||||
|
bool HasHdrData(FILE *fp);
|
||||||
|
bool LoadHdrData(FILE *fp);
|
||||||
|
void SaveHdrData(FILE *fp);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace MKBasic
|
} // namespace MKBasic
|
||||||
|
151
bin2hex.c
151
bin2hex.c
@ -24,29 +24,51 @@ int g_nStartAddr = 2048; /* $0800 */
|
|||||||
int g_nExecAddr = 2048; /* $0800 */
|
int g_nExecAddr = 2048; /* $0800 */
|
||||||
int g_nSuppressAutoExec = 1;
|
int g_nSuppressAutoExec = 1;
|
||||||
int g_nSuppressAllZeroRows = 0;
|
int g_nSuppressAllZeroRows = 0;
|
||||||
|
int g_nConvert2IntelHex = 0;
|
||||||
|
|
||||||
void ScanArgs(int argc, char *argv[]);
|
void ScanArgs(int argc, char *argv[]);
|
||||||
void ConvertFile(void);
|
void ConvertFile(void);
|
||||||
|
void Convert2IntelHex(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
* Method: Usage()
|
||||||
|
* Purpose: Print usage information/help.
|
||||||
|
* Arguments: char * - program name.
|
||||||
|
* Returns: n/a
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
*/
|
||||||
void Usage(char *prgn)
|
void Usage(char *prgn)
|
||||||
{
|
{
|
||||||
printf("\nProgram: %s\n Convert binary file to memory image definition for MKBASIC (VM65) emulator.\n\n", prgn);
|
printf("\nProgram: %s\n Convert binary file to Intel HEX format.\nOR\n", prgn);
|
||||||
|
printf(" Convert binary file to memory image definition for MKBASIC (VM65) emulator.\n\n");
|
||||||
printf("Copyright: Marek Karcz 2016. All rights reserved.\n");
|
printf("Copyright: Marek Karcz 2016. All rights reserved.\n");
|
||||||
printf("Free for personal and educational use.\n\n");
|
printf("Free for personal and educational use.\n\n");
|
||||||
printf("Usage:\n\n");
|
printf("Usage:\n\n");
|
||||||
printf(" %s -f input_fname -o output_fname [-w load_addr] [-x exec_addr] [-s] [-z]\n\n", prgn);
|
printf(" %s -f input -o output [-w addr] [-x exec] [[-s] [-z] | -i]\n\n", prgn);
|
||||||
printf("Where:\n\n");
|
printf("Where:\n\n");
|
||||||
printf(" input_fname - binary file name\n");
|
printf(" input - binary file name\n");
|
||||||
printf(" output_fname - output file name\n");
|
printf(" output - output file name\n");
|
||||||
printf(" load_addr - starting address to load data (default: %d)\n", g_nStartAddr);
|
printf(" addr - starting address to load data (default: %d)\n", g_nStartAddr);
|
||||||
printf(" exec_addr - address to auto-execute code from (default: %d)\n", g_nExecAddr);
|
printf(" exec - address to auto-execute code from (default: %d)\n", g_nExecAddr);
|
||||||
printf(" -s - suppress auto-execute statement in output\n");
|
printf(" -s - suppress auto-execute statement in output\n");
|
||||||
printf(" -z - suppress data blocks with 0-s only\n");
|
printf(" -z - suppress data blocks with 0-s only\n");
|
||||||
|
printf(" -i - convert to Intel HEX format\n");
|
||||||
|
printf(" NOTE: When this switch is used, addr, exec, -s, -z are ignored,\n");
|
||||||
|
printf(" addr = 0, exec is not set and data blocks with 0-s only\n");
|
||||||
|
printf(" are always suppressed.\n");
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* bin2hex -f InputFile -o OutputFile -w StartAddr
|
*--------------------------------------------------------------------
|
||||||
|
* Method: ScanArgs()
|
||||||
|
* Purpose: Scan/parse command line arguments and set internal
|
||||||
|
* flags and parameters.
|
||||||
|
* Arguments: int argc - # of command line arguments,
|
||||||
|
* char *argv[] - array of command line arguments.
|
||||||
|
* Returns: n/a
|
||||||
|
*--------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
void ScanArgs(int argc, char *argv[])
|
void ScanArgs(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
@ -85,11 +107,24 @@ void ScanArgs(int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
g_nSuppressAllZeroRows = 1;
|
g_nSuppressAllZeroRows = 1;
|
||||||
}
|
}
|
||||||
|
else if (strcmp(argv[n],"-i") == 0)
|
||||||
|
{
|
||||||
|
g_nConvert2IntelHex = 1;
|
||||||
|
}
|
||||||
|
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
* Method: ConvertFile()
|
||||||
|
* Purpose: Convert binary file to plain text memory definition
|
||||||
|
* file for MKBASIC (VM65) emulator.
|
||||||
|
* Arguments: n/a
|
||||||
|
* Returns: n/a
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
*/
|
||||||
void ConvertFile(void)
|
void ConvertFile(void)
|
||||||
{
|
{
|
||||||
FILE *fpi = NULL;
|
FILE *fpi = NULL;
|
||||||
@ -170,6 +205,96 @@ void ConvertFile(void)
|
|||||||
printf("ERROR: Unable to open input file.\n");
|
printf("ERROR: Unable to open input file.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
* Method: Convert2IntelHex()
|
||||||
|
* Purpose: Convert binary file to Intel HEX format.
|
||||||
|
* Arguments: n/a
|
||||||
|
* Returns: n/a
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
void Convert2IntelHex(void)
|
||||||
|
{
|
||||||
|
FILE *fpi = NULL;
|
||||||
|
FILE *fpo = NULL;
|
||||||
|
unsigned char bt[17], cksum;
|
||||||
|
char hex[80];
|
||||||
|
int i, allzero;
|
||||||
|
unsigned int addr;
|
||||||
|
|
||||||
|
addr = 0;
|
||||||
|
g_nSuppressAllZeroRows = 1;
|
||||||
|
g_nExecAddr = 0;
|
||||||
|
printf("Processing...\n");
|
||||||
|
printf("Start address: $%04x\n", addr);
|
||||||
|
if (NULL != (fpi = fopen(g_szInputFileName,"rb")))
|
||||||
|
{
|
||||||
|
if (NULL != (fpo = fopen(g_szHexFileName,"w")))
|
||||||
|
{
|
||||||
|
while(0 == feof(fpi) && addr <= 0xFFFF)
|
||||||
|
{
|
||||||
|
memset(bt, 0, 17);
|
||||||
|
memset(hex, 0, 80);
|
||||||
|
if (DEBUG) printf("Reading input file...");
|
||||||
|
fread(bt, sizeof(char), 16, fpi);
|
||||||
|
if (DEBUG) printf("done.\n");
|
||||||
|
if (DEBUG) printf("Preparing hex string...\n");
|
||||||
|
/* start the Intel HEX data record, all data blocks
|
||||||
|
generated by this program are 16-bytes long
|
||||||
|
*/
|
||||||
|
sprintf(hex, ":10%04x00", addr);
|
||||||
|
cksum = 0;
|
||||||
|
allzero = 1;
|
||||||
|
/* append data to record */
|
||||||
|
for(i=0; i<16; i++)
|
||||||
|
{
|
||||||
|
cksum += bt[i];
|
||||||
|
if (DEBUG) printf("Code: %d\n", bt[i]);
|
||||||
|
sprintf(hex, "%s%02x", hex, bt[i]);
|
||||||
|
if (allzero && bt[i]) allzero = 0;
|
||||||
|
}
|
||||||
|
cksum = ~cksum + 1; /* Two's complement of modulo 256 sum */
|
||||||
|
/* end record with check sum value */
|
||||||
|
sprintf(hex, "%s%02x", hex, cksum);
|
||||||
|
/* output only if non-zero data present in 16-byte block */
|
||||||
|
if (0 == g_nSuppressAllZeroRows
|
||||||
|
||
|
||||||
|
(g_nSuppressAllZeroRows && 0 == allzero)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
sprintf(hex, "%s\n", hex);
|
||||||
|
if (DEBUG) printf("Adding line: %s", hex);
|
||||||
|
fputs(hex, fpo);
|
||||||
|
}
|
||||||
|
addr += 16;
|
||||||
|
}
|
||||||
|
/* add EOF */
|
||||||
|
hex[0] = 0;
|
||||||
|
strcpy(hex, ":00000001FF");
|
||||||
|
sprintf(hex, "%s\n", hex);
|
||||||
|
fputs(hex, fpo);
|
||||||
|
fclose(fpi);
|
||||||
|
fclose(fpo);
|
||||||
|
printf("Done.\n");
|
||||||
|
printf("End address: $%04x\n", (addr <= 0xFFFF) ? addr : 0xFFFF);
|
||||||
|
printf("Run address: $%04x\n", g_nExecAddr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
printf("ERROR: Unable to create output file.\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
printf("ERROR: Unable to open input file.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
* Method: main()
|
||||||
|
* Purpose: Main program loop/routine.
|
||||||
|
* Arguments: int argc - # of provided in command line arguments.
|
||||||
|
* char *argv[] - array of command line arguments.
|
||||||
|
* Returns: int - always 0.
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
*/
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
if (argc == 1)
|
if (argc == 1)
|
||||||
@ -178,8 +303,12 @@ int main(int argc, char *argv[])
|
|||||||
ScanArgs(argc, argv);
|
ScanArgs(argc, argv);
|
||||||
if (*g_szInputFileName == 0 || *g_szHexFileName == 0)
|
if (*g_szInputFileName == 0 || *g_szHexFileName == 0)
|
||||||
Usage(argv[0]);
|
Usage(argv[0]);
|
||||||
else
|
else {
|
||||||
ConvertFile();
|
if (0 == g_nConvert2IntelHex)
|
||||||
|
ConvertFile();
|
||||||
|
else
|
||||||
|
Convert2IntelHex();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
11
ehbas.dat
11
ehbas.dat
@ -10,13 +10,15 @@ $FFE0
|
|||||||
ROMBEGIN
|
ROMBEGIN
|
||||||
$FFC0
|
$FFC0
|
||||||
ROMEND
|
ROMEND
|
||||||
$FFDF
|
$FFFF
|
||||||
|
;$FFDF
|
||||||
; Enable char IO and ROM
|
; Enable char IO and ROM
|
||||||
ENIO
|
ENIO
|
||||||
ENROM
|
ENROM
|
||||||
; Auto-execute
|
; Auto-execute
|
||||||
EXEC
|
;EXEC
|
||||||
$C000
|
;$C000
|
||||||
|
RESET
|
||||||
; Data/Code
|
; Data/Code
|
||||||
$D8, $A0, $08, $B9, $FB, $E0, $99, $00
|
$D8, $A0, $08, $B9, $FB, $E0, $99, $00
|
||||||
$02, $88, $10, $F7, $A2, $FF, $8E, $21
|
$02, $88, $10, $F7, $A2, $FF, $8E, $21
|
||||||
@ -2062,3 +2064,6 @@ $AD, $E1, $FF, $F0, $0C, $C9, $61, $90
|
|||||||
$06, $C9, $7B, $B0, $02, $29, $5F, $38
|
$06, $C9, $7B, $B0, $02, $29, $5F, $38
|
||||||
$60, $18, $60, $8D, $E0, $FF, $29, $FF
|
$60, $18, $60, $8D, $E0, $FF, $29, $FF
|
||||||
$60, $00, $00, $00, $00, $00, $00, $00
|
$60, $00, $00, $00, $00, $00, $00, $00
|
||||||
|
ORG
|
||||||
|
$FFFC
|
||||||
|
$00 $C0
|
||||||
|
281
main.cpp
281
main.cpp
@ -3,6 +3,7 @@
|
|||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <string.h>
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include "MKCpu.h"
|
#include "MKCpu.h"
|
||||||
#include "Memory.h"
|
#include "Memory.h"
|
||||||
@ -24,6 +25,109 @@ int g_stackdisp_lines = 1;
|
|||||||
|
|
||||||
bool ShowRegs(Regs *preg, VMachine *pvm, bool ioecho, bool showiostat);
|
bool ShowRegs(Regs *preg, VMachine *pvm, bool ioecho, bool showiostat);
|
||||||
void ShowHelp();
|
void ShowHelp();
|
||||||
|
void CmdArgHelp(string prgname);
|
||||||
|
void CopyrightBanner();
|
||||||
|
|
||||||
|
/*
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
* Method: RunSingleInstr()
|
||||||
|
* Purpose: Execute single instruction of the CPU (all cycles).
|
||||||
|
* Arguments: addr - unsigned short, instruction address
|
||||||
|
* Returns: pointer to CPU registers
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
Regs *RunSingleInstr(unsigned short addr)
|
||||||
|
{
|
||||||
|
Regs *ret = NULL;
|
||||||
|
|
||||||
|
do {
|
||||||
|
ret = pvm->Step(addr);
|
||||||
|
} while (ret->CyclesLeft > 0);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
* Method: RunSingleCurrInstr()
|
||||||
|
* Purpose: Execute single instruction of the CPU (all cycles)
|
||||||
|
* at current address.
|
||||||
|
* Arguments: n/a
|
||||||
|
* Returns: pointer to CPU registers
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
Regs *RunSingleCurrInstr()
|
||||||
|
{
|
||||||
|
Regs *ret = NULL;
|
||||||
|
|
||||||
|
do {
|
||||||
|
ret = pvm->Step();
|
||||||
|
} while (ret->CyclesLeft > 0);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
* Method: PrintLoadBinImgErr()
|
||||||
|
* Purpose: Print the warning/error message after loading binary
|
||||||
|
* image.
|
||||||
|
* Arguments: err - integer, error code
|
||||||
|
* Returns: n/a
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
void PrintLoadBinImgErr(int err)
|
||||||
|
{
|
||||||
|
bool pressenter = true;
|
||||||
|
switch (err) {
|
||||||
|
case 1: cout << "WARNING: Unexpected EOF (image shorter than 64kB)." << endl;
|
||||||
|
break;
|
||||||
|
case 2: cout << "WARNING: Unable to open memory image file." << endl;
|
||||||
|
break;
|
||||||
|
case 3: cout << "WARNING: Problem with binary image header." << endl;
|
||||||
|
break;
|
||||||
|
case 4: cout << "WARNING: No header found in binary image." << endl;
|
||||||
|
break;
|
||||||
|
case 5: cout << "WARNING: Problem with binary image header." << endl;
|
||||||
|
cout << "WARNING: Unexpected EOF (image shorter than 64kB)." << endl;
|
||||||
|
break;
|
||||||
|
case 6: cout << "WARNING: No header found in binary image." << endl;
|
||||||
|
cout << "WARNING: Unexpected EOF (image shorter than 64kB)." << endl;
|
||||||
|
break;
|
||||||
|
default: pressenter = false; break;
|
||||||
|
}
|
||||||
|
if (pressenter) {
|
||||||
|
cout << "Press [ENTER]...";
|
||||||
|
getchar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
* Method: PrintLoadHexImgErr()
|
||||||
|
* Purpose: Print the warning/error message after loading Intel HEX
|
||||||
|
* image.
|
||||||
|
* Arguments: err - integer, error code
|
||||||
|
* Returns: n/a
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
void PrintLoadHexImgErr(int err)
|
||||||
|
{
|
||||||
|
bool pressenter = true;
|
||||||
|
switch (err) {
|
||||||
|
case 1: cout << "WARNING: Unable to open file." << endl;
|
||||||
|
break;
|
||||||
|
case 2: cout << "ERROR: Syntax error." << endl;
|
||||||
|
break;
|
||||||
|
case 3: cout << "ERROR: Intel HEX format error." << endl;
|
||||||
|
break;
|
||||||
|
default: pressenter = false; break;
|
||||||
|
}
|
||||||
|
if (pressenter) {
|
||||||
|
cout << "Press [ENTER]...";
|
||||||
|
getchar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(LINUX)
|
#if defined(LINUX)
|
||||||
|
|
||||||
@ -233,14 +337,14 @@ void ShowMenu()
|
|||||||
{
|
{
|
||||||
cout << "------------------------------------+----------------------------------------" << endl;
|
cout << "------------------------------------+----------------------------------------" << endl;
|
||||||
cout << " C - continue, S - step | A - set address for next step" << endl;
|
cout << " C - continue, S - step | A - set address for next step" << endl;
|
||||||
cout << " G - go/cont. from new address | N - go number of steps" << endl;
|
cout << " G - go/cont. from new address | N - go number of steps, P - IRQ" << endl;
|
||||||
cout << " I - toggle char I/O emulation | X - execute from new address" << endl;
|
cout << " I - toggle char I/O emulation | X - execute from new address" << endl;
|
||||||
cout << " T - show I/O console | B - blank (clear) screen" << endl;
|
cout << " T - show I/O console | B - blank (clear) screen" << endl;
|
||||||
cout << " E - toggle I/O local echo | F - toggle registers animation" << endl;
|
cout << " E - toggle I/O local echo | F - toggle registers animation" << endl;
|
||||||
cout << " J - set animation delay | M - dump memory, W - write memory" << endl;
|
cout << " J - set animation delay | M - dump memory, W - write memory" << endl;
|
||||||
cout << " K - toggle ROM emulation | R - show registers" << endl;
|
cout << " K - toggle ROM emulation | R - show registers, Y - snapshot" << endl;
|
||||||
cout << " L - load memory image | O - display op-codes history" << endl;
|
cout << " L - load memory image | O - display op-codes history" << endl;
|
||||||
cout << " D - disassemble code in memory | Q - quit, H - help" << endl;
|
cout << " D - disassemble code in memory | Q - quit, 0 - reset, H - help" << endl;
|
||||||
cout << "------------------------------------+----------------------------------------" << endl;
|
cout << "------------------------------------+----------------------------------------" << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,7 +378,7 @@ void ShowMenu()
|
|||||||
while(step && nsteps > 1 && !brk && !lrts && !opbrk) { \
|
while(step && nsteps > 1 && !brk && !lrts && !opbrk) { \
|
||||||
cout << "addr: $" << hex << preg->PtrAddr << ", step: " << dec << stct; \
|
cout << "addr: $" << hex << preg->PtrAddr << ", step: " << dec << stct; \
|
||||||
cout << " \r"; \
|
cout << " \r"; \
|
||||||
preg = pvm->Step(); \
|
preg = RunSingleCurrInstr(); \
|
||||||
if (anim) { \
|
if (anim) { \
|
||||||
if (cls & ClsIfDirty) { pvm->ClearScreen(); cls = false; } \
|
if (cls & ClsIfDirty) { pvm->ClearScreen(); cls = false; } \
|
||||||
pvm->ScrHome(); \
|
pvm->ScrHome(); \
|
||||||
@ -292,6 +396,7 @@ void ShowMenu()
|
|||||||
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
|
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
|
bool loadbin = false, loadhex = false, reset = false, execvm = false;
|
||||||
#if defined(LINUX)
|
#if defined(LINUX)
|
||||||
signal(SIGINT, trap_signal);
|
signal(SIGINT, trap_signal);
|
||||||
signal(SIGTERM, trap_signal);
|
signal(SIGTERM, trap_signal);
|
||||||
@ -301,17 +406,48 @@ int main(int argc, char** argv) {
|
|||||||
#endif
|
#endif
|
||||||
string romfile("dummy.rom"), ramfile("dummy.ram");
|
string romfile("dummy.rom"), ramfile("dummy.ram");
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
ramfile = argv[1];
|
if (argc > 2) {
|
||||||
|
reset = execvm = loadbin = (0 == strcmp(argv[1], "-r")); // load binary image and reset
|
||||||
|
if (!loadbin) loadbin = (0 == strcmp(argv[1], "-b")); // just load binary image
|
||||||
|
if (!loadbin) loadhex = (0 == strcmp(argv[1], "-x")); // just load Intel HEX image
|
||||||
|
if (loadbin && loadhex) {
|
||||||
|
cout << "ERROR: Can't load both formats at the same time." << endl;
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
ramfile = argv[2];
|
||||||
|
} else {
|
||||||
|
if (0 == strcmp(argv[1], "-h")) {
|
||||||
|
CmdArgHelp(argv[0]);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
ramfile = argv[1];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
cout << endl;
|
cout << endl;
|
||||||
pvm = new VMachine(romfile, ramfile);
|
if (loadbin) {
|
||||||
|
pvm = new VMachine(romfile, "dummy.ram");
|
||||||
|
if (NULL != pvm) {
|
||||||
|
PrintLoadBinImgErr (pvm->LoadRAMBin(ramfile));
|
||||||
|
if (!reset && !execvm)
|
||||||
|
reset = execvm = pvm->IsAutoReset();
|
||||||
|
}
|
||||||
|
} else if (loadhex) {
|
||||||
|
pvm = new VMachine(romfile, "dummy.ram");
|
||||||
|
if (NULL != pvm) PrintLoadHexImgErr (pvm->LoadRAMHex(ramfile));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pvm = new VMachine(romfile, ramfile);
|
||||||
|
reset = execvm = pvm->IsAutoReset();
|
||||||
|
}
|
||||||
|
if (NULL == pvm) {
|
||||||
|
throw MKGenException("Out of memory");
|
||||||
|
}
|
||||||
pvm->ClearScreen();
|
pvm->ClearScreen();
|
||||||
cout << "Virtual Machine/CPU Emulator (MOS 6502) and Debugger." << endl;
|
CopyrightBanner();
|
||||||
cout << "Copyright (C) by Marek Karcz 2016. All rights reserved." << endl;
|
|
||||||
string cmd;
|
string cmd;
|
||||||
bool runvm = false, step = false, brk = false, execaddr = false, stop = true;
|
bool runvm = false, step = false, brk = false, execaddr = false, stop = true;
|
||||||
bool lrts = false, execvm = false, anim = false, enrom = pvm->IsROMEnabled();
|
bool lrts = false, anim = false, enrom = pvm->IsROMEnabled();
|
||||||
unsigned int newaddr = pvm->GetRunAddr(), ioaddr = pvm->GetCharIOAddr(), tmpaddr = 0x0000;
|
unsigned int newaddr = pvm->GetRunAddr(), ioaddr = pvm->GetCharIOAddr(), tmpaddr = 0x0000;
|
||||||
unsigned int rombegin = pvm->GetROMBegin(), romend = pvm->GetROMEnd(), delay = ANIM_DELAY;
|
unsigned int rombegin = pvm->GetROMBegin(), romend = pvm->GetROMEnd(), delay = ANIM_DELAY;
|
||||||
int nsteps = 0;
|
int nsteps = 0;
|
||||||
@ -325,11 +461,11 @@ int main(int argc, char** argv) {
|
|||||||
if (anim) pvm->ClearScreen();
|
if (anim) pvm->ClearScreen();
|
||||||
int stct = 1;
|
int stct = 1;
|
||||||
if (execaddr) {
|
if (execaddr) {
|
||||||
preg = ((step) ? pvm->Step(newaddr) : pvm->Run(newaddr));
|
preg = ((step) ? RunSingleInstr(newaddr) : pvm->Run(newaddr));
|
||||||
RUNSTEPS(step,nsteps,brk,preg,stct,pvm,lrts,anim,delay);
|
RUNSTEPS(step,nsteps,brk,preg,stct,pvm,lrts,anim,delay);
|
||||||
execaddr = false;
|
execaddr = false;
|
||||||
} else {
|
} else {
|
||||||
preg = ((step) ? pvm->Step() : pvm->Run());
|
preg = ((step) ? RunSingleCurrInstr() : pvm->Run());
|
||||||
RUNSTEPS(step,nsteps,brk,preg,stct,pvm,lrts,anim,delay);
|
RUNSTEPS(step,nsteps,brk,preg,stct,pvm,lrts,anim,delay);
|
||||||
}
|
}
|
||||||
if (step)
|
if (step)
|
||||||
@ -338,7 +474,13 @@ int main(int argc, char** argv) {
|
|||||||
runvm = step = false;
|
runvm = step = false;
|
||||||
newaddr = 0x10000;
|
newaddr = 0x10000;
|
||||||
} else if (execvm) {
|
} else if (execvm) {
|
||||||
preg = (execaddr ? pvm->Exec(newaddr) : pvm->Exec());
|
if (reset) {
|
||||||
|
pvm->Reset();
|
||||||
|
preg = pvm->GetRegs();
|
||||||
|
reset = false;
|
||||||
|
} else {
|
||||||
|
preg = (execaddr ? pvm->Exec(newaddr) : pvm->Exec());
|
||||||
|
}
|
||||||
execvm = false;
|
execvm = false;
|
||||||
execaddr = false;
|
execaddr = false;
|
||||||
brk = preg->SoftIrq;
|
brk = preg->SoftIrq;
|
||||||
@ -368,6 +510,24 @@ int main(int argc, char** argv) {
|
|||||||
char c = tolower(cmd.c_str()[0]);
|
char c = tolower(cmd.c_str()[0]);
|
||||||
if (c == 'h') { // display help
|
if (c == 'h') { // display help
|
||||||
ShowHelp();
|
ShowHelp();
|
||||||
|
} else if (c == 'p') { // Interrupt ReQuest
|
||||||
|
pvm->Interrupt();
|
||||||
|
cout << "OK" << endl;
|
||||||
|
} else if (c == 'y') { // save snapshot of current CPU and memory in binary image
|
||||||
|
string name;
|
||||||
|
cout << "Enter file name: ";
|
||||||
|
cin >> name;
|
||||||
|
cout << " [" << name << "]" << endl;
|
||||||
|
if (0 == pvm->SaveSnapshot(name)) {
|
||||||
|
cout << "OK" << endl;
|
||||||
|
} else {
|
||||||
|
cout << "ERROR!" << endl;
|
||||||
|
cout << "errno=" << errno << endl;
|
||||||
|
}
|
||||||
|
} else if (c == '0') { // reset CPU
|
||||||
|
reset = true;
|
||||||
|
execvm = true;
|
||||||
|
runvm = false;
|
||||||
} else if (c == 'o') {
|
} else if (c == 'o') {
|
||||||
queue<string> exechist(pvm->GetExecHistory());
|
queue<string> exechist(pvm->GetExecHistory());
|
||||||
cout << "PC : INSTR ACC | X | Y | PS | SP" << endl;
|
cout << "PC : INSTR ACC | X | Y | PS | SP" << endl;
|
||||||
@ -378,8 +538,8 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
} else if (c == 'l') { // load memory image
|
} else if (c == 'l') { // load memory image
|
||||||
char typ = 0;
|
char typ = 0;
|
||||||
while (tolower(typ) != 'b' && tolower(typ) != 'd') {
|
while (tolower(typ) != 'b' && tolower(typ) != 'd' && tolower(typ) != 'h') {
|
||||||
cout << "Type (B - binary/D - definition): ";
|
cout << "Type (B - binary/H - Intel HEX/D - definition): ";
|
||||||
cin >> typ;
|
cin >> typ;
|
||||||
}
|
}
|
||||||
cout << " [" << ((tolower(typ) == 'b') ? "binary" : "definition") << "]" << endl;
|
cout << " [" << ((tolower(typ) == 'b') ? "binary" : "definition") << "]" << endl;
|
||||||
@ -387,7 +547,8 @@ int main(int argc, char** argv) {
|
|||||||
cout << "Memory Image File Name: ";
|
cout << "Memory Image File Name: ";
|
||||||
cin >> name;
|
cin >> name;
|
||||||
cout << " [" << name << "]" << endl;
|
cout << " [" << name << "]" << endl;
|
||||||
if (typ == 'b') pvm->LoadRAMBin(name);
|
if (typ == 'b') PrintLoadBinImgErr (pvm->LoadRAMBin(name));
|
||||||
|
else if (typ == 'h') PrintLoadHexImgErr (pvm->LoadRAMHex(name));
|
||||||
else {
|
else {
|
||||||
pvm->LoadRAM(name);
|
pvm->LoadRAM(name);
|
||||||
if (pvm->IsAutoExec()) execvm = true;
|
if (pvm->IsAutoExec()) execvm = true;
|
||||||
@ -542,8 +703,63 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
* Method: ShowHel2p()
|
* Method: CopyrightBanner()
|
||||||
* Purpose: Display commands help.
|
* Purpose: Display copyright information.
|
||||||
|
* Arguments: n/a
|
||||||
|
* Returns: n/a
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
void CopyrightBanner()
|
||||||
|
{
|
||||||
|
cout << "Virtual Machine/CPU Emulator (MOS 6502) and Debugger." << endl;
|
||||||
|
cout << "Copyright (C) by Marek Karcz 2016. All rights reserved." << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
* Method: CmdArgHelp()
|
||||||
|
* Purpose: Display command line arguments help/Usage.
|
||||||
|
* Arguments: prgname - string, program name
|
||||||
|
* Returns: n/a
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
void CmdArgHelp(string prgname)
|
||||||
|
{
|
||||||
|
CopyrightBanner();
|
||||||
|
|
||||||
|
cout << endl << endl;
|
||||||
|
cout << "Usage:" << endl << endl;
|
||||||
|
cout << "\t" << prgname;
|
||||||
|
cout << " [-h] | [ramdeffile] | [-b ramimage] | [-r ramimage]" << endl;
|
||||||
|
cout << "\tOR" << endl;
|
||||||
|
cout << "\t" << prgname << " [-x intelheximage]";
|
||||||
|
cout << endl << endl;
|
||||||
|
cout << "Where:" << endl << endl;
|
||||||
|
cout << "\tramdeffile - RAM definition file name" << endl;
|
||||||
|
cout << "\tintelheximage - Intel HEX format file" << endl;
|
||||||
|
cout << "\tramimage - RAM binary image file name" << endl;
|
||||||
|
cout << R"(
|
||||||
|
|
||||||
|
When ran with no arguments, program will load default memory
|
||||||
|
definition files: default.rom, default.ram and will enter the debug
|
||||||
|
console menu.
|
||||||
|
When ramdeffile argument is provided, program will load the memory
|
||||||
|
definition from the file, set the flags and parameters depending on the
|
||||||
|
contents of the memory definition file and enter the corresponding mode
|
||||||
|
of operation as defined in that file.
|
||||||
|
If used with flag -b or -x, program will load memory from the provided image
|
||||||
|
file and enter the debug console menu.
|
||||||
|
If used with flag -r, program will load memory from the provided image
|
||||||
|
file and execute CPU reset sequence.
|
||||||
|
|
||||||
|
)";
|
||||||
|
cout << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*--------------------------------------------------------------------
|
||||||
|
* Method: ShowHelp()
|
||||||
|
* Purpose: Display Debugger Console Command Reference help.
|
||||||
* Arguments: n/a
|
* Arguments: n/a
|
||||||
* Returns: n/a
|
* Returns: n/a
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
@ -579,6 +795,8 @@ N - go number of steps
|
|||||||
Where: steps - number of steps in decimal format
|
Where: steps - number of steps in decimal format
|
||||||
Execute number of opcodes provided in steps argument starting
|
Execute number of opcodes provided in steps argument starting
|
||||||
from current address.
|
from current address.
|
||||||
|
P - IRQ
|
||||||
|
Send maskable interrupt request to CPU (set the IRQ line LOW).
|
||||||
W - write to memory
|
W - write to memory
|
||||||
Usage: W [address] [hexval] [hexval] ... 100
|
Usage: W [address] [hexval] [hexval] ... 100
|
||||||
Where: address - memory addr. in hexadecimal format [0000.FFFF],
|
Where: address - memory addr. in hexadecimal format [0000.FFFF],
|
||||||
@ -593,6 +811,10 @@ I - toggle char I/O emulation
|
|||||||
are interpreted as console character input.
|
are interpreted as console character input.
|
||||||
R - show registers
|
R - show registers
|
||||||
Displays CPU registers, flags and stack.
|
Displays CPU registers, flags and stack.
|
||||||
|
Y - snapshot
|
||||||
|
Usage: Y [file_name]
|
||||||
|
Where: file_name - the name of the output file.
|
||||||
|
Save snapshot of current CPU and memory in a binary file.
|
||||||
T - show I/O console
|
T - show I/O console
|
||||||
Displays/prints the contents of the virtual console screen.
|
Displays/prints the contents of the virtual console screen.
|
||||||
Note that in run mode (commands X, G or C), virtual screen is
|
Note that in run mode (commands X, G or C), virtual screen is
|
||||||
@ -623,13 +845,13 @@ K - toggle ROM emulation
|
|||||||
L - load memory image
|
L - load memory image
|
||||||
Usage: L [image_type] [image_name]
|
Usage: L [image_type] [image_name]
|
||||||
Where:
|
Where:
|
||||||
image_type - B (binary) OR D (definition),
|
image_type - B (binary), H (Intel HEX) OR D (definition),
|
||||||
image_name - name of the image file.
|
image_name - name of the image file.
|
||||||
This function allows to load new memory image from either binary
|
This function allows to load new memory image from either binary
|
||||||
image file or the ASCII definition file. The binary image is always
|
image file, Intel HEX format file or the ASCII definition file.
|
||||||
loaded from address 0x0000 and can be up to 64kB long. The definition
|
The binary image is always loaded from address 0x0000 and can be up to
|
||||||
file format is a plain text file that can contain following keywords
|
64kB long. The definition file format is a plain text file that can
|
||||||
and data:
|
contain following keywords and data:
|
||||||
|
|
||||||
ADDR This keyword defines the run address of the executable code.
|
ADDR This keyword defines the run address of the executable code.
|
||||||
It is optional, but if exists, it must be the 1-st keyword
|
It is optional, but if exists, it must be the 1-st keyword
|
||||||
@ -674,6 +896,9 @@ L - load memory image
|
|||||||
The next line that follows this keyword sets the address
|
The next line that follows this keyword sets the address
|
||||||
in decimal or hexadecimal format.
|
in decimal or hexadecimal format.
|
||||||
|
|
||||||
|
RESET Enables auto-reset of the CPU. After loading the memory
|
||||||
|
definition file, the CPU reset sequence will be initiated.
|
||||||
|
|
||||||
O - display op-codes history
|
O - display op-codes history
|
||||||
Show the history of last executed op-codes/instructions, full with
|
Show the history of last executed op-codes/instructions, full with
|
||||||
disassembled mnemonic and argument.
|
disassembled mnemonic and argument.
|
||||||
@ -682,6 +907,16 @@ D - diassemble code in memory
|
|||||||
Where: startaddr,endaddr - hexadecimal address [0000..FFFF].
|
Where: startaddr,endaddr - hexadecimal address [0000..FFFF].
|
||||||
Attempt to disassemble code in specified address range and display
|
Attempt to disassemble code in specified address range and display
|
||||||
the results (print) on the screen in symbolic form.
|
the results (print) on the screen in symbolic form.
|
||||||
|
0 - reset
|
||||||
|
Run the processor initialization sequence, just like the real CPU
|
||||||
|
when its RTS signal is set to LOW and HIGH again. CPU will disable
|
||||||
|
interrupts, copy address from vector $FFFC to processors PC and will
|
||||||
|
start executing code. Programmer must put initialization routine
|
||||||
|
under address pointed by $FFFC vector, which will set the arithmetic
|
||||||
|
mode, initialize stack, I/O devices and enable IRQ if needed before
|
||||||
|
jumping to main loop. The reset routine disables trapping last RTS
|
||||||
|
opcode if stack is empty, so the VM will never return from opcodes
|
||||||
|
execution loop unless user interrupts with CTRL-C or CTRL-Break.
|
||||||
|
|
||||||
NOTE:
|
NOTE:
|
||||||
1. If no arguments provided, each command will prompt user to enter
|
1. If no arguments provided, each command will prompt user to enter
|
||||||
@ -690,7 +925,7 @@ NOTE:
|
|||||||
by pressing CTRL-C or CTRL-Pause/Break, which will generate
|
by pressing CTRL-C or CTRL-Pause/Break, which will generate
|
||||||
a "Operator Interrupt". However in the character input mode
|
a "Operator Interrupt". However in the character input mode
|
||||||
use CTRL-Y combination or CTRL-Break (DOS), CTRL-C (Linux).
|
use CTRL-Y combination or CTRL-Break (DOS), CTRL-C (Linux).
|
||||||
You may need to press ENTER after that in input mode (DOS)
|
You may need to press ENTER after that in input mode (DOS).
|
||||||
)";
|
)";
|
||||||
cout << endl;
|
cout << endl;
|
||||||
}
|
}
|
2
makefile
2
makefile
@ -9,7 +9,7 @@ LIBS = -static-libgcc -m32 -g3 -ltermcap
|
|||||||
CLIBS = -static-libgcc -m32 -g3
|
CLIBS = -static-libgcc -m32 -g3
|
||||||
INCS =
|
INCS =
|
||||||
CXXINCS =
|
CXXINCS =
|
||||||
CXXFLAGS = $(CXXINCS) -m32 -std=c++0x -Wall -pedantic -g3
|
CXXFLAGS = $(CXXINCS) -m32 -std=c++0x -Wall -pedantic -g3 -fpermissive
|
||||||
#CFLAGS = $(INCS) -m32 -std=c++0x -Wall -pedantic -g3
|
#CFLAGS = $(INCS) -m32 -std=c++0x -Wall -pedantic -g3
|
||||||
CFLAGS = $(INCS) -m32 -Wall -pedantic -g3
|
CFLAGS = $(INCS) -m32 -Wall -pedantic -g3
|
||||||
RM = rm -f
|
RM = rm -f
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
; Created with BIN2HEX (C) Marek Karcz 2016. All rights reserved.
|
; Created with BIN2HEX (C) Marek Karcz 2016. All rights reserved.
|
||||||
; 03/13/16 00:08:59
|
; Mon Mar 14 00:17:15 2016
|
||||||
ADDR
|
ADDR
|
||||||
$0400
|
$0400
|
||||||
ORG
|
ORG
|
||||||
|
@ -13,6 +13,8 @@ $F0 $06 $8D $00 $E0 $E8 $D0 $F5
|
|||||||
$00 $00 $EA $4C $00 $02 $45 $6E
|
$00 $00 $EA $4C $00 $02 $45 $6E
|
||||||
$74 $65 $72 $20 $74 $65 $78 $74
|
$74 $65 $72 $20 $74 $65 $78 $74
|
||||||
$3A $00 $00 $00 $00 $00 $00 $00
|
$3A $00 $00 $00 $00 $00 $00 $00
|
||||||
|
ORG
|
||||||
|
$FFFC
|
||||||
|
$00 $02
|
||||||
ENIO
|
ENIO
|
||||||
EXEC
|
RESET
|
||||||
$0200
|
|
157
tinybasic.asm
157
tinybasic.asm
@ -55,19 +55,26 @@
|
|||||||
; 2/15/2016
|
; 2/15/2016
|
||||||
; Ported to my own 6502 emulator.
|
; Ported to my own 6502 emulator.
|
||||||
;
|
;
|
||||||
|
; 3/30/2016
|
||||||
|
;
|
||||||
|
; Changed char input address from blocking to non-blocking.
|
||||||
|
;
|
||||||
;
|
;
|
||||||
;--------------------------------------------------------------------------------------
|
;--------------------------------------------------------------------------------------
|
||||||
|
|
||||||
;.segment "BASIC"
|
.segment "BEGN"
|
||||||
|
|
||||||
|
.ORG $0000
|
||||||
|
|
||||||
|
.segment "BASIC"
|
||||||
|
|
||||||
;
|
;
|
||||||
; Tiny Basic starts here
|
; Tiny Basic starts here
|
||||||
;
|
;
|
||||||
.org $0400 ; Start of Basic. First 1K of ROM is shaded by I/O on the OMS-02
|
.ORG $4400 ; Start of Basic. First 1K of ROM is shaded by I/O on the OMS-02
|
||||||
|
|
||||||
SOBAS:
|
SOBAS:
|
||||||
|
|
||||||
;CLRSC = ClearScreen
|
|
||||||
C_00BC = $00BC ; These are needed because my assembler
|
C_00BC = $00BC ; These are needed because my assembler
|
||||||
C_20 = $20 ; does not hanle standard 6502 notation
|
C_20 = $20 ; does not hanle standard 6502 notation
|
||||||
C_22 = $22 ; properly
|
C_22 = $22 ; properly
|
||||||
@ -86,8 +93,8 @@ SR_V_H = SOBAS + $1F ; Base address for subroutine vector hi byte
|
|||||||
|
|
||||||
CV: jmp COLD_S ; Cold start vector
|
CV: jmp COLD_S ; Cold start vector
|
||||||
WV: jmp WARM_S ; Warm start vector
|
WV: jmp WARM_S ; Warm start vector
|
||||||
IN_V: jmp RCCHR ; Input routine address.
|
IN_V: jmp GetCh ; Input routine address.
|
||||||
OUT_V: jmp SNDCHR ; Output routine address.
|
OUT_V: jmp PutCh ; Output routine address.
|
||||||
BREAK: nop ; Begin dummy break routine
|
BREAK: nop ; Begin dummy break routine
|
||||||
clc ; Clear the carry flag
|
clc ; Clear the carry flag
|
||||||
rts ; End break routine
|
rts ; End break routine
|
||||||
@ -247,7 +254,7 @@ LBL003: .byte >ILTBL ;$1B ; $1B - hi byte of IL address
|
|||||||
COLD_S: lda #$00 ; Load accumulator with lo byte of lower and upper prg memory limits
|
COLD_S: lda #$00 ; Load accumulator with lo byte of lower and upper prg memory limits
|
||||||
sta $20 ; Store in $20
|
sta $20 ; Store in $20
|
||||||
sta $22 ; Store in $22
|
sta $22 ; Store in $22
|
||||||
lda #$1C ; Load accumulator with hi byte of lower and upper prg memory limits
|
lda #$50 ; Load accumulator with hi byte of lower and upper prg memory limits
|
||||||
sta $21 ; Store in $21
|
sta $21 ; Store in $21
|
||||||
sta $23 ; Store in $23
|
sta $23 ; Store in $23
|
||||||
; NOTE: $22,$23 vector will be updated by routine below to be the upper RAM limit for TB.
|
; NOTE: $22,$23 vector will be updated by routine below to be the upper RAM limit for TB.
|
||||||
@ -1288,9 +1295,9 @@ ILTBL: .byte $24, $3A, $91, $27, $10, $E1, $59, $C5, $2A, $56, $10, $11, $2C,
|
|||||||
; End of Tiny Basic
|
; End of Tiny Basic
|
||||||
|
|
||||||
|
|
||||||
;.segment "MAIN"
|
.segment "MAIN"
|
||||||
|
|
||||||
.org $0CF0 ; Address of main program
|
.org $4CF0 ; Address of main program
|
||||||
|
|
||||||
; Code needs work below here, BIOS must be changed for MKHBC-8-R1
|
; Code needs work below here, BIOS must be changed for MKHBC-8-R1
|
||||||
|
|
||||||
@ -1316,57 +1323,45 @@ ILTBL: .byte $24, $3A, $91, $27, $10, $E1, $59, $C5, $2A, $56, $10, $11, $2C,
|
|||||||
; sta ACIACN ; 2400, 8 bits, 1 stop bit, external Rx clock
|
; sta ACIACN ; 2400, 8 bits, 1 stop bit, external Rx clock
|
||||||
;--------------------
|
;--------------------
|
||||||
|
|
||||||
main:
|
main: cli
|
||||||
nop
|
cld
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
nop
|
|
||||||
jsr CLRSC ; Go clear the screen
|
jsr CLRSC ; Go clear the screen
|
||||||
ldy #$00 ; Offset for welcome message and prompt
|
ldy #$00 ; Offset for welcome message and prompt
|
||||||
jsr SNDMSG ; Go print it
|
jsr SNDMSG ; Go print it
|
||||||
ST_LP: JSR RCCHR ; Go get a character from the console
|
ST_LP: JSR GetCh ; Go get a character from the console
|
||||||
cmp #$43 ; Check for 'C'
|
cmp #$43 ; Check for 'C'
|
||||||
BNE IS_WRM ; If not branch to next check
|
BNE IS_WRM ; If not branch to next check
|
||||||
jmp COLD_S ; Otherwise cold-start Tiny Basic
|
jmp COLD_S ; Otherwise cold-start Tiny Basic
|
||||||
IS_WRM: cmp #$57 ; Check for 'W'
|
IS_WRM: cmp #$57 ; Check for 'W'
|
||||||
BNE PRMPT ; If not, branch to re-prompt them
|
BNE PRMPT ; If not, branch to re-prompt them
|
||||||
jmp WARM_S ; Otherwise warm-start Tiny Basic
|
jmp WARM_S ; Otherwise warm-start Tiny Basic
|
||||||
PRMPT: ldx #$2F ; Offset of prompt
|
PRMPT: ldy #$00 ; Offset of prompt
|
||||||
jsr SNDMSG ; Go print the prompt
|
jsr SNDMSG ; Go print the prompt
|
||||||
jmp ST_LP ; Go get the response
|
jmp ST_LP ; Go get the response
|
||||||
|
|
||||||
;.segment "MESG"
|
.segment "MESG"
|
||||||
|
|
||||||
.org $0E00 ; Address of message area
|
.org $4E00 ; Address of message area
|
||||||
MBLK:
|
MBLK:
|
||||||
|
|
||||||
;
|
;
|
||||||
; The message block begins at $1E00 and is at most 256 bytes long.
|
; The message block begins at $4E00 and is at most 256 bytes long.
|
||||||
; Messages terminate with an FF.
|
; Messages terminate with an FF.
|
||||||
;
|
;
|
||||||
.byte "OMEGA MICRO SYSTEMS"
|
.byte "MKHBC-8-R2 TINY BASIC 6502 PORT"
|
||||||
.byte $0D, $0A ;, $0A
|
.byte $0D, $0A ;, $0A
|
||||||
.byte "MKHBC-8-R1 TINY BASIC 6502 PORT"
|
.byte "Adapted from OMEGA MICRO SYSTEMS."
|
||||||
.byte $0D, $0A ;, $0A
|
.byte $0D, $0A ;, $0A
|
||||||
.byte "Version: 1.0.2, 1/11/2012"
|
.byte "Version: 1.0.3, 3/31/2016"
|
||||||
.byte $0D, $0A ;, $0A
|
.byte $0D, $0A ;, $0A
|
||||||
.byte "(NOTE: Use caps for Basic)"
|
.byte "(NOTE: Use upper case letters.)"
|
||||||
.byte $0D, $0A ;, $0A
|
.byte $0D, $0A ;, $0A
|
||||||
.byte "Boot ([C]old/[W]arm)? "
|
.byte "Boot ([C]old/[W]arm)? "
|
||||||
.byte $07, $FF
|
.byte $07, $FF
|
||||||
|
|
||||||
;.segment "SUBR"
|
.segment "SUBR"
|
||||||
|
|
||||||
.org $0F00 ;address of subroutine area
|
.org $4F00 ;address of subroutine area
|
||||||
|
|
||||||
SBLK:
|
SBLK:
|
||||||
;
|
;
|
||||||
@ -1377,14 +1372,6 @@ SBLK:
|
|||||||
|
|
||||||
StrPtr = $E0
|
StrPtr = $E0
|
||||||
|
|
||||||
; Kernel jump table
|
|
||||||
|
|
||||||
GetCh = $FFED
|
|
||||||
PutCh = $FFF0
|
|
||||||
Gets = $FFF3
|
|
||||||
Puts = $FFF6
|
|
||||||
|
|
||||||
|
|
||||||
;SNDCHR = PutCh
|
;SNDCHR = PutCh
|
||||||
;RCCHR = GetCh
|
;RCCHR = GetCh
|
||||||
|
|
||||||
@ -1426,9 +1413,9 @@ TwoBytePeek:
|
|||||||
|
|
||||||
CLRSC: ldx #$19 ; Load X - we're going tp print 25 lines
|
CLRSC: ldx #$19 ; Load X - we're going tp print 25 lines
|
||||||
lda #$0D ; CR
|
lda #$0D ; CR
|
||||||
jsr SNDCHR ; Send a carriage retuen
|
jsr PutCh ; Send a carriage retuen
|
||||||
lda #$0A ; LF
|
lda #$0A ; LF
|
||||||
CSLP: jsr SNDCHR ; Send the line feed
|
CSLP: jsr PutCh ; Send the line feed
|
||||||
dex ; One less to do
|
dex ; One less to do
|
||||||
bne CSLP ; Go send another untill we're done
|
bne CSLP ; Go send another untill we're done
|
||||||
rts ; Return
|
rts ; Return
|
||||||
@ -1440,57 +1427,73 @@ CSLP: jsr SNDCHR ; Send the line feed
|
|||||||
SNDMSG: lda MBLK,y ; Get a character from teh message block
|
SNDMSG: lda MBLK,y ; Get a character from teh message block
|
||||||
cmp #$FF ; Look for end of message marker
|
cmp #$FF ; Look for end of message marker
|
||||||
beq EXSM ; Finish up if it is
|
beq EXSM ; Finish up if it is
|
||||||
jsr SNDCHR ; Otherwise send the character
|
jsr PutCh ; Otherwise send the character
|
||||||
iny ; Increment the pointer
|
iny ; Increment the pointer
|
||||||
jmp SNDMSG ; Go get next character
|
jmp SNDMSG ; Go get next character
|
||||||
EXSM: rts ; Return
|
EXSM: rts ; Return
|
||||||
|
|
||||||
;
|
;
|
||||||
; Get a character from the ACIA
|
; Get a character from the character input device
|
||||||
; Runs into SNDCHR to provide echo
|
; Runs into SNDCHR to provide echo
|
||||||
;
|
;
|
||||||
RCCHR: ;lda ACIAST ; Read the ACAI status to (for OMS-02)
|
RCCHR: lda $E001 ; Check if a character typed (for emulator, non-blocking)
|
||||||
;and #$08 ; Check if there is character in the receiver (for OMS-02)
|
beq RCCHR ; Loop until we get one (for emulator)
|
||||||
;beq RCCHR ; Loop util we get one (for OMS-02)
|
cmp #'a' ; < 'a'?
|
||||||
;lda ACIARW ; Load it into the accumulator (for OMS-02)
|
bcc SNDCHR ; yes, done
|
||||||
LDA $E000 ; Check if a character typed (for emulator)
|
cmp #'{' ; >= '{'?
|
||||||
BEQ RCCHR ; Loop until we get one (for emulator)
|
bcs SNDCHR ; yes, done
|
||||||
|
and #$5f ; convert to upper case
|
||||||
;RCCHR: jsr GetCh
|
|
||||||
; jsr SNDCHR ; echo character to the console
|
|
||||||
; rts
|
|
||||||
|
|
||||||
;
|
;
|
||||||
;Send a character to the ACIA
|
; Send a character to the character output device
|
||||||
;
|
;
|
||||||
SNDCHR: sta $FE ; Save the character to be printed
|
SNDCHR: sta $FE ; Save the character to be printed
|
||||||
cmp #$FF ; Check for a bunch of characters
|
cmp #$FF ; Check for a bunch of characters
|
||||||
BEQ EXSC ; that we don't want to print to
|
beq EXSC ; that we don't want to print to
|
||||||
cmp #$00 ; the terminal and discard them to
|
cmp #$00 ; the terminal and discard them to
|
||||||
BEQ EXSC ; clean up the output
|
beq EXSC ; clean up the output
|
||||||
cmp #$91 ;
|
cmp #$91 ;
|
||||||
BEQ EXSC ;
|
beq EXSC ;
|
||||||
cmp #$93 ;
|
cmp #$93 ;
|
||||||
BEQ EXSC ;
|
beq EXSC ;
|
||||||
cmp #$80 ;
|
cmp #$80 ;
|
||||||
BEQ EXSC ;
|
beq EXSC ;
|
||||||
jmp SCLP
|
SCLP: lda $FE ; Restore the character
|
||||||
jsr PutCh ; MKHBC-8-R1
|
sta $E000 ; Transmit it (for emulator)
|
||||||
lda $fe
|
|
||||||
rts
|
|
||||||
SCLP: ;lda ACIAST ; Check ACIA Status (don't need for emulator)
|
|
||||||
;and #$10 ; See if transmiter it busy (don't need for emulator)
|
|
||||||
;beq SCLP ; Wait for it (don't need for emulator)
|
|
||||||
lda $FE ; Restore the character
|
|
||||||
;sta ACIARW ; Transmit it (for OMS-02)
|
|
||||||
STA $E000 ; Transmit it (for emulator)
|
|
||||||
EXSC: rts ; Return
|
EXSC: rts ; Return
|
||||||
|
|
||||||
; .org $1FFC ; Address of reset vector (for 6507 not required for emulator)
|
; Kernel jump table
|
||||||
;RSVEC
|
|
||||||
; .byte $F0, $1C ; Reset vector
|
;GetCh = $FFED
|
||||||
|
;PutCh = $FFF0
|
||||||
|
;Gets = $FFF3
|
||||||
|
;Puts = $FFF6
|
||||||
|
|
||||||
|
; String I/O not used at this time.
|
||||||
|
|
||||||
|
STRIN: rts
|
||||||
|
STROUT: rts
|
||||||
|
|
||||||
|
; Vector jumps handlers
|
||||||
|
|
||||||
|
NmiHandle: rti
|
||||||
|
RstHandle: jmp main
|
||||||
|
IrqHandle: rti
|
||||||
|
|
||||||
|
.segment "KERN"
|
||||||
|
|
||||||
|
.ORG $FFED
|
||||||
|
|
||||||
|
GetCh: jmp RCCHR
|
||||||
|
PutCh: jmp SNDCHR
|
||||||
|
Gets: jmp STRIN
|
||||||
|
Puts: jmp STROUT
|
||||||
|
|
||||||
|
.segment "VECT"
|
||||||
|
|
||||||
|
.ORG $FFFA
|
||||||
|
|
||||||
|
.byte <NmiHandle, >NmiHandle, <RstHandle, >RstHandle, <IrqHandle, >IrqHandle
|
||||||
|
|
||||||
|
|
||||||
; .org $3000 ; Address of last byte of EPROM
|
|
||||||
;EOROM:
|
|
||||||
|
|
||||||
;--------------------------- END ----------------------------------------------------------------------
|
;--------------------------- END ----------------------------------------------------------------------
|
||||||
|
40
tinybasic.cfg
Normal file
40
tinybasic.cfg
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
########################################################
|
||||||
|
#
|
||||||
|
# File: tinybasic.cfg
|
||||||
|
#
|
||||||
|
# Purpose: cc65 configuration file for Tiny Basic
|
||||||
|
# interpreter.
|
||||||
|
#
|
||||||
|
# Author: Marek Karcz
|
||||||
|
#
|
||||||
|
# Date created: January 2012
|
||||||
|
#
|
||||||
|
# Revision history:
|
||||||
|
#
|
||||||
|
# 2/13/2012
|
||||||
|
# Relocated from $0400 to $4400
|
||||||
|
#
|
||||||
|
# 3/31/2016
|
||||||
|
# Adapted for MKBASIC (VM65) emulator.
|
||||||
|
#
|
||||||
|
|
||||||
|
MEMORY {
|
||||||
|
RAM0: start = $0000, size = $4400, fill = yes;
|
||||||
|
RAM1: start = $4400, size = $08f0, fill = yes;
|
||||||
|
RAM2: start = $4cf0, size = $0110, fill = yes;
|
||||||
|
RAM3: start = $4e00, size = $0100, fill = yes;
|
||||||
|
RAM4: start = $4f00, size = $b0ed, fill = yes;
|
||||||
|
KERN: start = $ffed, size = $0d, fill = yes;
|
||||||
|
VECT: start = $fffa, size = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
SEGMENTS {
|
||||||
|
BEGN: load = RAM0, type = rw;
|
||||||
|
BASIC: load = RAM1, type = rw;
|
||||||
|
MAIN: load = RAM2, type = rw;
|
||||||
|
MESG: load = RAM3, type = rw;
|
||||||
|
SUBR: load = RAM4, type = rw;
|
||||||
|
KERN: load = KERN, type = ro;
|
||||||
|
VECT: load = VECT, type = ro;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user