From 565d733e0fdb7b5750c9ea56ffabf44a461e8310 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Thu, 27 Oct 2022 11:49:20 -0400 Subject: [PATCH] There is an assembler now --- .gitignore | 1 + marcie/main.go | 1 + masm/hellorld | Bin 0 -> 46 bytes masm/hellorld.mas | 25 ++++ masm/main.go | 305 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 332 insertions(+) create mode 100644 masm/hellorld create mode 100644 masm/hellorld.mas create mode 100644 masm/main.go diff --git a/.gitignore b/.gitignore index f8163a0..ec01d05 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /marcie/marcie +/masm/masm diff --git a/marcie/main.go b/marcie/main.go index b44e406..b32d04f 100644 --- a/marcie/main.go +++ b/marcie/main.go @@ -7,6 +7,7 @@ import "git.tebibyte.media/sashakoshka/marcie" func main () { if len(os.Args) != 2 { fmt.Println("specify file name") + os.Exit(1) } file, err := os.Open(os.Args[1]) diff --git a/masm/hellorld b/masm/hellorld new file mode 100644 index 0000000000000000000000000000000000000000..97839d58c5b973f21b2101df19597441703441ba GIT binary patch literal 46 tcmZQzxWLiDP{5GDAi!b3slYLTfq{#Gks*U2l_3WR^BIbOWC~CW3;?pg2BiQ1 literal 0 HcmV?d00001 diff --git a/masm/hellorld.mas b/masm/hellorld.mas new file mode 100644 index 0000000..4be5998 --- /dev/null +++ b/masm/hellorld.mas @@ -0,0 +1,25 @@ +counter, adr string +one, dec 1 + +string, chr h + chr e + chr l + chr l + chr o + chr r + chr l + chr d + hex a + hex 0 + +// if the current character is zero, halt +loopStart, loadi counter +skipcond 800 + halt + +// otherwise, output it, increment, and loop +output +load counter +add one +store counter +jump loopStart diff --git a/masm/main.go b/masm/main.go new file mode 100644 index 0000000..e5fbec0 --- /dev/null +++ b/masm/main.go @@ -0,0 +1,305 @@ +package main + +import "os" +import "fmt" +import "bufio" +import "errors" +import "strings" +import "strconv" +import "git.tebibyte.media/sashakoshka/marcie" + +type Statement struct { + label string + opcode marcie.Opcode + + lineNumber int + isOperation bool + + operand int16 + reference string + wantAddress bool +} + +const ORG = marcie.Opcode(0xFF) + +var mnemonics = map[string] marcie.Opcode { + "jns": marcie.JNS, + "load": marcie.LOAD, + "store": marcie.STORE, + "add": marcie.ADD, + "subt": marcie.SUBT, + "input": marcie.INPUT, + "output": marcie.OUTPUT, + "halt": marcie.HALT, + "skipcond": marcie.SKIPCOND, + "jump": marcie.JUMP, + "clear": marcie.CLEAR, + "addi": marcie.ADDI, + "jumpi": marcie.JUMPI, + "loadi": marcie.LOADI, + "storei": marcie.STOREI, + "org": ORG, +} + +var literalParsers = map[string] func (input string) (output int16, err error) { + "hex": parseHexLiteral, + "dec": parseDecLiteral, + "chr": parseCharLiteral, +} + +func main () { + if len(os.Args) != 3 { + fmt.Println("specify input and output file names") + os.Exit(1) + } + + inputFile, err := os.Open(os.Args[1]) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + inputScanner := bufio.NewScanner(inputFile) + inputScanner.Split(bufio.ScanLines) + + origin := 0 + originSet := false + + operations := []Statement { } + symbols := []Statement { } + symbolTable := map[string] int { } + + // parse + for lineNumber := 0; inputScanner.Scan(); lineNumber ++ { + tokens := strings.Fields(inputScanner.Text()) + if len(tokens) == 0 { continue } + + statement, err := parseStatement(tokens) + statement.lineNumber = lineNumber + + if statement.opcode == ORG { + if originSet { + err = errors.New("origin may only be set once") + } else { + origin = int(statement.operand) + originSet = true + } + } + + if statement.opcode != ORG { + if statement.isOperation { + operations = append(operations, statement) + } else { + symbols = append(symbols, statement) + } + } + + if err != nil { + fmt.Println ( + "file", os.Args[1], + "line", lineNumber + 1, + err) + os.Exit(1) + } + } + + // get symbol addresses + placer := origin + statements := append(operations, symbols...) + for _, statement := range statements { + if statement.label != "" { + _, alreadyDefined := symbolTable[statement.label] + if alreadyDefined { + err = errors.New ( + "symbol " + statement.label + + "defined more than once") + } else { + symbolTable[statement.label] = placer + } + } + placer ++ + } + + if inputScanner.Err() != nil { + fmt.Println(inputScanner.Err()) + os.Exit(1) + } + + outputFile, err := os.Create(os.Args[2]) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + // for _, statement := range statements { + // fmt.Println(statement) + // } +// + // for symbol, address := range symbolTable { + // fmt.Println(symbol, address) + // } + + // write origin + buffer := make([]byte, 2) + buffer[0] = byte((uint16(origin) >> 8) & 0xFF) + buffer[1] = byte(uint16(origin) & 0xFF) + _, err = outputFile.Write(buffer) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + // zero pad + // TODO: possibly make the simulator insert the program into the memory + // at the location specified by the origin instead of doing this + for index := 0; index < origin; index ++ { + _, err = outputFile.Write([]byte { 0, 0 }) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + } + + // resolve symbols and write to file + for index := 0; index < len(statements); index ++ { + statement := &statements[index] + + if statement.reference != "" { + address, exists := symbolTable[statement.reference] + if !exists { + fmt.Println ( + "file", os.Args[1], + "line", statement.lineNumber + 1, + "symbol", statement.reference, + "not found") + os.Exit(1) + } + statement.operand = int16(address) + } + + buffer := make([]byte, 2) + + if statement.isOperation { + opcode := uint16(statement.opcode) + operand := uint16(statement.operand) + buffer[0] = byte((opcode << 4) & 0xF0) + buffer[0] |= byte((operand >> 8) & 0x0F) + buffer[1] = byte(operand & 0xFF) + + } else { + operand := uint16(statement.operand) + buffer[0] = byte((operand >> 8) & 0xFF) + buffer[1] = byte(operand & 0xFF) + } + + _, err = outputFile.Write(buffer) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + } +} + +func parseStatement (tokens []string) (statement Statement, err error) { + if tokens[0][0] == '/' { + // skip line if it is a comment + return + } + + // get label if it exists + possibleLabel := tokens[0] + if possibleLabel[len(possibleLabel) - 1] == ',' { + statement.label = possibleLabel[:len(possibleLabel) - 1] + tokens = tokens[1:] + } + + // get mnemonic if it exists + if len(tokens) < 1 { + err = errors.New("expected operand") + return + } + mnemonic := strings.ToLower(tokens[0]) + opcode, exists := mnemonics[mnemonic] + if exists { + statement.opcode = opcode + statement.isOperation = true + tokens = tokens[1:] + } else if len(tokens) < 1 { + err = errors.New("unknown opcode " + mnemonic) + return + } + + // if there is no operand, go on to the next line + if len(tokens) < 1 { return } + + // get type signifier if it exists + signifier := strings.ToLower(tokens[0]) + parseFunction, exists := literalParsers[signifier] + if exists { + tokens = tokens[1:] + + } else if statement.isOperation { + if tokens[0][0] >= '0' && tokens[0][0] <= '9' { + // this must be a hex literal + parseFunction = parseHexLiteral + } else { + // this must be a variable name + statement.reference = tokens[0] + tokens = tokens[1:] + return + } + + } else if signifier == "adr" { + // if we are parsing a variable then we can set it to the + // address of another variable + tokens = tokens[1:] + + if len(tokens) < 1 { + err = errors.New("expected symbol name") + return + } + statement.reference = tokens[0] + tokens = tokens[1:] + return + + } else { + // statement needs to have a type signifier or be an + // operation, if it is neither we assume a mistyped + // opcode + err = errors.New("unknown opcode " + mnemonic) + return + } + + if len(tokens) < 1 { + err = errors.New("expected literal") + return + } + + statement.operand, err = parseFunction(tokens[0]) + + return +} + +func parseHexLiteral (input string) (output int16, err error) { + var intOutput int64 + intOutput, err = strconv.ParseInt(input, 16, 16) + output = int16(intOutput) + return +} + +func parseDecLiteral (input string) (output int16, err error) { + var intOutput int64 + intOutput, err = strconv.ParseInt(input, 10, 16) + output = int16(intOutput) + return +} + +func parseCharLiteral (input string) (output int16, err error) { + runes := []rune(input) + if len(runes) != 1 { + err = errors.New("excess data in char literal") + return + } + output = int16(runes[0]) + return +}