Mostly working assembler of dubious design
This commit is contained in:
parent
0ffa733de3
commit
a4a21f903b
|
@ -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
23
lex.go
|
@ -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 == '/' {
|
||||
|
|
6
main.go
6
main.go
|
@ -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()
|
||||
|
|
44
types.go
44
types.go
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue