i6502/cpu.go

534 lines
12 KiB
Go

package i6502
import "fmt"
/*
The Cpu only contains the AddressBus, through which 8-bit values can be read and written
at 16-bit addresses.
The Cpu has an 8-bit accumulator (A) and two 8-bit index registers (X,Y). There is a 16-bit
Program Counter (PC) and an 8-bit Stack Pointer (SP), pointing to addresses in 0x0100-01FF.
The status register (P) contains flags for Zero, Negative, Break, Decimal, IrqDisable,
Carry and Overflow flags.
*/
type Cpu struct {
A byte // Accumulator
X byte // Index register X
Y byte // Index register Y
PC uint16 // 16-bit program counter
P byte // Status Register
SP byte // Stack Pointer
Bus *AddressBus // The address bus
}
const (
ZeropageBase = 0x0000 // 0x0000-00FF Reserved for zeropage instructions
StackBase = 0x0100 // 0x0100-01FF Reserved for stack
ResetVector = 0xFFFC // 0xFFFC-FFFD
IrqVector = 0xFFFE // 0xFFFE-FFFF
)
// Create an new Cpu, using the AddressBus for accessing memory.
func NewCpu(bus *AddressBus) (*Cpu, error) {
return &Cpu{Bus: bus}, nil
}
// Returns a string containing the current state of the CPU.
func (c *Cpu) String() string {
str := ">>> CPU [ A ] [ X ] [ Y ] [ SP ] [ PC ] NVxBDIZC\n>>> 0x%02X 0x%02X 0x%02X 0x%02X 0x%04X %08b\n"
return fmt.Sprintf(str, c.A, c.X, c.Y, c.SP, c.PC, c.P)
}
/*
Reset the CPU, emulating the RESB pin.
The status register is reset to a know state (0x34, IrqDisabled set, Decimal unset, Break set).
Then the Program Counter is set to the value read from `ResetVector` (0xFFFC-FFFD).
Normally, no assumptions can be made about registers (A, X, Y) and the
Stack Pointer. For convenience, these are reset to 0x00 (A,X,Y) and 0xFF (SP).
*/
func (c *Cpu) Reset() {
c.PC = c.Bus.Read16(ResetVector)
c.P = 0x34
// Not specified, but let's clean up
c.A = 0x00
c.X = 0x00
c.Y = 0x00
c.SP = 0xFF
}
/*
Simulate the IRQ pin.
This will push the current Cpu state to the stack (P + PC) and set the PC
to the address read from the `IrqVector` (0xFFFE-FFFF)
*/
func (c *Cpu) Interrupt() {
c.handleIrq(c.PC)
}
// Handles an interrupt or BRK.
func (c *Cpu) handleIrq(PC uint16) {
c.stackPush(byte(PC >> 8))
c.stackPush(byte(PC))
c.stackPush(c.P)
c.setIrqDisable(true)
c.PC = c.Bus.Read16(IrqVector)
}
// Load the specified program data at the given memory location
// and point the Program Counter to the beginning of the program.
func (c *Cpu) LoadProgram(data []byte, location uint16) {
for i, b := range data {
c.Bus.WriteByte(location+uint16(i), b)
}
c.PC = location
}
func (c *Cpu) Steps(steps int) {
for i := 0; i < steps; i++ {
c.Step()
}
}
// Read and execute the instruction pointed to by the Program Counter (PC)
func (c *Cpu) Step() {
instruction := c.readNextInstruction()
c.PC += uint16(instruction.Size)
c.execute(instruction)
}
// Handle the execution of an instruction
func (c *Cpu) execute(instruction Instruction) {
switch instruction.opcodeId {
case nop:
break
case adc:
c.adc(instruction)
case sbc:
c.sbc(instruction)
case sec:
c.setCarry(true)
case sed:
c.setDecimal(true)
case sei:
c.setIrqDisable(true)
case clc:
c.setCarry(false)
case cld:
c.setDecimal(false)
case cli:
c.setIrqDisable(false)
case clv:
c.setOverflow(false)
case inx:
c.setX(c.X + 1)
case iny:
c.setY(c.Y + 1)
case inc:
c.inc(instruction)
case dex:
c.setX(c.X - 1)
case dey:
c.setY(c.Y - 1)
case dec:
c.dec(instruction)
case lda:
value := c.resolveOperand(instruction)
c.setA(value)
case ldx:
value := c.resolveOperand(instruction)
c.setX(value)
case ldy:
value := c.resolveOperand(instruction)
c.setY(value)
case ora:
value := c.resolveOperand(instruction)
c.setA(c.A | value)
case and:
value := c.resolveOperand(instruction)
c.setA(c.A & value)
case eor:
value := c.resolveOperand(instruction)
c.setA(c.A ^ value)
case sta:
address := c.memoryAddress(instruction)
c.Bus.WriteByte(address, c.A)
case stx:
address := c.memoryAddress(instruction)
c.Bus.WriteByte(address, c.X)
case sty:
address := c.memoryAddress(instruction)
c.Bus.WriteByte(address, c.Y)
case tax:
c.setX(c.A)
case tay:
c.setY(c.A)
case txa:
c.setA(c.X)
case tya:
c.setA(c.Y)
case tsx:
c.setX(c.SP)
case txs:
c.SP = c.X
case asl:
c.asl(instruction)
case lsr:
c.lsr(instruction)
case rol:
c.rol(instruction)
case ror:
c.ror(instruction)
case cmp:
value := c.resolveOperand(instruction)
c.setCarry(c.A >= value)
c.setArithmeticFlags(c.A - value)
case cpx:
value := c.resolveOperand(instruction)
c.setCarry(c.X >= value)
c.setArithmeticFlags(c.X - value)
case cpy:
value := c.resolveOperand(instruction)
c.setCarry(c.Y >= value)
c.setArithmeticFlags(c.Y - value)
case brk:
c.setBreak(true)
c.handleIrq(c.PC + 1)
case bcc:
if !c.getCarry() {
c.branch(instruction)
}
case bcs:
if c.getCarry() {
c.branch(instruction)
}
case bne:
if !c.getZero() {
c.branch(instruction)
}
case beq:
if c.getZero() {
c.branch(instruction)
}
case bpl:
if !c.getNegative() {
c.branch(instruction)
}
case bmi:
if c.getNegative() {
c.branch(instruction)
}
case bvc:
if !c.getOverflow() {
c.branch(instruction)
}
case bvs:
if c.getOverflow() {
c.branch(instruction)
}
case bit:
value := c.resolveOperand(instruction)
c.setNegative((value & 0x80) != 0)
c.setOverflow((value & 0x40) != 0)
c.setZero((c.A & value) == 0)
case php:
c.stackPush(c.P | 0x30)
case plp:
c.setP(c.stackPop())
case pha:
c.stackPush(c.A)
case pla:
value := c.stackPop()
c.setA(value)
case jmp:
c.PC = c.memoryAddress(instruction)
case jsr:
c.stackPush(byte((c.PC - 1) >> 8))
c.stackPush(byte(c.PC - 1))
c.PC = c.memoryAddress(instruction)
case rts:
c.PC = (uint16(c.stackPop()) | uint16(c.stackPop())<<8) + 1
case rti:
c.setP(c.stackPop())
c.PC = uint16(c.stackPop()) | uint16(c.stackPop())<<8
default:
panic(fmt.Errorf("Unimplemented instruction: %s", instruction))
}
}
func (c *Cpu) readNextInstruction() Instruction {
// Read the opcode
opcode := c.Bus.ReadByte(c.PC)
optype, ok := opTypes[opcode]
if !ok {
panic(fmt.Sprintf("Unknown or unimplemented opcode 0x%02X\n%s", opcode, c.String()))
}
instruction := Instruction{OpType: optype, Address: c.PC}
switch instruction.Size {
case 1: // Zero operand instruction
case 2: // 8-bit operand
instruction.Op8 = c.Bus.ReadByte(c.PC + 1)
case 3: // 16-bit operand
instruction.Op16 = c.Bus.Read16(c.PC + 1)
}
return instruction
}
func (c *Cpu) branch(in Instruction) {
relative := int8(in.Op8) // Signed!
if relative >= 0 {
c.PC += uint16(relative)
} else {
c.PC -= -uint16(relative)
}
}
func (c *Cpu) resolveOperand(in Instruction) uint8 {
switch in.addressingId {
case immediate:
return in.Op8
default:
return c.Bus.ReadByte(c.memoryAddress(in))
}
}
func (c *Cpu) memoryAddress(in Instruction) uint16 {
switch in.addressingId {
case absolute:
return in.Op16
case absoluteX:
return in.Op16 + uint16(c.X)
case absoluteY:
return in.Op16 + uint16(c.Y)
case indirect:
return c.Bus.Read16(in.Op16)
case indirectX:
return c.Bus.Read16(uint16(in.Op8 + c.X))
case indirectY:
return c.Bus.Read16(uint16(in.Op8)) + uint16(c.Y)
case relative:
panic("Relative addressing not yet implemented.")
case zeropage:
return uint16(in.Op8)
case zeropageX:
return uint16(in.Op8 + c.X)
case zeropageY:
return uint16(in.Op8 + c.Y)
default:
panic(fmt.Errorf("Unhandled addressing mode. Are you sure you are running a 6502 ROM?"))
}
}
// Add Memory to Accumulator with Carry
func (c *Cpu) adc(in Instruction) {
operand := c.resolveOperand(in)
carryIn := c.getCarryInt()
if c.getDecimal() {
c.adcDecimal(c.A, operand, carryIn)
} else {
c.adcNormal(c.A, operand, carryIn)
}
}
// Substract memory from Accummulator with carry
func (c *Cpu) sbc(in Instruction) {
operand := c.resolveOperand(in)
carryIn := c.getCarryInt()
// fmt.Printf("SBC: A: 0x%02X V: 0x%02X C: %b D: %v\n", c.A, operand, carryIn, c.getDecimal())
if c.getDecimal() {
c.sbcDecimal(c.A, operand, carryIn)
} else {
c.adcNormal(c.A, ^operand, carryIn)
}
}
func (c *Cpu) inc(in Instruction) {
address := c.memoryAddress(in)
value := c.Bus.ReadByte(address) + 1
c.Bus.WriteByte(address, value)
c.setArithmeticFlags(value)
}
func (c *Cpu) dec(in Instruction) {
address := c.memoryAddress(in)
value := c.Bus.ReadByte(address) - 1
c.Bus.WriteByte(address, value)
c.setArithmeticFlags(value)
}
func (c *Cpu) asl(in Instruction) {
switch in.addressingId {
case accumulator:
c.setCarry((c.A >> 7) == 1)
c.A <<= 1
c.setArithmeticFlags(c.A)
default:
address := c.memoryAddress(in)
value := c.Bus.ReadByte(address)
c.setCarry((value >> 7) == 1)
value <<= 1
c.Bus.WriteByte(address, value)
c.setArithmeticFlags(value)
}
}
func (c *Cpu) lsr(in Instruction) {
switch in.addressingId {
case accumulator:
c.setCarry((c.A & 0x01) == 1)
c.A >>= 1
c.setArithmeticFlags(c.A)
default:
address := c.memoryAddress(in)
value := c.Bus.ReadByte(address)
c.setCarry((value & 0x01) == 1)
value >>= 1
c.Bus.WriteByte(address, value)
c.setArithmeticFlags(value)
}
}
func (c *Cpu) rol(in Instruction) {
carry := c.getCarryInt()
switch in.addressingId {
case accumulator:
c.setCarry((c.A & 0x80) != 0)
c.A = c.A<<1 | carry
c.setArithmeticFlags(c.A)
default:
address := c.memoryAddress(in)
value := c.Bus.ReadByte(address)
c.setCarry((value & 0x80) != 0)
value = value<<1 | carry
c.Bus.WriteByte(address, value)
c.setArithmeticFlags(value)
}
}
func (c *Cpu) ror(in Instruction) {
carry := c.getCarryInt()
switch in.addressingId {
case accumulator:
c.setCarry(c.A&0x01 == 1)
c.A = c.A>>1 | carry<<7
c.setArithmeticFlags(c.A)
default:
address := c.memoryAddress(in)
value := c.Bus.ReadByte(address)
c.setCarry(value&0x01 == 1)
value = value>>1 | carry<<7
c.Bus.WriteByte(address, value)
c.setArithmeticFlags(value)
}
}
// Performs regular, 8-bit addition
func (c *Cpu) adcNormal(a uint8, b uint8, carryIn uint8) {
result16 := uint16(a) + uint16(b) + uint16(carryIn)
result := uint8(result16)
carryOut := (result16 & 0x100) != 0
overflow := (a^result)&(b^result)&0x80 != 0
// Set the carry flag if we exceed 8-bits
c.setCarry(carryOut)
// Set the overflow bit
c.setOverflow(overflow)
// Store the resulting value (8-bits)
c.setA(result)
}
// Performs addition in decimal mode
func (c *Cpu) adcDecimal(a uint8, b uint8, carryIn uint8) {
var carryB uint8 = 0
low := (a & 0x0F) + (b & 0x0F) + carryIn
if (low & 0xFF) > 9 {
low += 6
}
if low > 15 {
carryB = 1
}
high := (a >> 4) + (b >> 4) + carryB
if (high & 0xFF) > 9 {
high += 6
}
result := (low & 0x0F) | (high<<4)&0xF0
c.setCarry(high > 15)
c.setZero(result == 0)
c.setNegative(false) // BCD never sets negative
c.setOverflow(false) // BCD never sets overflow
c.A = result
}
func (c *Cpu) sbcDecimal(a uint8, b uint8, carryIn uint8) {
var carryB uint8 = 0
if carryIn == 0 {
carryIn = 1
} else {
carryIn = 0
}
low := (a & 0x0F) - (b & 0x0F) - carryIn
if (low & 0x10) != 0 {
low -= 6
}
if (low & 0x10) != 0 {
carryB = 1
}
high := (a >> 4) - (b >> 4) - carryB
if (high & 0x10) != 0 {
high -= 6
}
result := (low & 0x0F) | (high << 4)
c.setCarry((high & 0xFF) < 15)
c.setZero(result == 0)
c.setNegative(false) // BCD never sets negative
c.setOverflow(false) // BCD never sets overflow
c.A = result
}
func (c *Cpu) stackPush(data byte) {
c.Bus.WriteByte(StackBase+uint16(c.SP), data)
c.SP -= 1
}
func (c *Cpu) stackPeek() byte {
return c.Bus.ReadByte(StackBase + uint16(c.SP+1))
}
func (c *Cpu) stackPop() byte {
c.SP += 1
return c.Bus.ReadByte(StackBase + uint16(c.SP))
}