izapple2/core6502/execute.go

141 lines
3.4 KiB
Go
Raw Normal View History

package core6502
2019-01-26 16:05:51 +00:00
import (
"encoding/binary"
"fmt"
"io"
)
2019-02-09 23:15:14 +00:00
// https://www.masswerk.at/6502/6502_instruction_set.html
// http://www.emulator101.com/reference/6502-reference.html
// https://www.csh.rit.edu/~moffitt/docs/6502.html#FLAGS
// https://ia800509.us.archive.org/18/items/Programming_the_6502/Programming_the_6502.pdf
const (
maxInstructionSize = 3
)
// State represents the state of the simulated device
type State struct {
reg registers
mem Memory
cycles uint64
opcodes *[256]opcode
trace bool
lineCache []uint8
// We cache the allocation of a line to avoid a malloc per instruction. To be used only
// by ExecuteInstruction(). 2x speedup on the emulation!!
2019-01-26 16:05:51 +00:00
}
const (
vectorNMI uint16 = 0xfffa
vectorReset uint16 = 0xfffc
vectorBreak uint16 = 0xfffe
)
2019-01-28 23:06:15 +00:00
type opcode struct {
name string
bytes uint16
cycles int
addressMode int
action opFunc
2019-01-28 23:06:15 +00:00
}
type opFunc func(s *State, line []uint8, opcode opcode)
2019-01-28 23:06:15 +00:00
func (s *State) executeLine(line []uint8) {
opcode := s.opcodes[line[0]]
if opcode.cycles == 0 {
panic(fmt.Sprintf("Unknown opcode 0x%02x\n", line[0]))
}
2019-01-26 17:57:03 +00:00
opcode.action(s, line, opcode)
}
2019-02-09 23:15:14 +00:00
// ExecuteInstruction transforms the state given after a single instruction is executed.
func (s *State) ExecuteInstruction() {
pc := s.reg.getPC()
opcodeID := s.mem.PeekCode(pc)
opcode := s.opcodes[opcodeID]
if opcode.cycles == 0 {
panic(fmt.Sprintf("Unknown opcode 0x%02x\n", opcodeID))
}
if s.lineCache == nil {
s.lineCache = make([]uint8, maxInstructionSize)
}
for i := uint16(0); i < opcode.bytes; i++ {
s.lineCache[i] = s.mem.PeekCode(pc)
pc++
}
s.reg.setPC(pc)
if s.trace {
//fmt.Printf("%#04x %#02x\n", pc-opcode.bytes, opcodeID)
fmt.Printf("%#04x %-13s: ", pc-opcode.bytes, lineString(s.lineCache, opcode))
}
opcode.action(s, s.lineCache, opcode)
2019-05-09 22:09:15 +00:00
s.cycles += uint64(opcode.cycles)
if s.trace {
2020-04-02 21:52:23 +00:00
fmt.Printf("%v, [%02x]\n", s.reg, s.lineCache[0:opcode.bytes])
}
2019-02-10 15:25:03 +00:00
}
// Reset resets the processor. Moves the program counter to the vector in 0cfffc.
func (s *State) Reset() {
startAddress := getWord(s.mem, vectorReset)
s.cycles += 6
s.reg.setPC(startAddress)
}
2019-05-04 17:49:11 +00:00
// GetCycles returns the count of CPU cycles since last reset.
2019-05-09 22:09:15 +00:00
func (s *State) GetCycles() uint64 {
2019-05-04 17:49:11 +00:00
return s.cycles
}
// SetTrace activates tracing of the cpu execution
func (s *State) SetTrace(trace bool) {
s.trace = trace
}
// GetTrace gets trhe tracing state of the cpu execution
func (s *State) GetTrace() bool {
return s.trace
}
2020-06-07 16:23:39 +00:00
// GetPCAndSP returns the current program counter and stack pointer. Used to trace MLI calls
func (s *State) GetPCAndSP() (uint16, uint8) {
return s.reg.getPC(), s.reg.getSP()
}
// GetCarryAndAcc returns the value of te carry flag and the accumulator. Used to trace MLI calls
func (s *State) GetCarryAndAcc() (bool, uint8) {
return s.reg.getFlag(flagC), s.reg.getA()
}
// Save saves the CPU state (registers and cycle counter)
2019-10-05 23:26:00 +00:00
func (s *State) Save(w io.Writer) error {
err := binary.Write(w, binary.BigEndian, s.cycles)
if err != nil {
return err
}
binary.Write(w, binary.BigEndian, s.reg.data)
2019-10-05 23:26:00 +00:00
if err != nil {
return err
}
return nil
}
// Load loads the CPU state (registers and cycle counter)
2019-10-05 23:26:00 +00:00
func (s *State) Load(r io.Reader) error {
err := binary.Read(r, binary.BigEndian, &s.cycles)
if err != nil {
return err
}
err = binary.Read(r, binary.BigEndian, &s.reg.data)
if err != nil {
return err
}
return nil
}