mirror of
https://github.com/ariejan/i6502.git
synced 2024-10-31 23:09:44 +00:00
Merge pull request #4 from ariejan/issue_4_code_cleanup
Proper Golang packaging
This commit is contained in:
commit
36e18e5730
@ -1,36 +0,0 @@
|
|||||||
package i6502
|
|
||||||
|
|
||||||
// Addressing modes
|
|
||||||
const (
|
|
||||||
_ = iota
|
|
||||||
absolute
|
|
||||||
absoluteX
|
|
||||||
absoluteY
|
|
||||||
accumulator
|
|
||||||
immediate
|
|
||||||
implied
|
|
||||||
indirect
|
|
||||||
indirectX
|
|
||||||
indirectY
|
|
||||||
relative
|
|
||||||
zeropage
|
|
||||||
zeropageX
|
|
||||||
zeropageY
|
|
||||||
)
|
|
||||||
|
|
||||||
var addressingNames = [...]string{
|
|
||||||
"",
|
|
||||||
"absolute",
|
|
||||||
"absoluteX",
|
|
||||||
"absoluteY",
|
|
||||||
"accumulator",
|
|
||||||
"immediate",
|
|
||||||
"implied",
|
|
||||||
"(indirect)",
|
|
||||||
"(indirect,X)",
|
|
||||||
"(indirect),Y",
|
|
||||||
"relative",
|
|
||||||
"zeropage",
|
|
||||||
"zeropageX",
|
|
||||||
"zeropageY",
|
|
||||||
}
|
|
316
cpu.go
316
cpu.go
@ -3,8 +3,6 @@ package i6502
|
|||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
type Cpu struct {
|
type Cpu struct {
|
||||||
bus *AddressBus // The address bus
|
|
||||||
|
|
||||||
PC uint16 // 16-bit program counter
|
PC uint16 // 16-bit program counter
|
||||||
P byte // Status Register
|
P byte // Status Register
|
||||||
SP byte // Stack Pointer
|
SP byte // Stack Pointer
|
||||||
@ -12,16 +10,20 @@ type Cpu struct {
|
|||||||
A byte // Accumulator
|
A byte // Accumulator
|
||||||
X byte // X index register
|
X byte // X index register
|
||||||
Y byte // Y index register
|
Y byte // Y index register
|
||||||
|
|
||||||
|
Bus *AddressBus // The address bus
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ResetVector = 0xFFFC // 0xFFFC-FFFD
|
ResetVector = 0xFFFC // 0xFFFC-FFFD
|
||||||
IrqVector = 0xFFFE // 0xFFFE-FFFF
|
IrqVector = 0xFFFE // 0xFFFE-FFFF
|
||||||
|
|
||||||
|
StackBase = 0x0100 // One page 0x0100-01FF
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create an new Cpu instance with the specified AddressBus
|
// Create an new Cpu instance with the specified AddressBus
|
||||||
func NewCpu(bus *AddressBus) (*Cpu, error) {
|
func NewCpu(bus *AddressBus) (*Cpu, error) {
|
||||||
return &Cpu{bus: bus}, nil
|
return &Cpu{Bus: bus}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cpu) String() string {
|
func (c *Cpu) String() string {
|
||||||
@ -29,13 +31,13 @@ func (c *Cpu) String() string {
|
|||||||
return fmt.Sprintf(str, c.A, c.X, c.Y, c.SP, c.PC, c.P)
|
return fmt.Sprintf(str, c.A, c.X, c.Y, c.SP, c.PC, c.P)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cpu) HasAddressBus() bool {
|
func (c *Cpu) hasAddressBus() bool {
|
||||||
return c.bus != nil
|
return c.Bus != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the CPU, emulating the RESB pin.
|
// Reset the CPU, emulating the RESB pin.
|
||||||
func (c *Cpu) Reset() {
|
func (c *Cpu) Reset() {
|
||||||
c.PC = c.bus.Read16(ResetVector)
|
c.PC = c.Bus.Read16(ResetVector)
|
||||||
c.P = 0x34
|
c.P = 0x34
|
||||||
|
|
||||||
// Not specified, but let's clean up
|
// Not specified, but let's clean up
|
||||||
@ -57,14 +59,14 @@ func (c *Cpu) handleIrq(PC uint16) {
|
|||||||
|
|
||||||
c.setIrqDisable(true)
|
c.setIrqDisable(true)
|
||||||
|
|
||||||
c.PC = c.bus.Read16(IrqVector)
|
c.PC = c.Bus.Read16(IrqVector)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the specified program data at the given memory location
|
// 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) {
|
func (c *Cpu) LoadProgram(data []byte, location uint16) {
|
||||||
for i, b := range data {
|
for i, b := range data {
|
||||||
c.bus.Write(location+uint16(i), b)
|
c.Bus.Write(location+uint16(i), b)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.PC = location
|
c.PC = location
|
||||||
@ -72,10 +74,8 @@ func (c *Cpu) LoadProgram(data []byte, location uint16) {
|
|||||||
|
|
||||||
// Execute the instruction pointed to by the Program Counter (PC)
|
// Execute the instruction pointed to by the Program Counter (PC)
|
||||||
func (c *Cpu) Step() {
|
func (c *Cpu) Step() {
|
||||||
// fmt.Println(c)
|
|
||||||
instruction := c.readNextInstruction()
|
instruction := c.readNextInstruction()
|
||||||
c.PC += uint16(instruction.Size)
|
c.PC += uint16(instruction.Size)
|
||||||
// fmt.Println(instruction)
|
|
||||||
c.execute(instruction)
|
c.execute(instruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,9 +84,9 @@ func (c *Cpu) execute(instruction Instruction) {
|
|||||||
case nop:
|
case nop:
|
||||||
break
|
break
|
||||||
case adc:
|
case adc:
|
||||||
c.ADC(instruction)
|
c.adc(instruction)
|
||||||
case sbc:
|
case sbc:
|
||||||
c.SBC(instruction)
|
c.sbc(instruction)
|
||||||
case sec:
|
case sec:
|
||||||
c.setCarry(true)
|
c.setCarry(true)
|
||||||
case sed:
|
case sed:
|
||||||
@ -106,31 +106,40 @@ func (c *Cpu) execute(instruction Instruction) {
|
|||||||
case iny:
|
case iny:
|
||||||
c.setY(c.Y + 1)
|
c.setY(c.Y + 1)
|
||||||
case inc:
|
case inc:
|
||||||
c.INC(instruction)
|
c.inc(instruction)
|
||||||
case dex:
|
case dex:
|
||||||
c.setX(c.X - 1)
|
c.setX(c.X - 1)
|
||||||
case dey:
|
case dey:
|
||||||
c.setY(c.Y - 1)
|
c.setY(c.Y - 1)
|
||||||
case dec:
|
case dec:
|
||||||
c.DEC(instruction)
|
c.dec(instruction)
|
||||||
case lda:
|
case lda:
|
||||||
c.LDA(instruction)
|
value := c.resolveOperand(instruction)
|
||||||
|
c.setA(value)
|
||||||
case ldx:
|
case ldx:
|
||||||
c.LDX(instruction)
|
value := c.resolveOperand(instruction)
|
||||||
|
c.setX(value)
|
||||||
case ldy:
|
case ldy:
|
||||||
c.LDY(instruction)
|
value := c.resolveOperand(instruction)
|
||||||
|
c.setY(value)
|
||||||
case ora:
|
case ora:
|
||||||
c.ORA(instruction)
|
value := c.resolveOperand(instruction)
|
||||||
|
c.setA(c.A | value)
|
||||||
case and:
|
case and:
|
||||||
c.AND(instruction)
|
value := c.resolveOperand(instruction)
|
||||||
|
c.setA(c.A & value)
|
||||||
case eor:
|
case eor:
|
||||||
c.EOR(instruction)
|
value := c.resolveOperand(instruction)
|
||||||
|
c.setA(c.A ^ value)
|
||||||
case sta:
|
case sta:
|
||||||
c.STA(instruction)
|
address := c.memoryAddress(instruction)
|
||||||
|
c.Bus.Write(address, c.A)
|
||||||
case stx:
|
case stx:
|
||||||
c.STX(instruction)
|
address := c.memoryAddress(instruction)
|
||||||
|
c.Bus.Write(address, c.X)
|
||||||
case sty:
|
case sty:
|
||||||
c.STY(instruction)
|
address := c.memoryAddress(instruction)
|
||||||
|
c.Bus.Write(address, c.Y)
|
||||||
case tax:
|
case tax:
|
||||||
c.setX(c.A)
|
c.setX(c.A)
|
||||||
case tay:
|
case tay:
|
||||||
@ -144,21 +153,28 @@ func (c *Cpu) execute(instruction Instruction) {
|
|||||||
case txs:
|
case txs:
|
||||||
c.SP = c.X
|
c.SP = c.X
|
||||||
case asl:
|
case asl:
|
||||||
c.ASL(instruction)
|
c.asl(instruction)
|
||||||
case lsr:
|
case lsr:
|
||||||
c.LSR(instruction)
|
c.lsr(instruction)
|
||||||
case rol:
|
case rol:
|
||||||
c.ROL(instruction)
|
c.rol(instruction)
|
||||||
case ror:
|
case ror:
|
||||||
c.ROR(instruction)
|
c.ror(instruction)
|
||||||
case cmp:
|
case cmp:
|
||||||
c.CMP(instruction)
|
value := c.resolveOperand(instruction)
|
||||||
|
c.setCarry(c.A >= value)
|
||||||
|
c.setArithmeticFlags(c.A - value)
|
||||||
case cpx:
|
case cpx:
|
||||||
c.CPX(instruction)
|
value := c.resolveOperand(instruction)
|
||||||
|
c.setCarry(c.X >= value)
|
||||||
|
c.setArithmeticFlags(c.X - value)
|
||||||
case cpy:
|
case cpy:
|
||||||
c.CPY(instruction)
|
value := c.resolveOperand(instruction)
|
||||||
|
c.setCarry(c.Y >= value)
|
||||||
|
c.setArithmeticFlags(c.Y - value)
|
||||||
case brk:
|
case brk:
|
||||||
c.BRK()
|
c.setBreak(true)
|
||||||
|
c.handleIrq(c.PC + 1)
|
||||||
case bcc:
|
case bcc:
|
||||||
if !c.getCarry() {
|
if !c.getCarry() {
|
||||||
c.branch(instruction)
|
c.branch(instruction)
|
||||||
@ -192,7 +208,10 @@ func (c *Cpu) execute(instruction Instruction) {
|
|||||||
c.branch(instruction)
|
c.branch(instruction)
|
||||||
}
|
}
|
||||||
case bit:
|
case bit:
|
||||||
c.BIT(instruction)
|
value := c.resolveOperand(instruction)
|
||||||
|
c.setNegative((value & 0x80) != 0)
|
||||||
|
c.setOverflow((value & 0x40) != 0)
|
||||||
|
c.setZero((c.A & value) == 0)
|
||||||
case php:
|
case php:
|
||||||
c.stackPush(c.P | 0x30)
|
c.stackPush(c.P | 0x30)
|
||||||
case plp:
|
case plp:
|
||||||
@ -203,9 +222,11 @@ func (c *Cpu) execute(instruction Instruction) {
|
|||||||
value := c.stackPop()
|
value := c.stackPop()
|
||||||
c.setA(value)
|
c.setA(value)
|
||||||
case jmp:
|
case jmp:
|
||||||
c.JMP(instruction)
|
c.PC = c.memoryAddress(instruction)
|
||||||
case jsr:
|
case jsr:
|
||||||
c.JSR(instruction)
|
c.stackPush(byte((c.PC - 1) >> 8))
|
||||||
|
c.stackPush(byte(c.PC - 1))
|
||||||
|
c.PC = c.memoryAddress(instruction)
|
||||||
case rts:
|
case rts:
|
||||||
c.PC = (uint16(c.stackPop()) | uint16(c.stackPop())<<8) + 1
|
c.PC = (uint16(c.stackPop()) | uint16(c.stackPop())<<8) + 1
|
||||||
case rti:
|
case rti:
|
||||||
@ -216,6 +237,27 @@ func (c *Cpu) execute(instruction Instruction) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Cpu) readNextInstruction() Instruction {
|
||||||
|
// Read the opcode
|
||||||
|
opcode := c.Bus.Read(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.Read(c.PC + 1)
|
||||||
|
case 3: // 16-bit operand
|
||||||
|
instruction.Op16 = c.Bus.Read16(c.PC + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return instruction
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Cpu) branch(in Instruction) {
|
func (c *Cpu) branch(in Instruction) {
|
||||||
relative := int8(in.Op8) // Signed!
|
relative := int8(in.Op8) // Signed!
|
||||||
if relative >= 0 {
|
if relative >= 0 {
|
||||||
@ -230,7 +272,7 @@ func (c *Cpu) resolveOperand(in Instruction) uint8 {
|
|||||||
case immediate:
|
case immediate:
|
||||||
return in.Op8
|
return in.Op8
|
||||||
default:
|
default:
|
||||||
return c.bus.Read(c.memoryAddress(in))
|
return c.Bus.Read(c.memoryAddress(in))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,11 +285,11 @@ func (c *Cpu) memoryAddress(in Instruction) uint16 {
|
|||||||
case absoluteY:
|
case absoluteY:
|
||||||
return in.Op16 + uint16(c.Y)
|
return in.Op16 + uint16(c.Y)
|
||||||
case indirect:
|
case indirect:
|
||||||
return c.bus.Read16(in.Op16)
|
return c.Bus.Read16(in.Op16)
|
||||||
case indirectX:
|
case indirectX:
|
||||||
return c.bus.Read16(uint16(in.Op8 + c.X))
|
return c.Bus.Read16(uint16(in.Op8 + c.X))
|
||||||
case indirectY:
|
case indirectY:
|
||||||
return c.bus.Read16(uint16(in.Op8)) + uint16(c.Y)
|
return c.Bus.Read16(uint16(in.Op8)) + uint16(c.Y)
|
||||||
case relative:
|
case relative:
|
||||||
panic("Relative addressing not yet implemented.")
|
panic("Relative addressing not yet implemented.")
|
||||||
case zeropage:
|
case zeropage:
|
||||||
@ -260,3 +302,201 @@ func (c *Cpu) memoryAddress(in Instruction) uint16 {
|
|||||||
panic(fmt.Errorf("Unhandled addressing mode. Are you sure you are running a 6502 ROM?"))
|
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.Read(address) + 1
|
||||||
|
|
||||||
|
c.Bus.Write(address, value)
|
||||||
|
c.setArithmeticFlags(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cpu) dec(in Instruction) {
|
||||||
|
address := c.memoryAddress(in)
|
||||||
|
value := c.Bus.Read(address) - 1
|
||||||
|
|
||||||
|
c.Bus.Write(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.Read(address)
|
||||||
|
c.setCarry((value >> 7) == 1)
|
||||||
|
value <<= 1
|
||||||
|
c.Bus.Write(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.Read(address)
|
||||||
|
c.setCarry((value & 0x01) == 1)
|
||||||
|
value >>= 1
|
||||||
|
c.Bus.Write(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.Read(address)
|
||||||
|
c.setCarry((value & 0x80) != 0)
|
||||||
|
value = value<<1 | carry
|
||||||
|
c.Bus.Write(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.Read(address)
|
||||||
|
c.setCarry(value&0x01 == 1)
|
||||||
|
value = value>>1 | carry<<7
|
||||||
|
c.Bus.Write(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.Write(StackBase+uint16(c.SP), data)
|
||||||
|
c.SP -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cpu) stackPeek() byte {
|
||||||
|
return c.Bus.Read(StackBase + uint16(c.SP+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cpu) stackPop() byte {
|
||||||
|
c.SP += 1
|
||||||
|
return c.Bus.Read(StackBase + uint16(c.SP))
|
||||||
|
}
|
||||||
|
@ -1,271 +0,0 @@
|
|||||||
package i6502
|
|
||||||
|
|
||||||
// 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.Read(address) + 1
|
|
||||||
|
|
||||||
c.bus.Write(address, value)
|
|
||||||
c.setArithmeticFlags(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cpu) DEC(in Instruction) {
|
|
||||||
address := c.memoryAddress(in)
|
|
||||||
value := c.bus.Read(address) - 1
|
|
||||||
|
|
||||||
c.bus.Write(address, value)
|
|
||||||
c.setArithmeticFlags(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cpu) LDA(in Instruction) {
|
|
||||||
value := c.resolveOperand(in)
|
|
||||||
c.setA(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cpu) LDX(in Instruction) {
|
|
||||||
value := c.resolveOperand(in)
|
|
||||||
c.setX(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cpu) LDY(in Instruction) {
|
|
||||||
value := c.resolveOperand(in)
|
|
||||||
c.setY(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cpu) ORA(in Instruction) {
|
|
||||||
value := c.resolveOperand(in)
|
|
||||||
c.setA(c.A | value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cpu) AND(in Instruction) {
|
|
||||||
value := c.resolveOperand(in)
|
|
||||||
c.setA(c.A & value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cpu) EOR(in Instruction) {
|
|
||||||
value := c.resolveOperand(in)
|
|
||||||
c.setA(c.A ^ value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cpu) STA(in Instruction) {
|
|
||||||
address := c.memoryAddress(in)
|
|
||||||
c.bus.Write(address, c.A)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cpu) STX(in Instruction) {
|
|
||||||
address := c.memoryAddress(in)
|
|
||||||
c.bus.Write(address, c.X)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cpu) STY(in Instruction) {
|
|
||||||
address := c.memoryAddress(in)
|
|
||||||
c.bus.Write(address, c.Y)
|
|
||||||
}
|
|
||||||
|
|
||||||
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.Read(address)
|
|
||||||
c.setCarry((value >> 7) == 1)
|
|
||||||
value <<= 1
|
|
||||||
c.bus.Write(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.Read(address)
|
|
||||||
c.setCarry((value & 0x01) == 1)
|
|
||||||
value >>= 1
|
|
||||||
c.bus.Write(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.Read(address)
|
|
||||||
c.setCarry((value & 0x80) != 0)
|
|
||||||
value = value<<1 | carry
|
|
||||||
c.bus.Write(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.Read(address)
|
|
||||||
c.setCarry(value&0x01 == 1)
|
|
||||||
value = value>>1 | carry<<7
|
|
||||||
c.bus.Write(address, value)
|
|
||||||
c.setArithmeticFlags(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cpu) CMP(in Instruction) {
|
|
||||||
value := c.resolveOperand(in)
|
|
||||||
// fmt.Printf("CMP A: 0x%02X V: 0x%02X\n", c.A, value)
|
|
||||||
c.setCarry(c.A >= value)
|
|
||||||
c.setArithmeticFlags(c.A - value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cpu) CPX(in Instruction) {
|
|
||||||
value := c.resolveOperand(in)
|
|
||||||
c.setCarry(c.X >= value)
|
|
||||||
c.setArithmeticFlags(c.X - value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cpu) CPY(in Instruction) {
|
|
||||||
value := c.resolveOperand(in)
|
|
||||||
c.setCarry(c.Y >= value)
|
|
||||||
c.setArithmeticFlags(c.Y - value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cpu) BRK() {
|
|
||||||
c.setBreak(true)
|
|
||||||
c.handleIrq(c.PC + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cpu) BIT(in Instruction) {
|
|
||||||
value := c.resolveOperand(in)
|
|
||||||
c.setNegative((value & 0x80) != 0)
|
|
||||||
c.setOverflow((value & 0x40) != 0)
|
|
||||||
c.setZero((c.A & value) == 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cpu) JMP(in Instruction) {
|
|
||||||
c.PC = c.memoryAddress(in)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cpu) JSR(in Instruction) {
|
|
||||||
c.stackPush(byte((c.PC - 1) >> 8))
|
|
||||||
c.stackPush(byte(c.PC - 1))
|
|
||||||
c.PC = c.memoryAddress(in)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
19
cpu_stack.go
19
cpu_stack.go
@ -1,19 +0,0 @@
|
|||||||
package i6502
|
|
||||||
|
|
||||||
const (
|
|
||||||
StackBase = 0x0100
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Cpu) stackPush(data byte) {
|
|
||||||
c.bus.Write(StackBase+uint16(c.SP), data)
|
|
||||||
c.SP -= 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cpu) stackPeek() byte {
|
|
||||||
return c.bus.Read(StackBase + uint16(c.SP+1))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cpu) stackPop() byte {
|
|
||||||
c.SP += 1
|
|
||||||
return c.bus.Read(StackBase + uint16(c.SP))
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
package i6502
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStackPushPopPeek(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
cpu, _, _ := NewRamMachine()
|
|
||||||
|
|
||||||
assert.Equal(0xFF, cpu.SP)
|
|
||||||
|
|
||||||
cpu.stackPush(0x42)
|
|
||||||
cpu.stackPush(0xA0)
|
|
||||||
|
|
||||||
assert.Equal(0xFD, cpu.SP)
|
|
||||||
assert.Equal(0x42, cpu.bus.Read(0x1FF))
|
|
||||||
assert.Equal(0xA0, cpu.bus.Read(0x1FE))
|
|
||||||
|
|
||||||
peekValue := cpu.stackPeek()
|
|
||||||
assert.Equal(0xFD, cpu.SP)
|
|
||||||
assert.Equal(0xA0, peekValue)
|
|
||||||
|
|
||||||
popValue := cpu.stackPop()
|
|
||||||
assert.Equal(0xFE, cpu.SP)
|
|
||||||
assert.Equal(0xA0, popValue)
|
|
||||||
}
|
|
445
cpu_test.go
445
cpu_test.go
File diff suppressed because it is too large
Load Diff
@ -30,24 +30,3 @@ func (i Instruction) String() (output string) {
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cpu) readNextInstruction() Instruction {
|
|
||||||
// Read the opcode
|
|
||||||
opcode := c.bus.Read(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.Read(c.PC + 1)
|
|
||||||
case 3: // 16-bit operand
|
|
||||||
instruction.Op16 = c.bus.Read16(c.PC + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return instruction
|
|
||||||
}
|
|
||||||
|
35
opcodes.go
35
opcodes.go
@ -1,5 +1,40 @@
|
|||||||
package i6502
|
package i6502
|
||||||
|
|
||||||
|
// Addressing modes
|
||||||
|
const (
|
||||||
|
_ = iota
|
||||||
|
absolute
|
||||||
|
absoluteX
|
||||||
|
absoluteY
|
||||||
|
accumulator
|
||||||
|
immediate
|
||||||
|
implied
|
||||||
|
indirect
|
||||||
|
indirectX
|
||||||
|
indirectY
|
||||||
|
relative
|
||||||
|
zeropage
|
||||||
|
zeropageX
|
||||||
|
zeropageY
|
||||||
|
)
|
||||||
|
|
||||||
|
var addressingNames = [...]string{
|
||||||
|
"",
|
||||||
|
"absolute",
|
||||||
|
"absoluteX",
|
||||||
|
"absoluteY",
|
||||||
|
"accumulator",
|
||||||
|
"immediate",
|
||||||
|
"implied",
|
||||||
|
"(indirect)",
|
||||||
|
"(indirect,X)",
|
||||||
|
"(indirect),Y",
|
||||||
|
"relative",
|
||||||
|
"zeropage",
|
||||||
|
"zeropageX",
|
||||||
|
"zeropageY",
|
||||||
|
}
|
||||||
|
|
||||||
// OpCode table
|
// OpCode table
|
||||||
const (
|
const (
|
||||||
_ = iota
|
_ = iota
|
||||||
|
Loading…
Reference in New Issue
Block a user