From 5cfa7ada6545a223cfb5f28a4c92ac1fc9fb10c5 Mon Sep 17 00:00:00 2001 From: Scott Moynes Date: Thu, 23 Nov 2023 14:35:19 -0500 Subject: [PATCH] Update tutorial --- docs/TUTORIAL.md | 73 ++++++++++++++++++++++++---------------- examples/example.asm | 12 +++++++ internal/asm/gen.go | 2 +- internal/cli/cmd/asm.go | 26 ++++++++------ internal/cli/cmd/help.go | 4 +-- 5 files changed, 75 insertions(+), 42 deletions(-) create mode 100644 examples/example.asm diff --git a/docs/TUTORIAL.md b/docs/TUTORIAL.md index 10f29ca..3465f25 100644 --- a/docs/TUTORIAL.md +++ b/docs/TUTORIAL.md @@ -9,10 +9,10 @@ In this tutorial you will learn how to: language; and - execute the resulting program. -## Installation ## +## Dependencies ## -To install the π”Όπ•ƒπ•Šπ•€π”Ό, you will need Go version 1.21, or greater. To check if you -have a good version, run: +To install π”Όπ•ƒπ•Šπ•€π”Ό, you will need Go version 1.21, or greater. You can check if +you have a recent enough by running: ```console $ go version @@ -20,10 +20,10 @@ go version go1.21.4 darwin/amd64 ``` If you do not have Go 1.21 installed, you can get it from the Go download page: -https://go.dev/dl/ +. Alternatively, you might be able to use a package manager +for your platform to install a compatible Go version. -Alternatively, you might be able to use a package manager for your platform to -install a compatible Go version. +## Installation ## With Go installed, you can now download, build, and install π”Όπ•ƒπ•Šπ•€π”Ό. Run: @@ -32,8 +32,8 @@ $ go install github.com/smoynes/elsie@latset ``` Go will store the program in its `bin` directory. By default, the location is -configured with the `GOBIN` environment variable or the `GOPATH/bin` directory. -You can check with: +configured with the `GOBIN` environment variable or, if not set, the default is +the `GOPATH/bin` directory. You can check with: ```console $ go env GOPATH GOBIN @@ -42,8 +42,8 @@ $ go env GOPATH GOBIN ``` In this case `GOBIN` is unset so the `elsie` command is installed in -`/home/elise/bin/1.21.4/bin`. This directory may or may not be present in your -shell's `PATH`. For the sake of the tutorial, we'll assume it, is but do consult +`/home/elise/go/1.21.4/bin`. This directory may or may not be present in your +shell's `PATH`. For the sake of the tutorial, we'll assume it is, but do consult your configuration to add this directory to your system or user configuration. Say hello: @@ -58,10 +58,10 @@ Usage: elsie [option]... [arg]... Commands: - exec run a program - asm assemble source code into object code - demo run demo program - help display help for commands + exec run a program + asm assemble source code into object code + demo run demo program + help display help for commands Use `elsie help ` to get help for a command. exit status 1 @@ -71,7 +71,7 @@ exit status 1 ## Running the demo ## π”Όπ•ƒπ•Šπ•€π”Ό includes a silly, hard-coded demo that you can run it with the `demo` -command. You should see a few shocked characters printed and a message of +command. You should see a few shocked characters slowly printed and a message of gratitude. This is what success looks like: ```console @@ -84,11 +84,17 @@ MACHINE HALTED! ``` -The demo initialize the machine, outputs a message using BIOS system-calls, and -halts the machine. It is not much, but it is an honest program. +The demo does quite a bit of work for little reward. In detail, the demo: + +- initializes the virtual machine; +- loads a system image and the program into memory; +- executes the program instructions in sequence according to the control flow; +- outputs a message using BIOS system-calls, themselves small programs; +- halts the virtual machine. -You can also run the demo with additional logging enabled. You will see logs for -machine startup and its state after executing each instruction. +It is not much, but it is an honest program. You can also run the demo with +additional logging enabled. You will see logs for machine startup and its state +after executing each instruction. ```console $ elsie demo -log @@ -112,15 +118,17 @@ $ elsie demo -log ## Writing a program ## -While a hard-coded demo is impressive, it is also deeply unsatisfying. It is not -enough to interpret a pointless, pre-written program -- we also want to write -our own pointless programs for our machine to interpret. +A hard-coded demo is both impressive andd deeply unsatisfying. It is not enough +to interpret a pointless, pre-written program -- we also want to write our own +pointless programs! π”Όπ•ƒπ•Šπ•€π”Ό includes a translator that lets us write programs in a simple assembly -dialect called LC3ASM. We will use the `elsie asm` command to run the -assembler and produce machine code. Later, we will execute the program. +dialect called LC3ASM. We will use the `elsie asm` command to run +the assembler and produce machine code. Later, we will use its output to execute +our program. With this simple command, the full power of the LC-3 is at the tips +of our fingers. -First save a file named `example.asm` +First, save a file named `example.asm` ```asm .ORIG x3000 @@ -137,7 +145,7 @@ ASCII .DW 48 .END ``` -We'll look at the code in detail later -- for now, we'll run the assembler on +We'll look at the code in detail later on -- for now, we'll run the assembler on it: ```console @@ -145,8 +153,11 @@ $ elsie asm example.asm ``` No output is produced if successful. You may see error messages if you copied -the code incorrectly. The output is stored in a file called `a.o`, for lack of a -better default. It's contents should look like: +the code incorrectly or something else went wrong. You can run `elsie asm -log +example.asm` if you would like a chattier assembler. + +The output is stored in a file called `a.o`, for lack of a better default. It's +contents should look like: ``` :143000002207240704041081f021127f0ffbf02500050030d9 @@ -154,7 +165,11 @@ better default. It's contents should look like: ``` As you might be able to guess, the machine code is encoded as bytes in something -hexadecimal-y. This is all that is needed to be executed. +hexadecimal-y. This is object code, or byte code as it is sometimes called. This +is all that is needed to execute: π”Όπ•ƒπ•Šπ•€π”Ό loads this data into memory and begins +executing instructions herein. + +To execute the object code: ```console $ elsie exec a.o diff --git a/examples/example.asm b/examples/example.asm new file mode 100644 index 0000000..f9f37a4 --- /dev/null +++ b/examples/example.asm @@ -0,0 +1,12 @@ + .ORIG x3000 + LD R1,COUNT + LD R2,ASCII +LOOP BRz EXIT + ADD R0,R2,R1 + TRAP x21 + ADD R1,R1,#-1 + BR LOOP +EXIT HALT +COUNT .DW 5 +ASCII .DW 48 + .END diff --git a/internal/asm/gen.go b/internal/asm/gen.go index 262162c..252750f 100644 --- a/internal/asm/gen.go +++ b/internal/asm/gen.go @@ -16,7 +16,7 @@ import ( // // The generator starts at the beginning of the parsed-syntax table, generates code for each // operation, and then writes the generated code to the output (usually, a file). Use Encode to -// write as hex-encoded ASCII files or use WriteTo write binary object-code to a buffer. +// write as hex-encoded ASCII files or use WriteTo write to binary object-code to a buffer. // // During the generation pass, any syntax or semantic errors that prevent generating machine code // are immediately returned. The errors are wrapped in SyntaxErrors and may be tested and retrieved diff --git a/internal/cli/cmd/asm.go b/internal/cli/cmd/asm.go index 416a230..41924c4 100644 --- a/internal/cli/cmd/asm.go +++ b/internal/cli/cmd/asm.go @@ -22,6 +22,7 @@ func Assembler() cli.Command { } type assembler struct { + log bool debug bool output string } @@ -41,6 +42,7 @@ Assemble source into object code.`) func (a *assembler) FlagSet() *cli.FlagSet { fs := flag.NewFlagSet("asm", flag.ExitOnError) + fs.BoolVar(&a.log, "log", false, "enable logging") fs.BoolVar(&a.debug, "debug", false, "enable debug logging") fs.StringVar(&a.output, "o", "a.o", "output `filename`") @@ -49,7 +51,9 @@ func (a *assembler) FlagSet() *cli.FlagSet { // Run calls the assembler to assemble the assembly. func (a *assembler) Run(ctx context.Context, args []string, stdout io.Writer, logger *log.Logger) int { - if a.debug { + if a.log { + log.LogLevel.Set(log.Info) + } else if a.debug { log.LogLevel.Set(log.Debug) } @@ -65,14 +69,15 @@ func (a *assembler) Run(ctx context.Context, args []string, stdout io.Writer, lo return 1 } + logger.Info("Parsing source", "file", fn) parser.Parse(f) - } - logger.Debug("Parsed source", - "symbols", parser.Symbols().Count(), - "size", parser.Syntax().Size(), - "err", parser.Err(), - ) + logger.Debug("Parsed source", + "symbols", parser.Symbols().Count(), + "size", parser.Syntax().Size(), + "err", parser.Err(), + ) + } if parser.Err() != nil { logger.Error("Parse error", "err", parser.Err()) @@ -89,11 +94,10 @@ func (a *assembler) Run(ctx context.Context, args []string, stdout io.Writer, lo symbols := parser.Symbols() syntax := parser.Syntax() generator := asm.NewGenerator(symbols, syntax) + buf := bufio.NewWriter(out) logger.Debug("Writing object", "file", a.output) - buf := bufio.NewWriter(out) - objCode, err := generator.Encode() if err != nil { logger.Error("Compile error", "out", a.output, "err", err) @@ -106,12 +110,14 @@ func (a *assembler) Run(ctx context.Context, args []string, stdout io.Writer, lo return -1 } + logger.Debug("Wrote object", "file", a.output, "size", wrote) + if err := buf.Flush(); err != nil { logger.Error("I/O error", "out", a.output, "err", err) return -1 } - logger.Debug("Compiled object", + logger.Info("Compiled object", "out", a.output, "size", wrote, "symbols", symbols.Count(), diff --git a/internal/cli/cmd/help.go b/internal/cli/cmd/help.go index 055acc2..557f65e 100644 --- a/internal/cli/cmd/help.go +++ b/internal/cli/cmd/help.go @@ -56,10 +56,10 @@ Commands:`) for _, cmd := range h.cmd { fs := cmd.FlagSet() - fmt.Fprintf(out, " %-20s %s\n", fs.Name(), cmd.Description()) + fmt.Fprintf(out, " %-8s %s\n", fs.Name(), cmd.Description()) } - fmt.Fprintf(out, " %-20s %s\n", h.FlagSet().Name(), h.Description()) + fmt.Fprintf(out, " %-8s %s\n", h.FlagSet().Name(), h.Description()) fmt.Fprintln(out) fmt.Fprintln(out, "Use `elsie help ` to get help for a command.")