There is an assembler now
This commit is contained in:
parent
5b2e13dcf4
commit
565d733e0f
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
||||
/marcie/marcie
|
||||
/masm/masm
|
||||
|
@ -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])
|
||||
|
BIN
masm/hellorld
Normal file
BIN
masm/hellorld
Normal file
Binary file not shown.
25
masm/hellorld.mas
Normal file
25
masm/hellorld.mas
Normal file
@ -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
|
305
masm/main.go
Normal file
305
masm/main.go
Normal file
@ -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
|
||||
}
|
Loading…
Reference in New Issue
Block a user