From f3a84599a7034c2e746b952cd4bc9aa5967e4083 Mon Sep 17 00:00:00 2001 From: Jacob McDonnell Date: Thu, 23 May 2024 13:47:06 -0400 Subject: Refactoring Project --- assemble.go | 281 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 3 + parse.go | 51 ++++++++++ qdma | Bin 2355595 -> 0 bytes qdma.go | 243 ------------------------------------------------ symbol.go | 52 +++++++++++ test6.asm | 18 ++++ test6.bin | Bin 0 -> 28 bytes tests/test5.bin | Bin 56 -> 56 bytes 9 files changed, 405 insertions(+), 243 deletions(-) create mode 100755 assemble.go create mode 100755 go.mod create mode 100755 parse.go delete mode 100755 qdma create mode 100755 symbol.go create mode 100755 test6.asm create mode 100755 test6.bin mode change 100644 => 100755 tests/test5.bin diff --git a/assemble.go b/assemble.go new file mode 100755 index 0000000..98af916 --- /dev/null +++ b/assemble.go @@ -0,0 +1,281 @@ +package main + +import ( + "bufio" + "encoding/binary" + "fmt" + "os" + "regexp" + "strconv" + "strings" +) + +var isText bool = true + +var Instructions = map[string]struct { + isRtype bool + opcode uint32 +}{ + "add": {true, 32}, + "addi": {false, 8}, + "addiu": {false, 9}, + "sub": {true, 34}, + "subu": {true, 35}, + "mult": {true, 24}, + "multu": {true, 25}, + "div": {true, 26}, + "divu": {true, 27}, + "and": {true, 36}, + "andi": {false, 12}, + "or": {true, 37}, + "ori": {false, 13}, + "xor": {true, 38}, + "xori": {false, 14}, + "sll": {true, 0}, + "sra": {true, 3}, + "srl": {true, 2}, + "lw": {false, 35}, + "sw": {false, 43}, + "slt": {true, 42}, + "slti": {false, 10}, + "sltiu": {false, 11}, + "sltu": {true, 43}, + "beq": {false, 4}, + "bne": {false, 5}, + "j": {false, 2}, + "jal": {false, 3}, + "jr": {true, 8}, + "syscall": {false, 12}, +} + +var RegNums = map[string]uint32{ + "$zero": 0, + "$at": 1, + "$v0": 2, + "$v1": 3, + "$a0": 4, + "$a1": 5, + "$a2": 6, + "$a3": 7, + "$t0": 8, + "$t1": 9, + "$t2": 10, + "$t3": 11, + "$t4": 12, + "$t5": 13, + "$t6": 14, + "$t7": 15, + "$s0": 16, + "$s1": 17, + "$s2": 18, + "$s3": 19, + "$s4": 20, + "$s5": 21, + "$s6": 22, + "$s7": 23, + "$t8": 24, + "$t9": 25, + "$k0": 26, + "$k1": 27, + "$gp": 28, + "$sp": 29, + "$fp": 30, + "$ra": 31, +} + +func Encode(inst []string, pc int32) ([]byte, error) { + var ret uint32 = 0 + var imm int16 + bytes := make([]byte, 4) + for _, s := range inst { + switch s { + case "syscall": + binary.NativeEndian.PutUint32(bytes, 12) + return bytes, nil + case "nop": + binary.NativeEndian.PutUint32(bytes, 0) + return bytes, nil + } + } + + function := Instructions[inst[0]] + if function.isRtype && function.opcode == 8 { + ret = (RegNums[inst[1]] << 21) | function.opcode + } else if function.isRtype { + ret = (RegNums[inst[2]] << 21) | (RegNums[inst[3]] << + 16) | (RegNums[inst[1]] << 11) | function.opcode + } else if function.opcode == 2 || function.opcode == 3 { + label, _ := labels[inst[1]] + ret = (function.opcode << 26) | uint32(label&0x03FFFFFF) + } else if function.opcode == 35 || function.opcode == 43 { + immReg, err := regexp.Compile("^[0-9]+") + if err != nil { + return nil, err + } + regReg, err := regexp.Compile("\\$([a-z]|[0-9])+") + if err != nil { + return nil, err + } + t, err := strconv.Atoi(immReg.FindString(inst[2])) + if err != nil { + return nil, err + } + imm = int16(t) + reg := regReg.FindString(inst[2]) + ret = (function.opcode << 26) | (RegNums[reg] << 21) | + (RegNums[inst[1]] << 16) | (0xFFFF & uint32(imm)) + } else { + addr, isLabel := labels[inst[3]] + if isLabel { + imm = int16(int32(addr) - pc - 12) + } else { + i, err := strconv.Atoi(inst[3]) + if err != nil { + return nil, err + } + imm = int16(i) + } + ret = (function.opcode << 26) | (RegNums[inst[2]] << 21) | + (RegNums[inst[1]] << 16) | (0xFFFF & uint32(imm)) + } + binary.NativeEndian.PutUint32(bytes, ret) + return bytes, nil +} + +func EncodeData(line []string) ([]byte, error) { + var size int + var err error = nil + var data int64 + isString := false + isData := false + nullTerm := false + + line[1] = strings.ReplaceAll(line[1], "\"", "") + + switch line[0] { + case ".space": + size, err = strconv.Atoi(line[1]) + case ".word": + size = 4 + isData = true + case ".byte": + size = 1 + isData = true + case ".half": + size = 2 + isData = true + case ".asciiz": + size = len(line[1]) + 1 + isString = true + nullTerm = true + case ".ascii": + size = len(line[1]) + isString = true + } + if err != nil { + return nil, err + } + bytes := make([]byte, size) + + if isData { + if strings.Contains(line[1], "0x") { + line[1] = strings.ReplaceAll(line[1], "0x", "") + var t uint64 + t, err = strconv.ParseUint(line[1], 16, size*8) + data = int64(t) + } else { + data, err = strconv.ParseInt(line[1], 10, size*8) + } + if err != nil { + return nil, err + } + switch size { + case 1: + bytes[0] = uint8(data) + case 2: + binary.NativeEndian.PutUint16(bytes, uint16(data)) + case 4: + binary.NativeEndian.PutUint32(bytes, uint32(data)) + } + } else if isString { + if nullTerm { + for i, b := range []byte(line[1]) { + bytes[i] = b + } + bytes[size-1] = 0 + } else { + bytes = []byte(line[1]) + } + } + + return bytes, nil +} + +func Assemble(path string) { + file, err := os.Open(path) + if err != nil { + panic(err) + } + defer file.Close() + + out, err := os.Create(strings.ReplaceAll(path, ".asm", ".bin")) + if err != nil { + panic(err) + } + defer out.Close() + + scanner := bufio.NewScanner(file) + for i := 0; scanner.Scan(); i += 4 { + var s string = scanner.Text() + if s == "" { + i -= 4 + continue + } else if s == ".data" { + isText = false + continue + } else if s == ".text" { + isText = true + continue + } + + inst, err := Parse(s) + if err != nil { + panic(err) + } + + _, isLabel := labels[strings.ReplaceAll(inst[0], ":", "")] + if isLabel { + inst = inst[1:] + } + + if len(inst) == 0 || (len(inst) == 1 && inst[0] == "") { + i -= 4 + continue + } + + var bytes []byte + if isText { + bytes, err = Encode(inst, int32(i)) + } else { + bytes, err = EncodeData(inst) + } + if err != nil { + fmt.Fprintf(os.Stderr, "while encoding %s: %v", s, err) + panic(err) + } + if _, err = out.Write(bytes); err != nil { + panic(err) + } + } + + /* + // Write the new starting PC into the header + if _, err := out.Seek(0, io.SeekStart); err != nil { + panic(err) + } + */ + + if err := scanner.Err(); err != nil { + panic(err) + } +} diff --git a/go.mod b/go.mod new file mode 100755 index 0000000..9c9af7e --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/JacobMcDonnell/qdma + +go 1.22.2 diff --git a/parse.go b/parse.go new file mode 100755 index 0000000..da0c47e --- /dev/null +++ b/parse.go @@ -0,0 +1,51 @@ +package main + +import ( + "regexp" + "strings" +) + +func Parse(s string) ([]string, error) { + comments, err := regexp.Compile("#.*") + if err != nil { + panic(err) + } + + edgeWs, err := regexp.Compile("(^\\s+|\\s+$)+") + if err != nil { + return nil, err + } + + whiteSpace, err := regexp.Compile("\\s+") + if err != nil { + return nil, err + } + + repeatComma, err := regexp.Compile(",{2,}") + if err != nil { + return nil, err + } + + userStrings, err := regexp.Compile("\"([[:graph:]]|\\s)+\"") + if err != nil { + return nil, err + } + + placeHolder := "__PLACE_HOLDER__" + phReg, err := regexp.Compile(placeHolder) + if err != nil { + return nil, err + } + + us := userStrings.FindString(s) + s = userStrings.ReplaceAllString(s, placeHolder) + s = comments.ReplaceAllString(s, "") + s = edgeWs.ReplaceAllString(s, "") + s = whiteSpace.ReplaceAllString(s, ",") + s = repeatComma.ReplaceAllString(s, ",") + res := strings.Split(s, ",") + for i, arg := range res { + res[i] = phReg.ReplaceAllString(arg, us) + } + return res, nil +} diff --git a/qdma b/qdma deleted file mode 100755 index 454c066..0000000 Binary files a/qdma and /dev/null differ diff --git a/qdma.go b/qdma.go index 8aed06a..b28dfe9 100755 --- a/qdma.go +++ b/qdma.go @@ -1,252 +1,9 @@ package main import ( - "bufio" - "encoding/binary" - "fmt" "os" - "regexp" - "strconv" - "strings" ) -var Instructions = map[string]struct { - isRtype bool - opcode uint32 -}{ - "add": {true, 32}, - "addi": {false, 8}, - "addiu": {false, 9}, - "sub": {true, 34}, - "subu": {true, 35}, - "mult": {true, 24}, - "multu": {true, 25}, - "div": {true, 26}, - "divu": {true, 27}, - "and": {true, 36}, - "andi": {false, 12}, - "or": {true, 37}, - "ori": {false, 13}, - "xor": {true, 38}, - "xori": {false, 14}, - "sll": {true, 0}, - "sra": {true, 3}, - "srl": {true, 2}, - "lw": {false, 35}, - "sw": {false, 43}, - "slt": {true, 42}, - "slti": {false, 10}, - "sltiu": {false, 11}, - "sltu": {true, 43}, - "beq": {false, 4}, - "bne": {false, 5}, - "j": {false, 2}, - "jal": {false, 3}, - "jr": {true, 8}, - "syscall": {false, 12}, -} - -var RegNums = map[string]uint32{ - "$zero": 0, - "$at": 1, - "$v0": 2, - "$v1": 3, - "$a0": 4, - "$a1": 5, - "$a2": 6, - "$a3": 7, - "$t0": 8, - "$t1": 9, - "$t2": 10, - "$t3": 11, - "$t4": 12, - "$t5": 13, - "$t6": 14, - "$t7": 15, - "$s0": 16, - "$s1": 17, - "$s2": 18, - "$s3": 19, - "$s4": 20, - "$s5": 21, - "$s6": 22, - "$s7": 23, - "$t8": 24, - "$t9": 25, - "$k0": 26, - "$k1": 27, - "$gp": 28, - "$sp": 29, - "$fp": 30, - "$ra": 31, -} - -// Map for labels and their addresses -var labels map[string]uint = make(map[string]uint) - -func LabelFind(path string) { - file, err := os.Open(path) - if err != nil { - panic(err) - } - defer file.Close() - - input := bufio.NewScanner(file) - - for i := 0; input.Scan(); i += 4 { - if input.Text() == "" { - i -= 4 - continue - } - s, err := Parse(input.Text()) - if err != nil { - panic(err) - } - - if len(s) == 1 && s[0] == "" { - i -= 4 - continue - } - - hasLabel, err := regexp.MatchString("^.*:", s[0]) - if err != nil { - panic(err) - } - if hasLabel { - labels[strings.ReplaceAll(s[0], ":", "")] = uint(i) - if len(s) == 1 { - i -= 4 - } - } - } -} - -func Encode(inst []string, pc int32) (uint32, error) { - var ret uint32 = 0 - for _, s := range inst { - switch s { - case "syscall": - return 12, nil - case "nop": - return 0, nil - } - } - - function := Instructions[inst[0]] - if function.isRtype && function.opcode == 8 { - ret = (RegNums[inst[1]] << 21) | function.opcode - } else if function.isRtype { - ret = (RegNums[inst[2]] << 21) | (RegNums[inst[3]] << - 16) | (RegNums[inst[1]] << 11) | function.opcode - } else if function.opcode == 2 || function.opcode == 3 { - label, _ := labels[inst[1]] - ret = (function.opcode << 26) | uint32(label&0x03FFFFFF) - } else { - var imm int16 - addr, isLabel := labels[inst[3]] - if isLabel { - imm = int16(int32(addr) - pc - 12) - } else { - i, err := strconv.Atoi(inst[3]) - if err != nil { - return 0, err - } - imm = int16(i) - } - ret = (function.opcode << 26) | (RegNums[inst[2]] << 21) | - (RegNums[inst[1]] << 16) | (0x0000FFFF & uint32(imm)) - } - return ret, nil -} - -func Assemble(path string) { - file, err := os.Open(path) - if err != nil { - panic(err) - } - defer file.Close() - - out, err := os.Create(strings.ReplaceAll(path, ".asm", ".bin")) - if err != nil { - panic(err) - } - defer out.Close() - - scanner := bufio.NewScanner(file) - bs := make([]byte, 4) - for i := 0; scanner.Scan(); i += 4 { - var s string = scanner.Text() - if s == "" { - i -= 4 - continue - } - - inst, err := Parse(s) - if err != nil { - panic(err) - } - - _, isLabel := labels[strings.ReplaceAll(inst[0], ":", "")] - if isLabel { - inst = inst[1:] - } - - if len(inst) == 0 || (len(inst) == 1 && inst[0] == "") { - i -= 4 - continue - } - - bytes, err := Encode(inst, int32(i)) - if err != nil { - fmt.Fprintf(os.Stderr, "while encoding %s: %v", s, err) - panic(err) - } - binary.BigEndian.PutUint32(bs, bytes) - _, err = out.Write(bs) - if err != nil { - panic(err) - } - } - - if err := scanner.Err(); err != nil { - panic(err) - } -} - -func Parse(s string) ([]string, error) { - /* regex to match 00($t0) - offset, err := regexp.Compile("[0-9]+\\((.*)+\\)") - if err != nil { - panic(err) - }*/ - - comments, err := regexp.Compile("#.*") - if err != nil { - panic(err) - } - - edgeWs, err := regexp.Compile("(^\\s+|\\s+$)+") - if err != nil { - return nil, err - } - - whiteSpace, err := regexp.Compile("\\s+") - if err != nil { - return nil, err - } - - repeatComma, err := regexp.Compile(",{2,}") - if err != nil { - return nil, err - } - - s = comments.ReplaceAllString(s, "") - s = edgeWs.ReplaceAllString(s, "") - s = whiteSpace.ReplaceAllString(s, ",") - s = repeatComma.ReplaceAllString(s, ",") - return strings.Split(s, ","), nil -} - func main() { for _, arg := range os.Args[1:] { LabelFind(arg) // First pass to find all labels. diff --git a/symbol.go b/symbol.go new file mode 100755 index 0000000..f63bb8e --- /dev/null +++ b/symbol.go @@ -0,0 +1,52 @@ +package main + +import ( + "bufio" + "os" + "regexp" + "strings" +) + +// Map for labels and their addresses +var labels map[string]uint = make(map[string]uint) + +func LabelFind(path string) { + file, err := os.Open(path) + if err != nil { + panic(err) + } + defer file.Close() + + input := bufio.NewScanner(file) + + for i := 0; input.Scan(); i += 4 { + k := input.Text() + if k == "" { + i -= 4 + continue + } else if k == ".text" || k == ".data" { + i += 4 + continue + } + s, err := Parse(k) + if err != nil { + panic(err) + } + + if len(s) == 1 && s[0] == "" { + i -= 4 + continue + } + + hasLabel, err := regexp.MatchString("^.*:", s[0]) + if err != nil { + panic(err) + } + if hasLabel { + labels[strings.ReplaceAll(s[0], ":", "")] = uint(i) + if len(s) == 1 { + i -= 4 + } + } + } +} diff --git a/test6.asm b/test6.asm new file mode 100755 index 0000000..3a9a21f --- /dev/null +++ b/test6.asm @@ -0,0 +1,18 @@ +.text + +addi $t0, $zero, x +addi $v0, $zero, 1 +lw $a0, 0($t0) +syscall +addi $v0, $zero, 10 +syscall + +.data + +x: .word 0xFFFFFFFF +#fib: .space 40 +#h: .half 255 +#c: .byte 0 +#dogs: .ascii "Dogs are cool\n" +#cats: .asciiz "cats are cool\n" + diff --git a/test6.bin b/test6.bin new file mode 100755 index 0000000..8e47a8d Binary files /dev/null and b/test6.bin differ diff --git a/tests/test5.bin b/tests/test5.bin old mode 100644 new mode 100755 index 61cb805..dd1cc83 Binary files a/tests/test5.bin and b/tests/test5.bin differ -- cgit v1.2.3