Mostly working assembler of dubious design

This commit is contained in:
sloum 2024-01-03 22:36:11 -08:00
parent 0ffa733de3
commit a4a21f903b
4 changed files with 40 additions and 38 deletions

5
README.md Normal file
View File

@ -0,0 +1,5 @@
# bird-asm
An attempt at writing an assembler for [bird-vm](https://tildegit.org/sloum/bird-vm), a stack based vm. Both are learning projects riddled with poor design decisions and messy code.
They may have interesting aspects, but are mostly just personal projects.

23
lex.go
View File

@ -8,24 +8,33 @@ import (
)
var lexLine uint16
var strDelim byte = '`'
func lexString(r *strings.Reader) []token {
start := lexLine
b := make([]byte, 0)
for {
c, _, err := r.ReadRune()
if err != nil || c == strDelim{
if err != nil {
fmt.Fprintf(os.Stderr, errTmplt, start, "syntax", "unclosed string")
os.Exit(lexError)
} else if c == strDelim {
break
}
append(b, c)
} else if c == '\n' {
lexLine++
}
if c > 255 {
c = '?'
}
b = append(b, byte(c))
}
append(b, 0) // add a null for the end of the string
b = append(b, 0) // add a null for the end of the string
t := make([]token, 0)
for i := len(b)-1; i >= 0; i-- {
t = append(t, []token{token{"PSH", lexLine}, token{fmt.Sprintf("%02x", b[i]), lexLine}}...)
}
// Add the length
l := uint16(len(b))
t = append(t, []token{token{"PSH2", lexLine}, token{fmt.Sprintf("%02x", byte(b[i] >> 7)), lexLine}, token{fmt.Sprintf("%02x", byte(l & uint16r(0xFF))), lexLine}}...)
t = append(t, []token{token{"PSH2", lexLine}, token{fmt.Sprintf("%02x", byte(l >> 7)), lexLine}, token{fmt.Sprintf("%02x", byte(l & uint16(0xFF))), lexLine}}...)
return t
}
@ -43,7 +52,7 @@ func eatMacro(r *strings.Reader) (string, []token) {
case '[', '{', ']', '}':
// Allow these chars, but assign no meaning to them
continue
case '`':
case strDelim:
data = append(data, lexString(r)...)
case '/':
if c == '/' {

View File

@ -49,10 +49,12 @@ var (
}
)
var strDelim rune = '`'
func main() {
flag.BoolVar(&dryRun, "dry", false, "Does a dry run (doesnot produce output file, but shows errors)")
flag.BoolVar(&verbose, "v", false, "Run in verbose mode")
delim := flag.String("delim", "`", "Override the ascci char that delimits strings")
ver := flag.Bool("version", false, "Print version information and exit")
outFile := flag.String("o", "", "Output file path")
flag.Parse()
@ -62,6 +64,10 @@ func main() {
return
}
if *delim != "`" && *delim != "" {
strDelim = rune((*delim)[0])
}
setIn()
setOut(*outFile)
program := newProg()

View File

@ -9,7 +9,7 @@ import (
)
const (
errTmplt string = "line %d: %s: \033[1m%s\033[0m"
errTmplt string = "line %d: %s: \033[1m%s\033[0m\n"
)
type token struct {
@ -18,8 +18,8 @@ type token struct {
}
type label struct {
ref uint16
memContext bool
ref uint16
mem bool
}
type prog struct {
@ -29,7 +29,6 @@ type prog struct {
mp uint16
tokens []token
errors []string
memContext bool
labelCounts map[string]uint16
}
@ -41,7 +40,6 @@ func newProg() prog {
0,
make([]token, 0, 0),
make([]string, 0, 5),
false,
make(map[string]uint16),
}
}
@ -102,12 +100,13 @@ func (p *prog) Compile() {
b = append(b, ops["PSH2"])
i++
// Make the reference apply to the memory area
if v.memContext {
v.ref+=uint16(len(t))
ref := v.ref
if v.mem {
ref+=uint16(len(t))
}
b = append(b, byte(v.ref >> 8))
b = append(b, byte(ref >> 8))
i++
b = append(b, byte(v.ref & uint16(0xFF)))
b = append(b, byte(ref & uint16(0xFF)))
} else {
p.errors = append(p.errors, fmt.Sprintf(errTmplt, t[i].line, "Undefined label", t[i].val))
continue
@ -169,29 +168,22 @@ func (p *prog) buildLabels(t []token) []token {
tokens := make([]token, 0, len(t))
for _, l := range t {
switch l.val[0] {
case '@':
case '@', '+':
// parse label definition
if _, ok := p.labels[l.val[1:]]; ok {
p.errors = append(p.errors, fmt.Sprintf(errTmplt, l.line, "Cannot redefine", l.val))
p .errors = append(p.errors, fmt.Sprintf(errTmplt, l.line, "Cannot redefine", l.val))
continue
}
var la label
// TODO
// This is wrong. It cannot be known yet.
// Need to add an update step for address (unless it is memContext true)
if p.memContext {
if l.val[0] == '+' {
la.ref = p.mp
la.memContext = true
la.mem = true
} else {
la.ref = uint16(len(tokens))+2
}
p.labels[l.val[1:]] = la
case '$':
// pad memory
if !p.memContext {
p.errors = append(p.errors, fmt.Sprintf(errTmplt, l.line, "Memory padd in invalid context", "!program"))
continue
}
num := l.val[1:]
s, err := strconv.ParseUint(num, 16, 8)
if err != nil {
@ -199,16 +191,6 @@ func (p *prog) buildLabels(t []token) []token {
continue
}
p.mp += uint16(s)
case '!':
// Memory context change
if l.val[1:] == "memory" {
p.memContext = true
} else if l.val[1:] == "program" {
p.memContext = false
} else {
p.errors = append(p.errors, fmt.Sprintf(errTmplt, l.line, "Undefined", l.val))
continue
}
default:
tokens = append(tokens, l)
}
@ -246,7 +228,7 @@ TokenizationLoop:
case '[', '{', ']', '}':
// Allow these chars, but assign no meaning to them
continue
case '`':
case strDelim:
tokens = append(tokens, lexString(reader)...)
case '/':
c, _, err := reader.ReadRune()