commit message
This commit is contained in:
parent
24455d9eb8
commit
01679fd252
14 changed files with 410 additions and 1 deletions
22
asmblr/src/asmblr.gleam
Normal file
22
asmblr/src/asmblr.gleam
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import argv
|
||||
import asmblr/parser
|
||||
import asmblr/passes
|
||||
import simplifile
|
||||
|
||||
pub fn main() -> Nil {
|
||||
let arg = argv.load()
|
||||
case arg.arguments {
|
||||
[_program, fname, ..] -> {
|
||||
let assert Ok(content) = simplifile.read(fname)
|
||||
parser.parse(content)
|
||||
|> passes.build_symbol_table()
|
||||
|> fn(a) {
|
||||
echo a.symbols
|
||||
a
|
||||
}
|
||||
|> passes.generate_binary
|
||||
Nil
|
||||
}
|
||||
_ -> Nil
|
||||
}
|
||||
}
|
||||
67
asmblr/src/asmblr/definitions.gleam
Normal file
67
asmblr/src/asmblr/definitions.gleam
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
pub type Def {
|
||||
Def(opcode: Int, size: Int, mnemonic: String)
|
||||
}
|
||||
|
||||
pub const instructions = [
|
||||
Def(6, 3, "ADD"),
|
||||
Def(22, 3, "ADDF"),
|
||||
Def(36, 2, "ADDR"),
|
||||
Def(16, 3, "AND"),
|
||||
Def(45, 2, "CLEAR"),
|
||||
Def(10, 3, "COMP"),
|
||||
Def(34, 3, "COMPF"),
|
||||
Def(40, 2, "COMPR"),
|
||||
Def(9, 3, "DIV"),
|
||||
Def(25, 3, "DIVF"),
|
||||
Def(39, 2, "DIVR"),
|
||||
Def(49, 1, "FIX"),
|
||||
Def(48, 1, "FLOAT"),
|
||||
Def(61, 1, "HIO"),
|
||||
Def(15, 3, "J"),
|
||||
Def(12, 3, "JEQ"),
|
||||
Def(13, 3, "JGT"),
|
||||
Def(14, 3, "JLT"),
|
||||
Def(18, 3, "JSUB"),
|
||||
Def(0, 3, "LDA"),
|
||||
Def(26, 3, "LDB"),
|
||||
Def(20, 3, "LDCH"),
|
||||
Def(28, 3, "LDF"),
|
||||
Def(2, 3, "LDL"),
|
||||
Def(27, 3, "LDS"),
|
||||
Def(29, 3, "LDT"),
|
||||
Def(1, 3, "LDX"),
|
||||
Def(52, 3, "LPS"),
|
||||
Def(8, 3, "MUL"),
|
||||
Def(24, 3, "MULF"),
|
||||
Def(38, 2, "MULR"),
|
||||
Def(50, 1, "NORM"),
|
||||
Def(17, 3, "OR"),
|
||||
Def(54, 3, "RD"),
|
||||
Def(43, 2, "RMO"),
|
||||
Def(19, 3, "RSUB"),
|
||||
Def(41, 2, "SHIFTL"),
|
||||
Def(42, 2, "SHIFTR"),
|
||||
Def(60, 1, "SIO"),
|
||||
Def(59, 3, "SSK"),
|
||||
Def(3, 3, "STA"),
|
||||
Def(30, 3, "STB"),
|
||||
Def(21, 3, "STCH"),
|
||||
Def(32, 3, "STF"),
|
||||
Def(53, 3, "STI"),
|
||||
Def(5, 3, "STL"),
|
||||
Def(31, 3, "STS"),
|
||||
Def(58, 3, "STSW"),
|
||||
Def(33, 3, "STT"),
|
||||
Def(4, 3, "STX"),
|
||||
Def(7, 3, "SUB"),
|
||||
Def(23, 3, "SUBF"),
|
||||
Def(23, 2, "SUBR"),
|
||||
Def(23, 2, "SVC"),
|
||||
Def(23, 3, "TD"),
|
||||
Def(23, 1, "TIO"),
|
||||
Def(23, 3, "TIX"),
|
||||
Def(23, 2, "TIXR"),
|
||||
Def(23, 3, "WD"),
|
||||
]
|
||||
|
||||
pub const directives = ["START"]
|
||||
18
asmblr/src/asmblr/line.gleam
Normal file
18
asmblr/src/asmblr/line.gleam
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import asmblr/token
|
||||
import gleam/regexp
|
||||
|
||||
pub type SourceLine {
|
||||
SourceLine(number: Int, source: String)
|
||||
}
|
||||
|
||||
pub type Line {
|
||||
Line(number: Int, tokens: token.Tokens)
|
||||
}
|
||||
|
||||
pub fn parse_line(line: SourceLine) -> Line {
|
||||
let assert Ok(re) =
|
||||
regexp.from_string("^(\\w*)\\s*(\\w*)\\s*([\\w,#@]*)\\s*(.*)$")
|
||||
let assert [match, ..] = regexp.scan(re, line.source)
|
||||
let assert [label, operation, arguments, comments] = match.submatches
|
||||
Line(line.number, token.Tokens(label, operation, arguments, comments))
|
||||
}
|
||||
11
asmblr/src/asmblr/parser.gleam
Normal file
11
asmblr/src/asmblr/parser.gleam
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import asmblr/line
|
||||
import gleam/list
|
||||
import gleam/string
|
||||
|
||||
pub fn parse(source: String) -> List(line.Line) {
|
||||
string.split(source, "\n")
|
||||
|> list.index_map(fn(ln: String, i: Int) -> line.Line {
|
||||
line.SourceLine(i, ln) |> line.parse_line
|
||||
})
|
||||
|> echo
|
||||
}
|
||||
167
asmblr/src/asmblr/passes.gleam
Normal file
167
asmblr/src/asmblr/passes.gleam
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
import asmblr/definitions
|
||||
import asmblr/line
|
||||
import gleam/dict
|
||||
import gleam/int
|
||||
import gleam/list
|
||||
import gleam/option
|
||||
import gleam/regexp
|
||||
|
||||
pub type FirstPassOutput {
|
||||
FirstPassOutput(
|
||||
code: List(line.Line),
|
||||
symbols: dict.Dict(String, option.Option(Int)),
|
||||
)
|
||||
}
|
||||
|
||||
type FirstPassState {
|
||||
FirstPassState(symbols: dict.Dict(String, option.Option(Int)), pointer: Int)
|
||||
}
|
||||
|
||||
fn handle_label_first_pass(
|
||||
state: FirstPassState,
|
||||
code: line.Line,
|
||||
) -> FirstPassState {
|
||||
case code.tokens.label {
|
||||
option.None -> state
|
||||
option.Some(label) -> {
|
||||
assert !dict.has_key(state.symbols, label)
|
||||
FirstPassState(
|
||||
..state,
|
||||
symbols: dict.insert(state.symbols, label, option.Some(state.pointer)),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn increment_pointer_first_pass(
|
||||
state: FirstPassState,
|
||||
code: line.Line,
|
||||
) -> FirstPassState {
|
||||
case code.tokens.mnemonic {
|
||||
option.None -> state
|
||||
option.Some("+" <> _) -> {
|
||||
FirstPassState(..state, pointer: state.pointer + 4)
|
||||
}
|
||||
option.Some(mnemonic) -> {
|
||||
case
|
||||
list.find(definitions.instructions, fn(inst) {
|
||||
inst.mnemonic == mnemonic
|
||||
})
|
||||
{
|
||||
Ok(def) -> {
|
||||
let size = def.size
|
||||
FirstPassState(..state, pointer: state.pointer + size)
|
||||
}
|
||||
Error(Nil) -> state
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_directives_first_pass(
|
||||
state: FirstPassState,
|
||||
code: line.Line,
|
||||
) -> FirstPassState {
|
||||
case code.tokens.mnemonic {
|
||||
option.None -> state
|
||||
option.Some("START") -> state
|
||||
option.Some("ORG") -> {
|
||||
let assert option.Some(org) = code.tokens.arguments
|
||||
let assert Ok(org) = case org {
|
||||
"0x" <> num -> int.base_parse(num, 16)
|
||||
num -> int.base_parse(num, 10)
|
||||
}
|
||||
FirstPassState(..state, pointer: org)
|
||||
}
|
||||
_ -> state
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_line_first_pass(
|
||||
state: FirstPassState,
|
||||
code: line.Line,
|
||||
) -> FirstPassState {
|
||||
handle_label_first_pass(state, code)
|
||||
|> increment_pointer_first_pass(code)
|
||||
|> handle_directives_first_pass(code)
|
||||
}
|
||||
|
||||
pub fn build_symbol_table(code: List(line.Line)) -> FirstPassOutput {
|
||||
FirstPassOutput(
|
||||
code,
|
||||
list.fold(code, FirstPassState(dict.new(), 0), parse_line_first_pass).symbols,
|
||||
)
|
||||
}
|
||||
|
||||
fn get_register(register: String) -> Result(Int, Nil) {
|
||||
case register {
|
||||
"a" | "A" -> Ok(0)
|
||||
"x" | "X" -> Ok(1)
|
||||
"l" | "L" -> Ok(2)
|
||||
"b" | "B" -> Ok(3)
|
||||
"s" | "S" -> Ok(4)
|
||||
"t" | "T" -> Ok(5)
|
||||
_ -> Error(Nil)
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_line_second_pass(
|
||||
code: line.Line,
|
||||
symbols: dict.Dict(String, option.Option(int)),
|
||||
) -> BitArray {
|
||||
case code.tokens.mnemonic {
|
||||
option.None -> <<>>
|
||||
option.Some(mnemonic) -> {
|
||||
case
|
||||
list.find(definitions.instructions, fn(inst) {
|
||||
inst.mnemonic == mnemonic
|
||||
})
|
||||
{
|
||||
Ok(def) -> {
|
||||
case def.size {
|
||||
1 -> <<def.opcode:8>>
|
||||
2 -> {
|
||||
let assert option.Some(args) = code.tokens.arguments
|
||||
let assert Ok(re) = regexp.from_string("^.*?(\\w),.*?(\\w).*$")
|
||||
|
||||
let assert Ok(regexp.Match(
|
||||
_,
|
||||
[option.Some(arg1), option.Some(arg2)],
|
||||
)) = list.first(regexp.scan(re, args))
|
||||
let assert Ok(arg1) = get_register(arg1)
|
||||
let assert Ok(arg2) = get_register(arg2)
|
||||
<<def.opcode:8, arg1:4, arg2:4>>
|
||||
}
|
||||
3 -> {
|
||||
let n = 0
|
||||
let i = 0
|
||||
let x = 0
|
||||
let b = 0
|
||||
let p = 0
|
||||
let e = 1
|
||||
let assert Ok(re) = regexp.from_string("^(\\w*),(\\w)")
|
||||
let displacement = 1
|
||||
<<
|
||||
{ def.opcode / 4 }:6,
|
||||
n:1,
|
||||
i:1,
|
||||
x:1,
|
||||
b:1,
|
||||
p:1,
|
||||
e:1,
|
||||
displacement:12,
|
||||
>>
|
||||
}
|
||||
_ -> panic
|
||||
}
|
||||
}
|
||||
Error(Nil) -> <<>>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_binary(input: FirstPassOutput) -> BitArray {
|
||||
echo list.map(input.code, fn(l) { handle_line_second_pass(l, input.symbols) })
|
||||
<<0>>
|
||||
}
|
||||
14
asmblr/src/asmblr/token.gleam
Normal file
14
asmblr/src/asmblr/token.gleam
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import gleam/option
|
||||
|
||||
pub type Token {
|
||||
Token(column: Int, source: String)
|
||||
}
|
||||
|
||||
pub type Tokens {
|
||||
Tokens(
|
||||
label: option.Option(String),
|
||||
mnemonic: option.Option(String),
|
||||
arguments: option.Option(String),
|
||||
comments: option.Option(String),
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue