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:
Ariejan de Vroom 2014-08-12 20:58:07 +02:00
parent afa7d0d3f6
commit 0eaa592103
19 changed files with 0 additions and 2031 deletions

View File

@ -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))
}

View File

@ -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)
}

View File

@ -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()
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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},
}

View File

@ -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()
}

View File

@ -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
}

View File

@ -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>

View File

@ -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
}

BIN
intcore

Binary file not shown.

View File

@ -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()
}

BIN
main

Binary file not shown.

40
main.go
View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -1 +0,0 @@
../../asm/build/kernal.rom

View File

@ -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)
}
}