There is an assembler now
This commit is contained in:
parent
5b2e13dcf4
commit
565d733e0f
|
@ -1 +1,2 @@
|
||||||
/marcie/marcie
|
/marcie/marcie
|
||||||
|
/masm/masm
|
||||||
|
|
|
@ -7,6 +7,7 @@ import "git.tebibyte.media/sashakoshka/marcie"
|
||||||
func main () {
|
func main () {
|
||||||
if len(os.Args) != 2 {
|
if len(os.Args) != 2 {
|
||||||
fmt.Println("specify file name")
|
fmt.Println("specify file name")
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := os.Open(os.Args[1])
|
file, err := os.Open(os.Args[1])
|
||||||
|
|
Binary file not shown.
|
@ -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
|
|
@ -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