Files
VirtualC64-Core/C64/CPU.cpp
T
2015-11-18 00:34:41 -06:00

406 lines
10 KiB
C++
Executable File

/*
* (C) 2006 Dirk W. Hoffmann. All rights reserved.
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "C64.h"
CPU::CPU()
{
name = "CPU";
debug(2, " Creating CPU at address %p...\n", this);
// Chip model
chipModel = MOS6510;
// Establish callback for each instruction
registerInstructions();
// Clear all breakpoint tags
for (int i = 0; i < 65536; i++) {
breakpoint[i] = NO_BREAKPOINT;
}
// Register snapshot items
SnapshotItem items[] = {
// Lifetime items
{ &chipModel, sizeof(chipModel), KEEP_ON_RESET },
// Internal state
{ &A, sizeof(A), CLEAR_ON_RESET },
{ &X, sizeof(X), CLEAR_ON_RESET },
{ &Y, sizeof(Y), CLEAR_ON_RESET },
{ &PC, sizeof(PC), CLEAR_ON_RESET },
{ &PC_at_cycle_0, sizeof(PC_at_cycle_0), CLEAR_ON_RESET },
{ &SP, sizeof(SP), CLEAR_ON_RESET },
{ &N, sizeof(N), CLEAR_ON_RESET },
{ &V, sizeof(V), CLEAR_ON_RESET },
{ &B, sizeof(B), CLEAR_ON_RESET },
{ &D, sizeof(D), CLEAR_ON_RESET },
{ &I, sizeof(I), CLEAR_ON_RESET },
{ &Z, sizeof(Z), CLEAR_ON_RESET },
{ &C, sizeof(C), CLEAR_ON_RESET },
{ &opcode, sizeof(opcode), CLEAR_ON_RESET },
{ &addr_lo, sizeof(addr_lo), CLEAR_ON_RESET },
{ &addr_hi, sizeof(addr_hi), CLEAR_ON_RESET },
{ &ptr, sizeof(ptr), CLEAR_ON_RESET },
{ &pc_lo, sizeof(pc_lo), CLEAR_ON_RESET },
{ &pc_hi, sizeof(pc_hi), CLEAR_ON_RESET },
{ &overflow, sizeof(overflow), CLEAR_ON_RESET },
{ &data, sizeof(data), CLEAR_ON_RESET },
{ &port, sizeof(port), KEEP_ON_RESET }, // Reset in C64Memory::reset
{ &port_direction, sizeof(port_direction), KEEP_ON_RESET }, // Reset in C64Memory::reset
{ &external_port_bits, sizeof(external_port_bits), CLEAR_ON_RESET },
{ &rdyLine, sizeof(rdyLine), CLEAR_ON_RESET },
{ &irqLine, sizeof(irqLine), CLEAR_ON_RESET },
{ &nmiLine, sizeof(nmiLine), CLEAR_ON_RESET },
{ &nmiEdge, sizeof(nmiEdge), CLEAR_ON_RESET },
{ &interruptsPending, sizeof(interruptsPending), CLEAR_ON_RESET },
{ &nextPossibleIrqCycle, sizeof(nextPossibleIrqCycle), CLEAR_ON_RESET },
{ &nextPossibleNmiCycle, sizeof(nextPossibleNmiCycle), CLEAR_ON_RESET },
{ &errorState, sizeof(errorState), CLEAR_ON_RESET },
{ &callStack, sizeof(callStack), CLEAR_ON_RESET | WORD_FORMAT },
{ &callStackPointer, sizeof(callStackPointer), CLEAR_ON_RESET },
{ &oldI, sizeof(oldI), CLEAR_ON_RESET },
{ NULL, 0, 0 }};
registerSnapshotItems(items, sizeof(items));
}
CPU::~CPU()
{
debug(2, " Releasing CPU...\n");
}
void
CPU::reset()
{
VirtualComponent::reset();
external_port_bits = 0x1F;
rdyLine = true;
next = &CPU::fetch;
}
uint32_t
CPU::stateSize()
{
return VirtualComponent::stateSize() + 2;
}
void
CPU::loadFromBuffer(uint8_t **buffer)
{
VirtualComponent::loadFromBuffer(buffer);
next = CPU::callbacks[read16(buffer)];
}
void
CPU::saveToBuffer(uint8_t **buffer)
{
VirtualComponent::saveToBuffer(buffer);
for (uint16_t i = 0;; i++) {
if (callbacks[i] == NULL) {
panic("ERROR while saving state: Callback pointer not found!\n");
}
if (callbacks[i] == next) {
write16(buffer, i);
break;
}
}
}
void
CPU::dumpState()
{
msg("CPU:\n");
msg("----\n\n");
msg("%s\n", disassemble());
msg("Processor port : %02X\n", port);
msg("Port direction : %02X\n", port_direction);
msg(" Rdy line : %s\n", rdyLine ? "high" : "low");
msg(" Irq line : %02X\n", irqLine);
msg(" Nmi line : %02X %s\n", nmiLine, nmiEdge ? "(negative edge)" : "");
msg(" no IRQ before : %ull\n", nextPossibleIrqCycle);
msg(" no NMI before : %ull\n", nextPossibleNmiCycle);
msg(" IRQ routine : %02X%02X\n", mem->peek(0xFFFF), mem->peek(0xFFFE));
msg(" NMI routine : %02X%02X\n", mem->peek(0xFFFB), mem->peek(0xFFFA));
msg("\n");
}
void
CPU::setPortDirection(uint8_t value)
{
port_direction = value;
// TODO: A VIC byte will show up in ram[0x0000];
// "ram[0] = TheVIC->LastVICByte;" [Frodo]
// Store value of Bit 7, Bit 6 and Bit 3 if the corresponding bit lines are configured as outputs
uint8_t mask = 0xC8 & port_direction;
external_port_bits &= ~mask;
external_port_bits |= mask & port;
}
void
CPU::setPort(uint8_t value)
{
port = value;
// TODO: A VIC byte will show up in ram[0x0001];
// "ram[1] = TheVIC->LastVICByte;" [Frodo]
// Store value of Bit 7, Bit 6 and Bit 3 if the corresponding bit lines are configured as outputs
uint8_t mask = 0xC8 & port_direction;
external_port_bits &= ~mask;
external_port_bits |= mask & port;
// Datasette
if (port_direction & 0x20) {
c64->datasette.setMotor((value & 0x20) == 0);
}
}
void
CPU::setIRQLine(uint8_t bit)
{
assert(bit != 0);
if (irqLine == 0) {
nextPossibleIrqCycle = c64->getCycles() + 2;
}
irqLine |= bit;
interruptsPending = true;
}
bool
CPU::IRQLineRaisedLongEnough()
{
return c64->getCycles() >= nextPossibleIrqCycle;
}
bool
CPU::IRQsAreBlocked() {
bool result;
if (opcode == 0x78 /* SEI */ || opcode == 0x58 /* CLI */)
result = oldI;
else
result = I;
oldI = I;
return result;
}
void
CPU::setNMILine(uint8_t bit) // TODO: RENAME TO RAISE NMIline
{
assert(bit != 0);
if (!nmiLine) setNMIEdge();
nmiLine |= bit;
}
void
CPU::setNMIEdge()
{
nmiEdge = true;
interruptsPending = true;
nextPossibleNmiCycle = c64->getCycles() + 2;
}
void
CPU::clearNMIEdge()
{
nmiEdge = false;
interruptsPending = irqLine;
}
bool
CPU::NMILineRaisedLongEnough()
{
return c64->getCycles() >= nextPossibleNmiCycle;
}
// Instruction set
const char
*CPU::getMnemonic(uint8_t opcode)
{
return mnemonic[opcode];
}
CPU::AddressingMode
CPU::getAddressingMode(uint8_t opcode)
{
return addressingMode[opcode];
}
int
CPU::getLengthOfInstruction(uint8_t opcode)
{
switch(addressingMode[opcode]) {
case ADDR_IMPLIED:
case ADDR_ACCUMULATOR:
return 1;
case ADDR_IMMEDIATE:
case ADDR_ZERO_PAGE:
case ADDR_ZERO_PAGE_X:
case ADDR_ZERO_PAGE_Y:
case ADDR_INDIRECT_X:
case ADDR_INDIRECT_Y:
case ADDR_RELATIVE:
return 2;
case ADDR_ABSOLUTE:
case ADDR_ABSOLUTE_X:
case ADDR_ABSOLUTE_Y:
case ADDR_DIRECT:
case ADDR_INDIRECT:
return 3;
}
return 1;
}
char *
CPU::disassemble()
{
char buf[64], msg[128];
int i, op;
uint16_t pc = PC_at_cycle_0;
uint8_t opcode = mem->peek(pc);
strcpy(msg, "");
// Program counter
sprintf(buf, "%04X: ", pc);
strcat(msg, buf);
// Hex dump
for (i = 0; i < 3; i++) {
if (i < getLengthOfInstruction(opcode)) {
sprintf(buf, "%02X ", mem->peek(pc+i));
strcat(msg, buf);
} else {
sprintf(buf, " ");
strcat(msg, buf);
}
}
// Register
sprintf(buf, " %02X %02X %02X %02X ", A, X, Y, SP);
strcat(msg, buf);
// Flags
sprintf(buf, "%c%c%c%c%c%c%c%c ",
N ? 'N' : 'n',
V ? 'V' : 'v',
'-',
B ? 'B' : 'b',
D ? 'D' : 'd',
I ? 'I' : 'i',
Z ? 'Z' : 'z',
C ? 'C' : 'c');
strcat(msg, buf);
// Mnemonic
sprintf(buf, "%s ", getMnemonic(opcode));
strcat(msg, buf);
// Get operand as number
switch (addressingMode[opcode]) {
case CPU::ADDR_IMMEDIATE:
case CPU::ADDR_ZERO_PAGE:
case CPU::ADDR_ZERO_PAGE_X:
case CPU::ADDR_ZERO_PAGE_Y:
case CPU::ADDR_INDIRECT_X:
case CPU::ADDR_INDIRECT_Y:
op = mem->peek(pc+1);
break;
case CPU::ADDR_DIRECT:
case CPU::ADDR_INDIRECT:
case CPU::ADDR_ABSOLUTE:
case CPU::ADDR_ABSOLUTE_X:
case CPU::ADDR_ABSOLUTE_Y:
op = mem->peekWord(pc+1);
break;
case CPU::ADDR_RELATIVE:
op = pc + 2 + (int8_t)mem->peek(pc+1);
break;
default:
op = -1;
}
// Format operand
switch (addressingMode[opcode]) {
case CPU::ADDR_IMPLIED:
case CPU::ADDR_ACCUMULATOR:
sprintf(buf, " ");
break;
case CPU::ADDR_IMMEDIATE:
sprintf(buf, "#%02X", op);
break;
case CPU::ADDR_ZERO_PAGE:
sprintf(buf, "%02X", op);
break;
case CPU::ADDR_ZERO_PAGE_X:
sprintf(buf, "%02X,X", op);
break;
case CPU::ADDR_ZERO_PAGE_Y:
sprintf(buf, "%02X,Y", op);
break;
case CPU::ADDR_ABSOLUTE:
case CPU::ADDR_DIRECT:
sprintf(buf, "%04X", op);
break;
case CPU::ADDR_ABSOLUTE_X:
sprintf(buf, "%04X,X", op);
break;
case CPU::ADDR_ABSOLUTE_Y:
sprintf(buf, "%04X,Y", op);
break;
case CPU::ADDR_INDIRECT:
sprintf(buf, "(%04X)", op);
break;
case CPU::ADDR_INDIRECT_X:
sprintf(buf, "(%04X,X)", op);
break;
case CPU::ADDR_INDIRECT_Y:
sprintf(buf, "(%04X),Y", op);
break;
case CPU::ADDR_RELATIVE:
sprintf(buf, "%04X", op);
break;
default:
sprintf(buf, "???");
}
strcat(msg, buf);
return strdup(msg);
}
void
CPU::setErrorState(ErrorState state)
{
if (errorState == state)
return;
errorState = state;
c64->putMessage(MSG_CPU, state);
}