mksim
A PDP-8 emulator written in Go.
package main
import (
"bufio"
"fmt"
"io"
"os"
"strconv"
)
// Two complement's add 2 x 12-bit unsigned integers stored as uint16's
// Returns a 12-bit usigned int stored as uint16, and a carry flag to signify an overflow has
// occurred.
func MKadd(a, b uint16) (x uint16, c bool) {
c = false
x = a + b
// Check for 12-bit overflow
if x > 4095 {
c = true
x = x - 4096
}
return x, c
}
func MKcomplement(a uint16) (x uint16) {
// Complement the last 12 bits
x = a ^ 0o7777
// y := uint16(a)
// for i := 0; i < 12; i++ {
// x = (int16((1^a>>i)&1) << i) | x
// }
// // Detect signed int and convert it to negative
// if ((x >> 11) & 1) == 1 {
// // Keep bottom 11 bits
// x &= 0b011111111111
// // Set top bit to 1 (Convert to negative number)
// x |= 0b100000000000
// }
return
}
func MKrotateRight(a uint16, l bool) (x uint16, y bool) {
if a&1 == 1 {
y = true
}
x = a >> 1
if l {
x |= 0b100000000000
}
return
}
func MKrotateLeft(a uint16, l bool) (x uint16, y bool) {
if (a>>11)&1 == 1 {
y = true
}
x = (a << 1) & 0b111111111111
if l {
x |= 0b1
}
return
}
// Load an object file produced by pdpnasm.
// This function returns an array of 4096 int16's representing pdp8 memory
func LoadPObjFile(filename string) (mem [4096]uint16, err error) {
err = nil
// Open file and create new scanner
objFile, err := os.Open(filename)
if err != nil {
return
}
defer objFile.Close()
scanner := bufio.NewScanner(objFile)
// Loop over file line by line
var addr uint16 = 0
var data uint16
var rawData uint64
for scanner.Scan() {
rawData, err = strconv.ParseUint(scanner.Text(), 8, 16)
if err != nil {
return
}
data = uint16(rawData)
// If the 13th bit is set it's an address
if data > 0o7777 {
addr = (data & 0o7777)
} else {
mem[addr] = data
addr++
}
}
return
}
// Load a binary file in RIM format. These are produced by mkasm, but
// the RIM format was originally used for paper tapes for the PDP-8.
func LoadRIMFile(filename string) (mem [4096]uint16, err error) {
// Open file and create a new reader
rimFile, err := os.Open(filename)
if err != nil {
return
}
defer rimFile.Close()
rimReader := bufio.NewReader(rimFile)
// Skip over leading `0o200` bytes
for b, _ := rimReader.Peek(1); b[0] == 0o200; b, _ = rimReader.Peek(1) {
_, err = rimReader.Discard(1)
if err != nil {
return
}
}
// Loop until EOF or trailing `0o200` bytes
for {
block := make([]byte, 4)
read, err := rimReader.Read(block)
if read != len(block) && read > 0 { // Sometimes err == EOF and read != 0
if block[0] != 0o200 {
return mem, err
// panic("Incorrect format")
}
}
if err != nil {
if err == io.EOF {
break
} else {
return mem, err
}
}
var addr, data uint16
if b := block[0]; b == 0o200 { // Trailer bytes, break from loop
break
} else if b>>6&1 == 1 {
// Start of address byte, this means the format is correct-ish
addr = (uint16(block[0]&0o77) << 6) | uint16(block[1]&0o77)
data = (uint16(block[2]&0o77) << 6) | uint16(block[3]&0o77)
} else {
return mem, fmt.Errorf("incorrect format")
// panic("Incorrect format")
}
mem[addr] = data
}
return
}