diff --git a/connection.go b/connection.go new file mode 100644 index 0000000..009024e --- /dev/null +++ b/connection.go @@ -0,0 +1,108 @@ +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() +} diff --git a/devices/acia6551.go b/devices/acia6551.go index e51a243..a52fcc0 100644 --- a/devices/acia6551.go +++ b/devices/acia6551.go @@ -30,12 +30,16 @@ type Acia6551 struct { rxFull bool txEmpty bool + rxInterruptEnabled bool + txInterruptEnabled bool + + InterruptChan chan bool + RxChan chan byte TxChan chan byte } -func NewAcia6551() *Acia6551 { - fmt.Println("Resetting the Acia6551") +func NewAcia6551(cpu *Cpu) *Acia6551 { acia := &Acia6551{} acia.Reset() return acia @@ -54,6 +58,11 @@ func (a *Acia6551) Reset() { a.lastTxWrite = 0 a.lastRxRead = 0 a.overrun = false + + a.rxInterruptEnabled = false + a.txInterruptEnabled = false + + a.InterruptChan = make(chan bool, 0) } func (a *Acia6551) Size() int { @@ -91,7 +100,9 @@ func (a *Acia6551) RxWrite(data byte) { a.rx = data a.rxFull = true - // TODO: IRQs + if a.rxInterruptEnabled { + // getbus.assertIrq() + } } func (a *Acia6551) statusRegister() byte { @@ -153,7 +164,6 @@ func (a *Acia6551) HasRx() bool { func (a *Acia6551) debugTxOutput() { if a.HasTx() { a.TxChan <- a.TxRead() - fmt.Printf("%c", a.TxRead()) } } diff --git a/home.html b/home.html new file mode 100644 index 0000000..0537907 --- /dev/null +++ b/home.html @@ -0,0 +1,99 @@ + + + +i6502 Serial Terminal + + + + + + +
+
+ + +
+ + diff --git a/i6502.go b/i6502.go.old similarity index 100% rename from i6502.go rename to i6502.go.old diff --git a/machine.go b/machine.go new file mode 100644 index 0000000..5a551f8 --- /dev/null +++ b/machine.go @@ -0,0 +1,78 @@ +package main + +import ( + "github.com/ariejan/i6502/bus" + "github.com/ariejan/i6502/cpu" + "github.com/ariejan/i6502/devices" + "github.com/ariejan/i6502/memory" + "log" +) + +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 { + ram := memory.CreateRam() + + rom, err := memory.LoadRomFromFile("rom/ehbasic.rom") + if err != nil { + panic(err) + } + + acia6551 := devices.NewAcia6551() + + bus, _ := bus.CreateBus() + bus.Attach(ram, "32kB RAM", 0x0000) + bus.Attach(rom, "16kB ROM", 0xC000) + bus.Attach(acia6551, "ACIA 6551 Serial", 0x8800) + + cpu := &cpu.Cpu{Bus: bus, 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: + log.Printf("Rx: %c", data) + acia6551.RxChan <- data + } + } + }() + + cpu.Reset() + + return machine +} + +func (m *Machine) Reset() { + m.cpu.Reset() +} diff --git a/main b/main new file mode 100755 index 0000000..8339b8c Binary files /dev/null and b/main differ diff --git a/main.go b/main.go new file mode 100644 index 0000000..1e41695 --- /dev/null +++ b/main.go @@ -0,0 +1,40 @@ +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) + } +} diff --git a/rom/ehbasic.rom b/rom/ehbasic.rom index 24f4bef..e88c0c9 100644 Binary files a/rom/ehbasic.rom and b/rom/ehbasic.rom differ diff --git a/server.go b/server.go.old similarity index 100% rename from server.go rename to server.go.old