2019-02-16 19:15:41 +00:00
|
|
|
package core6502
|
2019-01-26 16:05:51 +00:00
|
|
|
|
2019-05-17 21:28:20 +00:00
|
|
|
import (
|
|
|
|
"encoding/binary"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
)
|
2019-02-09 23:15:14 +00:00
|
|
|
|
2019-02-28 22:54:38 +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
|
|
|
|
|
2019-02-16 19:15:41 +00:00
|
|
|
// State represents the state of the simulated device
|
|
|
|
type State struct {
|
2019-02-28 22:54:38 +00:00
|
|
|
reg registers
|
|
|
|
mem Memory
|
2019-05-09 22:09:15 +00:00
|
|
|
cycles uint64
|
2019-02-28 22:54:38 +00:00
|
|
|
opcodes *[256]opcode
|
2019-01-26 16:05:51 +00:00
|
|
|
}
|
|
|
|
|
2019-02-22 17:00:53 +00:00
|
|
|
const (
|
2019-02-22 21:19:08 +00:00
|
|
|
vectorNMI uint16 = 0xfffa
|
2019-02-22 17:00:53 +00:00
|
|
|
vectorReset uint16 = 0xfffc
|
|
|
|
vectorBreak uint16 = 0xfffe
|
|
|
|
)
|
|
|
|
|
2019-01-28 23:06:15 +00:00
|
|
|
type opcode struct {
|
2019-02-10 13:01:57 +00:00
|
|
|
name string
|
2019-02-12 23:03:43 +00:00
|
|
|
bytes uint8
|
2019-02-10 13:01:57 +00:00
|
|
|
cycles int
|
|
|
|
addressMode int
|
|
|
|
action opFunc
|
2019-01-28 23:06:15 +00:00
|
|
|
}
|
|
|
|
|
2019-02-16 19:15:41 +00:00
|
|
|
type opFunc func(s *State, line []uint8, opcode opcode)
|
2019-01-28 23:06:15 +00:00
|
|
|
|
2019-02-28 22:54:38 +00:00
|
|
|
func (s *State) executeLine(line []uint8) {
|
|
|
|
opcode := s.opcodes[line[0]]
|
2019-02-22 21:19:08 +00:00
|
|
|
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
|
|
|
|
2019-02-16 19:15:41 +00:00
|
|
|
// ExecuteInstruction transforms the state given after a single instruction is executed.
|
2019-02-28 22:54:38 +00:00
|
|
|
func (s *State) ExecuteInstruction(log bool) {
|
|
|
|
pc := s.reg.getPC()
|
|
|
|
opcodeID := s.mem.Peek(pc)
|
|
|
|
opcode := s.opcodes[opcodeID]
|
2019-02-22 21:19:08 +00:00
|
|
|
|
|
|
|
if opcode.cycles == 0 {
|
|
|
|
panic(fmt.Sprintf("Unknown opcode 0x%02x\n", opcodeID))
|
|
|
|
}
|
2019-02-12 23:03:43 +00:00
|
|
|
|
|
|
|
line := make([]uint8, opcode.bytes)
|
|
|
|
for i := uint8(0); i < opcode.bytes; i++ {
|
2019-02-28 22:54:38 +00:00
|
|
|
line[i] = s.mem.Peek(pc)
|
2019-02-12 23:03:43 +00:00
|
|
|
pc++
|
|
|
|
}
|
2019-02-28 22:54:38 +00:00
|
|
|
s.reg.setPC(pc)
|
2019-02-12 23:03:43 +00:00
|
|
|
|
2019-02-10 16:49:11 +00:00
|
|
|
if log {
|
2019-02-28 22:54:38 +00:00
|
|
|
fmt.Printf("%#04x %-12s: ", pc, lineString(line, opcode))
|
2019-02-10 16:49:11 +00:00
|
|
|
}
|
2019-02-09 23:15:14 +00:00
|
|
|
opcode.action(s, line, opcode)
|
2019-05-09 22:09:15 +00:00
|
|
|
s.cycles += uint64(opcode.cycles)
|
2019-02-10 16:49:11 +00:00
|
|
|
if log {
|
2019-02-28 22:54:38 +00:00
|
|
|
fmt.Printf("%v, [%02x]\n", s.reg, line)
|
2019-02-10 16:49:11 +00:00
|
|
|
}
|
2019-02-10 15:25:03 +00:00
|
|
|
}
|
|
|
|
|
2019-02-16 19:15:41 +00:00
|
|
|
// Reset resets the processor state. Moves the program counter to the vector in 0cfffc.
|
2019-02-28 22:54:38 +00:00
|
|
|
func (s *State) Reset() {
|
|
|
|
startAddress := getWord(s.mem, vectorReset)
|
2019-05-04 17:49:11 +00:00
|
|
|
s.cycles = 0
|
2019-02-28 22:54:38 +00:00
|
|
|
s.reg.setPC(startAddress)
|
2019-02-16 19:15:41 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2019-05-17 21:28:20 +00:00
|
|
|
// Save saves the CPU state (registers and cycle counter)
|
|
|
|
func (s *State) Save(w io.Writer) {
|
|
|
|
binary.Write(w, binary.BigEndian, s.cycles)
|
|
|
|
binary.Write(w, binary.BigEndian, s.reg.data)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load loads the CPU state (registers and cycle counter)
|
|
|
|
func (s *State) Load(r io.Reader) {
|
|
|
|
binary.Read(r, binary.BigEndian, &s.cycles)
|
|
|
|
binary.Read(r, binary.BigEndian, &s.reg.data)
|
|
|
|
}
|
|
|
|
|
2019-02-28 22:54:38 +00:00
|
|
|
func lineString(line []uint8, opcode opcode) string {
|
2019-02-10 15:25:03 +00:00
|
|
|
t := opcode.name
|
|
|
|
switch opcode.addressMode {
|
|
|
|
case modeImplicit:
|
|
|
|
case modeImplicitX:
|
|
|
|
case modeImplicitY:
|
|
|
|
//Nothing
|
|
|
|
case modeAccumulator:
|
|
|
|
t += fmt.Sprintf(" A")
|
|
|
|
case modeImmediate:
|
|
|
|
t += fmt.Sprintf(" #%02x", line[1])
|
|
|
|
case modeZeroPage:
|
|
|
|
t += fmt.Sprintf(" $%02x", line[1])
|
|
|
|
case modeZeroPageX:
|
|
|
|
t += fmt.Sprintf(" $%02x,X", line[1])
|
|
|
|
case modeZeroPageY:
|
|
|
|
t += fmt.Sprintf(" $%02x,Y", line[1])
|
|
|
|
case modeRelative:
|
|
|
|
t += fmt.Sprintf(" *%+x", int8(line[1]))
|
|
|
|
case modeAbsolute:
|
|
|
|
t += fmt.Sprintf(" $%04x", getWordInLine(line))
|
|
|
|
case modeAbsoluteX:
|
|
|
|
t += fmt.Sprintf(" $%04x,X", getWordInLine(line))
|
|
|
|
case modeAbsoluteY:
|
2019-02-16 16:32:06 +00:00
|
|
|
t += fmt.Sprintf(" $%04x,Y", getWordInLine(line))
|
2019-02-10 15:25:03 +00:00
|
|
|
case modeIndirect:
|
|
|
|
t += fmt.Sprintf(" ($%04x)", getWordInLine(line))
|
|
|
|
case modeIndexedIndirectX:
|
|
|
|
t += fmt.Sprintf(" ($%02x,X)", line[1])
|
|
|
|
case modeIndirectIndexedY:
|
|
|
|
t += fmt.Sprintf(" ($%02x),Y", line[1])
|
|
|
|
default:
|
|
|
|
t += "UNKNOWN MODE"
|
|
|
|
}
|
|
|
|
return t
|
2019-02-09 23:15:14 +00:00
|
|
|
}
|