github.com/jflude/taocp@v0.0.0-20240210234939-99f2a91af3c2/cmd/mix/main.go (about) 1 package main 2 3 import ( 4 "errors" 5 "flag" 6 "fmt" 7 "log" 8 "os" 9 "strings" 10 11 "github.com/jflude/taocp/mix" 12 ) 13 14 func main() { 15 log.SetFlags(0) 16 if err := run(); err != nil { 17 if errors.Is(err, mix.ErrHalted) { 18 log.Println(err) 19 } else { 20 log.Fatalln("error:", err) 21 } 22 } 23 } 24 25 func run() (err error) { 26 c := mix.NewComputer() 27 if env, ok := os.LookupEnv("MIX_ENABLE_INTERRUPTS"); ok { 28 env = strings.ToLower(env) 29 c.Interrupts = (env[0] == '1' || env[0] == 'y' || env == "true") 30 } 31 var unit [mix.DeviceCount]string 32 binding := *mix.DefaultBinding 33 for i, v := range binding { 34 if v != nil { 35 unit[i] = v.(string) 36 } 37 } 38 var op bool 39 var trace string 40 flag.IntVar(&c.BootFrom, "boot", c.BootFrom, "unit to boot from") 41 flag.BoolVar(&c.Interrupts, "int", c.Interrupts, "enable interrupts") 42 flag.BoolVar(&op, "op", op, "involve the operator") 43 flag.StringVar(&trace, "trace", trace, "output trace to file") 44 flag.IntVar(&c.Trigger, "trigger", c.Trigger, 45 "trace addresses above this") 46 flag.StringVar(&unit[0], "t0", unit[0], "") 47 flag.StringVar(&unit[1], "t1", unit[1], "") 48 flag.StringVar(&unit[2], "t2", unit[2], "") 49 flag.StringVar(&unit[3], "t3", unit[3], "") 50 flag.StringVar(&unit[4], "t4", unit[4], "") 51 flag.StringVar(&unit[5], "t5", unit[5], "") 52 flag.StringVar(&unit[6], "t6", unit[6], "") 53 flag.StringVar(&unit[7], "t7", unit[7], "") 54 flag.StringVar(&unit[8], "d8", unit[8], "") 55 flag.StringVar(&unit[9], "d9", unit[9], "") 56 flag.StringVar(&unit[10], "d10", unit[10], "") 57 flag.StringVar(&unit[11], "d11", unit[11], "") 58 flag.StringVar(&unit[12], "d12", unit[12], "") 59 flag.StringVar(&unit[13], "d13", unit[13], "") 60 flag.StringVar(&unit[14], "d14", unit[14], "") 61 flag.StringVar(&unit[15], "d15", unit[15], "") 62 flag.StringVar(&unit[16], "rdr", unit[16], "") 63 flag.StringVar(&unit[17], "pun", unit[17], "") 64 flag.StringVar(&unit[18], "prt", unit[18], "") 65 flag.StringVar(&unit[19], "tty", unit[19], "") 66 flag.StringVar(&unit[20], "pap", unit[20], "") 67 flag.Parse() 68 for i, v := range unit { 69 if v != "" { 70 binding[i] = v 71 } else { 72 binding[i] = nil 73 } 74 } 75 if err = c.Bind(&binding); err != nil { 76 return 77 } 78 defer func() { 79 if err2 := c.Shutdown(); err2 != nil { 80 if err == nil { 81 err = err2 82 } else { 83 log.Println("error:", err2) 84 } 85 } 86 }() 87 if trace != "" { 88 c.Tracer, err = os.OpenFile(trace, 89 os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 90 if err != nil { 91 return 92 } 93 defer func() { 94 if err2 := c.Tracer.Close(); err2 != nil { 95 if err == nil { 96 err = err2 97 } else { 98 log.Println("error:", err2) 99 } 100 } 101 }() 102 } 103 if !op { 104 err = reportGo(c) 105 return 106 } 107 for { 108 var y bool 109 if y, err = yesOrNo("Go (Y/n)? "); err != nil || !y { 110 break 111 } 112 if err = reportGo(c); !errors.Is(err, mix.ErrHalted) { 113 break 114 } 115 } 116 return 117 } 118 119 func yesOrNo(prompt string) (bool, error) { 120 fmt.Print(prompt) 121 var s string 122 _, err := fmt.Scanf("%s", &s) 123 if err != nil && !strings.Contains(err.Error(), "unexpected newline") { 124 return false, err 125 } 126 return s == "" || s[0] == 'y' || s[0] == 'Y', nil 127 } 128 129 func reportGo(c *mix.Computer) error { 130 err := c.GoButton() 131 if c.Elapsed > 0 { 132 err = fmt.Errorf("%w (elapsed: %du, idle: %du)", 133 err, c.Elapsed, c.Idle) 134 } 135 return err 136 }