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  }