diff --git a/cpu.go b/cpu.go index ccbbd17..a53d5cf 100644 --- a/cpu.go +++ b/cpu.go @@ -2,15 +2,25 @@ 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 - A byte // Accumulator - X byte // X index register - Y byte // Y index register - Bus *AddressBus // The address bus } @@ -23,21 +33,27 @@ const ( ) -// Create an new Cpu instance with the specified AddressBus +// 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) } -func (c *Cpu) hasAddressBus() bool { - return c.Bus != nil -} +/* +Reset the CPU, emulating the RESB pin. -// 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 @@ -49,11 +65,17 @@ func (c *Cpu) Reset() { c.SP = 0xFF } -// Simulate the IRQ pin +/* +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)) @@ -65,7 +87,7 @@ func (c *Cpu) handleIrq(PC uint16) { } // Load the specified program data at the given memory location -// and point the Program Counter to the beginning of the program +// 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) @@ -74,13 +96,14 @@ func (c *Cpu) LoadProgram(data []byte, location uint16) { c.PC = location } -// Execute the instruction pointed to by the Program Counter (PC) +// 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: diff --git a/cpu_test.go b/cpu_test.go index 0bf88d3..f4bfccc 100644 --- a/cpu_test.go +++ b/cpu_test.go @@ -76,6 +76,9 @@ func TestCpuReset(t *testing.T) { // **1101** is specified, but we are satisfied with // 00110100 here. assert.Equal(0x34, cpu.P) + assert.True(cpu.getIrqDisable()) + assert.False(cpu.getDecimal()) + assert.True(cpu.getBreak()) // Read PC from $FFFC-FFFD assert.Equal(0x1234, cpu.PC) diff --git a/instruction.go b/instruction.go index f4ae53d..1d714df 100644 --- a/instruction.go +++ b/instruction.go @@ -5,19 +5,15 @@ import ( ) type Instruction struct { - // Embed OpType - OpType + OpType // Embed OpType - // 8-bit operand for 2-byte instructions - Op8 byte + Op8 byte // 8-bit operand for 2-byte instructions + Op16 uint16 // 16-bit operand for 3-byte instructions - // 16-bit operand for 3-byte instructions - Op16 uint16 - - // Address location where this instruction got read - Address uint16 + Address uint16 // Address location where this instruction got read, for debugging purposes } +// Return a string containing debug information about the instruction and operands. func (i Instruction) String() (output string) { switch i.Size { case 1: diff --git a/memory.go b/memory.go index 84bd5ee..d55c259 100644 --- a/memory.go +++ b/memory.go @@ -1,5 +1,9 @@ package i6502 +/* +Anything implementing the Memory interface can be attached to the AddressBus +and become accessible by the Cpu. +*/ type Memory interface { Size() uint16 Read(address uint16) byte diff --git a/opcodes.go b/opcodes.go index 3cbfab9..f14a257 100644 --- a/opcodes.go +++ b/opcodes.go @@ -160,20 +160,12 @@ var instructionNames = [...]string{ // addressing mode. It also includes some extra information for the // emulator, like number of cycles type OpType struct { - // The actual Opcode byte read from memory - Opcode byte + Opcode byte // 65(C)02 Opcode, this includes an instruction and addressing mode + Size uint8 // Size of the entire instruction in bytes + Cycles uint8 // Number of clock cycles required to complete this instruction - // Opcode ID - opcodeId uint8 - - // Addressing Mode ID - addressingId uint8 - - // Size of this instruction, either 1, 2 or 3 bytes - Size uint8 - - // Number of clock cycles this instruction needs - Cycles uint8 + opcodeId uint8 // Decoded opcode Id, + addressingId uint8 // Decoded address mode Id } var opTypes = map[uint8]OpType{ diff --git a/ram.go b/ram.go index 8b3ea29..7ceabbb 100644 --- a/ram.go +++ b/ram.go @@ -1,9 +1,13 @@ package i6502 +/* +Random Access Memory, read/write, can be of any size. +*/ type Ram struct { data []byte } +// Create a new Ram component of the given size. func NewRam(size int) (*Ram, error) { return &Ram{data: make([]byte, size)}, nil }