mksim

A PDP-8 emulator written in Go.

package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"os"
	"os/exec"
)

type CLIArgs struct {
	ProgName string
	InFile   string

	NoGui bool

	Return bool // Print AC before exiting

	// HALT on startup
	HALT bool
	// Exit on HALT
	EXIT bool

	// Clock Speed
	F_CPU int64

	// File[path] to use as virtual paper tape
	// TapeFile  string
	// iTapeFile string
	// oTapeFile string

	// Lock memory viewer to page
	Page int
}

func printUsage() {
	fmt.Println("Usage:", os.Args[0], "[options] <in_file>")
	fmt.Printf("\nOptions:\n")
	flag.PrintDefaults()
}

func parseArgs() CLIArgs {

	args := CLIArgs{}

	// Set program name and usage print function
	args.ProgName = os.Args[0]
	flag.Usage = printUsage

	// Add flags
	flag.Int64Var(&args.F_CPU, "F_CPU", 8000000, "simulated clock `speed`")

	flag.IntVar(&args.Page, "lock", -1, "Lock memory viewer to `page`")

	flag.BoolVar(&args.HALT, "halt", false, "HALT the machine before first instruction cycle")
	flag.BoolVar(&args.EXIT, "exit", false, "Exit the simulator on HALT")

	flag.BoolVar(&args.NoGui, "no-gui", false, "Do not display curses ui")

	flag.BoolVar(&args.Return, "print-return", false, "Print return code (AC) upon exiting")

	// flag.StringVar(&args.TapeFile, "tape", "mk-12.tape", "Specify `path` to file for virtual tape reader/punch")
	// flag.StringVar(&args.iTapeFile, "itape", "", "Specify `path` to file for virtual tape reader")
	// flag.StringVar(&args.oTapeFile, "otape", "", "Specify `path` to file for virtual tape punch")

	help := flag.Bool("help", false, "Print this message and exit")

	// Parse
	flag.Parse()

	if *help {
		flag.Usage()
		os.Exit(0)
	}

	// if args.iTapeFile == "" {
	// 	args.iTapeFile = args.TapeFile
	// }
	// if args.oTapeFile == "" {
	// 	args.oTapeFile = args.TapeFile
	// }

	// Get remaining positional argument (infile)
	if len(flag.Args()) == 1 {
		args.InFile = flag.Arg(0)
	} else {
		flag.Usage()
		os.Exit(1)
	}

	return args
}

type CLIFrontPanel struct {
}

func (fp *CLIFrontPanel) PowerOn(mk MK12) {

}

func (fp *CLIFrontPanel) PowerOff() {

}

func (fp *CLIFrontPanel) Update(mk MK12) {

}

func (fp *CLIFrontPanel) ReadSwitches() uint16 {
	return 0
}

type StdinKeyboard struct {
	Stdin             *os.File
	lastKey           []byte
	originalSttyState bytes.Buffer
}

func (sk *StdinKeyboard) getSttyState(state *bytes.Buffer) (err error) {
	cmd := exec.Command("stty", "-g")
	cmd.Stdin = sk.Stdin
	cmd.Stdout = state
	return cmd.Run()
}

func (sk *StdinKeyboard) saveSttyState() (err error) {
	cmd := exec.Command("stty", "-g")
	cmd.Stdin = sk.Stdin
	cmd.Stdout = &sk.originalSttyState
	return cmd.Run()
}

func (sk *StdinKeyboard) setSttyState(state *bytes.Buffer) (err error) {
	cmd := exec.Command("stty", state.String())
	cmd.Stdin = sk.Stdin
	cmd.Stdout = nil
	return cmd.Run()
}

func NewStdinKeyboard() (sk StdinKeyboard) {
	sk = StdinKeyboard{
		Stdin:   os.Stdin,
		lastKey: make([]byte, 1),
	}
	sk.saveSttyState()
	defer sk.ResetSttyState()

	// disable input buffering
	err := sk.setSttyState(bytes.NewBufferString("cbreak"))
	// err := exec.Command("stty", "-F", "/dev/tty", "cbreak", "min", "1").Run()
	if err != nil {
		panic(err)
	}
	// do not display entered characters on the screen
	err = sk.setSttyState(bytes.NewBufferString("-echo"))
	// err = exec.Command("stty", "-F", "/dev/tty", "-echo").Run()
	if err != nil {
		panic(err)
	}

	return sk
}

func (sk StdinKeyboard) Buffered() (buffered int) {
	buffered, err := sk.Stdin.Read(sk.lastKey)
	if err != nil {
		if err == io.EOF {
			// sk.Stdin.Seek(0, 0)
			return 0
		}
		panic(err)
	}
	return
}

func (sk StdinKeyboard) ReadByte() (char byte, err error) {
	char = sk.lastKey[0]
	return
}

func (sk *StdinKeyboard) ResetSttyState() {
	sk.setSttyState(&sk.originalSttyState)
}