810 lines
25 KiB
C#
810 lines
25 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using BizHawk.Common;
|
|
using BizHawk.Common.NumberExtensions;
|
|
using BizHawk.Emulation.Common;
|
|
|
|
namespace BizHawk.Emulation.Cores.Consoles.ChannelF
|
|
{
|
|
/// <summary>
|
|
/// Fairchild F3850 (F8) CPU (Channel F-specific implementation)
|
|
///
|
|
/// The F8 microprocessor is made up of separate interchangeable devices
|
|
/// The Channel F has:
|
|
/// * x1 F3850 CPU (central processing unit)
|
|
/// * x2 F3851 PSU (program storage unit)
|
|
/// The CPU does not have its own data counters or program counters, rather each F8 component connected to the CPU
|
|
/// holds their own PCs and SPs and are all connected to the ROMC (ROM control) pins that are serviced by the CPU.
|
|
/// Every device must respond to changes in the CPU ROMC pins output and they each update their PCs and DCs in the same way.
|
|
/// e.g. SPs and PCs should always be identical
|
|
/// Each device has a factory ROM mask applied and with every ROMC change observed is able to know whether it should respond (via the shared data bus)
|
|
/// or not based on the value within its counters.
|
|
///
|
|
/// For this reason we will hold the PCs and SPs within the F3850 implementation.
|
|
///
|
|
/// We are currently also *not* using a separate F3851 implementation. In reality the F3851 chip has/does:
|
|
/// * 1024 byte masked ROM
|
|
/// * x2 16-bit program counters
|
|
/// * x1 16-bit data counter
|
|
/// * Programmable timer
|
|
/// * Interrupt logic
|
|
///
|
|
/// However, the Channel F does not use the timer or interrupt logic at all (as far as I can see) so we can hopefully just
|
|
/// maintain the PC and DC here in the CPU and move the ROMs into the core.
|
|
/// </summary>
|
|
public sealed partial class F3850
|
|
{
|
|
// operations that can take place in an instruction
|
|
public const byte ROMC_01 = 1;
|
|
public const byte ROMC_02 = 2;
|
|
public const byte ROMC_03_S = 3;
|
|
public const byte ROMC_04 = 4;
|
|
public const byte ROMC_05 = 5;
|
|
public const byte ROMC_06 = 6;
|
|
public const byte ROMC_07 = 7;
|
|
public const byte ROMC_08 = 8;
|
|
public const byte ROMC_09 = 9;
|
|
public const byte ROMC_0A = 10;
|
|
public const byte ROMC_0B = 11;
|
|
public const byte ROMC_0C = 12;
|
|
public const byte ROMC_0D = 13;
|
|
public const byte ROMC_0E = 14;
|
|
public const byte ROMC_0F = 15;
|
|
public const byte ROMC_10 = 16;
|
|
public const byte ROMC_11 = 17;
|
|
public const byte ROMC_12 = 18;
|
|
public const byte ROMC_13 = 19;
|
|
public const byte ROMC_14 = 20;
|
|
public const byte ROMC_15 = 21;
|
|
public const byte ROMC_16 = 22;
|
|
public const byte ROMC_17 = 23;
|
|
public const byte ROMC_18 = 24;
|
|
public const byte ROMC_19 = 25;
|
|
public const byte ROMC_1A = 26;
|
|
public const byte ROMC_1B = 27;
|
|
public const byte ROMC_1C_S = 28;
|
|
public const byte ROMC_1D = 29;
|
|
public const byte ROMC_1E = 30;
|
|
public const byte ROMC_1F = 31;
|
|
public const byte ROMC_00_S = 32;
|
|
public const byte ROMC_00_L = 33;
|
|
public const byte ROMC_03_L = 34;
|
|
public const byte ROMC_1C_L = 35;
|
|
|
|
public const byte IDLE = 0;
|
|
public const byte END = 51;
|
|
|
|
public const byte OP_LR8 = 100;
|
|
public const byte OP_SHFT_R = 101;
|
|
public const byte OP_SHFT_L = 102;
|
|
public const byte OP_LNK = 103;
|
|
public const byte OP_DI = 104;
|
|
public const byte OP_EI = 105;
|
|
public const byte OP_INC8 = 106;
|
|
public const byte OP_AND8 = 107;
|
|
public const byte OP_OR8 = 108;
|
|
public const byte OP_XOR8 = 109;
|
|
//public const byte OP_COM = 110;
|
|
public const byte OP_SUB8 = 110;
|
|
public const byte OP_ADD8 = 111;
|
|
public const byte OP_CI = 112;
|
|
public const byte OP_IS_INC = 113;
|
|
public const byte OP_IS_DEC = 114;
|
|
public const byte OP_LISU = 115;
|
|
public const byte OP_LISL = 116;
|
|
public const byte OP_BT = 117;
|
|
public const byte OP_ADD8D = 118;
|
|
public const byte OP_BR7 = 119;
|
|
public const byte OP_BF = 120;
|
|
public const byte OP_IN = 121;
|
|
public const byte OP_OUT = 122;
|
|
//public const byte OP_AS_IS = 123;
|
|
//public const byte OP_XS_IS = 124;
|
|
//public const byte OP_NS_IS = 125;
|
|
public const byte OP_LR_A_DB_IO = 126;
|
|
public const byte OP_DS = 127;
|
|
//public const byte OP_CLEAR_FLAGS = 126;
|
|
//public const byte OP_SET_FLAGS_SZ = 127;
|
|
public const byte OP_LIS = 128;
|
|
|
|
|
|
public F3850()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
ResetRegisters();
|
|
TotalExecutedCycles = 0;
|
|
instr_pntr = 0;
|
|
|
|
PopulateCURINSTR(
|
|
ROMC_1C_S, // S
|
|
IDLE,
|
|
IDLE,
|
|
IDLE,
|
|
ROMC_08, // L
|
|
IDLE,
|
|
IDLE,
|
|
IDLE,
|
|
IDLE,
|
|
IDLE,
|
|
ROMC_00_S, // S
|
|
IDLE,
|
|
IDLE,
|
|
END);
|
|
|
|
ClearFlags_Func();
|
|
FlagICB = false;
|
|
}
|
|
|
|
public IMemoryCallbackSystem MemoryCallbacks { get; set; }
|
|
|
|
// Memory Access
|
|
public Func<ushort, byte> ReadMemory;
|
|
public Action<ushort, byte> WriteMemory;
|
|
public Func<ushort, byte> PeekMemory;
|
|
public Func<ushort, byte> DummyReadMemory;
|
|
|
|
// Hardware I/O Port Access
|
|
public Func<ushort, byte> ReadHardware;
|
|
public Action<ushort, byte> WriteHardware;
|
|
|
|
public Action<ushort> OnExecFetch;
|
|
|
|
public void SetCallbacks
|
|
(
|
|
Func<ushort, byte> ReadMemory,
|
|
Func<ushort, byte> DummyReadMemory,
|
|
Func<ushort, byte> PeekMemory,
|
|
Action<ushort, byte> WriteMemory,
|
|
Func<ushort, byte> ReadHardware,
|
|
Action<ushort, byte> WriteHardware
|
|
)
|
|
{
|
|
this.ReadMemory = ReadMemory;
|
|
this.DummyReadMemory = DummyReadMemory;
|
|
this.PeekMemory = PeekMemory;
|
|
this.WriteMemory = WriteMemory;
|
|
this.ReadHardware = ReadHardware;
|
|
this.WriteHardware = WriteHardware;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Runs a single CPU clock cycle
|
|
/// </summary>
|
|
public void ExecuteOne()
|
|
{
|
|
switch (cur_instr[instr_pntr++])
|
|
{
|
|
// always the last tick within an opcode instruction cycle
|
|
case END:
|
|
OnExecFetch?.Invoke(RegPC0);
|
|
TraceCallback?.Invoke(State());
|
|
opcode = (byte)Regs[DB];
|
|
instr_pntr = 0;
|
|
FetchInstruction();
|
|
break;
|
|
|
|
// used as timing 'padding'
|
|
case IDLE:
|
|
break;
|
|
|
|
// load one register into another (or databus)
|
|
case OP_LR8:
|
|
LR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
|
|
break;
|
|
|
|
// load DB into A (as a part of an IN or INS instruction)
|
|
case OP_LR_A_DB_IO:
|
|
LR_A_IO_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
|
|
break;
|
|
|
|
// loads supplied index value into register
|
|
case OP_LIS:
|
|
Regs[ALU1] = (byte)(cur_instr[instr_pntr++] & 0x0F);
|
|
LR_Func(A, ALU1);
|
|
break;
|
|
|
|
// Shift register n bit positions to the right (zero fill)
|
|
case OP_SHFT_R:
|
|
SR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
|
|
break;
|
|
|
|
// Shift register n bit positions to the left (zero fill)
|
|
case OP_SHFT_L:
|
|
SL_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
|
|
break;
|
|
|
|
// x <- (x) ADD y
|
|
case OP_ADD8:
|
|
ADD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
|
|
break;
|
|
|
|
// x <- (x) MINUS y
|
|
case OP_SUB8:
|
|
SUB_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
|
|
break;
|
|
|
|
// x <- (x) ADD y (decimal)
|
|
case OP_ADD8D:
|
|
ADDD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
|
|
break;
|
|
|
|
// A <- (A) + (C)
|
|
case OP_LNK:
|
|
Regs[ALU0] = (byte)(FlagC ? 1 : 0);
|
|
ADD_Func(A, ALU0);
|
|
break;
|
|
|
|
// Clear ICB status bit
|
|
case OP_DI:
|
|
FlagICB = false;
|
|
break;
|
|
|
|
// Set ICB status bit
|
|
case OP_EI:
|
|
FlagICB = true;
|
|
break;
|
|
|
|
// x <- (y) XOR DB
|
|
case OP_XOR8:
|
|
XOR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
|
|
break;
|
|
|
|
// x <- (x) + 1
|
|
case OP_INC8:
|
|
ADD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
|
|
break;
|
|
|
|
// x <- (y) & DB
|
|
case OP_AND8:
|
|
AND_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
|
|
break;
|
|
|
|
// x <- (y) | DB
|
|
case OP_OR8:
|
|
OR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]);
|
|
break;
|
|
|
|
// DB + (x) + 1 (modify flags without saving result)
|
|
case OP_CI:
|
|
CI_Func();
|
|
break;
|
|
|
|
// ISAR is incremented
|
|
case OP_IS_INC:
|
|
Regs[ISAR] = (byte)((Regs[ISAR]& 0x38) | ((Regs[ISAR] + 1) & 0x07));
|
|
break;
|
|
|
|
// ISAR is decremented
|
|
case OP_IS_DEC:
|
|
Regs[ISAR] = (byte)((Regs[ISAR] & 0x38) | ((Regs[ISAR] - 1) & 0x07));
|
|
break;
|
|
|
|
// set the upper octal ISAR bits (b3,b4,b5)
|
|
case OP_LISU:
|
|
Regs[ISAR] = (byte)((((Regs[ISAR] & 0x07) | (cur_instr[instr_pntr++] & 0x07) << 3)) & 0x3F);
|
|
break;
|
|
|
|
// set the lower octal ISAR bits (b0,b1,b2)
|
|
case OP_LISL:
|
|
Regs[ISAR] = (byte) (((Regs[ISAR] & 0x38) | (cur_instr[instr_pntr++] & 0x07)) & 0x3F);
|
|
break;
|
|
|
|
// decrement scratchpad byte
|
|
//case OP_DS:
|
|
//SUB_Func(cur_instr[instr_pntr++], ONE);
|
|
//break;
|
|
|
|
// Branch on TRUE
|
|
case OP_BT:
|
|
bool branchBT = false;
|
|
switch (cur_instr[instr_pntr++])
|
|
{
|
|
case 0:
|
|
// do not branch
|
|
break;
|
|
|
|
case 1:
|
|
// branch if positive (sign bit is set)
|
|
if (FlagS) branchBT = true;
|
|
break;
|
|
|
|
case 2:
|
|
// branch on carry (carry bit is set)
|
|
if (FlagC) branchBT = true;
|
|
break;
|
|
|
|
case 3:
|
|
// branch if positive or on carry
|
|
if (FlagS || FlagC) branchBT = true;
|
|
break;
|
|
|
|
case 4:
|
|
// branch if zero (zero bit is set)
|
|
if (FlagZ) branchBT = true;
|
|
break;
|
|
|
|
case 5:
|
|
// branch if positive and zero
|
|
if (FlagS || FlagZ) branchBT = true;
|
|
break;
|
|
|
|
case 6:
|
|
// branch if zero or on carry
|
|
if (FlagZ || FlagC) branchBT = true;
|
|
break;
|
|
case 7:
|
|
// branch if positive or on carry or zero
|
|
if (FlagS || FlagC || FlagZ) branchBT = true;
|
|
break;
|
|
}
|
|
|
|
instr_pntr = 0;
|
|
if (branchBT) DO_BRANCH();
|
|
else DONT_BRANCH();
|
|
break;
|
|
|
|
// Branch on ISARL
|
|
case OP_BR7:
|
|
instr_pntr = 1; // lose a cycle
|
|
if (Regs[ISAR].Bit(0) && Regs[ISAR].Bit(1) && Regs[ISAR].Bit(2))
|
|
{
|
|
DONT_BRANCH();
|
|
}
|
|
else
|
|
{
|
|
DO_BRANCH();
|
|
}
|
|
break;
|
|
|
|
// Branch on FALSE
|
|
case OP_BF:
|
|
bool branchBF = false;
|
|
switch (cur_instr[instr_pntr++])
|
|
{
|
|
case 0:
|
|
// unconditional branch relative
|
|
branchBF = true;
|
|
break;
|
|
|
|
case 1:
|
|
// branch on negative (sign bit is reset)
|
|
if (!FlagS) branchBF = true;
|
|
break;
|
|
|
|
case 2:
|
|
// branch if no carry (carry bit is reset)
|
|
if (!FlagC) branchBF = true;
|
|
break;
|
|
|
|
case 3:
|
|
// branch if no carry and negative
|
|
if (!FlagC && !FlagS) branchBF = true;
|
|
break;
|
|
|
|
case 4:
|
|
// branch if not zero (zero bit is reset)
|
|
if (!FlagZ) branchBF = true;
|
|
break;
|
|
|
|
case 5:
|
|
// branch if not zero and negative
|
|
if (!FlagS && !FlagZ) branchBF = true;
|
|
break;
|
|
|
|
case 6:
|
|
// branch if no carry and result is no zero
|
|
if (!FlagC && !FlagZ) branchBF = true;
|
|
break;
|
|
|
|
case 7:
|
|
// branch if not zero, carry and sign
|
|
if (!FlagS && !FlagC && !FlagZ) branchBF = true;
|
|
break;
|
|
|
|
case 8:
|
|
// branch if there is no overflow (OVF bit is reset)
|
|
if (!FlagO) branchBF = true;
|
|
break;
|
|
|
|
case 9:
|
|
// branch if negative and no overflow
|
|
if (!FlagS && !FlagO) branchBF = true;
|
|
break;
|
|
|
|
case 0xA:
|
|
// branch if no overflow and no carry
|
|
if (!FlagO && !FlagC) branchBF = true;
|
|
break;
|
|
|
|
case 0xB:
|
|
// branch if no overflow, no carry & negative
|
|
if (!FlagO && !FlagC && !FlagS) branchBF = true;
|
|
break;
|
|
|
|
case 0xC:
|
|
// branch if no overflow and not zero
|
|
if (!FlagO && !FlagZ) branchBF = true;
|
|
break;
|
|
|
|
case 0xD:
|
|
// branch if no overflow, not zero and neg
|
|
if (!FlagS && !FlagO && !FlagZ) branchBF = true;
|
|
break;
|
|
|
|
case 0xE:
|
|
// branch if no overflow, no carry & not zero
|
|
if (!FlagO && !FlagC && !FlagZ) branchBF = true;
|
|
break;
|
|
|
|
case 0xF:
|
|
// all neg
|
|
if (!FlagO && !FlagC && !FlagS && FlagZ) branchBF = true;
|
|
break;
|
|
}
|
|
|
|
instr_pntr = 0;
|
|
if (branchBF) DO_BRANCH();
|
|
else DONT_BRANCH();
|
|
break;
|
|
|
|
// A <- (I/O Port 0 or 1)
|
|
case OP_IN:
|
|
instr_pntr++; // dest == A
|
|
Regs[ALU0] = cur_instr[instr_pntr++]; // src
|
|
IN_Func(A, ALU0);
|
|
break;
|
|
|
|
// I/O Port 0 or 1 <- (A)
|
|
case OP_OUT:
|
|
WriteHardware(cur_instr[instr_pntr++], (byte)Regs[cur_instr[instr_pntr++]]);
|
|
break;
|
|
|
|
// instruction fetch
|
|
// The device whose address space includes the contents of the PC0 register must place on the data bus the op code addressed by PC0;
|
|
// then all devices increments the content of PC0.
|
|
// CYCLE LENGTH: S
|
|
case ROMC_00_S:
|
|
Read_Func(DB, PC0l, PC0h);
|
|
RegPC0++;
|
|
break;
|
|
|
|
// instruction fetch
|
|
// The device whose address space includes the contents of the PC0 register must place on the data bus the op code addressed by PC0;
|
|
// then all devices increments the content of PC0.
|
|
// CYCLE LENGTH: L
|
|
case ROMC_00_L:
|
|
Read_Func(DB, PC0l, PC0h);
|
|
RegPC0++;
|
|
break;
|
|
|
|
// The device whose address space includes the contents of the PC0 register must place on the data bus the contents of the memory location
|
|
// addressed by by PC0; then all devices add the 8-bit value on the data bus, as a signed binary number, to PC0
|
|
// CYCLE LENGTH: L
|
|
case ROMC_01:
|
|
Read_Func(DB, PC0l, PC0h);
|
|
RegPC0 += (ushort)((SByte) Regs[DB]);
|
|
break;
|
|
|
|
// The device whose DC0 address addresses a memory word within the address space of that device must place on the data bus the contents
|
|
// of the memory location addressed by DC0; then all devices increment DC0
|
|
// CYCLE LENGTH: L
|
|
case ROMC_02:
|
|
Read_Func(DB, DC0l, DC0h);
|
|
RegDC0++;
|
|
break;
|
|
|
|
// Similar to 0x00, except that it is used for Immediate Operand fetches (using PC0) instead of instruction fetches
|
|
// CYCLE LENGTH: S
|
|
case ROMC_03_S:
|
|
Read_Func(DB, PC0l, PC0h);
|
|
RegPC0++;
|
|
Regs[IO] = Regs[DB];
|
|
break;
|
|
|
|
// Similar to 0x00, except that it is used for Immediate Operand fetches (using PC0) instead of instruction fetches
|
|
// CYCLE LENGTH: L
|
|
case ROMC_03_L:
|
|
Read_Func(DB, PC0l, PC0h);
|
|
RegPC0++;
|
|
Regs[IO] = Regs[DB];
|
|
break;
|
|
|
|
// Copy the contents of PC1 into PC0
|
|
// CYCLE LENGTH: S
|
|
case ROMC_04:
|
|
RegPC0 = RegPC1;
|
|
break;
|
|
|
|
// Store the data bus contents into the memory location pointed to by DC0; increment DC0
|
|
// CYCLE LENGTH: L
|
|
case ROMC_05:
|
|
Write_Func(DC0l, DC0h, DB);
|
|
break;
|
|
|
|
// Place the high order byte of DC0 on the data bus
|
|
// CYCLE LENGTH: L
|
|
case ROMC_06:
|
|
Regs[DB] = (byte)Regs[DC0h];
|
|
break;
|
|
|
|
// Place the high order byte of PC1 on the data bus
|
|
// CYCLE LENGTH: L
|
|
case ROMC_07:
|
|
Regs[DB] = (byte)Regs[PC1h];
|
|
break;
|
|
|
|
// All devices copy the contents of PC0 into PC1. The CPU outputs zero on the data bus in this ROMC state.
|
|
// Load the data bus into both halves of PC0, this clearing the register.
|
|
// CYCLE LENGTH: L
|
|
case ROMC_08:
|
|
RegPC1 = RegPC0;
|
|
Regs[DB] = 0;
|
|
Regs[PC0h] = 0;
|
|
Regs[PC0l] = 0;
|
|
break;
|
|
|
|
// The device whose address space includes the contents of the DC0 register must place the low order byte of DC0 onto the data bus
|
|
// CYCLE LENGTH: L
|
|
case ROMC_09:
|
|
Regs[DB] = (byte)Regs[DC0l];
|
|
break;
|
|
|
|
// All devices add the 8-bit value on the data bus, treated as a signed binary number, to the data counter
|
|
// CYCLE LENGTH: L
|
|
case ROMC_0A:
|
|
RegDC0 += (ushort) ((sbyte) Regs[DB]);
|
|
break;
|
|
|
|
// The device whose address space includes the value in PC1 must place the low order byte of PC1 on the data bus
|
|
// CYCLE LENGTH: L
|
|
case ROMC_0B:
|
|
Regs[DB] = (byte)Regs[PC1l];
|
|
break;
|
|
|
|
// The device whose address space includes the contents of the PC0 register must place the contents of the memory word addressed by PC0
|
|
// onto the data bus; then all devices move the value that has just been placed on the data bus into the low order byte of PC0
|
|
// CYCLE LENGTH: L
|
|
case ROMC_0C:
|
|
Read_Func(DB, PC0l, PC0h);
|
|
Regs[PC0l] = Regs[DB];
|
|
break;
|
|
|
|
// All devices store in PC1 the current contents of PC0, incremented by 1; PC0 is unaltered
|
|
// CYCLE LENGTH: S
|
|
case ROMC_0D:
|
|
RegPC1 = (ushort)(RegPC0 + 1);
|
|
break;
|
|
|
|
// The device whose address space includes the contents of PC0 must place the contents of the word addressed by PC0 onto the data bus.
|
|
// The value on the data bus is then moved to the low order byte of DC0 by all devices
|
|
// CYCLE LENGTH: L
|
|
case ROMC_0E:
|
|
Read_Func(DB, PC0l, PC0h);
|
|
Regs[DC0l] = Regs[DB];
|
|
break;
|
|
|
|
// The interrupting device with the highest priority must place the low order byte of the interrupt vector on the data bus.
|
|
// All devices must copy the contents of PC0 into PC1. All devices must move the contents of the data bus into the low order byte of PC0
|
|
// CYCLE LENGTH: L
|
|
case ROMC_0F:
|
|
throw new NotImplementedException("ROMC 0x0F not implemented");
|
|
|
|
// Inhibit any modification to the interrupt priority logic
|
|
// CYCLE LENGTH: L
|
|
case ROMC_10:
|
|
throw new NotImplementedException("ROMC 0x10 not implemented");
|
|
|
|
// The device whose memory space includes the contents of PC0 must place the contents of the addressed memory word on the data bus.
|
|
// All devices must then move the contents of the data bus to the upper byte of DC0
|
|
// CYCLE LENGTH: L
|
|
case ROMC_11:
|
|
Read_Func(DB, PC0l, PC0h);
|
|
Regs[DC0h] = Regs[DB];
|
|
break;
|
|
|
|
// All devices copy the contents of PC0 into PC1. All devices then move the contents of the data bus into the low order byte of PC0
|
|
// CYCLE LENGTH: L
|
|
case ROMC_12:
|
|
RegPC1 = RegPC0;
|
|
Regs[PC0l] = Regs[DB];
|
|
break;
|
|
|
|
// The interrupting device with the highest priority must move the high order half of the interrupt vector onto the data bus.
|
|
// All devices must move the conetnts of the data bus into the high order byte of of PC0. The interrupting device resets its
|
|
// interrupt circuitry (so that it is no longer requesting CPU servicing and can respond to another interrupt)
|
|
// CYCLE LENGTH: L
|
|
case ROMC_13:
|
|
throw new NotImplementedException("ROMC 0x13 not implemented");
|
|
|
|
// All devices move the contents of the data bus into the high order byte of PC0
|
|
// CYCLE LENGTH: L
|
|
case ROMC_14:
|
|
Regs[PC0h] = Regs[DB];
|
|
break;
|
|
|
|
// All devices move the contents of the data bus into the high order byte of PC1
|
|
// CYCLE LENGTH: L
|
|
case ROMC_15:
|
|
Regs[PC1h] = Regs[DB];
|
|
break;
|
|
|
|
// All devices move the contents of the data bus into the high order byte of DC0
|
|
// CYCLE LENGTH: L
|
|
case ROMC_16:
|
|
Regs[DC0h] = Regs[DB];
|
|
break;
|
|
|
|
// All devices move the contents of the data bus into the low order byte of PC0
|
|
// CYCLE LENGTH: L
|
|
case ROMC_17:
|
|
Regs[PC0l] = Regs[DB];
|
|
break;
|
|
|
|
// All devices move the contents of the data bus into the low order byte of PC1
|
|
// CYCLE LENGTH: L
|
|
case ROMC_18:
|
|
Regs[PC1l] = Regs[DB];
|
|
break;
|
|
|
|
// All devices move the contents of the data bus into the low order byte of DC0
|
|
// CYCLE LENGTH: L
|
|
case ROMC_19:
|
|
Regs[DC0l] = Regs[DB];
|
|
break;
|
|
|
|
// During the prior cycle, an I/O port timer or interrupt control register was addressed; the device containing the addressed
|
|
// port must move the current contents of the data bus into the addressed port
|
|
// CYCLE LENGTH: L
|
|
case ROMC_1A:
|
|
WriteHardware(Regs[IO], (byte)Regs[DB]);
|
|
break;
|
|
|
|
// During the prior cycle, the data bus specified the address of an I/O port. The device containing the addressed I/O port
|
|
// must place the contents of the I/O port on the data bus. (Note that the contents of the timer and interrupt control
|
|
// registers cannot be read back onto the data bus)
|
|
// CYCLE LENGTH: L
|
|
case ROMC_1B:
|
|
IN_Func(DB, IO);
|
|
//Regs[DB] = ReadHardware(Regs[IO]);
|
|
break;
|
|
|
|
// None
|
|
// CYCLE LENGTH: S
|
|
case ROMC_1C_S:
|
|
break;
|
|
|
|
// None
|
|
// CYCLE LENGTH: L
|
|
case ROMC_1C_L:
|
|
break;
|
|
|
|
// Devices with DC0 and DC1 registers must switch registers. Devices without a DC1 register perform no operation
|
|
// CYCLE LENGTH: S
|
|
case ROMC_1D:
|
|
// we have no DC1 in this implementation
|
|
break;
|
|
|
|
// The device whose address space includes the contents of PC0 must place the low order byte of PC0 onto the data bus
|
|
// CYCLE LENGTH: L
|
|
case ROMC_1E:
|
|
Regs[DB] = (byte)Regs[PC0l];
|
|
break;
|
|
|
|
// The device whose address space includes the contents of PC0 must place the high order byte of PC0 onto the data bus
|
|
// CYCLE LENGTH: L
|
|
case ROMC_1F:
|
|
Regs[DB] = (byte)Regs[PC0h];
|
|
break;
|
|
}
|
|
|
|
TotalExecutedCycles++;
|
|
}
|
|
|
|
public Action<TraceInfo> TraceCallback;
|
|
|
|
public string TraceHeader => "F3850: PC, machine code, mnemonic, operands, flags (IOZCS), registers (PC1, DC0, A, ISAR, DB, IO, J, H, K, Q, R00-R63), Cycles";
|
|
|
|
public TraceInfo State(bool disassemble = true)
|
|
{
|
|
int bytes_read = 0;
|
|
ushort pc = (ushort)(RegPC0 - 1);
|
|
string disasm = disassemble ? Disassemble(pc, ReadMemory, out bytes_read) : "---";
|
|
string byte_code = null;
|
|
|
|
for (ushort i = 0; i < bytes_read; i++)
|
|
{
|
|
byte_code += ReadMemory((ushort)(pc + i)).ToString("X2");
|
|
if (i < (bytes_read - 1))
|
|
{
|
|
byte_code += " ";
|
|
}
|
|
}
|
|
|
|
return new TraceInfo
|
|
{
|
|
Disassembly = string.Format(
|
|
"{0:X4}: {1} {2}",
|
|
pc,
|
|
byte_code.PadRight(12),
|
|
disasm.PadRight(26)),
|
|
RegisterInfo = string.Format(
|
|
"Flags:{75}{76}{77}{78}{79} " +
|
|
"PC1:{0:X4} DC0:{1:X4} A:{2:X2} ISAR:{3:X2} DB:{4:X2} IO:{5:X2} J:{6:X2} H:{7:X4} K:{8:X4} Q:{9:X4} " +
|
|
"R0:{10:X2} R1:{11:X2} R2:{12:X2} R3:{13:X2} R4:{14:X2} R5:{15:X2} R6:{16:X2} R7:{17:X2} R8:{18:X2} R9:{19:X2} " +
|
|
"R10:{20:X2} R11:{21:X2} R12:{22:X2} R13:{23:X2} R14:{24:X2} R15:{25:X2} R16:{26:X2} R17:{27:X2} R18:{28:X2} R19:{29:X2} " +
|
|
"R20:{30:X2} R21:{31:X2} R22:{32:X2} R23:{33:X2} R24:{34:X2} R25:{35:X2} R26:{36:X2} R27:{37:X2} R28:{38:X2} R29:{39:X2} " +
|
|
"R30:{40:X2} R31:{41:X2} R32:{42:X2} R33:{43:X2} R34:{44:X2} R35:{45:X2} R36:{46:X2} R37:{47:X2} R38:{48:X2} R39:{49:X2} " +
|
|
"R40:{50:X2} R41:{51:X2} R42:{52:X2} R43:{53:X2} R44:{54:X2} R45:{55:X2} R46:{56:X2} R47:{57:X2} R48:{58:X2} R49:{59:X2} " +
|
|
"R50:{60:X2} R51:{61:X2} R52:{62:X2} R53:{63:X2} R54:{64:X2} R55:{65:X2} R56:{66:X2} R57:{67:X2} R58:{68:X2} R59:{69:X2} " +
|
|
"R60:{70:X2} R61:{71:X2} R62:{72:X2} R63:{73:X2} " +
|
|
"Cy:{74}",
|
|
RegPC1,
|
|
RegDC0,
|
|
Regs[A],
|
|
Regs[ISAR],
|
|
Regs[DB],
|
|
Regs[IO],
|
|
Regs[J],
|
|
(ushort)(Regs[Hl] | (Regs[Hh] << 8)),
|
|
(ushort)(Regs[Kl] | (Regs[Kh] << 8)),
|
|
(ushort)(Regs[Ql] | (Regs[Qh] << 8)),
|
|
Regs[0], Regs[1], Regs[2], Regs[3], Regs[4], Regs[5], Regs[6], Regs[7], Regs[8], Regs[9],
|
|
Regs[10], Regs[11], Regs[12], Regs[13], Regs[14], Regs[15], Regs[16], Regs[17], Regs[18], Regs[19],
|
|
Regs[20], Regs[21], Regs[22], Regs[23], Regs[24], Regs[25], Regs[26], Regs[27], Regs[28], Regs[29],
|
|
Regs[30], Regs[31], Regs[32], Regs[33], Regs[34], Regs[35], Regs[36], Regs[37], Regs[38], Regs[39],
|
|
Regs[40], Regs[41], Regs[42], Regs[43], Regs[44], Regs[45], Regs[46], Regs[47], Regs[48], Regs[49],
|
|
Regs[50], Regs[51], Regs[52], Regs[53], Regs[54], Regs[55], Regs[56], Regs[57], Regs[58], Regs[59],
|
|
Regs[60], Regs[61], Regs[62], Regs[63],
|
|
TotalExecutedCycles,
|
|
FlagICB ? "I" : "i",
|
|
FlagO ? "O" : "o",
|
|
FlagZ ? "Z" : "z",
|
|
FlagC ? "C" : "c",
|
|
FlagS ? "S" : "s"),
|
|
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Optimization method to set cur_instr
|
|
/// </summary>
|
|
private void PopulateCURINSTR(byte d0 = 0, byte d1 = 0, byte d2 = 0, byte d3 = 0, byte d4 = 0, byte d5 = 0, byte d6 = 0, byte d7 = 0, byte d8 = 0,
|
|
byte d9 = 0, byte d10 = 0, byte d11 = 0, byte d12 = 0, byte d13 = 0, byte d14 = 0, byte d15 = 0, byte d16 = 0, byte d17 = 0, byte d18 = 0,
|
|
byte d19 = 0, byte d20 = 0, byte d21 = 0, byte d22 = 0, byte d23 = 0, byte d24 = 0, byte d25 = 0, byte d26 = 0, byte d27 = 0, byte d28 = 0,
|
|
byte d29 = 0, byte d30 = 0, byte d31 = 0, byte d32 = 0, byte d33 = 0, byte d34 = 0, byte d35 = 0, byte d36 = 0, byte d37 = 0)
|
|
{
|
|
cur_instr[0] = d0; cur_instr[1] = d1; cur_instr[2] = d2;
|
|
cur_instr[3] = d3; cur_instr[4] = d4; cur_instr[5] = d5;
|
|
cur_instr[6] = d6; cur_instr[7] = d7; cur_instr[8] = d8;
|
|
cur_instr[9] = d9; cur_instr[10] = d10; cur_instr[11] = d11;
|
|
cur_instr[12] = d12; cur_instr[13] = d13; cur_instr[14] = d14;
|
|
cur_instr[15] = d15; cur_instr[16] = d16; cur_instr[17] = d17;
|
|
cur_instr[18] = d18; cur_instr[19] = d19; cur_instr[20] = d20;
|
|
cur_instr[21] = d21; cur_instr[22] = d22; cur_instr[23] = d23;
|
|
cur_instr[24] = d24; cur_instr[25] = d25; cur_instr[26] = d26;
|
|
cur_instr[27] = d27; cur_instr[28] = d28; cur_instr[29] = d29;
|
|
cur_instr[30] = d30; cur_instr[31] = d31; cur_instr[32] = d32;
|
|
cur_instr[33] = d33; cur_instr[34] = d34; cur_instr[35] = d35;
|
|
cur_instr[36] = d36; cur_instr[37] = d37;
|
|
}
|
|
|
|
public void SyncState(Serializer ser)
|
|
{
|
|
ser.BeginSection(nameof(F3850));
|
|
ser.Sync(nameof(Regs), ref Regs, false);
|
|
ser.Sync(nameof(cur_instr), ref cur_instr, false);
|
|
ser.Sync(nameof(instr_pntr), ref instr_pntr);
|
|
ser.EndSection();
|
|
}
|
|
}
|
|
}
|