1
0
mirror of https://github.com/ariejan/i6502.git synced 2024-05-28 07:41:32 +00:00
i6502/cpu.go

263 lines
4.9 KiB
Go
Raw Normal View History

2014-08-12 19:28:30 +00:00
package i6502
2014-08-13 11:51:53 +00:00
import "fmt"
2014-08-12 19:28:30 +00:00
type Cpu struct {
2014-08-13 07:26:33 +00:00
bus *AddressBus // The address bus
PC uint16 // 16-bit program counter
P byte // Status Register
SP byte // Stack Pointer
A byte // Accumulator
X byte // X index register
Y byte // Y index register
2014-08-13 07:26:33 +00:00
}
const (
2014-08-13 09:49:49 +00:00
ResetVector = 0xFFFC // 0xFFFC-FFFD
2014-08-16 08:35:00 +00:00
IrqVector = 0xFFFE // 0xFFFE-FFFF
2014-08-13 07:26:33 +00:00
)
2014-08-13 09:49:49 +00:00
// Create an new Cpu instance with the specified AddressBus
2014-08-13 07:26:33 +00:00
func NewCpu(bus *AddressBus) (*Cpu, error) {
return &Cpu{bus: bus}, nil
}
2014-08-16 08:35:00 +00:00
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)
}
2014-08-13 07:26:33 +00:00
func (c *Cpu) HasAddressBus() bool {
return c.bus != nil
2014-08-12 19:28:30 +00:00
}
2014-08-13 09:49:49 +00:00
// Reset the CPU, emulating the RESB pin.
2014-08-13 07:26:33 +00:00
func (c *Cpu) Reset() {
c.PC = c.bus.Read16(ResetVector)
c.P = 0x34
2014-08-16 08:35:00 +00:00
// Not specified, but let's clean up
c.A = 0x00
c.X = 0x00
c.Y = 0x00
c.SP = 0xFF
}
// Simulate the IRQ pin
func (c *Cpu) Interrupt() {
c.handleIrq(c.PC)
}
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)
2014-08-12 19:28:30 +00:00
}
2014-08-13 09:49:49 +00:00
// 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.Write(location+uint16(i), b)
}
c.PC = location
}
2014-08-13 11:51:53 +00:00
// Execute the instruction pointed to by the Program Counter (PC)
func (c *Cpu) Step() {
2014-08-16 08:35:00 +00:00
// fmt.Println(c)
2014-08-13 11:51:53 +00:00
instruction := c.readNextInstruction()
c.PC += uint16(instruction.Size)
// fmt.Println(instruction)
2014-08-13 11:51:53 +00:00
c.execute(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:
2014-08-16 08:35:00 +00:00
c.setIrqDisable(true)
case clc:
c.setCarry(false)
case cld:
c.setDecimal(false)
case cli:
2014-08-16 08:35:00 +00:00
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:
c.LDA(instruction)
case ldx:
c.LDX(instruction)
case ldy:
c.LDY(instruction)
case ora:
c.ORA(instruction)
case and:
c.AND(instruction)
case eor:
c.EOR(instruction)
case sta:
c.STA(instruction)
case stx:
c.STX(instruction)
case sty:
c.STY(instruction)
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:
2014-08-16 08:35:00 +00:00
c.SP = c.X
case asl:
c.ASL(instruction)
case lsr:
c.LSR(instruction)
2014-08-16 08:35:00 +00:00
case rol:
c.ROL(instruction)
case ror:
c.ROR(instruction)
case cmp:
c.CMP(instruction)
case cpx:
c.CPX(instruction)
case cpy:
c.CPY(instruction)
case brk:
c.BRK()
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:
c.BIT(instruction)
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.JMP(instruction)
case jsr:
c.JSR(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))
}
}
2014-08-16 08:35:00 +00:00
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.Read(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?"))
}
2014-08-13 11:51:53 +00:00
}