mirror of
https://github.com/ariejan/i6502.git
synced 2025-01-13 17:33:17 +00:00
Remove old prototype code.
This code can still be found in the `prototype` branch. https://github.com/ariejan/i6502/tree/prototype
This commit is contained in:
parent
afa7d0d3f6
commit
0eaa592103
104
bus/bus.go
104
bus/bus.go
@ -1,104 +0,0 @@
|
||||
// Package bus represents the 16-bit address and 8-bit data bus for the
|
||||
// 6502. Different memory modules can be used for different addresses.
|
||||
package bus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ariejan/i6502/memory"
|
||||
)
|
||||
|
||||
type busModule struct {
|
||||
memory memory.Memory
|
||||
name string
|
||||
start uint16
|
||||
end uint16
|
||||
}
|
||||
|
||||
// A 16-bit address and 8-bit data bus. It maps access to different
|
||||
// attached modules, like Ram or Rom.
|
||||
type Bus struct {
|
||||
modules []busModule
|
||||
}
|
||||
|
||||
func (module busModule) String() string {
|
||||
return fmt.Sprintf("%s\t0x%04X-%04X\n", module.name, module.start, module.end)
|
||||
}
|
||||
|
||||
func (bus *Bus) String() string {
|
||||
output := "\n\nAddress bus modules:\n\n"
|
||||
|
||||
for _, module := range bus.modules {
|
||||
// output = append(output, module.String())
|
||||
output += module.String()
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
// Creates a new Bus, no modules are attached by default.
|
||||
func CreateBus() (*Bus, error) {
|
||||
return &Bus{modules: make([]busModule, 0)}, nil
|
||||
}
|
||||
|
||||
func (bus *Bus) Attach(memory memory.Memory, name string, offset uint16) error {
|
||||
offsetMemory := OffsetMemory{Offset: offset, Memory: memory}
|
||||
end := offset + uint16(memory.Size()-1)
|
||||
|
||||
module := busModule{memory: offsetMemory, name: name, start: offset, end: end}
|
||||
|
||||
bus.modules = append(bus.modules, module)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bus *Bus) backendFor(address uint16) (memory.Memory, error) {
|
||||
for _, module := range bus.modules {
|
||||
if address >= module.start && address <= module.end {
|
||||
return module.memory, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("No module addressable at 0x%04X\n", address)
|
||||
}
|
||||
|
||||
// Read an 8-bit value from the module mapped on the bus at the
|
||||
// given 16-bit address.
|
||||
func (bus *Bus) Read(address uint16) byte {
|
||||
memory, err := bus.backendFor(address)
|
||||
if err != nil {
|
||||
fmt.Printf("Reading from invalid address $%04X. Returning $00\n", address)
|
||||
return 0x00
|
||||
// panic(err)
|
||||
}
|
||||
|
||||
value := memory.Read(address)
|
||||
return value
|
||||
}
|
||||
|
||||
// Write an 8-bit value to the module mapped on the bus through the
|
||||
// 16-bit address.
|
||||
func (bus *Bus) Write(address uint16, value byte) {
|
||||
memory, err := bus.backendFor(address)
|
||||
if err != nil {
|
||||
fmt.Printf("Writing to invalid address $%04X. Faking write.\n", address)
|
||||
return
|
||||
// panic(err)
|
||||
}
|
||||
|
||||
memory.Write(address, value)
|
||||
}
|
||||
|
||||
// Helper method to read a 16-bit value from
|
||||
// Note: LSB goes first: address [lo] + address+1 [hi]
|
||||
func (bus *Bus) Read16(address uint16) uint16 {
|
||||
lo := uint16(bus.Read(address))
|
||||
hi := uint16(bus.Read(address + 1))
|
||||
return hi<<8 | lo
|
||||
}
|
||||
|
||||
// Helper to write a 16-bit value to address and address + 1
|
||||
// Note: LSB goes first: address [lo] + address+1 [hi]
|
||||
func (bus *Bus) Write16(address uint16, value uint16) {
|
||||
bus.Write(address, byte(value))
|
||||
bus.Write(address+1, byte(value>>8))
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package bus
|
||||
|
||||
import (
|
||||
"github.com/ariejan/i6502/memory"
|
||||
)
|
||||
|
||||
// The AddressDecoder routes a full 16-bit address to the
|
||||
// appropriate relatie Memory component address.
|
||||
type OffsetMemory struct {
|
||||
Offset uint16
|
||||
memory.Memory
|
||||
}
|
||||
|
||||
func (offsetMemory OffsetMemory) Read(address uint16) byte {
|
||||
return offsetMemory.Memory.Read(address - offsetMemory.Offset)
|
||||
}
|
||||
|
||||
func (offsetMemory OffsetMemory) Write(address uint16, value byte) {
|
||||
offsetMemory.Memory.Write(address-offsetMemory.Offset, value)
|
||||
}
|
108
connection.go
108
connection.go
@ -1,108 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gorilla/websocket"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
pongWait = 60 * time.Second
|
||||
pingPeriod = (pongWait * 9) / 10
|
||||
maxMessageSize = 512
|
||||
)
|
||||
|
||||
type connection struct {
|
||||
// The i6502 machine
|
||||
machine *Machine
|
||||
|
||||
// Websocket connection
|
||||
ws *websocket.Conn
|
||||
|
||||
// Outgoing data channel
|
||||
send chan []byte
|
||||
}
|
||||
|
||||
var (
|
||||
upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
}
|
||||
)
|
||||
|
||||
// Pump received websocket messages into the machine
|
||||
func (c *connection) readPump() {
|
||||
defer func() {
|
||||
c.ws.Close()
|
||||
}()
|
||||
|
||||
c.ws.SetReadLimit(maxMessageSize)
|
||||
c.ws.SetReadDeadline(time.Now().Add(pongWait))
|
||||
c.ws.SetPongHandler(func(string) error { c.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
|
||||
for {
|
||||
_, message, err := c.ws.ReadMessage()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
c.writeBytesToMachine(message)
|
||||
}
|
||||
}
|
||||
|
||||
// Pump serial output from the machine into the socket
|
||||
func (c *connection) writePump() {
|
||||
ticker := time.NewTicker(pingPeriod)
|
||||
|
||||
defer func() {
|
||||
c.ws.Close()
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case data, ok := <-c.machine.SerialTx:
|
||||
if !ok {
|
||||
c.write(websocket.CloseMessage, []byte{})
|
||||
return
|
||||
}
|
||||
if err := c.write(websocket.TextMessage, []byte{data}); err != nil {
|
||||
return
|
||||
}
|
||||
case <-ticker.C:
|
||||
if err := c.write(websocket.PingMessage, []byte{}); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *connection) write(messageType int, payload []byte) error {
|
||||
c.ws.SetWriteDeadline(time.Now().Add(10 * time.Second))
|
||||
return c.ws.WriteMessage(messageType, payload)
|
||||
}
|
||||
|
||||
func (c *connection) writeBytesToMachine(data []byte) {
|
||||
for _, b := range data {
|
||||
log.Printf("%c", b)
|
||||
c.machine.SerialRx <- b
|
||||
}
|
||||
}
|
||||
|
||||
func serveWs(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "GET" {
|
||||
http.Error(w, "Method not allowed", 405)
|
||||
return
|
||||
}
|
||||
|
||||
ws, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
c := &connection{machine: CreateMachine(), send: make(chan []byte, 256), ws: ws}
|
||||
|
||||
go c.writePump()
|
||||
|
||||
c.machine.Reset()
|
||||
c.readPump()
|
||||
}
|
721
cpu/cpu.go
721
cpu/cpu.go
@ -1,721 +0,0 @@
|
||||
package cpu
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ariejan/i6502/bus"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Status register flags
|
||||
const (
|
||||
sCarry = iota
|
||||
sZero
|
||||
sInterrupt
|
||||
sDecimal
|
||||
sBreak
|
||||
_
|
||||
sOverflow
|
||||
sNegative
|
||||
)
|
||||
|
||||
// Beginning of the stack.
|
||||
// The stack grows downward, so it starts at 0x1FF
|
||||
const (
|
||||
StackBase = 0x0100
|
||||
NmiVector = 0xFFFA // + 0xFFFB
|
||||
ResetVector = 0xFFFC // + 0xFFFD
|
||||
IrqVector = 0xFFFE // + 0xFFFF
|
||||
)
|
||||
|
||||
type Cpu struct {
|
||||
// Program counter
|
||||
PC uint16
|
||||
|
||||
// Accumulator
|
||||
A byte
|
||||
|
||||
// X, Y general purpose/index registers
|
||||
X byte
|
||||
Y byte
|
||||
|
||||
// Stack pointer
|
||||
SP byte
|
||||
|
||||
// Status registers
|
||||
SR byte
|
||||
|
||||
// Memory bus
|
||||
Bus *bus.Bus
|
||||
|
||||
IrqChan chan bool
|
||||
NmiChan chan bool
|
||||
|
||||
// Handle exiting
|
||||
ExitChan chan int
|
||||
}
|
||||
|
||||
// Reset the CPU, identical to triggering the RESB pin.
|
||||
// This resets the status register and loads the _reset vector_ from
|
||||
// address 0xFFFC into the Program Counter. Note this is a 16 bit value, read from
|
||||
// 0xFFFC-FFFD
|
||||
func (c *Cpu) Reset() {
|
||||
c.PC = c.Bus.Read16(ResetVector)
|
||||
c.SR = 0x34
|
||||
}
|
||||
|
||||
func (c *Cpu) String() string {
|
||||
return fmt.Sprintf(
|
||||
"CPU PC:0x%04X A:0x%02X X:0x%02X Y:0x%02X SP:0x%02X SR:%s",
|
||||
c.PC, c.A, c.X, c.Y, c.SP,
|
||||
c.statusString(),
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Cpu) handleIrq(returnPC uint16) {
|
||||
c.handleInterrupt(returnPC, IrqVector)
|
||||
}
|
||||
|
||||
func (c *Cpu) handleNmi() {
|
||||
c.handleInterrupt(c.PC, NmiVector)
|
||||
}
|
||||
|
||||
func (c *Cpu) handleInterrupt(returnPC uint16, vector uint16) {
|
||||
c.setStatus(sBreak, true)
|
||||
|
||||
// Push PC + 1 onto stack
|
||||
c.stackPush(byte(returnPC >> 8))
|
||||
c.stackPush(byte(returnPC))
|
||||
// Push status register to the stack
|
||||
c.stackPush(c.SR)
|
||||
|
||||
// Disable interrupts
|
||||
c.setStatus(sInterrupt, true)
|
||||
|
||||
c.PC = c.Bus.Read16(vector)
|
||||
}
|
||||
|
||||
func (c *Cpu) stackPush(data byte) {
|
||||
c.Bus.Write(StackBase+uint16(c.SP), data)
|
||||
c.SP--
|
||||
}
|
||||
|
||||
func (c *Cpu) stackPop() byte {
|
||||
c.SP++
|
||||
return c.Bus.Read(StackBase + uint16(c.SP))
|
||||
}
|
||||
|
||||
func (c *Cpu) Step() {
|
||||
select {
|
||||
case <-c.IrqChan:
|
||||
if !c.getStatus(sInterrupt) {
|
||||
// Handle interrupt
|
||||
c.handleIrq(c.PC)
|
||||
}
|
||||
case <-c.NmiChan:
|
||||
c.handleNmi()
|
||||
default:
|
||||
// Read the instruction (including operands)
|
||||
instruction := ReadInstruction(c.PC, c.Bus)
|
||||
|
||||
// Move the Program Counter forward, depending
|
||||
// on the size of the optype we just read.
|
||||
c.PC += uint16(instruction.Bytes)
|
||||
|
||||
fmt.Printf(instruction.String())
|
||||
|
||||
// Execute the instruction
|
||||
c.execute(instruction)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cpu) stackHead(offset int8) uint16 {
|
||||
address := uint16(StackBase) + uint16(c.SP) + uint16(offset)
|
||||
val8 := c.Bus.Read(address)
|
||||
val16 := c.Bus.Read16(address)
|
||||
fmt.Printf("Addressing Stack at 0x%04X (8: 0x%02X; 16: 0x%04X from PC 0x%04X\n", address, val8, val16, c.PC)
|
||||
return address
|
||||
}
|
||||
|
||||
func (c *Cpu) resolveOperand(in Instruction) uint8 {
|
||||
switch in.addressing {
|
||||
case immediate:
|
||||
return in.Op8
|
||||
default:
|
||||
return c.Bus.Read(c.memoryAddress(in))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cpu) memoryAddress(in Instruction) uint16 {
|
||||
switch in.addressing {
|
||||
case absolute:
|
||||
return in.Op16
|
||||
case absoluteX:
|
||||
return in.Op16 + uint16(c.X)
|
||||
case absoluteY:
|
||||
return in.Op16 + uint16(c.Y)
|
||||
|
||||
case indirect:
|
||||
location := uint16(in.Op16)
|
||||
return c.Bus.Read16(location)
|
||||
|
||||
// Indexed Indirect (X)
|
||||
// Operand is the zero-page location of a little-endian 16-bit base address.
|
||||
// The X register is added (wrapping; discarding overflow) before loading.
|
||||
// The resulting address loaded from (base+X) becomes the effective operand.
|
||||
// (base + X) must be in zero-page.
|
||||
case indirectX:
|
||||
location := uint16(in.Op8 + c.X)
|
||||
if location == 0xFF {
|
||||
panic("Indexed indirect high-byte not on zero page.")
|
||||
}
|
||||
return c.Bus.Read16(location)
|
||||
|
||||
// Indirect Indexed (Y)
|
||||
// Operand is the zero-page location of a little-endian 16-bit address.
|
||||
// The address is loaded, and then the Y register is added to it.
|
||||
// The resulting loaded_address + Y becomes the effective operand.
|
||||
case indirectY:
|
||||
return c.Bus.Read16(uint16(in.Op8)) + uint16(c.Y)
|
||||
|
||||
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: 0x%02X (%s). Are you sure your rom is compatible?", in.addressing, addressingNames[in.addressing]))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cpu) getStatus(bit uint8) bool {
|
||||
return c.getStatusInt(bit) == 1
|
||||
}
|
||||
|
||||
func (c *Cpu) getStatusInt(bit uint8) uint8 {
|
||||
return (c.SR >> bit) & 1
|
||||
}
|
||||
|
||||
func (c *Cpu) setStatus(bit uint8, state bool) {
|
||||
if state {
|
||||
c.SR |= 1 << bit
|
||||
} else {
|
||||
c.SR &^= 1 << bit
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cpu) updateStatus(value uint8) {
|
||||
c.setStatus(sZero, value == 0)
|
||||
c.setStatus(sNegative, (value>>7) == 1)
|
||||
}
|
||||
|
||||
func (c *Cpu) statusString() string {
|
||||
chars := "nv_bdizc"
|
||||
out := make([]string, 8)
|
||||
for i := 0; i < 8; i++ {
|
||||
if c.getStatus(uint8(7 - i)) {
|
||||
out[i] = string(chars[i])
|
||||
} else {
|
||||
out[i] = "-"
|
||||
}
|
||||
}
|
||||
return strings.Join(out, "")
|
||||
}
|
||||
|
||||
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) execute(in Instruction) {
|
||||
switch in.id {
|
||||
case adc:
|
||||
c.ADC(in)
|
||||
case and:
|
||||
c.AND(in)
|
||||
case asl:
|
||||
c.ASL(in)
|
||||
case bcc:
|
||||
c.BCC(in)
|
||||
case bcs:
|
||||
c.BCS(in)
|
||||
case beq:
|
||||
c.BEQ(in)
|
||||
case bmi:
|
||||
c.BMI(in)
|
||||
case bne:
|
||||
c.BNE(in)
|
||||
case bpl:
|
||||
c.BPL(in)
|
||||
case brk:
|
||||
c.BRK(in)
|
||||
case clc:
|
||||
c.CLC(in)
|
||||
case cld:
|
||||
c.CLD(in)
|
||||
case cli:
|
||||
c.CLI(in)
|
||||
case cmp:
|
||||
c.CMP(in)
|
||||
case cpx:
|
||||
c.CPX(in)
|
||||
case cpy:
|
||||
c.CPY(in)
|
||||
case dec:
|
||||
c.DEC(in)
|
||||
case dex:
|
||||
c.DEX(in)
|
||||
case dey:
|
||||
c.DEY(in)
|
||||
case eor:
|
||||
c.EOR(in)
|
||||
case inc:
|
||||
c.INC(in)
|
||||
case inx:
|
||||
c.INX(in)
|
||||
case iny:
|
||||
c.INY(in)
|
||||
case jmp:
|
||||
c.JMP(in)
|
||||
case jsr:
|
||||
c.JSR(in)
|
||||
case lda:
|
||||
c.LDA(in)
|
||||
case ldx:
|
||||
c.LDX(in)
|
||||
case ldy:
|
||||
c.LDY(in)
|
||||
case lsr:
|
||||
c.LSR(in)
|
||||
case nop:
|
||||
c.NOP(in)
|
||||
case ora:
|
||||
c.ORA(in)
|
||||
case pha:
|
||||
c.PHA(in)
|
||||
case php:
|
||||
c.PHP(in)
|
||||
case pla:
|
||||
c.PLA(in)
|
||||
case plp:
|
||||
c.PLP(in)
|
||||
case rol:
|
||||
c.ROL(in)
|
||||
case ror:
|
||||
c.ROR(in)
|
||||
case rti:
|
||||
c.RTI(in)
|
||||
case rts:
|
||||
c.RTS(in)
|
||||
case sbc:
|
||||
c.SBC(in)
|
||||
case sec:
|
||||
c.SEC(in)
|
||||
case sei:
|
||||
c.SEI(in)
|
||||
case sta:
|
||||
c.STA(in)
|
||||
case stx:
|
||||
c.STX(in)
|
||||
case sty:
|
||||
c.STY(in)
|
||||
case tax:
|
||||
c.TAX(in)
|
||||
case tay:
|
||||
c.TAY(in)
|
||||
case tsx:
|
||||
c.TSX(in)
|
||||
case txa:
|
||||
c.TXA(in)
|
||||
case txs:
|
||||
c.TXS(in)
|
||||
case tya:
|
||||
c.TYA(in)
|
||||
case _end:
|
||||
c._END(in)
|
||||
default:
|
||||
panic(fmt.Sprintf("Unhandled instruction: %X", in.OpType))
|
||||
}
|
||||
}
|
||||
|
||||
// ADC: Add memory and carry to accumulator.
|
||||
func (c *Cpu) ADC(in Instruction) {
|
||||
value16 := uint16(c.A) + uint16(c.resolveOperand(in)) + uint16(c.getStatusInt(sCarry))
|
||||
c.setStatus(sCarry, value16 > 0xFF)
|
||||
c.A = uint8(value16)
|
||||
c.updateStatus(c.A)
|
||||
}
|
||||
|
||||
// AND: And accumulator with memory.
|
||||
func (c *Cpu) AND(in Instruction) {
|
||||
c.A &= c.resolveOperand(in)
|
||||
c.updateStatus(c.A)
|
||||
}
|
||||
|
||||
// ASL: Shift memory or accumulator left one bit.
|
||||
func (c *Cpu) ASL(in Instruction) {
|
||||
switch in.addressing {
|
||||
case accumulator:
|
||||
c.setStatus(sCarry, (c.A>>7) == 1) // carry = old bit 7
|
||||
c.A <<= 1
|
||||
c.updateStatus(c.A)
|
||||
default:
|
||||
address := c.memoryAddress(in)
|
||||
value := c.Bus.Read(address)
|
||||
c.setStatus(sCarry, (value>>7) == 1) // carry = old bit 7
|
||||
value <<= 1
|
||||
c.Bus.Write(address, value)
|
||||
c.updateStatus(value)
|
||||
}
|
||||
}
|
||||
|
||||
// BCC: Branch if carry clear.
|
||||
func (c *Cpu) BCC(in Instruction) {
|
||||
if !c.getStatus(sCarry) {
|
||||
c.branch(in)
|
||||
}
|
||||
}
|
||||
|
||||
// BCS: Branch if carry set.
|
||||
func (c *Cpu) BCS(in Instruction) {
|
||||
if c.getStatus(sCarry) {
|
||||
c.branch(in)
|
||||
}
|
||||
}
|
||||
|
||||
// BEQ: Branch if equal (z=1).
|
||||
func (c *Cpu) BEQ(in Instruction) {
|
||||
if c.getStatus(sZero) {
|
||||
c.branch(in)
|
||||
}
|
||||
}
|
||||
|
||||
// BMI: Branch if negative.
|
||||
func (c *Cpu) BMI(in Instruction) {
|
||||
if c.getStatus(sNegative) {
|
||||
c.branch(in)
|
||||
}
|
||||
}
|
||||
|
||||
// BNE: Branch if not equal.
|
||||
func (c *Cpu) BNE(in Instruction) {
|
||||
if !c.getStatus(sZero) {
|
||||
c.branch(in)
|
||||
}
|
||||
}
|
||||
|
||||
// BPL: Branch if positive.
|
||||
func (c *Cpu) BPL(in Instruction) {
|
||||
if !c.getStatus(sNegative) {
|
||||
c.branch(in)
|
||||
}
|
||||
}
|
||||
|
||||
// BRK: software interrupt
|
||||
func (c *Cpu) BRK(in Instruction) {
|
||||
fmt.Println("BRK:", c)
|
||||
c.ExitChan <- 42
|
||||
|
||||
// Force interrupt
|
||||
// if !c.getStatus(sInterrupt) {
|
||||
// c.handleIrq(c.PC + 1)
|
||||
// }
|
||||
}
|
||||
|
||||
// CLC: Clear carry flag.
|
||||
func (c *Cpu) CLC(in Instruction) {
|
||||
c.setStatus(sCarry, false)
|
||||
}
|
||||
|
||||
// CLD: Clear decimal mode flag.
|
||||
func (c *Cpu) CLD(in Instruction) {
|
||||
c.setStatus(sDecimal, false)
|
||||
}
|
||||
|
||||
// CLI: Clear interrupt-disable flag.
|
||||
func (c *Cpu) CLI(in Instruction) {
|
||||
c.setStatus(sInterrupt, true)
|
||||
}
|
||||
|
||||
// CMP: Compare accumulator with memory.
|
||||
func (c *Cpu) CMP(in Instruction) {
|
||||
value := c.resolveOperand(in)
|
||||
c.setStatus(sCarry, c.A >= value)
|
||||
c.updateStatus(c.A - value)
|
||||
}
|
||||
|
||||
// CPX: Compare index register X with memory.
|
||||
func (c *Cpu) CPX(in Instruction) {
|
||||
value := c.resolveOperand(in)
|
||||
c.setStatus(sCarry, c.X >= value)
|
||||
c.updateStatus(c.X - value)
|
||||
}
|
||||
|
||||
// CPY: Compare index register Y with memory.
|
||||
func (c *Cpu) CPY(in Instruction) {
|
||||
value := c.resolveOperand(in)
|
||||
c.setStatus(sCarry, c.Y >= value)
|
||||
c.updateStatus(c.Y - value)
|
||||
}
|
||||
|
||||
// DEC: Decrement.
|
||||
func (c *Cpu) DEC(in Instruction) {
|
||||
address := c.memoryAddress(in)
|
||||
value := c.Bus.Read(address) - 1
|
||||
c.Bus.Write(address, value)
|
||||
c.updateStatus(value)
|
||||
}
|
||||
|
||||
// DEX: Decrement index register X.
|
||||
func (c *Cpu) DEX(in Instruction) {
|
||||
c.X--
|
||||
c.updateStatus(c.X)
|
||||
}
|
||||
|
||||
// DEY: Decrement index register Y.
|
||||
func (c *Cpu) DEY(in Instruction) {
|
||||
c.Y--
|
||||
c.updateStatus(c.Y)
|
||||
}
|
||||
|
||||
// EOR: Exclusive-OR accumulator with memory.
|
||||
func (c *Cpu) EOR(in Instruction) {
|
||||
value := c.resolveOperand(in)
|
||||
c.A ^= value
|
||||
c.updateStatus(c.A)
|
||||
}
|
||||
|
||||
// INC: Increment.
|
||||
func (c *Cpu) INC(in Instruction) {
|
||||
address := c.memoryAddress(in)
|
||||
value := c.Bus.Read(address) + 1
|
||||
c.Bus.Write(address, value)
|
||||
c.updateStatus(value)
|
||||
}
|
||||
|
||||
// INX: Increment index register X.
|
||||
func (c *Cpu) INX(in Instruction) {
|
||||
c.X++
|
||||
c.updateStatus(c.X)
|
||||
}
|
||||
|
||||
// INY: Increment index register Y.
|
||||
func (c *Cpu) INY(in Instruction) {
|
||||
c.Y++
|
||||
c.updateStatus(c.Y)
|
||||
}
|
||||
|
||||
// JMP: Jump.
|
||||
func (c *Cpu) JMP(in Instruction) {
|
||||
c.PC = c.memoryAddress(in)
|
||||
}
|
||||
|
||||
// JSR: Jump to subroutine.
|
||||
func (c *Cpu) JSR(in Instruction) {
|
||||
c.Bus.Write16(c.stackHead(-1), c.PC-1)
|
||||
c.SP -= 2
|
||||
c.PC = in.Op16
|
||||
}
|
||||
|
||||
// LDA: Load accumulator from memory.
|
||||
func (c *Cpu) LDA(in Instruction) {
|
||||
c.A = c.resolveOperand(in)
|
||||
c.updateStatus(c.A)
|
||||
}
|
||||
|
||||
// LDX: Load index register X from memory.
|
||||
func (c *Cpu) LDX(in Instruction) {
|
||||
c.X = c.resolveOperand(in)
|
||||
c.updateStatus(c.X)
|
||||
}
|
||||
|
||||
// LDY: Load index register Y from memory.
|
||||
func (c *Cpu) LDY(in Instruction) {
|
||||
c.Y = c.resolveOperand(in)
|
||||
c.updateStatus(c.Y)
|
||||
}
|
||||
|
||||
// LSR: Logical shift memory or accumulator right.
|
||||
func (c *Cpu) LSR(in Instruction) {
|
||||
switch in.addressing {
|
||||
case accumulator:
|
||||
c.setStatus(sCarry, c.A&1 == 1)
|
||||
c.A >>= 1
|
||||
c.updateStatus(c.A)
|
||||
default:
|
||||
address := c.memoryAddress(in)
|
||||
value := c.Bus.Read(address)
|
||||
c.setStatus(sCarry, value&1 == 1)
|
||||
value >>= 1
|
||||
c.Bus.Write(address, value)
|
||||
c.updateStatus(value)
|
||||
}
|
||||
}
|
||||
|
||||
// NOP: No operation.
|
||||
func (c *Cpu) NOP(in Instruction) {
|
||||
}
|
||||
|
||||
// ORA: OR accumulator with memory.
|
||||
func (c *Cpu) ORA(in Instruction) {
|
||||
c.A |= c.resolveOperand(in)
|
||||
c.updateStatus(c.A)
|
||||
}
|
||||
|
||||
// PHA: Push accumulator onto stack.
|
||||
func (c *Cpu) PHA(in Instruction) {
|
||||
c.stackPush(c.A)
|
||||
}
|
||||
|
||||
// PHP: Push SR to stack
|
||||
func (c *Cpu) PHP(in Instruction) {
|
||||
c.stackPush(c.SR)
|
||||
}
|
||||
|
||||
// PLA: Pull accumulator from stack.
|
||||
func (c *Cpu) PLA(in Instruction) {
|
||||
c.A = c.stackPop()
|
||||
}
|
||||
|
||||
// PLP: Pull SR from stack
|
||||
func (c *Cpu) PLP(in Instruction) {
|
||||
c.SR = c.stackPop()
|
||||
}
|
||||
|
||||
// ROL: Rotate memory or accumulator left one bit.
|
||||
func (c *Cpu) ROL(in Instruction) {
|
||||
carry := c.getStatusInt(sCarry)
|
||||
switch in.addressing {
|
||||
case accumulator:
|
||||
c.setStatus(sCarry, c.A>>7 == 1)
|
||||
c.A = c.A<<1 | carry
|
||||
c.updateStatus(c.A)
|
||||
default:
|
||||
address := c.memoryAddress(in)
|
||||
value := c.Bus.Read(address)
|
||||
c.setStatus(sCarry, value>>7 == 1)
|
||||
value = value<<1 | carry
|
||||
c.Bus.Write(address, value)
|
||||
c.updateStatus(value)
|
||||
}
|
||||
}
|
||||
|
||||
// ROR: Rotate memory or accumulator left one bit.
|
||||
func (c *Cpu) ROR(in Instruction) {
|
||||
carry := c.getStatusInt(sCarry)
|
||||
switch in.addressing {
|
||||
case accumulator:
|
||||
c.setStatus(sCarry, c.A&1 == 1)
|
||||
c.A = c.A>>1 | carry<<7
|
||||
c.updateStatus(c.A)
|
||||
default:
|
||||
address := c.memoryAddress(in)
|
||||
value := c.Bus.Read(address)
|
||||
c.setStatus(sCarry, value&1 == 1)
|
||||
value = value>>1 | carry<<7
|
||||
c.Bus.Write(address, value)
|
||||
c.updateStatus(value)
|
||||
}
|
||||
}
|
||||
|
||||
// RTI: Return from interrupt
|
||||
func (c *Cpu) RTI(in Instruction) {
|
||||
c.SR = c.stackPop()
|
||||
c.PC = c.Bus.Read16(c.stackHead(1))
|
||||
c.SP += 2
|
||||
fmt.Printf("RTI: Returning to 0x%04X", c.PC)
|
||||
}
|
||||
|
||||
// RTS: Return from subroutine.
|
||||
func (c *Cpu) RTS(in Instruction) {
|
||||
c.PC = c.Bus.Read16(c.stackHead(1)) + 1
|
||||
c.SP += 2
|
||||
}
|
||||
|
||||
// SBC: Subtract memory with borrow from accumulator.
|
||||
func (c *Cpu) SBC(in Instruction) {
|
||||
valueSigned := int16(c.A) - int16(c.resolveOperand(in))
|
||||
if !c.getStatus(sCarry) {
|
||||
valueSigned--
|
||||
}
|
||||
c.A = uint8(valueSigned)
|
||||
|
||||
// v: Set if signed overflow; cleared if valid sign result.
|
||||
// TODO: c.setStatus(sOverflow, something)
|
||||
|
||||
// c: Set if unsigned borrow not required; cleared if unsigned borrow.
|
||||
c.setStatus(sCarry, valueSigned >= 0)
|
||||
|
||||
// n: Set if most significant bit of result is set; else cleared.
|
||||
// z: Set if result is zero; else cleared.
|
||||
c.updateStatus(c.A)
|
||||
}
|
||||
|
||||
// SEC: Set carry flag.
|
||||
func (c *Cpu) SEC(in Instruction) {
|
||||
c.setStatus(sCarry, true)
|
||||
}
|
||||
|
||||
// SEI: Set interrupt-disable flag.
|
||||
func (c *Cpu) SEI(in Instruction) {
|
||||
c.setStatus(sInterrupt, false)
|
||||
}
|
||||
|
||||
// STA: Store accumulator to memory.
|
||||
func (c *Cpu) STA(in Instruction) {
|
||||
c.Bus.Write(c.memoryAddress(in), c.A)
|
||||
}
|
||||
|
||||
// STX: Store index register X to memory.
|
||||
func (c *Cpu) STX(in Instruction) {
|
||||
c.Bus.Write(c.memoryAddress(in), c.X)
|
||||
}
|
||||
|
||||
// STY: Store index register Y to memory.
|
||||
func (c *Cpu) STY(in Instruction) {
|
||||
c.Bus.Write(c.memoryAddress(in), c.Y)
|
||||
}
|
||||
|
||||
// TAX: Transfer accumulator to index register X.
|
||||
func (c *Cpu) TAX(in Instruction) {
|
||||
c.X = c.A
|
||||
c.updateStatus(c.X)
|
||||
}
|
||||
|
||||
// TAY: Transfer accumulator to index register Y.
|
||||
func (c *Cpu) TAY(in Instruction) {
|
||||
c.Y = c.A
|
||||
c.updateStatus(c.Y)
|
||||
}
|
||||
|
||||
// TSX: Transfer stack pointer to index register X.
|
||||
func (c *Cpu) TSX(in Instruction) {
|
||||
c.X = c.SP
|
||||
c.updateStatus(c.X)
|
||||
}
|
||||
|
||||
// TXA: Transfer index register X to accumulator.
|
||||
func (c *Cpu) TXA(in Instruction) {
|
||||
c.A = c.X
|
||||
c.updateStatus(c.A)
|
||||
}
|
||||
|
||||
// TXS: Transfer index register X to stack pointer.
|
||||
func (c *Cpu) TXS(in Instruction) {
|
||||
c.SP = c.X
|
||||
c.updateStatus(c.SP)
|
||||
}
|
||||
|
||||
// TYA: Transfer index register Y to accumulator.
|
||||
func (c *Cpu) TYA(in Instruction) {
|
||||
c.A = c.Y
|
||||
c.updateStatus(c.A)
|
||||
}
|
||||
|
||||
func (c *Cpu) _END(in Instruction) {
|
||||
c.ExitChan <- int(c.X)
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
package cpu
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ariejan/i6502/bus"
|
||||
)
|
||||
|
||||
// Instruction is a self-contained, variable length instruction,
|
||||
// it includes the the operation type and a possible 8 or 16 bit operand.
|
||||
type Instruction struct {
|
||||
OpType
|
||||
|
||||
// 2 byte instructions have a single byte operand
|
||||
Op8 uint8
|
||||
|
||||
// 3 byte instruction have a double byte operand
|
||||
Op16 uint16
|
||||
}
|
||||
|
||||
func (i *Instruction) String() string {
|
||||
var output string
|
||||
|
||||
switch i.Bytes {
|
||||
case 1:
|
||||
output = fmt.Sprintf("0x%02X - %s\n", i.Opcode, instructionNames[i.id])
|
||||
case 2:
|
||||
output = fmt.Sprintf("0x%02X - %s %02X\n", i.Opcode, instructionNames[i.id], i.Op8)
|
||||
case 3:
|
||||
output = fmt.Sprintf("0x%02X - %s %04X\n", i.Opcode, instructionNames[i.id], i.Op16)
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
func ReadInstruction(pc uint16, bus *bus.Bus) Instruction {
|
||||
// Read the opcode
|
||||
opcode := bus.Read(pc)
|
||||
|
||||
// Do we know this opcode in our optypes table?
|
||||
optype, ok := optypes[opcode]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Unknown opcode $%02X at $%04X", opcode, pc))
|
||||
}
|
||||
|
||||
instruction := Instruction{OpType: optype}
|
||||
switch instruction.Bytes {
|
||||
case 1: // No operand.
|
||||
case 2:
|
||||
instruction.Op8 = bus.Read(pc + 1)
|
||||
case 3:
|
||||
instruction.Op16 = bus.Read16(pc + 1)
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown instruction length (%d) for $%02X at $04X", instruction.Bytes, opcode, pc))
|
||||
}
|
||||
|
||||
return instruction
|
||||
}
|
340
cpu/op_type.go
340
cpu/op_type.go
@ -1,340 +0,0 @@
|
||||
package cpu
|
||||
|
||||
// 6502 Addressing modes
|
||||
const (
|
||||
_ = iota
|
||||
absolute
|
||||
absoluteX
|
||||
absoluteY
|
||||
accumulator
|
||||
immediate
|
||||
implied
|
||||
indirect
|
||||
indirectX
|
||||
indirectY
|
||||
relative
|
||||
zeropage
|
||||
zeropageX
|
||||
zeropageY
|
||||
)
|
||||
|
||||
// Human readable address modes
|
||||
var addressingNames = [...]string{
|
||||
"",
|
||||
"absolute",
|
||||
"absoluteX",
|
||||
"absoluteY",
|
||||
"accumulator",
|
||||
"immediate",
|
||||
"implied",
|
||||
"(indirect)",
|
||||
"(indirect,X)",
|
||||
"(indirect),Y",
|
||||
"relative",
|
||||
"zeropage",
|
||||
"zeropageX",
|
||||
"zeropageY",
|
||||
}
|
||||
|
||||
// 6502 Instruction mnemonics.
|
||||
// OpCodes are a combination of an instruction and an addressing mode
|
||||
const (
|
||||
_ = iota
|
||||
adc
|
||||
and
|
||||
asl
|
||||
bcc
|
||||
bcs
|
||||
beq
|
||||
bit
|
||||
bmi
|
||||
bne
|
||||
bpl
|
||||
brk
|
||||
bvc
|
||||
bvs
|
||||
clc
|
||||
cld
|
||||
cli
|
||||
clv
|
||||
cmp
|
||||
cpx
|
||||
cpy
|
||||
dec
|
||||
dex
|
||||
dey
|
||||
eor
|
||||
inc
|
||||
inx
|
||||
iny
|
||||
jmp
|
||||
jsr
|
||||
lda
|
||||
ldx
|
||||
ldy
|
||||
lsr
|
||||
nop
|
||||
ora
|
||||
pha
|
||||
php
|
||||
pla
|
||||
plp
|
||||
rol
|
||||
ror
|
||||
rti
|
||||
rts
|
||||
sbc
|
||||
sec
|
||||
sed
|
||||
sei
|
||||
sta
|
||||
stx
|
||||
sty
|
||||
tax
|
||||
tay
|
||||
tsx
|
||||
txa
|
||||
txs
|
||||
tya
|
||||
_end
|
||||
)
|
||||
|
||||
// Human readable instruction mnemonics
|
||||
var instructionNames = [...]string{
|
||||
"",
|
||||
"ADC",
|
||||
"AND",
|
||||
"ASL",
|
||||
"BCC",
|
||||
"BCS",
|
||||
"BEQ",
|
||||
"BIT",
|
||||
"BMI",
|
||||
"BNE",
|
||||
"BPL",
|
||||
"BRK",
|
||||
"BVC",
|
||||
"BVS",
|
||||
"CLC",
|
||||
"CLD",
|
||||
"CLI",
|
||||
"CLV",
|
||||
"CMP",
|
||||
"CPX",
|
||||
"CPY",
|
||||
"DEC",
|
||||
"DEX",
|
||||
"DEY",
|
||||
"EOR",
|
||||
"INC",
|
||||
"INX",
|
||||
"INY",
|
||||
"JMP",
|
||||
"JSR",
|
||||
"LDA",
|
||||
"LDX",
|
||||
"LDY",
|
||||
"LSR",
|
||||
"NOP",
|
||||
"ORA",
|
||||
"PHA",
|
||||
"PHP",
|
||||
"PLA",
|
||||
"PLP",
|
||||
"ROL",
|
||||
"ROR",
|
||||
"RTI",
|
||||
"RTS",
|
||||
"SBC",
|
||||
"SEC",
|
||||
"SED",
|
||||
"SEI",
|
||||
"STA",
|
||||
"STX",
|
||||
"STY",
|
||||
"TAX",
|
||||
"TAY",
|
||||
"TSX",
|
||||
"TXA",
|
||||
"TXS",
|
||||
"TYA",
|
||||
"_END",
|
||||
}
|
||||
|
||||
// The OpType consists of a 6502 instruction and addressing mode.
|
||||
// It does not include any following operand
|
||||
type OpType struct {
|
||||
// Opcode is the instruction + addresing mode
|
||||
Opcode byte
|
||||
|
||||
// Internal id for the used instruction/mnemonic
|
||||
id uint8
|
||||
|
||||
// Internal id for the addressing mode
|
||||
addressing uint8
|
||||
|
||||
// Bytes is the size of the instruction and its expected operands.
|
||||
// 1 byte: opcode with implicit or null operand
|
||||
// 2 bytes: opcode with immediate or zeropage operand (8-bit operand)
|
||||
// 3 bytes: opcode with address operands
|
||||
Bytes uint8
|
||||
|
||||
// Cycles, number of clock cycles it takes to execute this opcode
|
||||
Cycles uint8
|
||||
}
|
||||
|
||||
// See http://www.e-tradition.net/bytes/6502/6502_instruction_set.html for details
|
||||
var optypes = map[uint8]OpType{
|
||||
0x69: OpType{0x69, adc, immediate, 2, 2},
|
||||
0x65: OpType{0x69, adc, zeropage, 2, 3},
|
||||
0x75: OpType{0x75, adc, zeropageX, 2, 4},
|
||||
0x6D: OpType{0x6D, adc, absolute, 3, 4},
|
||||
0x7D: OpType{0x7D, adc, absoluteX, 3, 4},
|
||||
0x79: OpType{0x79, adc, absoluteY, 3, 4},
|
||||
0x61: OpType{0x61, adc, indirectX, 2, 6},
|
||||
0x71: OpType{0x71, adc, indirectY, 2, 5},
|
||||
0x29: OpType{0x29, and, immediate, 2, 2},
|
||||
0x25: OpType{0x25, and, zeropage, 2, 3},
|
||||
0x35: OpType{0x35, and, zeropageX, 2, 4},
|
||||
0x2D: OpType{0x2D, and, absolute, 3, 4},
|
||||
0x3D: OpType{0x3D, and, absoluteX, 3, 4},
|
||||
0x39: OpType{0x39, and, absoluteY, 3, 4},
|
||||
0x21: OpType{0x21, and, indirectX, 2, 6},
|
||||
0x31: OpType{0x31, and, indirectY, 2, 5},
|
||||
0x0A: OpType{0x0A, asl, accumulator, 1, 2},
|
||||
0x06: OpType{0x06, asl, zeropage, 2, 5},
|
||||
0x16: OpType{0x16, asl, zeropageX, 2, 6},
|
||||
0x0E: OpType{0x0E, asl, absolute, 3, 6},
|
||||
0x1E: OpType{0x1E, asl, absoluteX, 3, 7},
|
||||
0x90: OpType{0x90, bcc, relative, 2, 2},
|
||||
0xB0: OpType{0xB0, bcs, relative, 2, 2},
|
||||
0xF0: OpType{0xF0, beq, relative, 2, 2},
|
||||
0x24: OpType{0x24, bit, zeropage, 2, 3},
|
||||
0x2C: OpType{0x2C, bit, absolute, 3, 4},
|
||||
0x30: OpType{0x30, bmi, relative, 2, 2},
|
||||
0xD0: OpType{0xD0, bne, relative, 2, 2},
|
||||
0x10: OpType{0x10, bpl, relative, 2, 2},
|
||||
0x00: OpType{0x00, brk, implied, 1, 7},
|
||||
0x50: OpType{0x50, bvc, relative, 2, 2},
|
||||
0x70: OpType{0x70, bvs, relative, 2, 2},
|
||||
0x18: OpType{0x18, clc, implied, 1, 2},
|
||||
0xD8: OpType{0xD8, cld, implied, 1, 2},
|
||||
0x58: OpType{0x58, cli, implied, 1, 2},
|
||||
0xB8: OpType{0xB8, clv, implied, 1, 2},
|
||||
0xC9: OpType{0xC9, cmp, immediate, 2, 2},
|
||||
0xC5: OpType{0xC5, cmp, zeropage, 2, 3},
|
||||
0xD5: OpType{0xD5, cmp, zeropageX, 2, 4},
|
||||
0xCD: OpType{0xCD, cmp, absolute, 3, 4},
|
||||
0xDD: OpType{0xDD, cmp, absoluteX, 3, 4},
|
||||
0xD9: OpType{0xD9, cmp, absoluteY, 3, 4},
|
||||
0xC1: OpType{0xC1, cmp, indirectX, 2, 6},
|
||||
0xD1: OpType{0xD1, cmp, indirectY, 2, 5},
|
||||
0xE0: OpType{0xE0, cpx, immediate, 2, 2},
|
||||
0xE4: OpType{0xE4, cpx, zeropage, 2, 3},
|
||||
0xEC: OpType{0xEC, cpx, absolute, 3, 4},
|
||||
0xC0: OpType{0xC0, cpy, immediate, 2, 2},
|
||||
0xC4: OpType{0xC4, cpy, zeropage, 2, 3},
|
||||
0xCC: OpType{0xCC, cpy, absolute, 3, 4},
|
||||
0xC6: OpType{0xC6, dec, zeropage, 2, 5},
|
||||
0xD6: OpType{0xD6, dec, zeropageX, 2, 6},
|
||||
0xCE: OpType{0xCE, dec, absolute, 3, 3},
|
||||
0xDE: OpType{0xDE, dec, absoluteX, 3, 7},
|
||||
0xCA: OpType{0xCA, dex, implied, 1, 2},
|
||||
0x88: OpType{0x88, dey, implied, 1, 2},
|
||||
0x49: OpType{0x49, eor, immediate, 2, 2},
|
||||
0x45: OpType{0x45, eor, zeropage, 2, 3},
|
||||
0x55: OpType{0x55, eor, zeropageX, 2, 4},
|
||||
0x4D: OpType{0x4D, eor, absolute, 3, 4},
|
||||
0x5D: OpType{0x5D, eor, absoluteX, 3, 4},
|
||||
0x59: OpType{0x59, eor, absoluteY, 3, 4},
|
||||
0x41: OpType{0x41, eor, indirectX, 2, 6},
|
||||
0x51: OpType{0x51, eor, indirectY, 2, 5},
|
||||
0xE6: OpType{0xE6, inc, zeropage, 2, 5},
|
||||
0xF6: OpType{0xF6, inc, zeropageX, 2, 6},
|
||||
0xEE: OpType{0xEE, inc, absolute, 3, 6},
|
||||
0xFE: OpType{0xFE, inc, absoluteX, 3, 7},
|
||||
0xE8: OpType{0xE8, inx, implied, 1, 2},
|
||||
0xC8: OpType{0xC8, iny, implied, 1, 2},
|
||||
0x4C: OpType{0x4C, jmp, absolute, 3, 3},
|
||||
0x6C: OpType{0x6C, jmp, indirect, 3, 5},
|
||||
0x20: OpType{0x20, jsr, absolute, 3, 6},
|
||||
0xA9: OpType{0xA9, lda, immediate, 2, 2},
|
||||
0xA5: OpType{0xA5, lda, zeropage, 2, 3},
|
||||
0xB5: OpType{0xB5, lda, zeropageX, 2, 4},
|
||||
0xAD: OpType{0xAD, lda, absolute, 3, 4},
|
||||
0xBD: OpType{0xBD, lda, absoluteX, 3, 4},
|
||||
0xB9: OpType{0xB9, lda, absoluteY, 3, 4},
|
||||
0xA1: OpType{0xA1, lda, indirectX, 2, 6},
|
||||
0xB1: OpType{0xB1, lda, indirectY, 2, 5},
|
||||
0xA2: OpType{0xA2, ldx, immediate, 2, 2},
|
||||
0xA6: OpType{0xA6, ldx, zeropage, 2, 3},
|
||||
0xB6: OpType{0xB6, ldx, zeropageY, 2, 4},
|
||||
0xAE: OpType{0xAE, ldx, absolute, 3, 4},
|
||||
0xBE: OpType{0xBE, ldx, absoluteY, 3, 4},
|
||||
0xA0: OpType{0xA0, ldy, immediate, 2, 2},
|
||||
0xA4: OpType{0xA4, ldy, zeropage, 2, 3},
|
||||
0xB4: OpType{0xB4, ldy, zeropageX, 2, 4},
|
||||
0xAC: OpType{0xAC, ldy, absolute, 3, 4},
|
||||
0xBC: OpType{0xBC, ldy, absoluteX, 3, 4},
|
||||
0x4A: OpType{0x4A, lsr, accumulator, 1, 2},
|
||||
0x46: OpType{0x46, lsr, zeropage, 2, 5},
|
||||
0x56: OpType{0x56, lsr, zeropageX, 2, 6},
|
||||
0x4E: OpType{0x4E, lsr, absolute, 3, 6},
|
||||
0x5E: OpType{0x5E, lsr, absoluteX, 3, 7},
|
||||
0xEA: OpType{0xEA, nop, implied, 1, 2},
|
||||
0x09: OpType{0x09, ora, immediate, 2, 2},
|
||||
0x05: OpType{0x05, ora, zeropage, 2, 3},
|
||||
0x15: OpType{0x15, ora, zeropageX, 2, 4},
|
||||
0x0D: OpType{0x0D, ora, absolute, 3, 4},
|
||||
0x1D: OpType{0x1D, ora, absoluteX, 3, 4},
|
||||
0x19: OpType{0x19, ora, absoluteY, 3, 4},
|
||||
0x01: OpType{0x01, ora, indirectX, 2, 6},
|
||||
0x11: OpType{0x11, ora, indirectY, 2, 5},
|
||||
0x48: OpType{0x48, pha, implied, 1, 3},
|
||||
0x08: OpType{0x08, php, implied, 1, 3},
|
||||
0x68: OpType{0x68, pla, implied, 1, 4},
|
||||
0x28: OpType{0x28, php, implied, 1, 4},
|
||||
0x2A: OpType{0x2A, rol, accumulator, 1, 2},
|
||||
0x26: OpType{0x26, rol, zeropage, 2, 5},
|
||||
0x36: OpType{0x36, rol, zeropageX, 2, 6},
|
||||
0x2E: OpType{0x2E, rol, absolute, 3, 6},
|
||||
0x3E: OpType{0x3E, rol, absoluteX, 3, 7},
|
||||
0x6A: OpType{0x6A, ror, accumulator, 1, 2},
|
||||
0x66: OpType{0x66, ror, zeropage, 2, 5},
|
||||
0x76: OpType{0x76, ror, zeropageX, 2, 6},
|
||||
0x6E: OpType{0x6E, ror, absolute, 3, 6},
|
||||
0x7E: OpType{0x7E, ror, absoluteX, 3, 7},
|
||||
0x40: OpType{0x40, rti, implied, 1, 6},
|
||||
0x60: OpType{0x60, rts, implied, 1, 6},
|
||||
0xE9: OpType{0xE9, sbc, immediate, 2, 2},
|
||||
0xE5: OpType{0xE5, sbc, zeropage, 2, 3},
|
||||
0xF5: OpType{0xF5, sbc, zeropageX, 2, 4},
|
||||
0xED: OpType{0xED, sbc, absolute, 3, 4},
|
||||
0xFD: OpType{0xFD, sbc, absoluteX, 3, 4},
|
||||
0xF9: OpType{0xF9, sbc, absoluteY, 3, 4},
|
||||
0xE1: OpType{0xE1, sbc, indirectX, 2, 6},
|
||||
0xF1: OpType{0xF1, sbc, indirectY, 2, 5},
|
||||
0x38: OpType{0x38, sec, implied, 1, 2},
|
||||
0xF8: OpType{0xF8, sed, implied, 1, 2},
|
||||
0x78: OpType{0x78, sei, implied, 1, 2},
|
||||
0x85: OpType{0x85, sta, zeropage, 2, 3},
|
||||
0x95: OpType{0x95, sta, zeropageX, 2, 4},
|
||||
0x8D: OpType{0x8D, sta, absolute, 3, 4},
|
||||
0x9D: OpType{0x9D, sta, absoluteX, 3, 5},
|
||||
0x99: OpType{0x99, sta, absoluteY, 3, 5},
|
||||
0x81: OpType{0x81, sta, indirectX, 2, 6},
|
||||
0x91: OpType{0x91, sta, indirectY, 2, 6},
|
||||
0x86: OpType{0x86, stx, zeropage, 2, 3},
|
||||
0x96: OpType{0x96, stx, zeropageY, 2, 4},
|
||||
0x8E: OpType{0x8E, stx, absolute, 3, 4},
|
||||
0x84: OpType{0x84, sty, zeropage, 2, 3},
|
||||
0x94: OpType{0x94, sty, zeropageX, 2, 4},
|
||||
0x8C: OpType{0x8C, sty, absolute, 3, 4},
|
||||
0xAA: OpType{0xAA, tax, implied, 1, 2},
|
||||
0xA8: OpType{0xA8, tay, implied, 1, 2},
|
||||
0xBA: OpType{0xBA, tsx, implied, 1, 2},
|
||||
0x8A: OpType{0x8A, txa, implied, 1, 2},
|
||||
0x9A: OpType{0x9A, txs, implied, 1, 2},
|
||||
0x98: OpType{0x98, tya, implied, 1, 2},
|
||||
0xFF: OpType{0xFF, _end, implied, 1, 1},
|
||||
}
|
@ -1,208 +0,0 @@
|
||||
package devices
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
aciaData = iota
|
||||
aciaStatus
|
||||
aciaCommand
|
||||
aciaControl
|
||||
)
|
||||
|
||||
var baudRateSelectors = [...]int{0, 50, 75, 110, 135, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, 19200}
|
||||
|
||||
type Acia6551 struct {
|
||||
// Registers
|
||||
rx byte
|
||||
tx byte
|
||||
commandRegister byte
|
||||
controlRegister byte
|
||||
|
||||
// Other required bits and pieces
|
||||
lastTxWrite int64
|
||||
lastRxRead int64
|
||||
overrun bool
|
||||
baudRate int
|
||||
baudRateDelay int64
|
||||
rxFull bool
|
||||
txEmpty bool
|
||||
|
||||
rxInterruptEnabled bool
|
||||
txInterruptEnabled bool
|
||||
|
||||
InterruptChan chan bool
|
||||
|
||||
TxChan chan byte
|
||||
}
|
||||
|
||||
func NewAcia6551(interruptChan chan bool) *Acia6551 {
|
||||
acia := &Acia6551{InterruptChan: interruptChan}
|
||||
acia.Reset()
|
||||
|
||||
return acia
|
||||
}
|
||||
|
||||
func (a *Acia6551) Reset() {
|
||||
a.TxChan = make(chan byte, 4096)
|
||||
|
||||
a.tx = 0
|
||||
a.txEmpty = true
|
||||
|
||||
a.rx = 0
|
||||
a.rxFull = false
|
||||
|
||||
a.lastTxWrite = 0
|
||||
a.lastRxRead = 0
|
||||
a.overrun = false
|
||||
|
||||
a.rxInterruptEnabled = false
|
||||
a.txInterruptEnabled = false
|
||||
}
|
||||
|
||||
func (a *Acia6551) Size() int {
|
||||
return 4
|
||||
}
|
||||
|
||||
func (a *Acia6551) Read(address uint16) byte {
|
||||
switch address {
|
||||
case aciaData:
|
||||
return a.rxRead()
|
||||
case aciaStatus:
|
||||
return a.statusRegister()
|
||||
case aciaCommand:
|
||||
return a.commandRegister
|
||||
case aciaControl:
|
||||
return a.controlRegister
|
||||
default:
|
||||
panic(fmt.Errorf("ACIA 6551 cannot handle addressing 0x%04X", address))
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Acia6551) rxRead() byte {
|
||||
a.lastRxRead = unixTime()
|
||||
a.overrun = false
|
||||
a.rxFull = false
|
||||
return a.rx
|
||||
}
|
||||
|
||||
func (a *Acia6551) RxWrite(data byte) {
|
||||
// Oh noes!
|
||||
if a.rxFull {
|
||||
a.overrun = true
|
||||
}
|
||||
|
||||
a.rx = data
|
||||
a.rxFull = true
|
||||
|
||||
if a.rxInterruptEnabled {
|
||||
a.InterruptChan <- true
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Acia6551) statusRegister() byte {
|
||||
now := unixTime()
|
||||
status := byte(0)
|
||||
|
||||
if a.rxFull && (now >= (a.lastRxRead + a.baudRateDelay)) {
|
||||
status |= 0x08
|
||||
}
|
||||
|
||||
if a.txEmpty && (now >= (a.lastTxWrite + a.baudRateDelay)) {
|
||||
status |= 0x10
|
||||
}
|
||||
|
||||
if a.overrun {
|
||||
status |= 0x04
|
||||
}
|
||||
|
||||
return status
|
||||
}
|
||||
|
||||
func (a *Acia6551) Write(address uint16, value byte) {
|
||||
switch address {
|
||||
case aciaData:
|
||||
a.txWrite(value)
|
||||
case aciaStatus:
|
||||
a.Reset()
|
||||
case aciaCommand:
|
||||
a.setCommandRegister(value)
|
||||
case aciaControl:
|
||||
a.setControlRegister(value)
|
||||
default:
|
||||
panic(fmt.Errorf("ACIA 6551 cannot handle addressing 0x%04X", address))
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Acia6551) txWrite(value byte) {
|
||||
a.lastTxWrite = unixTime()
|
||||
a.tx = value
|
||||
a.txEmpty = false
|
||||
|
||||
// Post for others
|
||||
a.debugTxOutput()
|
||||
}
|
||||
|
||||
func (a *Acia6551) TxRead() byte {
|
||||
a.txEmpty = true
|
||||
|
||||
if a.txInterruptEnabled {
|
||||
a.InterruptChan <- true
|
||||
}
|
||||
|
||||
return a.tx
|
||||
}
|
||||
|
||||
func (a *Acia6551) HasTx() bool {
|
||||
return !a.txEmpty
|
||||
}
|
||||
|
||||
func (a *Acia6551) HasRx() bool {
|
||||
return a.rxFull
|
||||
}
|
||||
|
||||
func (a *Acia6551) debugTxOutput() {
|
||||
if a.HasTx() {
|
||||
a.TxChan <- a.TxRead()
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Acia6551) setCommandRegister(data byte) {
|
||||
fmt.Printf("Setting Acia6551 Command Register: %02X\n", data)
|
||||
|
||||
a.commandRegister = data
|
||||
|
||||
a.rxInterruptEnabled = ((data >> 1) & 1) == 0
|
||||
a.txInterruptEnabled = ((data>>2)&1) == 1 && ((data>>3)&1) == 0
|
||||
|
||||
fmt.Printf("RxIRQ: %t; TxIRQ: %t\n", a.rxInterruptEnabled, a.txInterruptEnabled)
|
||||
}
|
||||
|
||||
func (a *Acia6551) setControlRegister(data byte) {
|
||||
a.controlRegister = data
|
||||
|
||||
if data == 0x00 {
|
||||
a.Reset()
|
||||
} else {
|
||||
a.setBaudRate(baudRateSelectors[data&0x0f])
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Acia6551) setBaudRate(baudRate int) {
|
||||
fmt.Printf("Setting baudrate at %d\n", baudRate)
|
||||
|
||||
a.baudRate = baudRate
|
||||
|
||||
// Set baudRateDelay in nanoseconds. It's an approximation.
|
||||
if baudRate > 0 {
|
||||
a.baudRateDelay = int64((1.0 / float64(baudRate)) * 1000000000 * 8)
|
||||
} else {
|
||||
a.baudRateDelay = 0
|
||||
}
|
||||
}
|
||||
|
||||
func unixTime() int64 {
|
||||
return time.Now().UnixNano()
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package devices
|
||||
|
||||
type Via6522 struct {
|
||||
}
|
||||
|
||||
func (v *Via6522) Size() int {
|
||||
return 0x10
|
||||
}
|
||||
|
||||
func (v *Via6522) Read(address uint16) byte {
|
||||
return 0x00
|
||||
}
|
||||
|
||||
func (v *Via6522) Write(address uint16, data byte) {
|
||||
// NOP
|
||||
}
|
||||
|
||||
func NewVia6522(interruptChan chan bool) *Via6522 {
|
||||
via := &Via6522{}
|
||||
return via
|
||||
}
|
99
home.html
99
home.html
@ -1,99 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>i6502 Serial Terminal</title>
|
||||
<link href='http://fonts.googleapis.com/css?family=Droid+Sans+Mono' rel='stylesheet' type='text/css'>
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
|
||||
var conn;
|
||||
var msg = $("#msg");
|
||||
var log = $("#log");
|
||||
|
||||
function appendLog(msg) {
|
||||
var d = log[0]
|
||||
var doScroll = d.scrollTop == d.scrollHeight - d.clientHeight;
|
||||
msg.appendTo(log)
|
||||
if (doScroll) {
|
||||
d.scrollTop = d.scrollHeight - d.clientHeight;
|
||||
}
|
||||
}
|
||||
|
||||
$("#form").submit(function() {
|
||||
if (!conn) {
|
||||
return false;
|
||||
}
|
||||
if (!msg.val()) {
|
||||
return false;
|
||||
}
|
||||
conn.send(msg.val());
|
||||
msg.val("");
|
||||
return false
|
||||
});
|
||||
|
||||
if (window["WebSocket"]) {
|
||||
conn = new WebSocket("ws://{{$}}/ws");
|
||||
conn.onclose = function(evt) {
|
||||
appendLog($("<div><b>Connection closed.</b></div>"))
|
||||
}
|
||||
conn.onmessage = function(evt) {
|
||||
if (evt.data.indexOf("\n") == 0) {
|
||||
appendLog($("<br/>"))
|
||||
} else {
|
||||
appendLog($("<span/>").text(evt.data))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
appendLog($("<div><b>Your browser does not support WebSockets.</b></div>"))
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style type="text/css">
|
||||
html {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: gray;
|
||||
}
|
||||
|
||||
#log {
|
||||
background: black;
|
||||
color: #e0e0e0;
|
||||
font-family: 'Droid Sans Mono', courier;
|
||||
margin: 0;
|
||||
padding: 0.5em 0.5em 0.5em 0.5em;
|
||||
position: absolute;
|
||||
top: 0.5em;
|
||||
left: 0.5em;
|
||||
right: 0.5em;
|
||||
bottom: 3em;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#form {
|
||||
padding: 0 0.5em 0 0.5em;
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
bottom: 1em;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<form id="form">
|
||||
<input type="submit" value="Send" />
|
||||
<input type="text" id="msg" size="64"/>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
75
i6502.go.old
75
i6502.go.old
@ -1,75 +0,0 @@
|
||||
/*
|
||||
i6502 is a software emulator of my i6502 home-built computer. It uses
|
||||
the 65C02 microprocessor, 32kB RAM and 16kB ROM.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/ariejan/i6502/bus"
|
||||
"github.com/ariejan/i6502/cpu"
|
||||
"github.com/ariejan/i6502/devices"
|
||||
"github.com/ariejan/i6502/memory"
|
||||
"os"
|
||||
"os/signal"
|
||||
)
|
||||
|
||||
func main() {
|
||||
os.Exit(mainReturningStatus())
|
||||
}
|
||||
|
||||
func mainReturningStatus() int {
|
||||
// 32kB RAM
|
||||
ram := memory.CreateRam()
|
||||
|
||||
// 16kB ROM, filled from file
|
||||
rom, err := memory.LoadRomFromFile("rom/ehbasic.rom")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
acia6551 := devices.NewAcia6551()
|
||||
|
||||
// 16-bit address bus
|
||||
bus, _ := bus.CreateBus()
|
||||
bus.Attach(ram, "32kB RAM", 0x0000)
|
||||
bus.Attach(rom, "16kB ROM", 0xC000)
|
||||
bus.Attach(acia6551, "ACIA 6551", 0x8800)
|
||||
|
||||
fmt.Println(bus)
|
||||
|
||||
exitChan := make(chan int, 0)
|
||||
|
||||
cpu := &cpu.Cpu{Bus: bus, ExitChan: exitChan}
|
||||
cpu.Reset()
|
||||
|
||||
go serialServer(cpu, acia6551)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
cpu.Step()
|
||||
}
|
||||
}()
|
||||
|
||||
var (
|
||||
sig os.Signal
|
||||
exitStatus int
|
||||
)
|
||||
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, os.Interrupt)
|
||||
|
||||
select {
|
||||
case exitStatus = <-exitChan:
|
||||
// Okay, handle the rest of the code
|
||||
case sig = <-sigChan:
|
||||
fmt.Println("\nGot signal: ", sig)
|
||||
exitStatus = 1
|
||||
}
|
||||
|
||||
fmt.Println(cpu)
|
||||
fmt.Println("Dumping RAM into `core` file")
|
||||
ram.Dump("core")
|
||||
|
||||
return exitStatus
|
||||
}
|
93
machine.go
93
machine.go
@ -1,93 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/ariejan/i6502/bus"
|
||||
"github.com/ariejan/i6502/cpu"
|
||||
"github.com/ariejan/i6502/devices"
|
||||
"github.com/ariejan/i6502/memory"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Machine struct {
|
||||
// Outgoing bytes, using the serial interface
|
||||
SerialTx chan byte
|
||||
|
||||
// Incoming bytes, using the serial interface
|
||||
SerialRx chan byte
|
||||
|
||||
// The cpu, bus etc.
|
||||
cpu *cpu.Cpu
|
||||
bus *bus.Bus
|
||||
}
|
||||
|
||||
// Creates a new i6502 Machine instance
|
||||
func CreateMachine() *Machine {
|
||||
// Channel for handling interrupts
|
||||
irqChan := make(chan bool, 0)
|
||||
nmiChan := make(chan bool, 0)
|
||||
|
||||
ram := memory.CreateRam()
|
||||
|
||||
rom, err := memory.LoadRomFromFile("rom/ehbasic.rom")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
acia6551 := devices.NewAcia6551(irqChan)
|
||||
via6522 := devices.NewVia6522(irqChan)
|
||||
|
||||
bus, _ := bus.CreateBus()
|
||||
bus.Attach(ram, "32kB RAM", 0x0000)
|
||||
bus.Attach(rom, "16kB ROM", 0xC000)
|
||||
bus.Attach(via6522, "VIA 6522 Parallel", 0x8000)
|
||||
bus.Attach(acia6551, "ACIA 6551 Serial", 0x8800)
|
||||
|
||||
cpu := &cpu.Cpu{Bus: bus, IrqChan: irqChan, NmiChan: nmiChan, ExitChan: make(chan int, 0)}
|
||||
|
||||
machine := &Machine{SerialTx: make(chan byte, 256), SerialRx: make(chan byte, 256), cpu: cpu, bus: bus}
|
||||
|
||||
// Run the CPU
|
||||
go func() {
|
||||
for {
|
||||
cpu.Step()
|
||||
}
|
||||
}()
|
||||
|
||||
// Connect acia6551 Tx to SerialTx
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case data := <-acia6551.TxChan:
|
||||
machine.SerialTx <- data
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Connect SerialRx to acia6551 Rx
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case data := <-machine.SerialRx:
|
||||
acia6551.RxWrite(data)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-machine.cpu.ExitChan:
|
||||
ram.Dump("intcore")
|
||||
os.Exit(42)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
cpu.Reset()
|
||||
|
||||
return machine
|
||||
}
|
||||
|
||||
func (m *Machine) Reset() {
|
||||
m.cpu.Reset()
|
||||
}
|
40
main.go
40
main.go
@ -1,40 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"net/http"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var (
|
||||
addr = flag.String("addr", ":6123", "http service address")
|
||||
homeTmpl = template.Must(template.ParseFiles("home.html"))
|
||||
)
|
||||
|
||||
func serveHome(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/" {
|
||||
http.Error(w, "Not found", 404)
|
||||
return
|
||||
}
|
||||
|
||||
if r.Method != "GET" {
|
||||
http.Error(w, "Method not allowed", 405)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
homeTmpl.Execute(w, r.Host)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
http.HandleFunc("/", serveHome)
|
||||
http.HandleFunc("/ws", serveWs)
|
||||
|
||||
err := http.ListenAndServe(*addr, nil)
|
||||
if err != nil {
|
||||
log.Fatal("ListenAndServe: ", err)
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
// Package memory provides different memory components
|
||||
// and a common interface.
|
||||
package memory
|
||||
|
||||
type Memory interface {
|
||||
Size() int
|
||||
Read(address uint16) byte
|
||||
Write(address uint16, value byte)
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// Ram provides 32kB of Random Access Memory
|
||||
type Ram struct {
|
||||
data [0x8000]byte
|
||||
}
|
||||
|
||||
func (ram *Ram) Size() int {
|
||||
return len(ram.data)
|
||||
}
|
||||
|
||||
func (ram *Ram) Read(address uint16) byte {
|
||||
return ram.data[address]
|
||||
}
|
||||
|
||||
func (ram *Ram) Write(address uint16, value byte) {
|
||||
ram.data[address] = value
|
||||
}
|
||||
|
||||
func CreateRam() *Ram {
|
||||
return &Ram{}
|
||||
}
|
||||
|
||||
func (ram *Ram) Dump(path string) {
|
||||
err := ioutil.WriteFile(path, ram.data[:], 0640)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// Rom provies 16kB of Read Only Memory, typcially loaded from file.
|
||||
type Rom struct {
|
||||
data [0x4000]byte
|
||||
}
|
||||
|
||||
func (rom *Rom) Size() int {
|
||||
return len(rom.data)
|
||||
}
|
||||
|
||||
func (rom *Rom) Read(address uint16) byte {
|
||||
return rom.data[address]
|
||||
}
|
||||
|
||||
func (rom *Rom) Write(address uint16, value byte) {
|
||||
panic("Cannot write to ROM!")
|
||||
}
|
||||
|
||||
// Load ROM from a binary file. The data is placed
|
||||
// at the beginning of ROM.
|
||||
func LoadRomFromFile(path string) (*Rom, error) {
|
||||
// Read data from file
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create the rom instance
|
||||
rom := &Rom{}
|
||||
|
||||
// Load data into ROM
|
||||
for i, b := range data {
|
||||
rom.data[i] = b
|
||||
}
|
||||
|
||||
return rom, nil
|
||||
}
|
@ -1 +0,0 @@
|
||||
../../asm/build/kernal.rom
|
@ -1,60 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/ariejan/i6502/cpu"
|
||||
"github.com/ariejan/i6502/devices"
|
||||
"net"
|
||||
)
|
||||
|
||||
func handleConnection(c net.Conn, cpu *cpu.Cpu, acia *devices.Acia6551) {
|
||||
|
||||
// Force telnet into character mode
|
||||
c.Write([]byte("\377\375\042\377\373\001"))
|
||||
c.Write([]byte("-- i6502 Serial Terminal --\n"))
|
||||
|
||||
// Transfer output to the client
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case data := <-acia.TxChan:
|
||||
c.Write([]byte{data})
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
reader := bufio.NewReader(c)
|
||||
|
||||
for {
|
||||
b, err := reader.ReadByte()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Push to CPU
|
||||
acia.RxChan <- b
|
||||
}
|
||||
}()
|
||||
|
||||
fmt.Println("Client connected. Resetting CPU.")
|
||||
cpu.Reset()
|
||||
}
|
||||
|
||||
func serialServer(cpu *cpu.Cpu, acia *devices.Acia6551) {
|
||||
listen, err := net.Listen("tcp", ":6000")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for {
|
||||
conn, err := listen.Accept()
|
||||
defer conn.Close()
|
||||
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
go handleConnection(conn, cpu, acia)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user