summaryrefslogtreecommitdiff
path: root/plugins/sid/sidplay-libs/libsidplay/src/mos6510/cycle_based/mos6510c.i
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/sid/sidplay-libs/libsidplay/src/mos6510/cycle_based/mos6510c.i')
-rw-r--r--plugins/sid/sidplay-libs/libsidplay/src/mos6510/cycle_based/mos6510c.i2562
1 files changed, 2562 insertions, 0 deletions
diff --git a/plugins/sid/sidplay-libs/libsidplay/src/mos6510/cycle_based/mos6510c.i b/plugins/sid/sidplay-libs/libsidplay/src/mos6510/cycle_based/mos6510c.i
new file mode 100644
index 00000000..78d57ba6
--- /dev/null
+++ b/plugins/sid/sidplay-libs/libsidplay/src/mos6510/cycle_based/mos6510c.i
@@ -0,0 +1,2562 @@
+/***************************************************************************
+ mos6510.i - Cycle Accurate 6510 emulation
+ -------------------
+ begin : Thu May 11 06:22:40 BST 2000
+ copyright : (C) 2000 by Simon White
+ email : s_a_white@email.com
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+/***************************************************************************
+ * $Log: mos6510c.i,v $
+ * Revision 1.30 2002/12/16 08:42:58 s_a_white
+ * Fixed use of nothrow to be namespaced with std::.
+ *
+ * Revision 1.29 2002/11/28 20:35:06 s_a_white
+ * Reduced number of thrown exceptions when dma occurs.
+ *
+ * Revision 1.28 2002/11/25 20:10:55 s_a_white
+ * A bus access failure should stop the CPU dead like the cycle never started.
+ * This is currently simulated using throw (execption handling) for now.
+ *
+ * Revision 1.27 2002/11/21 19:52:48 s_a_white
+ * CPU upgraded to be like other components. Theres nolonger a clock call,
+ * instead events are registered to occur at a specific time.
+ *
+ * Revision 1.26 2002/11/19 22:57:33 s_a_white
+ * Initial support for external DMA to steal cycles away from the CPU.
+ *
+ * Revision 1.25 2002/11/01 19:22:36 s_a_white
+ * Removed debug printf.
+ *
+ * Revision 1.24 2002/11/01 17:35:27 s_a_white
+ * Frame based support for old sidplay1 modes.
+ *
+ * Revision 1.23 2002/03/12 18:48:03 s_a_white
+ * Tidied illegal instruction debug print out.
+ *
+ * Revision 1.22 2001/12/11 19:24:15 s_a_white
+ * More GCC3 Fixes.
+ *
+ * Revision 1.21 2001/11/16 19:21:03 s_a_white
+ * Sign fixes.
+ *
+ * Revision 1.20 2001/10/28 21:31:26 s_a_white
+ * Removed kernel debuging code.
+ *
+ * Revision 1.19 2001/09/03 22:21:52 s_a_white
+ * When initialising the status register and therefore unmasking the irqs,
+ * check the irq line to see if any are pending.
+ *
+ * Revision 1.18 2001/08/10 20:05:50 s_a_white
+ * Fixed RMW instructions which broke due to the optimisation.
+ *
+ * Revision 1.17 2001/08/05 15:46:02 s_a_white
+ * No longer need to check on which cycle an instruction ends or when to print
+ * debug information.
+ *
+ * Revision 1.16 2001/07/14 13:15:30 s_a_white
+ * Accumulator is now unsigned, which improves code readability. Emulation
+ * tested with testsuite 2.15. Various instructions required modification.
+ *
+ * Revision 1.15 2001/04/20 22:23:11 s_a_white
+ * Handling of page boundary crossing now correct for branch instructions.
+ *
+ * Revision 1.14 2001/03/28 22:59:59 s_a_white
+ * Converted some bad envReadMemByte's to
+ * envReadMemDataByte
+ *
+ * Revision 1.13 2001/03/28 21:17:34 s_a_white
+ * Added support for proper RMW instructions.
+ *
+ * Revision 1.12 2001/03/24 18:09:17 s_a_white
+ * On entry to interrupt routine the first instruction in the handler is now always
+ * executed before pending interrupts are re-checked.
+ *
+ * Revision 1.11 2001/03/22 22:40:43 s_a_white
+ * Added new header for definition of nothrow.
+ *
+ * Revision 1.10 2001/03/21 22:27:18 s_a_white
+ * Change to IRQ error message.
+ *
+ * Revision 1.9 2001/03/19 23:46:35 s_a_white
+ * NMI no longer sets I flag. RTI and store instructions are no longer
+ * overlapped.
+ *
+ * Revision 1.8 2001/03/09 22:28:51 s_a_white
+ * Speed optimisation update and fix for interrupt flag in PushSR call.
+ *
+ * Revision 1.7 2001/02/22 08:28:57 s_a_white
+ * Interrupt masking fixed.
+ *
+ * Revision 1.6 2001/02/13 23:01:44 s_a_white
+ * envReadMemDataByte now used for some memory accesses.
+ *
+ * Revision 1.5 2000/12/24 00:45:38 s_a_white
+ * HAVE_EXCEPTIONS update
+ *
+ * Revision 1.4 2000/12/14 23:55:07 s_a_white
+ * PushSR optimisation and PopSR code cleanup.
+ *
+ ***************************************************************************/
+/*
+const char _sidtune_CHRtab[256] = // CHR$ conversion table (0x01 = no output)
+{
+ 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0xd, 0x1, 0x1,
+ 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
+ 0x20,0x21, 0x1,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
+ 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
+ 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
+ 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x24,0x5d,0x20,0x20,
+ // alternative: CHR$(92=0x5c) => ISO Latin-1(0xa3)
+ 0x2d,0x23,0x7c,0x2d,0x2d,0x2d,0x2d,0x7c,0x7c,0x5c,0x5c,0x2f,0x5c,0x5c,0x2f,0x2f,
+ 0x5c,0x23,0x5f,0x23,0x7c,0x2f,0x58,0x4f,0x23,0x7c,0x23,0x2b,0x7c,0x7c,0x26,0x5c,
+ // 0x80-0xFF
+ 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
+ 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
+ 0x20,0x7c,0x23,0x2d,0x2d,0x7c,0x23,0x7c,0x23,0x2f,0x7c,0x7c,0x2f,0x5c,0x5c,0x2d,
+ 0x2f,0x2d,0x2d,0x7c,0x7c,0x7c,0x7c,0x2d,0x2d,0x2d,0x2f,0x5c,0x5c,0x2f,0x2f,0x23,
+ 0x2d,0x23,0x7c,0x2d,0x2d,0x2d,0x2d,0x7c,0x7c,0x5c,0x5c,0x2f,0x5c,0x5c,0x2f,0x2f,
+ 0x5c,0x23,0x5f,0x23,0x7c,0x2f,0x58,0x4f,0x23,0x7c,0x23,0x2b,0x7c,0x7c,0x26,0x5c,
+ 0x20,0x7c,0x23,0x2d,0x2d,0x7c,0x23,0x7c,0x23,0x2f,0x7c,0x7c,0x2f,0x5c,0x5c,0x2d,
+ 0x2f,0x2d,0x2d,0x7c,0x7c,0x7c,0x7c,0x2d,0x2d,0x2d,0x2f,0x5c,0x5c,0x2f,0x2f,0x23
+};
+*/
+
+#include "config.h"
+
+#ifdef HAVE_EXCEPTIONS
+# include <new>
+#endif
+
+// Microsoft Visual C++ Version Number to work around compiler bug
+// Currently both Visual C++ Versions 5, 6 are broken.
+#define _MSC_VER_BAD_NEW 1200 /* Defines VC6 and below */
+//char filetmp[0x100];
+//int filepos = 0;
+
+//-------------------------------------------------------------------------//
+//-------------------------------------------------------------------------//
+// Status Register Routines //
+//-------------------------------------------------------------------------//
+//-------------------------------------------------------------------------//
+// Use macros to access flags. Allows compatiblity with other versions
+// of this emulation
+// Set N and Z flags according to byte
+#define setFlagsNZ(x) (Register_z_Flag = (Register_n_Flag = (uint_least8_t) (x)))
+#define setFlagN(x) (Register_n_Flag = (uint_least8_t) (x))
+#define setFlagV(x) (Register_v_Flag = (uint_least8_t) (x))
+#define setFlagD(x) (Register_Status = (Register_Status & ~(1 << SR_DECIMAL)) \
+ | (((x) != 0) << SR_DECIMAL))
+#define setFlagI(x) (Register_Status = (Register_Status & ~(1 << SR_INTERRUPT)) \
+ | (((x) != 0) << SR_INTERRUPT))
+#define setFlagZ(x) (Register_z_Flag = (uint_least8_t) (x))
+#define setFlagC(x) (Register_c_Flag = (uint_least8_t) (x))
+
+
+#define getFlagN() ((Register_n_Flag & (1 << SR_NEGATIVE)) != 0)
+#define getFlagV() (Register_v_Flag != 0)
+#define getFlagD() ((Register_Status & (1 << SR_DECIMAL)) != 0)
+#define getFlagI() ((Register_Status & (1 << SR_INTERRUPT)) != 0)
+#define getFlagZ() (Register_z_Flag == 0)
+#define getFlagC() (Register_c_Flag != 0)
+
+#define stealCycle() \
+ interrupts.delay++; \
+ throw((int_least8_t) -1);
+
+
+// Handle bus access signals
+void MOS6510::aecSignal (bool state)
+{ // If the cpu blocked waiting for the bus
+ // the schedule a retry.
+ aec = state;
+ if (state && m_blocked)
+ {
+ m_blocked = false;
+ eventContext.schedule (this, 1);
+ }
+}
+
+void MOS6510::rdySignal (bool state)
+{ // If the cpu blocked waiting for the bus
+ // the schedule a retry.
+ rdy = state;
+ if (state && m_blocked)
+ {
+ m_blocked = false;
+ eventContext.schedule (this, 1);
+ }
+}
+
+// Push P on stack, decrement S
+void MOS6510::PushSR (bool b_flag)
+{
+ if (aec)
+ {
+ uint_least16_t addr = Register_StackPointer;
+ endian_16hi8 (addr, SP_PAGE);
+ /* Rev 1.04 - Corrected flag mask */
+ Register_Status &= ((1 << SR_NOTUSED) | (1 << SR_INTERRUPT) |
+ (1 << SR_DECIMAL) | (1 << SR_BREAK));
+ Register_Status |= (getFlagN () << SR_NEGATIVE);
+ Register_Status |= (getFlagV () << SR_OVERFLOW);
+ Register_Status |= (getFlagZ () << SR_ZERO);
+ Register_Status |= (getFlagC () << SR_CARRY);
+ envWriteMemByte (addr, Register_Status & ~((!b_flag) << SR_BREAK));
+ Register_StackPointer--;
+ }
+ else
+ { // Address bus not ready
+ stealCycle();
+ }
+}
+
+void MOS6510::PushSR (void)
+{
+ PushSR (true);
+}
+
+// increment S, Pop P off stack
+void MOS6510::PopSR (void)
+{
+ if (rdy && aec)
+ {
+ bool newFlagI, oldFlagI;
+ oldFlagI = getFlagI ();
+
+ // Get status register off stack
+ Register_StackPointer++;
+ {
+ uint_least16_t addr = Register_StackPointer;
+ endian_16hi8 (addr, SP_PAGE);
+ Register_Status = envReadMemByte (addr);
+ }
+ Register_Status |= ((1 << SR_NOTUSED) | (1 << SR_BREAK));
+ setFlagN (Register_Status);
+ setFlagV (Register_Status & (1 << SR_OVERFLOW));
+ setFlagZ (!(Register_Status & (1 << SR_ZERO)));
+ setFlagC (Register_Status & (1 << SR_CARRY));
+
+ // I flag change is delayed by 1 instruction
+ newFlagI = getFlagI ();
+ interrupts.irqLatch = oldFlagI ^ newFlagI;
+ // Check to see if interrupts got re-enabled
+ if (!newFlagI && interrupts.irqs)
+ interrupts.irqRequest = true;
+ }
+ else
+ { // Address bus not ready
+ stealCycle();
+ }
+}
+
+
+//-------------------------------------------------------------------------//
+//-------------------------------------------------------------------------//
+// Interrupt Routines //
+//-------------------------------------------------------------------------//
+//-------------------------------------------------------------------------//
+#define iIRQSMAX 3
+enum
+{
+ oNONE = -1,
+ oRST,
+ oNMI,
+ oIRQ
+};
+
+enum
+{
+ iNONE = 0,
+ iRST = 1 << oRST,
+ iNMI = 1 << oNMI,
+ iIRQ = 1 << oIRQ
+};
+
+void MOS6510::triggerRST (void)
+{
+ interrupts.pending |= iRST;
+}
+
+void MOS6510::triggerNMI (void)
+{
+ interrupts.pending |= iNMI;
+ interrupts.nmiClock = eventContext.getTime ();
+}
+
+// Level triggered interrupt
+void MOS6510::triggerIRQ (void)
+{ // IRQ Suppressed
+ if (!getFlagI ())
+ interrupts.irqRequest = true;
+ if (!interrupts.irqs++)
+ interrupts.irqClock = eventContext.getTime ();
+
+ if (interrupts.irqs > iIRQSMAX)
+ {
+ printf ("\nMOS6510 ERROR: An external component is not clearing down it's IRQs.\n\n");
+ exit (-1);
+ }
+}
+
+void MOS6510::clearIRQ (void)
+{
+ if (interrupts.irqs > 0)
+ {
+ if (!(--interrupts.irqs))
+ { // Clear off the interrupts
+ interrupts.irqRequest = false;
+ }
+ }
+}
+
+bool MOS6510::interruptPending (void)
+{
+ int_least8_t offset, pending;
+ static const int_least8_t offTable[] = {oNONE, oRST, oNMI, oRST,
+ oIRQ, oRST, oNMI, oRST};
+ // Update IRQ pending
+ if (!interrupts.irqLatch)
+ {
+ interrupts.pending &= ~iIRQ;
+ if (interrupts.irqRequest)
+ interrupts.pending |= iIRQ;
+ }
+
+ pending = interrupts.pending;
+MOS6510_interruptPending_check:
+ // Service the highest priority interrupt
+ offset = offTable[pending];
+ switch (offset)
+ {
+ case oNONE:
+ return false;
+
+ case oNMI:
+ {
+ // Try to determine if we should be processing the NMI yet
+ event_clock_t cycles = eventContext.getTime (interrupts.nmiClock);
+ if (cycles >= interrupts.delay)
+ {
+ interrupts.pending &= ~iNMI;
+ break;
+ }
+
+ // NMI delayed so check for other interrupts
+ pending &= ~iNMI;
+ goto MOS6510_interruptPending_check;
+ }
+
+ case oIRQ:
+ {
+ // Try to determine if we should be processing the IRQ yet
+ event_clock_t cycles = eventContext.getTime (interrupts.irqClock);
+ if (cycles >= interrupts.delay)
+ break;
+
+ // NMI delayed so check for other interrupts
+ pending &= ~iIRQ;
+ goto MOS6510_interruptPending_check;
+ }
+
+ case oRST:
+ break;
+ }
+
+#ifdef MOS6510_DEBUG
+ if (dodump)
+ {
+ printf ("****************************************************\n");
+ switch (offset)
+ {
+ case oIRQ:
+ printf (" IRQ Routine\n");
+ break;
+ case oNMI:
+ printf (" NMI Routine\n");
+ break;
+ case oRST:
+ printf (" RST Routine\n");
+ break;
+ }
+ printf ("****************************************************\n");
+ }
+#endif
+
+ // Start the interrupt
+ instrCurrent = &interruptTable[offset];
+ procCycle = instrCurrent->cycle;
+ cycleCount = 0;
+ return true;
+}
+
+void MOS6510::RSTRequest (void)
+{
+ envReset ();
+}
+
+void MOS6510::NMIRequest (void)
+{
+ if (rdy && aec)
+ endian_16lo8 (Cycle_EffectiveAddress, envReadMemDataByte (0xFFFA));
+ else
+ { // Address bus not ready
+ stealCycle();
+ }
+}
+
+void MOS6510::NMI1Request (void)
+{
+ if (rdy && aec)
+ {
+ endian_16hi8 (Cycle_EffectiveAddress, envReadMemDataByte (0xFFFB));
+ endian_32lo16 (Register_ProgramCounter, Cycle_EffectiveAddress);
+ }
+ else
+ { // Address bus not ready
+ stealCycle();
+ }
+}
+
+void MOS6510::IRQRequest (void)
+{
+ PushSR (false);
+ setFlagI (true);
+ interrupts.irqRequest = false;
+}
+
+void MOS6510::IRQ1Request (void)
+{
+ if (rdy && aec)
+ endian_16lo8 (Cycle_EffectiveAddress, envReadMemDataByte (0xFFFE));
+ else
+ { // Address bus not ready
+ stealCycle();
+ }
+}
+
+void MOS6510::IRQ2Request (void)
+{
+ if (rdy && aec)
+ {
+ endian_16hi8 (Cycle_EffectiveAddress, envReadMemDataByte (0xFFFF));
+ endian_32lo16 (Register_ProgramCounter, Cycle_EffectiveAddress);
+ }
+ else
+ { // Address bus not ready
+ stealCycle();
+ }
+}
+
+void MOS6510::NextInstr (void)
+{
+ if (!interruptPending ())
+ FetchOpcode ();
+}
+
+
+//-------------------------------------------------------------------------//
+//-------------------------------------------------------------------------//
+// Common Instruction Addressing Routines //
+// Addressing operations as described in 64doc by John West and //
+// Marko Makela //
+//-------------------------------------------------------------------------//
+//-------------------------------------------------------------------------//
+
+// Fetch opcode, increment PC
+// Addressing Modes: All
+void MOS6510::FetchOpcode (void)
+{
+ if (rdy && aec)
+ { // On new instruction all interrupt delays are reset
+ interrupts.delay = MOS6510_INTERRUPT_DELAY;
+ interrupts.irqLatch = false;
+
+ instrStartPC = endian_32lo16 (Register_ProgramCounter++);
+ instrOpcode = envReadMemByte (instrStartPC);
+ // Convert opcode to pointer in instruction table
+ instrCurrent = &instrTable[instrOpcode];
+ Instr_Operand = 0;
+ procCycle = instrCurrent->cycle;
+ cycleCount = 0;
+ clock ();
+ }
+ else
+ { // Address bus not ready
+ stealCycle();
+ }
+}
+
+// Fetch value, increment PC
+/* Addressing Modes: Immediate
+ Relative
+*/
+void MOS6510::FetchDataByte (void)
+{ // Get data byte from memory
+ Cycle_Data = envReadMemByte (endian_32lo16 (Register_ProgramCounter));
+ Register_ProgramCounter++;
+
+ // Nextline used for Debug
+ Instr_Operand = (uint_least16_t) Cycle_Data;
+}
+
+// Fetch low address byte, increment PC
+/* Addressing Modes: Stack Manipulation
+ Absolute
+ Zero Page
+ Zerp Page Indexed
+ Absolute Indexed
+ Absolute Indirect
+*/
+void MOS6510::FetchLowAddr (void)
+{
+ if (rdy && aec)
+ {
+ Cycle_EffectiveAddress = envReadMemByte (endian_32lo16 (Register_ProgramCounter));
+ Register_ProgramCounter++;
+
+ // Nextline used for Debug
+ Instr_Operand = Cycle_EffectiveAddress;
+ }
+ else
+ { // Address bus not ready
+ stealCycle();
+ }
+}
+
+// Read from address, add index register X to it
+// Addressing Modes: Zero Page Indexed
+void MOS6510::FetchLowAddrX (void)
+{
+ FetchLowAddr ();
+ Cycle_EffectiveAddress = (Cycle_EffectiveAddress + Register_X) & 0xFF;
+}
+
+// Read from address, add index register Y to it
+// Addressing Modes: Zero Page Indexed
+void MOS6510::FetchLowAddrY (void)
+{
+ FetchLowAddr ();
+ Cycle_EffectiveAddress = (Cycle_EffectiveAddress + Register_Y) & 0xFF;
+}
+
+// Fetch high address byte, increment PC (Absoulte Addressing)
+// Low byte must have been obtained first!
+// Addressing Modes: Absolute
+void MOS6510::FetchHighAddr (void)
+{
+ if (rdy && aec)
+ { // Get the high byte of an address from memory
+ endian_16hi8 (Cycle_EffectiveAddress, envReadMemByte (endian_32lo16 (Register_ProgramCounter)));
+ Register_ProgramCounter++;
+
+ // Nextline used for Debug
+ endian_16hi8 (Instr_Operand, endian_16hi8 (Cycle_EffectiveAddress));
+ }
+ else
+ { // Address bus not ready
+ stealCycle();
+ }
+}
+
+// Fetch high byte of address, add index register X to low address byte,
+// increment PC
+// Addressing Modes: Absolute Indexed
+void MOS6510::FetchHighAddrX (void)
+{
+ uint8_t page;
+ // Rev 1.05 (saw) - Call base Function
+ FetchHighAddr ();
+ page = endian_16hi8 (Cycle_EffectiveAddress);
+ Cycle_EffectiveAddress += Register_X;
+
+#ifdef MOS6510_ACCURATE_CYCLES
+ // Handle page boundary crossing
+ if (endian_16hi8 (Cycle_EffectiveAddress) == page)
+ cycleCount++;
+#endif
+}
+
+// Same as above except dosen't worry about page crossing
+void MOS6510::FetchHighAddrX2 (void)
+{
+ FetchHighAddr ();
+ Cycle_EffectiveAddress += Register_X;
+}
+
+// Fetch high byte of address, add index register Y to low address byte,
+// increment PC
+// Addressing Modes: Absolute Indexed
+void MOS6510::FetchHighAddrY (void)
+{
+ uint8_t page;
+ // Rev 1.05 (saw) - Call base Function
+ FetchHighAddr ();
+ page = endian_16hi8 (Cycle_EffectiveAddress);
+ Cycle_EffectiveAddress += Register_Y;
+
+#ifdef MOS6510_ACCURATE_CYCLES
+ // Handle page boundary crossing
+ if (endian_16hi8 (Cycle_EffectiveAddress) == page)
+ cycleCount++;
+#endif
+}
+
+// Same as above except dosen't worry about page crossing
+void MOS6510::FetchHighAddrY2 (void)
+{
+ FetchHighAddr ();
+ Cycle_EffectiveAddress += Register_Y;
+}
+
+// Fetch pointer address low, increment PC
+/* Addressing Modes: Absolute Indirect
+ Indirect indexed (post Y)
+*/
+void MOS6510::FetchLowPointer (void)
+{
+ if (rdy && aec)
+ {
+ Cycle_Pointer = envReadMemByte (endian_32lo16 (Register_ProgramCounter));
+ Register_ProgramCounter++;
+ // Nextline used for Debug
+ Instr_Operand = Cycle_Pointer;
+ }
+ else
+ { // Address bus not ready
+ stealCycle();
+ }
+}
+
+// Read pointer from the address and add X to it
+// Addressing Modes: Indexed Indirect (pre X)
+void MOS6510::FetchLowPointerX (void)
+{
+ if (rdy && aec)
+ {
+ endian_16hi8 (Cycle_Pointer, envReadMemDataByte (Cycle_Pointer));
+ // Page boundary crossing is not handled
+ Cycle_Pointer = (Cycle_Pointer + Register_X) & 0xFF;
+ }
+ else
+ { // Address bus not ready
+ stealCycle();
+ }
+}
+
+// Fetch pointer address high, increment PC
+// Addressing Modes: Absolute Indirect
+void MOS6510::FetchHighPointer (void)
+{
+ if (rdy && aec)
+ {
+ endian_16hi8 (Cycle_Pointer, envReadMemByte (endian_32lo16 (Register_ProgramCounter)));
+ Register_ProgramCounter++;
+
+ // Nextline used for Debug
+ endian_16hi8 (Instr_Operand, endian_16hi8 (Cycle_Pointer));
+ }
+ else
+ { // Address bus not ready
+ stealCycle();
+ }
+}
+
+// Fetch effective address low
+/* Addressing Modes: Indirect
+ Indexed Indirect (pre X)
+ Indirect indexed (post Y)
+*/
+void MOS6510::FetchLowEffAddr (void)
+{
+ if (rdy && aec)
+ Cycle_EffectiveAddress = envReadMemDataByte (Cycle_Pointer);
+ else
+ { // Address bus not ready
+ stealCycle();
+ }
+}
+
+// Fetch effective address high
+/* Addressing Modes: Indirect
+ Indexed Indirect (pre X)
+*/
+void MOS6510::FetchHighEffAddr (void)
+{
+ if (rdy && aec)
+ { // Rev 1.03 (Mike) - Extra +1 removed
+ endian_16lo8 (Cycle_Pointer, (Cycle_Pointer + 1) & 0xff);
+ endian_16hi8 (Cycle_EffectiveAddress, envReadMemDataByte (Cycle_Pointer));
+ }
+ else
+ { // Address bus not ready
+ stealCycle();
+ }
+}
+
+// Fetch effective address high, add Y to low byte of effective address
+// Addressing Modes: Indirect indexed (post Y)
+void MOS6510::FetchHighEffAddrY (void)
+{
+ uint8_t page;
+ // Rev 1.05 (saw) - Call base Function
+ FetchHighEffAddr ();
+ page = endian_16hi8 (Cycle_EffectiveAddress);
+ Cycle_EffectiveAddress += Register_Y;
+
+#ifdef MOS6510_ACCURATE_CYCLES
+ // Handle page boundary crossing
+ if (endian_16hi8 (Cycle_EffectiveAddress) == page)
+ cycleCount++;
+#endif
+}
+
+// Same as above except dosen't worry about page crossing
+void MOS6510::FetchHighEffAddrY2 (void)
+{
+ FetchHighEffAddr ();
+ Cycle_EffectiveAddress += Register_Y;
+}
+
+//-------------------------------------------------------------------------//
+//-------------------------------------------------------------------------//
+// Common Data Accessing Routines //
+// Data Accessing operations as described in 64doc by John West and //
+// Marko Makela //
+//-------------------------------------------------------------------------//
+//-------------------------------------------------------------------------//
+
+void MOS6510::FetchEffAddrDataByte (void)
+{
+ if (rdy && aec)
+ Cycle_Data = envReadMemDataByte (Cycle_EffectiveAddress);
+ else
+ { // Address bus not ready
+ stealCycle();
+ }
+}
+
+void MOS6510::PutEffAddrDataByte (void)
+{
+ if (aec)
+ envWriteMemByte (Cycle_EffectiveAddress, Cycle_Data);
+ else
+ { // Address bus not ready
+ stealCycle();
+ }
+}
+
+// Used for Read Modify Write (RMW) instructions
+void MOS6510::FetchPutEffAddrDataByte (void)
+{
+ FetchEffAddrDataByte ();
+ PutEffAddrDataByte ();
+}
+
+// Push Program Counter Low Byte on stack, decrement S
+void MOS6510::PushLowPC (void)
+{
+ if (aec)
+ {
+ uint_least16_t addr;
+ addr = Register_StackPointer;
+ endian_16hi8 (addr, SP_PAGE);
+ envWriteMemByte (addr, endian_32lo8 (Register_ProgramCounter));
+ Register_StackPointer--;
+ }
+ else
+ { // Address bus not ready
+ stealCycle();
+ }
+}
+
+// Push Program Counter High Byte on stack, decrement S
+void MOS6510::PushHighPC (void)
+{
+ if (aec)
+ {
+ uint_least16_t addr;
+ addr = Register_StackPointer;
+ endian_16hi8 (addr, SP_PAGE);
+ envWriteMemByte (addr, endian_32hi8 (Register_ProgramCounter));
+ Register_StackPointer--;
+ }
+ else
+ { // Address bus not ready
+ stealCycle();
+ }
+}
+
+// Increment stack and pull program counter low byte from stack,
+void MOS6510::PopLowPC (void)
+{
+ if (rdy && aec)
+ {
+ uint_least16_t addr;
+ Register_StackPointer++;
+ addr = Register_StackPointer;
+ endian_16hi8 (addr, SP_PAGE);
+ endian_16lo8 (Cycle_EffectiveAddress, envReadMemDataByte (addr));
+ }
+ else
+ { // Address bus not ready
+ stealCycle();
+ }
+}
+
+// Increment stack and pull program counter high byte from stack,
+void MOS6510::PopHighPC (void)
+{
+ if (rdy && aec)
+ {
+ uint_least16_t addr;
+ Register_StackPointer++;
+ addr = Register_StackPointer;
+ endian_16hi8 (addr, SP_PAGE);
+ endian_16hi8 (Cycle_EffectiveAddress, envReadMemDataByte (addr));
+ }
+ else
+ { // Address bus not ready
+ stealCycle();
+ }
+}
+
+void MOS6510::WasteCycle (void)
+{
+}
+
+void MOS6510::DebugCycle (void)
+{
+ if (dodump)
+ DumpState ();
+ clock ();
+}
+
+
+//-------------------------------------------------------------------------//
+//-------------------------------------------------------------------------//
+// Common Instruction Opcodes //
+// See and 6510 Assembly Book for more information on these instructions //
+//-------------------------------------------------------------------------//
+//-------------------------------------------------------------------------//
+
+void MOS6510::brk_instr (void)
+{
+ PushSR ();
+ setFlagI (true);
+ interrupts.irqRequest = false;
+
+ // Check for an NMI, and switch over if pending
+ if (interrupts.pending & iNMI)
+ {
+ event_clock_t cycles = eventContext.getTime (interrupts.nmiClock);
+ if (cycles >= interrupts.delay)
+ {
+ interrupts.pending &= ~iNMI;
+ instrCurrent = &interruptTable[oNMI];
+ procCycle = &instrCurrent->cycle[cycleCount];
+ }
+ }
+}
+
+void MOS6510::cld_instr (void)
+{
+ setFlagD (false);
+}
+
+void MOS6510::cli_instr (void)
+{
+ bool oldFlagI = getFlagI ();
+ setFlagI (false);
+ // I flag change is delayed by 1 instruction
+ interrupts.irqLatch = oldFlagI ^ getFlagI ();
+ // Check to see if interrupts got re-enabled
+ if (interrupts.irqs)
+ interrupts.irqRequest = true;
+}
+
+void MOS6510::jmp_instr (void)
+{
+ endian_32lo16 (Register_ProgramCounter, Cycle_EffectiveAddress);
+}
+
+void MOS6510::jsr_instr (void)
+{ // JSR uses absolute addressing in this emulation,
+ // hence the -1. The real SID does not use this addressing
+ // mode.
+ Register_ProgramCounter--;
+ PushHighPC ();
+}
+
+void MOS6510::pha_instr (void)
+{
+ if (aec)
+ {
+ uint_least16_t addr;
+ addr = Register_StackPointer;
+ endian_16hi8 (addr, SP_PAGE);
+ envWriteMemByte (addr, Register_Accumulator);
+ Register_StackPointer--;
+ }
+ else
+ { // Address bus not ready
+ cycleCount--;
+ return;
+ }
+}
+
+/* RTI does not delay the IRQ I flag change as it is set 3 cycles before
+ * the end of the opcode, and thus the 6510 has enough time to call the
+ * interrupt routine as soon as the opcode ends, if necessary. */
+void MOS6510::rti_instr (void)
+{
+#ifdef MOS6510_DEBUG
+ if (dodump)
+ printf ("****************************************************\n\n");
+#endif
+
+ endian_32lo16 (Register_ProgramCounter, Cycle_EffectiveAddress);
+ interrupts.irqLatch = false;
+}
+
+void MOS6510::rts_instr (void)
+{
+/*
+ // Hack - Output character to screen
+ if (Register_ProgramCounter == 0xffd3)
+ {
+ char ch = _sidtune_CHRtab[Register_Accumulator];
+ switch (ch)
+ {
+ case 0:
+ break;
+ case 1:
+ printf (" ");
+ fprintf (stderr, " ");
+ case 0xd:
+ printf ("\n");
+ fprintf (stderr, "\n");
+ filepos = 0;
+ break;
+ default:
+ filetmp[filepos++] = ch;
+ printf ("%c", ch);
+ fprintf (stderr, "%c", ch);
+ }
+ }
+
+ if (Register_ProgramCounter == 0xe170)
+ {
+ filetmp[filepos] = '\0';
+ envLoadFile (filetmp);
+ }
+*/
+ endian_32lo16 (Register_ProgramCounter, Cycle_EffectiveAddress);
+ Register_ProgramCounter++;
+}
+
+void MOS6510::sed_instr (void)
+{
+ setFlagD (true);
+}
+
+void MOS6510::sei_instr (void)
+{
+ bool oldFlagI = getFlagI ();
+ setFlagI (true);
+ // I flag change is delayed by 1 instruction
+ interrupts.irqLatch = oldFlagI ^ getFlagI ();
+ interrupts.irqRequest = false;
+}
+
+void MOS6510::sta_instr (void)
+{
+ Cycle_Data = Register_Accumulator;
+ PutEffAddrDataByte ();
+}
+
+void MOS6510::stx_instr (void)
+{
+ Cycle_Data = Register_X;
+ PutEffAddrDataByte ();
+}
+
+void MOS6510::sty_instr (void)
+{
+ Cycle_Data = Register_Y;
+ PutEffAddrDataByte ();
+}
+
+
+
+//-------------------------------------------------------------------------//
+//-------------------------------------------------------------------------//
+// Common Instruction Undocumented Opcodes //
+// See documented 6502-nmo.opc by Adam Vardy for more details //
+//-------------------------------------------------------------------------//
+//-------------------------------------------------------------------------//
+
+// Undocumented - This opcode stores the result of A AND X AND the high
+// byte of the target address of the operand +1 in memory.
+void MOS6510::axa_instr (void)
+{
+ Cycle_Data = Register_X & Register_Accumulator & (endian_16hi8 (Cycle_EffectiveAddress) + 1);
+ PutEffAddrDataByte ();
+}
+
+// Undocumented - AXS ANDs the contents of the A and X registers (without changing the
+// contents of either register) and stores the result in memory.
+// AXS does not affect any flags in the processor status register.
+void MOS6510::axs_instr (void)
+{
+ Cycle_Data = Register_Accumulator & Register_X;
+}
+
+/* Not required - Operation performed By another method
+// Undocumented - HLT crashes the microprocessor. When this opcode is executed, program
+// execution ceases. No hardware interrupts will execute either. The author
+// has characterized this instruction as a halt instruction since this is the
+// most straightforward explanation for this opcode's behaviour. Only a reset
+// will restart execution. This opcode leaves no trace of any operation
+// performed! No registers affected.
+void MOS6510::hlt_instr (void)
+{
+}
+*/
+
+/* Not required - Operation performed By another method
+void MOS6510::nop_instr (void)
+{
+}
+*/
+
+/* Not required - Operation performed By another method
+void MOS6510::php_instr (void)
+{
+}
+*/
+
+// Undocumented - This opcode ANDs the contents of the Y register with <ab+1> and stores the
+// result in memory.
+void MOS6510::say_instr (void)
+{
+ Cycle_Data = Register_Y & (endian_16hi8 (Cycle_EffectiveAddress) + 1);
+}
+
+/* Not required - Operation performed By another method
+// Undocumented - skip next byte.
+void MOS6510::skb_instr (void)
+{
+ Register_ProgramCounter++;
+}
+*/
+
+/* Not required - Operation performed By another method
+// Undocumented - skip next word.
+void MOS6510::skw_instr (void)
+{
+ Register_ProgramCounter += 2;
+}
+*/
+
+// Undocumented - This opcode ANDs the contents of the X register with <ab+1> and stores the
+// result in memory.
+void MOS6510::xas_instr (void)
+{
+ Cycle_Data = Register_X & (endian_16hi8 (Cycle_EffectiveAddress) + 1);
+}
+
+
+#ifdef X86
+#include "MOS6510\CYCLE_~1\X86.CPP"
+//#include "MOS6510\CYCLE_BASED\X86.CPP"
+#else
+
+//-------------------------------------------------------------------------//
+//-------------------------------------------------------------------------//
+// Generic Binary Coded Decimal Correction //
+//-------------------------------------------------------------------------//
+//-------------------------------------------------------------------------//
+
+void MOS6510::Perform_ADC (void)
+{
+ uint C = getFlagC ();
+ uint A = Register_Accumulator;
+ uint s = Cycle_Data;
+ uint regAC2 = A + s + C;
+
+ if (getFlagD ())
+ { // BCD mode
+ uint lo = (A & 0x0f) + (s & 0x0f) + C;
+ uint hi = (A & 0xf0) + (s & 0xf0);
+ if (lo > 0x09) lo += 0x06;
+ if (lo > 0x0f) hi += 0x10;
+
+ setFlagZ (regAC2);
+ setFlagN (hi);
+ setFlagV (((hi ^ A) & 0x80) && !((A ^ s) & 0x80));
+ if (hi > 0x90) hi += 0x60;
+
+ setFlagC (hi > 0xff);
+ Register_Accumulator = (hi | (lo & 0x0f));
+ }
+ else
+ { // Binary mode
+ setFlagC (regAC2 > 0xff);
+ setFlagV (((regAC2 ^ A) & 0x80) && !((A ^ s) & 0x80));
+ setFlagsNZ (Register_Accumulator = regAC2 & 0xff);
+ }
+}
+
+void MOS6510::Perform_SBC (void)
+{
+ uint C = !getFlagC ();
+ uint A = Register_Accumulator;
+ uint s = Cycle_Data;
+ uint regAC2 = A - s - C;
+
+ setFlagC (regAC2 < 0x100);
+ setFlagV (((regAC2 ^ A) & 0x80) && ((A ^ s) & 0x80));
+ setFlagsNZ (regAC2);
+
+ if (getFlagD ())
+ { // BCD mode
+ uint lo = (A & 0x0f) - (s & 0x0f) - C;
+ uint hi = (A & 0xf0) - (s & 0xf0);
+ if (lo & 0x10)
+ {
+ lo -= 0x06;
+ hi -= 0x10;
+ }
+ if (hi & 0x100) hi -= 0x60;
+ Register_Accumulator = (hi | (lo & 0x0f));
+ }
+ else
+ { // Binary mode
+ Register_Accumulator = regAC2 & 0xff;
+ }
+}
+
+
+
+//-------------------------------------------------------------------------//
+//-------------------------------------------------------------------------//
+// Generic Instruction Addressing Routines //
+//-------------------------------------------------------------------------/
+
+
+//-------------------------------------------------------------------------//
+//-------------------------------------------------------------------------//
+// Generic Instruction Opcodes //
+// See and 6510 Assembly Book for more information on these instructions //
+//-------------------------------------------------------------------------//
+//-------------------------------------------------------------------------//
+
+void MOS6510::adc_instr (void)
+{
+ Perform_ADC ();
+}
+
+void MOS6510::and_instr (void)
+{
+ setFlagsNZ (Register_Accumulator &= Cycle_Data);
+}
+
+void MOS6510::ane_instr (void)
+{
+ setFlagsNZ (Register_Accumulator = (Register_Accumulator | 0xee) & Register_X & Cycle_Data);
+}
+
+void MOS6510::asl_instr (void)
+{
+ setFlagC (Cycle_Data & 0x80);
+ setFlagsNZ (Cycle_Data <<= 1);
+}
+
+void MOS6510::asla_instr (void)
+{
+ setFlagC (Register_Accumulator & 0x80);
+ setFlagsNZ (Register_Accumulator <<= 1);
+}
+
+void MOS6510::branch_instr (bool condition)
+{
+ if (condition)
+#ifdef MOS6510_ACCURATE_CYCLES
+ {
+ uint8_t page;
+ page = endian_32hi8 (Register_ProgramCounter);
+ Register_ProgramCounter += (int8_t) Cycle_Data;
+
+ // Handle page boundary crossing
+ if (endian_32hi8 (Register_ProgramCounter) == page)
+ {
+ cycleCount++;
+ interrupts.delay++;
+ }
+ }
+ else
+ {
+ cycleCount += 2;
+ }
+#else
+ Register_ProgramCounter += (int8_t) Cycle_Data;
+#endif
+}
+
+void MOS6510::bcc_instr (void)
+{
+ branch_instr (!getFlagC ());
+}
+
+void MOS6510::bcs_instr (void)
+{
+ branch_instr (getFlagC ());
+}
+
+void MOS6510::beq_instr (void)
+{
+ branch_instr (getFlagZ ());
+}
+
+void MOS6510::bit_instr (void)
+{
+ setFlagZ (Register_Accumulator & Cycle_Data);
+ setFlagN (Cycle_Data);
+ setFlagV (Cycle_Data & 0x40);
+}
+
+void MOS6510::bmi_instr (void)
+{
+ branch_instr (getFlagN ());
+}
+
+void MOS6510::bne_instr (void)
+{
+ branch_instr (!getFlagZ ());
+}
+
+void MOS6510::bpl_instr(void)
+{
+ branch_instr (!getFlagN ());
+}
+
+void MOS6510::bvc_instr (void)
+{
+ branch_instr (!getFlagV ());
+}
+
+void MOS6510::bvs_instr (void)
+{
+ branch_instr (getFlagV ());
+}
+
+void MOS6510::clc_instr (void)
+{
+ setFlagC (false);
+}
+
+void MOS6510::clv_instr (void)
+{
+ setFlagV (false);
+}
+
+void MOS6510::cmp_instr (void)
+{
+ uint_least16_t tmp = (uint_least16_t) Register_Accumulator - Cycle_Data;
+ setFlagsNZ (tmp);
+ setFlagC (tmp < 0x100);
+}
+
+void MOS6510::cpx_instr (void)
+{
+ uint_least16_t tmp = (uint_least16_t) Register_X - Cycle_Data;
+ setFlagsNZ (tmp);
+ setFlagC (tmp < 0x100);
+}
+
+void MOS6510::cpy_instr (void)
+{
+ uint_least16_t tmp = (uint_least16_t) Register_Y - Cycle_Data;
+ setFlagsNZ (tmp);
+ setFlagC (tmp < 0x100);
+}
+
+void MOS6510::dec_instr (void)
+{
+ setFlagsNZ (--Cycle_Data);
+}
+
+void MOS6510::dex_instr (void)
+{
+ setFlagsNZ (--Register_X);
+}
+
+void MOS6510::dey_instr (void)
+{
+ setFlagsNZ (--Register_Y);
+}
+
+void MOS6510::eor_instr (void)
+{
+ setFlagsNZ (Register_Accumulator^= Cycle_Data);
+}
+
+void MOS6510::inc_instr (void)
+{
+ setFlagsNZ (++Cycle_Data);
+}
+
+void MOS6510::inx_instr (void)
+{
+ setFlagsNZ (++Register_X);
+}
+
+void MOS6510::iny_instr (void)
+{
+ setFlagsNZ (++Register_Y);
+}
+
+void MOS6510::lda_instr (void)
+{
+ setFlagsNZ (Register_Accumulator = Cycle_Data);
+}
+
+void MOS6510::ldx_instr (void)
+{
+ setFlagsNZ (Register_X = Cycle_Data);
+}
+
+void MOS6510::ldy_instr (void)
+{
+ setFlagsNZ (Register_Y = Cycle_Data);
+}
+
+void MOS6510::lsr_instr (void)
+{
+ setFlagC (Cycle_Data & 0x01);
+ setFlagsNZ (Cycle_Data >>= 1);
+}
+
+void MOS6510::lsra_instr (void)
+{
+ setFlagC (Register_Accumulator & 0x01);
+ setFlagsNZ (Register_Accumulator >>= 1);
+}
+
+void MOS6510::ora_instr (void)
+{
+ setFlagsNZ (Register_Accumulator |= Cycle_Data);
+}
+
+void MOS6510::pla_instr (void)
+{
+ if (rdy && aec)
+ {
+ uint_least16_t addr;
+ Register_StackPointer++;
+ addr = Register_StackPointer;
+ endian_16hi8 (addr, SP_PAGE);
+ setFlagsNZ (Register_Accumulator = envReadMemByte (addr));
+ }
+ else
+ { // Address bus not ready
+ cycleCount--;
+ return;
+ }
+}
+
+void MOS6510::rol_instr (void)
+{
+ uint8_t tmp = Cycle_Data & 0x80;
+ Cycle_Data <<= 1;
+ if (getFlagC ()) Cycle_Data |= 0x01;
+ setFlagsNZ (Cycle_Data);
+ setFlagC (tmp);
+}
+
+void MOS6510::rola_instr (void)
+{
+ uint8_t tmp = Register_Accumulator & 0x80;
+ Register_Accumulator <<= 1;
+ if (getFlagC ()) Register_Accumulator |= 0x01;
+ setFlagsNZ (Register_Accumulator);
+ setFlagC (tmp);
+}
+
+void MOS6510::ror_instr (void)
+{
+ uint8_t tmp = Cycle_Data & 0x01;
+ Cycle_Data >>= 1;
+ if (getFlagC ()) Cycle_Data |= 0x80;
+ setFlagsNZ (Cycle_Data);
+ setFlagC (tmp);
+}
+
+void MOS6510::rora_instr (void)
+{
+ uint8_t tmp = Register_Accumulator & 0x01;
+ Register_Accumulator >>= 1;
+ if (getFlagC ()) Register_Accumulator |= 0x80;
+ setFlagsNZ (Register_Accumulator);
+ setFlagC (tmp);
+}
+
+void MOS6510::sbx_instr (void)
+{
+ uint tmp = (Register_X & Register_Accumulator) - Cycle_Data;
+ setFlagsNZ (Register_X = tmp & 0xff);
+ setFlagC (tmp < 0x100);
+}
+
+void MOS6510::sbc_instr (void)
+{
+ Perform_SBC ();
+}
+
+void MOS6510::sec_instr (void)
+{
+ setFlagC (true);
+}
+
+void MOS6510::shs_instr (void)
+{
+ endian_16lo8 (Register_StackPointer, (Register_Accumulator & Register_X));
+ Cycle_Data = (endian_16hi8 (Cycle_EffectiveAddress) + 1) & Register_StackPointer;
+}
+
+void MOS6510::tax_instr (void)
+{
+ setFlagsNZ (Register_X = Register_Accumulator);
+}
+
+void MOS6510::tay_instr (void)
+{
+ setFlagsNZ (Register_Y = Register_Accumulator);
+}
+
+void MOS6510::tsx_instr (void)
+{ // Rev 1.03 (saw) - Got these tsx and txs reversed
+ setFlagsNZ (Register_X = endian_16lo8 (Register_StackPointer));
+}
+
+void MOS6510::txa_instr (void)
+{
+ setFlagsNZ (Register_Accumulator = Register_X);
+}
+
+void MOS6510::txs_instr (void)
+{ // Rev 1.03 (saw) - Got these tsx and txs reversed
+ endian_16lo8 (Register_StackPointer, Register_X);
+}
+
+void MOS6510::tya_instr (void)
+{
+ setFlagsNZ (Register_Accumulator = Register_Y);
+}
+
+void MOS6510::illegal_instr (void)
+{
+ printf ("\n\nILLEGAL INSTRUCTION, resetting emulation. **************\n");
+ DumpState ();
+ printf ("********************************************************\n");
+ // Perform Environment Reset
+ envReset ();
+}
+
+
+//-------------------------------------------------------------------------//
+//-------------------------------------------------------------------------//
+// Generic Instruction Undocuemented Opcodes //
+// See documented 6502-nmo.opc by Adam Vardy for more details //
+//-------------------------------------------------------------------------//
+//-------------------------------------------------------------------------//
+
+// Undocumented - This opcode ANDs the contents of the A register with an immediate value and
+// then LSRs the result.
+void MOS6510::alr_instr (void)
+{
+ Register_Accumulator &= Cycle_Data;
+ setFlagC (Register_Accumulator & 0x01);
+ setFlagsNZ (Register_Accumulator >>= 1);
+}
+
+// Undcouemented - ANC ANDs the contents of the A register with an immediate value and then
+// moves bit 7 of A into the Carry flag. This opcode works basically
+// identically to AND #immed. except that the Carry flag is set to the same
+// state that the Negative flag is set to.
+void MOS6510::anc_instr (void)
+{
+ setFlagsNZ (Register_Accumulator &= Cycle_Data);
+ setFlagC (getFlagN ());
+}
+
+// Undocumented - This opcode ANDs the contents of the A register with an immediate value and
+// then RORs the result (Implementation based on that of Frodo C64 Emulator)
+void MOS6510::arr_instr (void)
+{
+ uint8_t data = Cycle_Data & Register_Accumulator;
+ Register_Accumulator = data >> 1;
+ if (getFlagC ()) Register_Accumulator |= 0x80;
+
+ if (getFlagD ())
+ {
+ setFlagN (0);
+ if (getFlagC ()) setFlagN (1 << SR_NEGATIVE);
+ setFlagZ (Register_Accumulator);
+ setFlagV ((data ^ Register_Accumulator) & 0x40);
+
+ if ((data & 0x0f) + (data & 0x01) > 5)
+ Register_Accumulator = Register_Accumulator & 0xf0 | (Register_Accumulator + 6) & 0x0f;
+ setFlagC (((data + (data & 0x10)) & 0x1f0) > 0x50);
+ if (getFlagC ())
+ Register_Accumulator += 0x60;
+ }
+ else
+ {
+ setFlagsNZ (Register_Accumulator);
+ setFlagC (Register_Accumulator & 0x40);
+ setFlagV ((Register_Accumulator & 0x40) ^ ((Register_Accumulator & 0x20) << 1));
+ }
+}
+
+// Undocumented - This opcode ASLs the contents of a memory location and then ORs the result
+// with the accumulator.
+void MOS6510::aso_instr (void)
+{
+ setFlagC (Cycle_Data & 0x80);
+ Cycle_Data <<= 1;
+ setFlagsNZ (Register_Accumulator |= Cycle_Data);
+}
+
+// Undocumented - This opcode DECs the contents of a memory location and then CMPs the result
+// with the A register.
+void MOS6510::dcm_instr (void)
+{
+ uint_least16_t tmp;
+ Cycle_Data--;
+ tmp = (uint_least16_t) Register_Accumulator - Cycle_Data;
+ setFlagsNZ (tmp);
+ setFlagC (tmp < 0x100);
+}
+
+// Undocumented - This opcode INCs the contents of a memory location and then SBCs the result
+// from the A register.
+void MOS6510::ins_instr (void)
+{
+ Cycle_Data++;
+ Perform_SBC ();
+}
+
+// Undocumented - This opcode ANDs the contents of a memory location with the contents of the
+// stack pointer register and stores the result in the accumulator, the X
+// register, and the stack pointer. Affected flags: N Z.
+void MOS6510::las_instr (void)
+{
+ setFlagsNZ (Cycle_Data &= endian_16lo8 (Register_StackPointer));
+ Register_Accumulator = Cycle_Data;
+ Register_X = Cycle_Data;
+ Register_StackPointer = Cycle_Data;
+}
+
+// Undocumented - This opcode loads both the accumulator and the X register with the contents
+// of a memory location.
+void MOS6510::lax_instr (void)
+{
+ setFlagsNZ (Register_Accumulator = Register_X = Cycle_Data);
+}
+
+// Undocumented - LSE LSRs the contents of a memory location and then EORs the result with
+// the accumulator.
+void MOS6510::lse_instr (void)
+{
+ setFlagC (Cycle_Data & 0x01);
+ Cycle_Data >>= 1;
+ setFlagsNZ (Register_Accumulator ^= Cycle_Data);
+}
+
+// Undocumented - This opcode ORs the A register with #xx, ANDs the result with an immediate
+// value, and then stores the result in both A and X.
+// xx may be EE,EF,FE, OR FF, but most emulators seem to use EE
+void MOS6510::oal_instr (void)
+{
+ setFlagsNZ (Register_X = (Register_Accumulator = (Cycle_Data & (Register_Accumulator | 0xee))));
+}
+
+// Undocumented - RLA ROLs the contents of a memory location and then ANDs the result with
+// the accumulator.
+void MOS6510::rla_instr (void)
+{
+ uint8_t tmp = Cycle_Data & 0x80;
+ Cycle_Data = Cycle_Data << 1;
+ if (getFlagC ()) Cycle_Data |= 0x01;
+ setFlagC (tmp);
+ setFlagsNZ (Register_Accumulator &= Cycle_Data);
+}
+
+// Undocumented - RRA RORs the contents of a memory location and then ADCs the result with
+// the accumulator.
+void MOS6510::rra_instr (void)
+{
+ uint8_t tmp = Cycle_Data & 0x01;
+ Cycle_Data >>= 1;
+ if (getFlagC ()) Cycle_Data |= 0x80;
+ setFlagC (tmp);
+ Perform_ADC ();
+}
+
+// Undocumented - This opcode ANDs the contents of the A and X registers (without changing
+// the contents of either register) and transfers the result to the stack
+// pointer. It then ANDs that result with the contents of the high byte of
+// the target address of the operand +1 and stores that final result in
+// memory.
+void MOS6510::tas_instr (void)
+{
+ endian_16lo8 (Register_StackPointer, Register_Accumulator & Register_X);
+ uint_least16_t tmp = Register_StackPointer & (Cycle_EffectiveAddress + 1);
+ Cycle_Data = (signed) endian_16lo8 (tmp);
+}
+
+#endif // X86
+
+
+//-------------------------------------------------------------------------//
+// Initialise and create CPU Chip //
+
+//MOS6510::MOS6510 (model_t _model, const char *id)
+MOS6510::MOS6510 (EventContext *context)
+:eventContext(*context),
+ Event("CPU")
+{
+ struct ProcessorOperations *instr;
+ uint8_t legalMode = true;
+ uint8_t legalInstr = true;
+ uint i, pass;
+
+ //----------------------------------------------------------------------
+ // Build up the processor instruction table
+ for (i = 0; i < 0x100; i++)
+ {
+#if MOS6510_DEBUG > 1
+ printf ("Building Command %d[%02x]..", i, i);
+#endif
+
+ // Pass 1 allocates the memory, Pass 2 builds the instruction
+ instr = &instrTable[i];
+ instr->cycle = NULL;
+
+ for (pass = 0; pass < 2; pass++)
+ {
+ enum {WRITE = 0, RMW = 1, READ = 2};
+ int access = WRITE;
+ cycleCount = -1;
+ legalMode = true;
+ legalInstr = true;
+ if (pass) procCycle = instr->cycle;
+
+ switch (i)
+ {
+ // Accumulator or Implied addressing
+ case ASLn: case CLCn: case CLDn: case CLIn: case CLVn: case DEXn:
+ case DEYn: case INXn: case INYn: case LSRn: case NOPn_: case PHAn:
+ case PHPn: case PLAn: case PLPn: case ROLn: case RORn: case RTIn:
+ case RTSn: case SECn: case SEDn: case SEIn: case TAXn: case TAYn:
+ case TSXn: case TXAn: case TXSn: case TYAn:
+#ifdef MOS6510_ACCURATE_CYCLES
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+#endif
+ break;
+
+ // Immediate and Relative Addressing Mode Handler
+ case ADCb: case ANDb: case ANCb_: case ANEb: case ASRb: case ARRb:
+ case BCCr: case BCSr: case BEQr: case BMIr: case BNEr: case BPLr:
+ case BRKn: case BVCr: case BVSr: case CMPb: case CPXb: case CPYb:
+ case EORb: case LDAb: case LDXb: case LDYb: case LXAb: case NOPb_:
+ case ORAb: case SBCb_: case SBXb:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchDataByte;
+ break;
+
+ // Zero Page Addressing Mode Handler - Read & RMW
+ case ADCz: case ANDz: case BITz: case CMPz: case CPXz: case CPYz:
+ case EORz: case LAXz: case LDAz: case LDXz: case LDYz: case ORAz:
+ case NOPz_: case SBCz:
+ access++;
+ case ASLz: case DCPz: case DECz: case INCz: case ISBz: case LSRz:
+ case ROLz: case RORz: case SREz: case SLOz: case RLAz: case RRAz:
+ access++;
+ case SAXz: case STAz: case STXz: case STYz:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchLowAddr;
+ if (access == READ) {
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchEffAddrDataByte;
+ } else if (access == RMW) {
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchPutEffAddrDataByte;
+ }
+ break;
+
+ // Zero Page with X Offset Addressing Mode Handler
+ case ADCzx: case ANDzx: case CMPzx: case EORzx: case LDAzx: case LDYzx:
+ case NOPzx_: case ORAzx: case SBCzx:
+ access++;
+ case ASLzx: case DCPzx: case DECzx: case INCzx: case ISBzx: case LSRzx:
+ case RLAzx: case ROLzx: case RORzx: case RRAzx: case SLOzx: case SREzx:
+ access++;
+ case STAzx: case STYzx:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchLowAddrX;
+#ifdef MOS6510_ACCURATE_CYCLES
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+#endif
+ if (access == READ) {
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchEffAddrDataByte;
+ } else if (access == RMW) {
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchPutEffAddrDataByte;
+ }
+ break;
+
+ // Zero Page with Y Offset Addressing Mode Handler
+ case LDXzy: case LAXzy:
+ access = READ;
+ case STXzy: case SAXzy:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchLowAddrY;
+#ifdef MOS6510_ACCURATE_CYCLES
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+#endif
+ if (access == READ) {
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchEffAddrDataByte;
+ }
+ break;
+
+ // Absolute Addressing Mode Handler
+ case ADCa: case ANDa: case BITa: case CMPa: case CPXa: case CPYa:
+ case EORa: case LAXa: case LDAa: case LDXa: case LDYa: case NOPa:
+ case ORAa: case SBCa:
+ access++;
+ case ASLa: case DCPa: case DECa: case INCa: case ISBa: case LSRa:
+ case ROLa: case RORa: case SLOa: case SREa: case RLAa: case RRAa:
+ access++;
+ case JMPw: case JSRw: case SAXa: case STAa: case STXa: case STYa:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchLowAddr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchHighAddr;
+ if (access == READ) {
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchEffAddrDataByte;
+ } else if (access == RMW) {
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchPutEffAddrDataByte;
+ }
+ break;
+
+ // Absolute With X Offset Addressing Mode Handler (Read)
+ case ADCax: case ANDax: case CMPax: case EORax: case LDAax:
+ case LDYax: case NOPax_: case ORAax: case SBCax:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchLowAddr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchHighAddrX;
+#ifdef MOS6510_ACCURATE_CYCLES
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+#endif
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchEffAddrDataByte;
+ break;
+
+ // Absolute X (No page crossing handled)
+ case ASLax: case DCPax: case DECax: case INCax: case ISBax:
+ case LSRax: case RLAax: case ROLax: case RORax: case RRAax:
+ case SLOax: case SREax:
+ access = RMW;
+ case SHYax: case STAax:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchLowAddr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchHighAddrX2;
+#ifdef MOS6510_ACCURATE_CYCLES
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+#endif
+ if (access == RMW) {
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchPutEffAddrDataByte;
+ }
+ break;
+
+ // Absolute With Y Offset Addresing Mode Handler (Read)
+ case ADCay: case ANDay: case CMPay: case EORay: case LASay:
+ case LAXay: case LDAay: case LDXay: case ORAay: case SBCay:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchLowAddr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchHighAddrY;
+#ifdef MOS6510_ACCURATE_CYCLES
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+#endif
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchEffAddrDataByte;
+ break;
+
+ // Absolute Y (No page crossing handled)
+ case DCPay: case ISBay: case RLAay: case RRAay: case SLOay:
+ case SREay:
+ access = RMW;
+ case SHAay: case SHSay: case SHXay: case STAay:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchLowAddr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchHighAddrY2;
+#ifdef MOS6510_ACCURATE_CYCLES
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+#endif
+ if (access == RMW) {
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchPutEffAddrDataByte;
+ }
+ break;
+
+ // Absolute Indirect Addressing Mode Handler
+ case JMPi:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchLowPointer;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchHighPointer;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchLowEffAddr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchHighEffAddr;
+ break;
+
+ // Indexed with X Preinc Addressing Mode Handler
+ case ADCix: case ANDix: case CMPix: case EORix: case LAXix: case LDAix:
+ case ORAix: case SBCix:
+ access++;
+ case DCPix: case ISBix: case SLOix: case SREix: case RLAix: case RRAix:
+ access++;
+ case SAXix: case STAix:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchLowPointer;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchLowPointerX;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchLowEffAddr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchHighEffAddr;
+ if (access == READ) {
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchEffAddrDataByte;
+ } else if (access == RMW) {
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchPutEffAddrDataByte;
+ }
+ break;
+
+ // Indexed with Y Postinc Addressing Mode Handler (Read)
+ case ADCiy: case ANDiy: case CMPiy: case EORiy: case LAXiy:
+ case LDAiy: case ORAiy: case SBCiy:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchLowPointer;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchLowEffAddr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchHighEffAddrY;
+#ifdef MOS6510_ACCURATE_CYCLES
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+#endif
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchEffAddrDataByte;
+ break;
+
+ // Indexed Y (No page crossing handled)
+ case DCPiy: case ISBiy: case RLAiy: case RRAiy: case SLOiy:
+ case SREiy:
+ access = RMW;
+ case SHAiy: case STAiy:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchLowPointer;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchLowEffAddr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchHighEffAddrY2;
+#ifdef MOS6510_ACCURATE_CYCLES
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+#endif
+ if (access == RMW) {
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchPutEffAddrDataByte;
+ }
+ break;
+
+ default:
+ legalMode = false;
+ break;
+ }
+
+#ifdef MOS6510_DEBUG
+ if (legalMode)
+ {
+ cycleCount++;
+ if (pass) procCycle[cycleCount] = &MOS6510::DebugCycle;
+ }
+#endif // MOS6510_DEBUG
+
+ //---------------------------------------------------------------------------------------
+ // Addressing Modes Finished, other cycles are instruction dependent
+ switch(i)
+ {
+ case ADCz: case ADCzx: case ADCa: case ADCax: case ADCay: case ADCix:
+ case ADCiy: case ADCb:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::adc_instr;
+ break;
+
+ case ANCb_:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::anc_instr;
+ break;
+
+ case ANDz: case ANDzx: case ANDa: case ANDax: case ANDay: case ANDix:
+ case ANDiy: case ANDb:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::and_instr;
+ break;
+
+ case ANEb: // Also known as XAA
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::ane_instr;
+ break;
+
+ case ARRb:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::arr_instr;
+ break;
+
+ case ASLn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::asla_instr;
+ break;
+
+ case ASLz: case ASLzx: case ASLa: case ASLax:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::asl_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PutEffAddrDataByte;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case ASRb: // Also known as ALR
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::alr_instr;
+ break;
+
+ case BCCr:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::bcc_instr;
+#ifdef MOS6510_ACCURATE_CYCLES
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+#endif
+ break;
+
+ case BCSr:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::bcs_instr;
+#ifdef MOS6510_ACCURATE_CYCLES
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+#endif
+ break;
+
+ case BEQr:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::beq_instr;
+#ifdef MOS6510_ACCURATE_CYCLES
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+#endif
+ break;
+
+ case BITz: case BITa:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::bit_instr;
+ break;
+
+ case BMIr:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::bmi_instr;
+#ifdef MOS6510_ACCURATE_CYCLES
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+#endif
+ break;
+
+ case BNEr:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::bne_instr;
+#ifdef MOS6510_ACCURATE_CYCLES
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+#endif
+ break;
+
+ case BPLr:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::bpl_instr;
+#ifdef MOS6510_ACCURATE_CYCLES
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+#endif
+ break;
+
+ case BRKn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PushHighPC;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PushLowPC;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::brk_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::IRQ1Request;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::IRQ2Request;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchOpcode;
+ break;
+
+ case BVCr:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::bvc_instr;
+#ifdef MOS6510_ACCURATE_CYCLES
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+#endif
+ break;
+
+ case BVSr:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::bvs_instr;
+#ifdef MOS6510_ACCURATE_CYCLES
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+#endif
+ break;
+
+ case CLCn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::clc_instr;
+ break;
+
+ case CLDn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::cld_instr;
+ break;
+
+ case CLIn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::cli_instr;
+ break;
+
+ case CLVn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::clv_instr;
+ break;
+
+ case CMPz: case CMPzx: case CMPa: case CMPax: case CMPay: case CMPix:
+ case CMPiy: case CMPb:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::cmp_instr;
+ break;
+
+ case CPXz: case CPXa: case CPXb:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::cpx_instr;
+ break;
+
+ case CPYz: case CPYa: case CPYb:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::cpy_instr;
+ break;
+
+ case DCPz: case DCPzx: case DCPa: case DCPax: case DCPay: case DCPix:
+ case DCPiy: // Also known as DCM
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::dcm_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PutEffAddrDataByte;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case DECz: case DECzx: case DECa: case DECax:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::dec_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PutEffAddrDataByte;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case DEXn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::dex_instr;
+ break;
+
+ case DEYn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::dey_instr;
+ break;
+
+ case EORz: case EORzx: case EORa: case EORax: case EORay: case EORix:
+ case EORiy: case EORb:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::eor_instr;
+ break;
+
+/* HLT // Also known as JAM
+ case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52:
+ case 0x62: case 0x72: case 0x92: case 0xb2: case 0xd2: case 0xf2:
+ case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52:
+ case 0x62: case 0x72: case 0x92: case 0xb2: case 0xd2: case 0xf2:
+ cycleCount++; if (pass) procCycle[cycleCount] = hlt_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+*/
+
+ case INCz: case INCzx: case INCa: case INCax:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::inc_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PutEffAddrDataByte;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case INXn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::inx_instr;
+ break;
+
+ case INYn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::iny_instr;
+ break;
+
+ case ISBz: case ISBzx: case ISBa: case ISBax: case ISBay: case ISBix:
+ case ISBiy: // Also known as INS
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::ins_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PutEffAddrDataByte;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case JMPw: case JMPi:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::jmp_instr;
+ break;
+
+ case JSRw:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::jsr_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PushLowPC;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::jmp_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case LASay:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::las_instr;
+ break;
+
+ case LAXz: case LAXzy: case LAXa: case LAXay: case LAXix: case LAXiy:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::lax_instr;
+ break;
+
+ case LDAz: case LDAzx: case LDAa: case LDAax: case LDAay: case LDAix:
+ case LDAiy: case LDAb:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::lda_instr;
+ break;
+
+ case LDXz: case LDXzy: case LDXa: case LDXay: case LDXb:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::ldx_instr;
+ break;
+
+ case LDYz: case LDYzx: case LDYa: case LDYax: case LDYb:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::ldy_instr;
+ break;
+
+ case LSRn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::lsra_instr;
+ break;
+
+ case LSRz: case LSRzx: case LSRa: case LSRax:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::lsr_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PutEffAddrDataByte;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case NOPn_: case NOPb_:
+ // Should not be required!
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case NOPz_: case NOPzx_: case NOPa: case NOPax_:
+ // NOPb NOPz NOPzx - Also known as SKBn
+ // NOPa NOPax - Also known as SKWn
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case LXAb: // Also known as OAL
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::oal_instr;
+ break;
+
+ case ORAz: case ORAzx: case ORAa: case ORAax: case ORAay: case ORAix:
+ case ORAiy: case ORAb:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::ora_instr;
+ break;
+
+ case PHAn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::pha_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case PHPn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PushSR;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case PLAn:
+#ifdef MOS6510_ACCURATE_CYCLES
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+#endif
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::pla_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case PLPn:
+#ifdef MOS6510_ACCURATE_CYCLES
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+#endif
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PopSR;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case RLAz: case RLAzx: case RLAix: case RLAa: case RLAax: case RLAay:
+ case RLAiy:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::rla_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PutEffAddrDataByte;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case ROLn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::rola_instr;
+ break;
+
+ case ROLz: case ROLzx: case ROLa: case ROLax:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::rol_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PutEffAddrDataByte;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case RORn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::rora_instr;
+ break;
+
+ case RORz: case RORzx: case RORa: case RORax:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::ror_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PutEffAddrDataByte;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case RRAa: case RRAax: case RRAay: case RRAz: case RRAzx: case RRAix:
+ case RRAiy:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::rra_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PutEffAddrDataByte;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case RTIn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PopSR;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PopLowPC;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PopHighPC;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::rti_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case RTSn:
+#ifdef MOS6510_ACCURATE_CYCLES
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+#endif
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PopLowPC;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PopHighPC;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::rts_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case SAXz: case SAXzy: case SAXa: case SAXix: // Also known as AXS
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::axs_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PutEffAddrDataByte;
+ break;
+
+ case SBCz: case SBCzx: case SBCa: case SBCax: case SBCay: case SBCix:
+ case SBCiy: case SBCb_:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::sbc_instr;
+ break;
+
+ case SBXb:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::sbx_instr;
+ break;
+
+ case SECn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::sec_instr;
+ break;
+
+ case SEDn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::sed_instr;
+ break;
+
+ case SEIn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::sei_instr;
+ break;
+
+ case SHAay: case SHAiy: // Also known as AXA
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::axa_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case SHSay: // Also known as TAS
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::shs_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PutEffAddrDataByte;
+ break;
+
+ case SHXay: // Also known as XAS
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::xas_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PutEffAddrDataByte;
+ break;
+
+ case SHYax: // Also known as SAY
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::say_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PutEffAddrDataByte;
+ break;
+
+ case SLOz: case SLOzx: case SLOa: case SLOax: case SLOay: case SLOix:
+ case SLOiy: // Also known as ASO
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::aso_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PutEffAddrDataByte;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case SREz: case SREzx: case SREa: case SREax: case SREay: case SREix:
+ case SREiy: // Also known as LSE
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::lse_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PutEffAddrDataByte;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case STAz: case STAzx: case STAa: case STAax: case STAay: case STAix:
+ case STAiy:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::sta_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case STXz: case STXzy: case STXa:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::stx_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case STYz: case STYzx: case STYa:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::sty_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ break;
+
+ case TAXn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::tax_instr;
+ break;
+
+ case TAYn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::tay_instr;
+ break;
+
+ case TSXn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::tsx_instr;
+ break;
+
+ case TXAn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::txa_instr;
+ break;
+
+ case TXSn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::txs_instr;
+ break;
+
+ case TYAn:
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::tya_instr;
+ break;
+
+ default:
+ legalInstr = false;
+ break;
+ }
+
+ if (!(legalMode || legalInstr))
+ {
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::illegal_instr;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ }
+ else if (!(legalMode && legalInstr))
+ {
+ printf ("\nInstruction 0x%x: Not built correctly.\n\n", i);
+ exit(1);
+ }
+
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::NextInstr;
+ cycleCount++;
+ if (!pass)
+ { // Pass 1 - Allocate Memory
+ if (cycleCount)
+ {
+#if defined(_MSC_VER) && (_MSC_VER <= _MSC_VER_BAD_NEW)
+ typedef void (MOS6510::*ptr2cycle) (void);
+ instr->cycle = (ptr2cycle*) new char[sizeof (ptr2cycle) *cycleCount];
+#else
+# ifdef HAVE_EXCEPTIONS
+ instr->cycle = new(std::nothrow) (void (MOS6510::*[cycleCount]) (void));
+# else
+ instr->cycle = new (void (MOS6510::*[cycleCount]) (void));
+# endif
+#endif // _MSC_VER
+ if (!instr->cycle)
+ goto MOS6510_MemAllocFailed;
+ }
+ }
+ else
+ instr->opcode = i;
+
+#if MOS6510_DEBUG > 1
+ printf (".");
+#endif
+ }
+
+ instr->cycles = cycleCount;
+#if MOS6510_DEBUG > 1
+ printf ("Done [%d Cycles]\n", cycleCount);
+#endif
+ }
+
+ //----------------------------------------------------------------------
+ // Build interrupts
+ for (i = 0; i < 3; i++)
+ {
+#if MOS6510_DEBUG > 1
+ printf ("Building Interrupt %d[%02x]..", i, i);
+#endif
+
+ // Pass 1 allocates the memory, Pass 2 builds the interrupt
+ instr = &interruptTable[i];
+ instr->cycle = NULL;
+ instr->opcode = 0;
+
+ for (int pass = 0; pass < 2; pass++)
+ {
+ cycleCount = -1;
+ if (pass) procCycle = instr->cycle;
+
+ switch (i)
+ {
+ case oRST:
+#ifdef MOS6510_ACCURATE_CYCLES
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+#endif
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::RSTRequest;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchOpcode;
+ break;
+
+ case oNMI:
+#ifdef MOS6510_ACCURATE_CYCLES
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+#endif
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PushHighPC;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PushLowPC;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::IRQRequest;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::NMIRequest;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::NMI1Request;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchOpcode;
+ break;
+
+ case oIRQ:
+#ifdef MOS6510_ACCURATE_CYCLES
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::WasteCycle;
+#endif
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PushHighPC;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::PushLowPC;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::IRQRequest;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::IRQ1Request;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::IRQ2Request;
+ cycleCount++; if (pass) procCycle[cycleCount] = &MOS6510::FetchOpcode;
+ break;
+ }
+
+ cycleCount++;
+ if (!pass)
+ { // Pass 1 - Allocate Memory
+ if (cycleCount)
+ {
+#if defined(_MSC_VER) && (_MSC_VER <= _MSC_VER_BAD_NEW)
+ typedef void (MOS6510::*ptr2cycle) (void);
+ instr->cycle = (ptr2cycle*) new char[sizeof (ptr2cycle) *cycleCount];
+#else
+# ifdef HAVE_EXCEPTIONS
+ instr->cycle = new(std::nothrow) (void (MOS6510::*[cycleCount]) (void));
+# else
+ instr->cycle = new (void (MOS6510::*[cycleCount]) (void));
+# endif
+#endif // _MSC_VER
+ if (!instr->cycle)
+ goto MOS6510_MemAllocFailed;
+ }
+ }
+
+#if MOS6510_DEBUG > 1
+ printf (".");
+#endif
+ }
+
+ instr->cycles = cycleCount;
+#if MOS6510_DEBUG > 1
+ printf ("Done [%d Cycles]\n", cycleCount);
+#endif
+ }
+
+ // Intialise Processor Registers
+ Register_Accumulator = 0;
+ Register_X = 0;
+ Register_Y = 0;
+
+ Cycle_EffectiveAddress = 0;
+ Cycle_Data = 0;
+ fetchCycle[0] = &MOS6510::NextInstr;
+
+ dodump = false;
+ Initialise ();
+return;
+
+MOS6510_MemAllocFailed:
+ printf ("Unable to allocate enough memory.\n\n");
+exit (-1);
+}
+
+MOS6510::~MOS6510 ()
+{
+ struct ProcessorOperations *instr;
+ uint i;
+
+ // Remove Opcodes
+ for (i = 0; i < 0x100; i++)
+ {
+ instr = &instrTable[i];
+ if (instr->cycle != NULL) delete [] instr->cycle;
+ }
+
+ // Remove Interrupts
+ for (i = 0; i < 3; i++)
+ {
+ instr = &interruptTable[i];
+ if (instr->cycle != NULL) delete [] instr->cycle;
+ }
+}
+
+
+//-------------------------------------------------------------------------//
+// Initialise CPU Emulation (Registers) //
+void MOS6510::Initialise (void)
+{
+ // Reset stack
+ Register_StackPointer = endian_16 (SP_PAGE, 0xFF);
+
+ // Reset Cycle Count
+ cycleCount = 0;
+ procCycle = fetchCycle;
+
+ // Reset Status Register
+ Register_Status = (1 << SR_NOTUSED) | (1 << SR_BREAK);
+ // FLAGS are set from data directly and do not require
+ // being calculated first before setting. E.g. if you used
+ // SetFlags (0), N flag would = 0, and Z flag would = 1.
+ setFlagsNZ (1);
+ setFlagC (false);
+ setFlagV (false);
+
+ // Set PC to some value
+ Register_ProgramCounter = 0;
+ // IRQs pending check
+ interrupts.irqLatch = false;
+ interrupts.irqRequest = false;
+ if (interrupts.irqs)
+ interrupts.irqRequest = true;
+
+ // Signals
+ aec = true;
+ rdy = true;
+
+ m_blocked = false;
+ eventContext.schedule (this, 1);
+}
+
+//-------------------------------------------------------------------------//
+// Reset CPU Emulation //
+void MOS6510::reset (void)
+{
+ // Reset Interrupts
+ interrupts.pending = false;
+ interrupts.irqs = 0;
+ interrupts.delay = MOS6510_INTERRUPT_DELAY;
+
+ // Internal Stuff
+ Initialise ();
+
+ // Requires External Bits
+ // Read from reset vector for program entry point
+ endian_16lo8 (Cycle_EffectiveAddress, envReadMemDataByte (0xFFFC));
+ endian_16hi8 (Cycle_EffectiveAddress, envReadMemDataByte (0xFFFD));
+ Register_ProgramCounter = Cycle_EffectiveAddress;
+// filepos = 0;
+}
+
+//-------------------------------------------------------------------------//
+// Module Credits //
+void MOS6510::credits (char *sbuffer)
+{ // Copy credits to buffer
+ sprintf (sbuffer, "%sModule : MOS6510 Cycle Exact Emulation\n", sbuffer);
+ sprintf (sbuffer, "%sWritten By : %s\n", sbuffer, MOS6510_AUTHOR);
+ sprintf (sbuffer, "%sVersion : %s\n", sbuffer, MOS6510_VERSION);
+ sprintf (sbuffer, "%sReleased : %s\n", sbuffer, MOS6510_DATE);
+ sprintf (sbuffer, "%sEmail : %s\n", sbuffer, MOS6510_EMAIL);
+}