commit 9b4c94f07693bf4d51126899cbecf7ca5b3d904e from: Onana Onana Xavier Manuel date: Fri Sep 25 04:51:30 2026 UTC Organized toolchain a bit commit - e0074095e24797bb794ffd465b0b2a6442caa8d9 commit + 9b4c94f07693bf4d51126899cbecf7ca5b3d904e blob - 7de524159eeb3940ea7270e030f030511e494f59 blob + 1133285b38617bb7472f5063ad4ee08c6cd06f2c --- tools/6c +++ tools/6c @@ -1,49 +1,118 @@ #!/usr/local/bin/lua51 ---[[ -This is the structure a token must follow. The token can be -followed by a series of raw bytes if Length > 0. +----------------------------------------------------------------------- +-- TOKEN MATCHING +----------------------------------------------------------------------- +local mnemonics = { "mov", "cli", "hlt", "ret", "sti" } -Token { - u32 Column; - u32 Line; - u16 Type; - u16 Length; -} +local symbols = { ",", "[", "]", "(", ")", "{", "}", "*", "/", "-", + "+", "!", "~", "#", "?", ";", ":" } -Types are eight Opcodes, or C keywords +local regs8 = { "al", "ah", "bl", "bh", "cl", "ch", "dl", "dh" } -]]-- +local asmkwd = { "asm" } -progname="6c" +local tk_type = { } +local count = 0 --- Program error +for i = 1, #mnemonics do + count = count + 1 + tk_type[mnemonics[i]] = count +end + +for i = 1, #symbols do + count = count + 1 + tk_type[symbols[i]] = count +end + +for i = 1, #regs8 do + count = count + 1 + tk_type[regs8[i]] = count +end + +for i = 1, #asmkwd do + count = count + 1 + tk_type[asmkwd[i]] = count +end + +local function cincadd() + count = count + 1 + return count +end +tk_type.hexadecimal = cincadd() +tk_type.integer = cincadd() +tk_type.id = cincadd() +tk_type["\n"] = cincadd() + +----------------------------------------------------------------------- +-- DEBUGING +----------------------------------------------------------------------- local function perror(msg) print(progname .. ": error: " .. msg) os.exit(1) end -if arg[1] == nil then - print("usage: " .. progname .. " input [output]") - os.exit(1) +----------------------------------------------------------------------- +-- BOOLEAN check +----------------------------------------------------------------------- +local function isnumber(n) + if n:match("[0-9]") then + return true + end + return false end -if arg[2] == nil then - arg[2] = string.gsub(arg[1], "%.%w+$", ".6") +local function issymbol(char) + for i,v in ipairs(symbols) do + if v == char then + return true + end + end + return false end -src = io.open(arg[1], "r") -dst = io.open(arg[2], "wb") +local function gettokenkey(m) + for i, v in pairs(tk_type) do + if tk_type[i] == m.type then + return i + end + end + return nil +end -if src == nil then - perror("failed to open src"); +local function ismnemonic(m) + local key = gettokenkey(m) + + if key == nil then + return false + end + + for i,v in ipairs(mnemonics) do + if v == key then + return true + end + end + return false end -if dst == nil then - perror("failed to open dst"); - src:close() +local function isregs8(m) + local key = gettokenkey(m) + + if key == nil then + return false + end + + for i,v in ipairs(regs8) do + if v == key then + return true + end + end + return false end +----------------------------------------------------------------------- +-- BITWISE OPERATIONS +----------------------------------------------------------------------- local function brshift(value, bit) return math.floor(value / (2 ^ bit)) end @@ -68,7 +137,42 @@ local function band(a, b) return result end -local function write_u32(value) +local function bor(a, b) + local result = 0 + local shift = 1 + + while a > 0 or b > 0 do + local r1 = a % 2 + local r2 = b % 2 + + if r1 == 1 or r2 == 1 then + result = result + shift + end + + a = math.floor(a / 2) + b = math.floor(b / 2) + shift = shift * 2 + end + + return result +end + +----------------------------------------------------------------------- +-- WRITE OPERATIONS +----------------------------------------------------------------------- +local function writeu8(dst, value) + local b = band(value, 0xff) + dst:write(string.char(b)) +end + +local function writeu16(dst, value) + local b2 = band(brshift(value, 8), 0xff) + local b1 = band(value, 0xff) + + dst:write(string.char(b2, b1)) -- Little Endian x86 +end + +local function writeu32(dst, value) local b4 = band(brshift(value, 24), 0xff) local b3 = band(brshift(value, 16), 0xff) local b2 = band(brshift(value, 8), 0xff) @@ -77,117 +181,80 @@ local function write_u32(value) dst:write(string.char(b4, b3, b2, b1)) -- Little Endian x86 end -local function write_u16(value) - local b2 = band(brshift(value, 8), 0xff) - local b1 = band(value, 0xff) - dst:write(string.char(b2, b1)) -- Little Endian x86 -end +local function intval(intstr) + local len = #intstr + local res = 0 -local function write_u8(value) - local b = band(value, 0xff) - dst:write(string.char(b)) + for i = 1, len do + local s = intstr:sub(len - i + 1, len - i + 1) + s = s * (10 ^ (i - 1)) + res = res + s + end + return res end -local TOKEN_ASM = 0 -local TOKEN_LEFT_CURLY_BRACKET = 1 -local TOKEN_RIGHT_CURLY_BRACKET = 2 -local TOKEN_MULTIPLY = 3 -local TOKEN_DIVIDE = 4 -local TOKEN_SUBTRACT = 5 -local TOKEN_ADD = 6 -local TOKEN_LEFT_SQUARE_BRACKET = 7 -local TOKEN_RIGHT_SQUARE_BRACKET = 8 -local TOKEN_LEFT_PARANTHESIS = 9 -local TOKEN_RIGHT_PARANTHESIS = 10 -local TOKEN_QUESTION = 11 -local TOKEN_EXCLAMATION = 12 -local TOKEN_TILDA = 13 -local TOKEN_PERIOD = 14 -local TOKEN_COLUMN = 15 -local TOKEN_INTEGER = 16 -local TOKEN_ID = 17 -local TOKEN_STRING = 18 -local TOKEN_SEMICOLON = 19 -local TOKEN_HASH = 20 -local TOKEN_COMMA = 21 -local TOKEN_HLT = 22 -local TOKEN_RET = 23 -local TOKEN_CLI = 24 -local TOKEN_STI = 25 -local TOKEN_AH = 26 -local TOKEN_AL = 27 -local TOKEN_BH = 28 -local TOKEN_BL = 29 -local TOKEN_CL = 30 -local TOKEN_CH = 31 -local TOKEN_DL = 32 -local TOKEN_DH = 33 -local TOKEN_MOV = 34 -local TOKEN_HEXADECIMAL = 35 +function hexval(hex) + local dig = hex:sub(3) + local len = #dig + local result = 0 -local function issymbol(char) - if char == "{" or - char == "}" or - char == "*" or - char == "/" or - char == "-" or - char == "+" or - char == "[" or - char == "]" or - char == "(" or - char == ")" or - char == "?" or - char == "!" or - char == "~" or - char == "." or - char == ":" or - char == ";" or - char == "," or - char == "#" then - return true + for i = 1,len do + local d = dig:sub(i, i) + + if d:lower() == "a" then + d = 10 + elseif d:lower() == "b" then + d = 11 + elseif d:lower() == "c" then + d = 12 + elseif d:lower() == "d" then + d = 13 + elseif d:lower() == "e" then + d = 14 + elseif d:lower() == "f" then + d = 15 + end + + result = result + d * (16 ^ (len - i)) end - return false + + return result end +----------------------------------------------------------------------- +-- Checking arguments and setting read and write file +----------------------------------------------------------------------- -local function isnumber(char) - if char == "0" or - char == "1" or - char == "2" or - char == "3" or - char == "4" or - char == "5" or - char == "6" or - char == "7" or - char == "8" or - char == "9" then - return true - end - return false +progname = "6c" +local input = arg[1] +local output = arg[2] + +if input == nil then + print("usage: 6c input output") + os.exit(1) end -local function readid(line, s) - local len = #line - local buf = { } +if output == nil then + output = string.gsub(input, "%.%w+$", ".6") +end - for i = s, len do - local char = line:sub(i, i) - if char == " " then - break - end +local src = io.open(input, "r") +local dst = io.open(output, "wb") - if issymbol(char) == true then - break - end +----------------------------------------------------------------------- +-- Compiler functions +----------------------------------------------------------------------- +local function emittoken(line, col, type, len, raw) + writeu32(dst, line) + writeu32(dst, col) + writeu16(dst, type) + writeu16(dst, len) - if i == s and isnumber(char) == true then - break + if len > 0 then + for i = 1, len do + writeu8(dst, string.byte(raw:sub(i, i))) end - - table.insert(buf, char) end - - return table.concat(buf) end local function readint(line, s) @@ -198,9 +265,13 @@ local function readint(line, s) for i = s, len do local char = line:sub(i, i) + if char == "" or char == " " or issymbol(char) then + break + end + if isnumber(char) == false then - if hex == false then - if char == "h" and (i - s) == 1 then + if hex == false and (i - s) == 1 then + if char == "h" then local prefix = line:sub(i - 1, i - 1) if prefix ~= "0" then @@ -209,18 +280,10 @@ local function readint(line, s) hex = true else - if char ~= "" and char ~= " " then - print("illegal sufix for number line : " .. line) - end + perror("illegal character `" .. char .. "` line : " .. line) end else - if char == "" or char == " " then - break - end - - if char ~= "a" and char ~= "A" and char ~= "b" and char ~= "B" and - char ~= "c" and char ~= "C" and char ~= "d" and char ~= "D" and - char ~= "e" and char ~= "E" and char ~= "f" and char ~= "F" then + if not char:match("[0-9a-fA-F]") then perror("illegal sufix `" .. char .. "` for hexadecimal " .. line) end end @@ -232,160 +295,93 @@ local function readint(line, s) return table.concat(buf) end -local nline = 0; +local function readid(line, s) + local len = #line + local buf = { } + for i = s, len do + local char = line:sub(i, i) + if char == " " then + break + end + + if issymbol(char) == true then + break + end + + if i == s and isnumber(char) == true then + break + end + + table.insert(buf, char) + end + + return table.concat(buf) +end + +----------------------------------------------------------------------- +-- Tokenizing +----------------------------------------------------------------------- + +local nline = 0 while true do - line = src:read("*l"); - if (line == nil) then - break; + local line = src:read("*l") + if line == nil then + break end local i = 1 - local len = #line - local column = 0 + local ncol = 0 - while i <= len do + while i <= #line do local char = line:sub(i, i) - local buf = { } if issymbol(char) == true then - write_u32(nline) - write_u32(column) - if char == "{" then - write_u16(TOKEN_LEFT_CURLY_BRACKET) - elseif char == "}" then - write_u16(TOKEN_RIGHT_CURLY_BRACKET) - elseif char == "*" then - write_u16(TOKEN_MULTIPLY) - elseif char == "/" then - write_u16(TOKEN_DIVIDE) - elseif char == "." then - write_u16(TOKEN_PERIOD) - elseif char == ":" then - write_u16(TOKEN_COLUMN) - elseif char == "[" then - write_u16(TOKEN_LEFT_SQUARE_BRACKET) - elseif char == "]" then - write_u16(TOKEN_RIGHT_SQUARE_BRACKET) - elseif char == "(" then - write_u16(TOKEN_LEFT_PARANTHESIS) - elseif char == ")" then - write_u16(TOKEN_RIGHT_PARANTHESIS) - elseif char == ";" then - write_u16(TOKEN_SEMICOLON) - elseif char == "#" then - write_u16(TOKEN_HASH) - elseif char == "," then - write_u16(TOKEN_COMMA) - else - print("Unknown symbol : " .. char) + if tk_type[char] == nil then + perror("Unknown symbol `" .. char .. "`") end - - write_u16(0) + emittoken(nline, ncol, tk_type[char], 0, nil) i = i + 1 - - elseif char == " " or char == "\t" then - i = i + 1 - - elseif char == "\n" then - i = i + 1 - - elseif isnumber(char) then - local sint = readint(line, i) + elseif isnumber(char) == true then + local data = readint(line, i) local ishex = false - if sint:sub(2, 2) == "h" then + if data:sub(2, 2) == "h" then ishex = true end - local j = 1 - write_u32(nline) - write_u32(column) if ishex == true then - write_u16(TOKEN_HEXADECIMAL) + emittoken(nline, ncol, tk_type.hexadecimal, #data, data) else - write_u16(TOKEN_INTEGER) + emittoken(nline, ncol, tk_type.integer, #data, data) end - write_u16(#sint) - i = i + #sint - - while j <= #sint do - local c = sint:sub(j, j) - write_u8(string.byte(c)) - j = j + 1 - end - - elseif (char:match(".")) then + i = i + #data + elseif char == " " or char == "\t" then + i = i + 1 + else local id = readid(line, i) + if #id == 0 then + break + end - write_u32(nline) - write_u32(column) - if id == "asm" then - write_u16(TOKEN_ASM) - write_u16(0) - elseif id == "hlt" then - write_u16(TOKEN_HLT) - write_u16(0) - elseif id == "ret" then - write_u16(TOKEN_RET) - write_u16(0) - elseif id == "cli" then - write_u16(TOKEN_CLI) - write_u16(0) - elseif id == "sti" then - write_u16(TOKEN_STI) - write_u16(0) - elseif id == "ah" then - write_u16(TOKEN_AH) - write_u16(0) - elseif id == "al" then - write_u16(TOKEN_AL) - write_u16(0) - elseif id == "bh" then - write_u16(TOKEN_BH) - write_u16(0) - elseif id == "bl" then - write_u16(TOKEN_BL) - write_u16(0) - elseif id == "ch" then - write_u16(TOKEN_CH) - write_u16(0) - elseif id == "cl" then - write_u16(TOKEN_CL) - write_u16(0) - elseif id == "dh" then - write_u16(TOKEN_DH) - write_u16(0) - elseif id == "dl" then - write_u16(TOKEN_DL) - write_u16(0) - elseif id == "mov" then - write_u16(TOKEN_MOV) - write_u16(0) + if tk_type[id] == nil then + emittoken(nline, ncol, tk_type.id, #id, id) else - local j = 1 - write_u16(TOKEN_ID) - write_u16(#id) - - -- Raw bytes - while j <= #id do - local c = id:sub(j, j) - write_u8(string.byte(c)) - j = j + 1 - end + emittoken(nline, ncol, tk_type[id], 0, nil) end + i = i + #id - else - i = i + 1 end - column = i + ncol = i end + emittoken(nline, ncol, tk_type["\n"], 0, nil) nline = nline + 1 end +----------------------------------------------------------------------- +-- Close files +----------------------------------------------------------------------- src:close() dst:close() - -print(progname .. ": " .. arg[1] .. " -> " .. arg[2]) blob - ce8ce69a13ac2aa9f4ce553f4fe23518d06d60e8 blob + 116e3a97da138a8770841c72c42d761751f72500 --- tools/6l +++ tools/6l @@ -1,33 +1,118 @@ #!/usr/local/bin/lua51 -progname = "6l" +----------------------------------------------------------------------- +-- TOKEN MATCHING +----------------------------------------------------------------------- +local mnemonics = { "mov", "cli", "hlt", "ret", "sti" } -if (arg[1] == nil) then - print("usage: " .. progname .. " [file ...]") - os.exit(1) +local symbols = { ",", "[", "]", "(", ")", "{", "}", "*", "/", "-", + "+", "!", "~", "#", "?", ";", ":" } + +local regs8 = { "al", "ah", "bl", "bh", "cl", "ch", "dl", "dh" } + +local asmkwd = { "asm" } + +local tk_type = { } +local count = 0 + +for i = 1, #mnemonics do + count = count + 1 + tk_type[mnemonics[i]] = count end -if arg[2] == nil then - arg[2] = "6." ..string.gsub(arg[1], "%.%w+$", "") +for i = 1, #symbols do + count = count + 1 + tk_type[symbols[i]] = count end +for i = 1, #regs8 do + count = count + 1 + tk_type[regs8[i]] = count +end + +for i = 1, #asmkwd do + count = count + 1 + tk_type[asmkwd[i]] = count +end + +local function cincadd() + count = count + 1 + return count +end +tk_type.hexadecimal = cincadd() +tk_type.integer = cincadd() +tk_type.id = cincadd() +tk_type["\n"] = cincadd() + +----------------------------------------------------------------------- +-- DEBUGING +----------------------------------------------------------------------- local function perror(msg) print(progname .. ": error: " .. msg) os.exit(1) end -src = io.open(arg[1], "rb") +----------------------------------------------------------------------- +-- BOOLEAN check +----------------------------------------------------------------------- +local function isnumber(n) + if n:match("[0-9]") then + return true + end + return false +end -if src == nil then - perror("failed to open " .. arg[1]) +local function issymbol(char) + for i,v in ipairs(symbols) do + if v == char then + return true + end + end + return false end -dst = io.open(arg[2], "wb") +local function gettokenkey(m) + for i, v in pairs(tk_type) do + if tk_type[i] == m.type then + return i + end + end + return nil +end -if dst == nil then - perror("failed to open " .. arg[2]) +local function ismnemonic(m) + local key = gettokenkey(m) + + if key == nil then + return false + end + + for i,v in ipairs(mnemonics) do + if v == key then + return true + end + end + return false end +local function isregs8(m) + local key = gettokenkey(m) + + if key == nil then + return false + end + + for i,v in ipairs(regs8) do + if v == key then + return true + end + end + return false +end + +----------------------------------------------------------------------- +-- BITWISE OPERATIONS +----------------------------------------------------------------------- local function brshift(value, bit) return math.floor(value / (2 ^ bit)) end @@ -36,6 +121,22 @@ local function blshift(value, bit) return value * (2 ^ bit) end +local function band(a, b) + local result = 0 + local bitval = 1 + + while a > 0 and b > 0 do + if (a % 2 == 1) and (b % 2 == 1) then + result = result + bitval + end + a = math.floor(a / 2) + b = math.floor(b / 2) + bitval = bitval * 2 + end + + return result +end + local function bor(a, b) local result = 0 local shift = 1 @@ -56,80 +157,41 @@ local function bor(a, b) return result end ---[[ +----------------------------------------------------------------------- +-- WRITE OPERATIONS +----------------------------------------------------------------------- +local function writeu8(dst, value) + local b = band(value, 0xff) + dst:write(string.char(b)) +end -Token { - u32 Line; - u32 Column; - u16 Type; - u16 Length; -} +local function writeu16(dst, value) + local b2 = band(brshift(value, 8), 0xff) + local b1 = band(value, 0xff) -]]-- + dst:write(string.char(b2, b1)) -- Little Endian x86 +end -local TOKEN_ASM = 0 -local TOKEN_LEFT_CURLY_BRACKET = 1 -local TOKEN_RIGHT_CURLY_BRACKET = 2 -local TOKEN_MULTIPLY = 3 -local TOKEN_DIVIDE = 4 -local TOKEN_SUBTRACT = 5 -local TOKEN_ADD = 6 -local TOKEN_LEFT_SQUARE_BRACKET = 7 -local TOKEN_RIGHT_SQUARE_BRACKET = 8 -local TOKEN_LEFT_PARANTHESIS = 9 -local TOKEN_RIGHT_PARANTHESIS = 10 -local TOKEN_QUESTION = 11 -local TOKEN_EXCLAMATION = 12 -local TOKEN_TILDA = 13 -local TOKEN_PERIOD = 14 -local TOKEN_COLUMN = 15 -local TOKEN_INTEGER = 16 -local TOKEN_ID = 17 -local TOKEN_STRING = 18 -local TOKEN_SEMICOLON = 19 -local TOKEN_HASH = 20 -local TOKEN_COMMA = 21 -local TOKEN_HLT = 22 -local TOKEN_RET = 23 -local TOKEN_CLI = 24 -local TOKEN_STI = 25 -local TOKEN_AH = 26 -local TOKEN_AL = 27 -local TOKEN_BH = 28 -local TOKEN_BL = 29 -local TOKEN_CL = 30 -local TOKEN_CH = 31 -local TOKEN_DL = 32 -local TOKEN_DH = 33 -local TOKEN_MOV = 34 -local TOKEN_HEXADECIMAL = 35 +local function writeu32(dst, value) + local b4 = band(brshift(value, 24), 0xff) + local b3 = band(brshift(value, 16), 0xff) + local b2 = band(brshift(value, 8), 0xff) + local b1 = band(value, 0xff) -local asm = { - hlt = 0xF4, - ret = 0xC3, -- Near return - cli = 0xFA, - sti = 0xFB, -} + dst:write(string.char(b4, b3, b2, b1)) -- Little Endian x86 +end -local MODE_NONE = 0 -local MODE_ASM = 1 -function readtoken() - local header = src:read(12) +local function intval(intstr) + local len = #intstr + local res = 0 - if header == nil then - return nil + for i = 1, len do + local s = intstr:sub(len - i + 1, len - i + 1) + s = s * (10 ^ (i - 1)) + res = res + s end - - local b = { string.byte(header, 1, 12) } - - local line = b[4] + blshift(b[3], 8) + blshift(b[2], 16) + blshift(b[1], 24) - local col = b[8] + blshift(b[7], 8) + blshift(b[6], 16) + blshift(b[5], 24) - local type = b[10] + blshift(b[9], 8) - local length = b[12] + blshift(b[11], 8) - local raw = src:read(length) - - return { column = col, line = line, type = type, length = length, raw = raw } + return res end function hexval(hex) @@ -159,204 +221,228 @@ function hexval(hex) return result end +----------------------------------------------------------------------- +-- Checking arguments +----------------------------------------------------------------------- +progname = "6l" +local input = arg[1] +local output = arg[2] -local mode = MODE_NONE -local brace_depth = 0 -local begin = src:seek("cur") +local symbol_table = { } +local offset = 0 +local parser = { + inasm = false, -- In Assembly + insec = false, -- In Section +} -while true do - local token = readtoken() - if token == nil then - break +if input == nil then + print("usage: 6l input output") + os.exit(1) +end + +if output == nil then + output = string.gsub(input, "%.%w+$", "") +end + +local src = io.open(input, "rb") +if src == nil then + perror("failed to open `" .. input .. "`") +end + +local dst = io.open("6." .. output, "wb") +if dst == nil then + perror("failed to open `" .. output .. "`") +end + +----------------------------------------------------------------------- +-- Utility +----------------------------------------------------------------------- +local function readhdr() + local hdr = src:read(12) + if hdr == nil then + return nil end - -- Syntax check - if token.type == TOKEN_ASM then - if mode == MODE_ASM then - perror("error: nested asm attempt at line " .. token.line .. " column " .. token.column) - break - end + local b = { string.byte((hdr), 1, 12) } - local next = readtoken() - if next == nil or next.type ~= TOKEN_LEFT_CURLY_BRACKET then - perror("error: missing '{' after asm at line " .. token.line .. " column " .. token.column) - break - end + local line = b[4] + blshift(b[3], 8) + blshift(b[2], 16) + blshift(b[1], 24) + local col = b[8] + blshift(b[7], 8) + blshift(b[6], 16) + blshift(b[5], 24) + local type = b[10] + blshift(b[9], 8) + local len = b[12] + blshift(b[11], 8) + local raw = nil - mode = MODE_ASM - elseif token.type == TOKEN_HLT then - if mode ~= MODE_ASM then - perror("error: hlt instruction outside asm block at line " .. token.line .. " column " .. - token.column) - end + if len > 0 then raw = src:read(len) end + return { line = line, col = col, type = type, len = len, raw = raw } +end - local next = readtoken() +-- This functions reads an asm line and tells if its a label or instructions +-- If the return type is instructions, it will tell if its imm8, r8, etc +-- +-- To keep the assembler simple we will only allow 0-3 operands. More is simply +-- silly and unecessary for the goal. +local ASM_UNKNOWN = 0 +local ASM_LABEL = 1 +local ASM_INSTRUCTION = 2 +local ASM_R8 = 3 +local ASM_IMM8 = 4 - if next ~= nil and next.line == token.line then - perror("error: illegal token next to hlt on line " .. token.line .. " column " .. - token.column) +local function operlyze(op, ret) + if isregs8(op) == true then + ret.opcount = 1 + table.insert(ret.details, ASM_R8) + elseif op.type == tk_type.integer then + ret.opcount = 1 + local val = intval(op.raw) + + if val < 256 then + table.insert(ret.details, ASM_IMM8) + else + perror("operand 1 of instruction on line " .. op.line .. " is too big!") end - elseif token.type == TOKEN_RET then - if mode ~= MODE_ASM then - perror("error: ret instruction out of asm block at line " .. token.line .. " column " .. - token.column) - end + elseif op.type == tk_type.hexadecimal then + ret.opcount = 1 + local val = hexval(op.raw) - local next = readtoken() - if next ~= nil and next.line == token.line then - perror("error: illegal token after ret on line " .. token.line " .. column " .. - token.column) + if val < 256 then + table.insert(ret.details, ASM_IMM8) + else + perror("operand 1 of instruction on line " .. op.line .. " is too big!") end - elseif token.type == TOKEN_CLI then - if mode ~= MODE_ASM then - perror("error: cli instruction out of asm block at line " .. token.line .. " column " .. - token.column) - end + elseif op.type == tk_type["\n"] then + ret.opcount = 0 + else + perror("unknown operand `" .. gettokenkey(op) .. "` at line " .. op.line) + end +end - local next = readtoken() - if next ~= nil and next.line == token.line then - perror("error: illegal token after cli on line " .. token.line " .. column " .. - token.column) +local function asmlyze(first) + if ismnemonic(first) == true then + local ret = { + type = ASM_INSTRUCTION, + opcount = 0, + details = { } + } + + local op1 = readhdr() + operlyze(op1, ret) + + if ret.opcount == 0 then + return ret end - elseif token.type == TOKEN_STI then - if mode ~= MODE_ASM then - perror("error: sti instruction out of asm block at line " .. token.line .. " column " .. - token.column) - end - local next = readtoken() - if next ~= nil and next.line == token.line then - perror("error: illegal token after sti on line " .. token.line " .. column " .. - token.column) + local com = readhdr() + + if com.type == tk_type["\n"] then + return ret end - elseif token.type == TOKEN_MOV then - if mode ~= MODE_ASM then - perror("error: sti instruction out of asm block at line " .. token.line .. " column " .. - token.column) + if com.type ~= tk_type[","] then + perror("missing comma next to first operand on line " .. op1.line) end - local op1 = readtoken() + local op2 = readhdr() + operlyze(op2, ret) - if op1.type ~= TOKEN_AL and op1.type ~= TOKEN_AH and op1.type ~= TOKEN_BL and - op1.type ~= TOKEN_BH and op1.type ~= TOKEN_CH and - op1.type ~= TOKEN_CL and op1.type ~= TOKEN_DH and - op1.type ~= TOKEN_DL then - perror("illegal first operand for mov at line " .. token.line .. " column " .. - token.column) + if ret.opcount == 0 then + return ret end - local comma = readtoken() + com = readhdr() + if com.type == tk_type["\n"] then + return ret + end - if comma.type ~= TOKEN_COMMA then - perror("missing comma next to first operand line " .. token.line .. " column " .. - token.column) + if com.type ~= tk_type[","] then + perror("missing comma next to second operand on line " .. op1.line) end - local op2 = readtoken() + local op3 = readhdr() + operlyze(op3, ret) - if op2.type ~= TOKEN_AL and op2.type ~= TOKEN_AH and op2.type ~= TOKEN_BL and - op2.type ~= TOKEN_BH and op2.type ~= TOKEN_CH and - op2.type ~= TOKEN_CL and op2.type ~= TOKEN_DH and - op2.type ~= TOKEN_DL and op2.type ~= TOKEN_HEXADECIMAL then - perror("illegal second operand for mov at line " .. token.line .. " column " .. - token.column) + if ret.opcount == 0 then + return ret end - -- Checking if hex value is to high: mov r8, imm8 - if op2.type == TOKEN_HEXADECIMAL and - (op1.type == TOKEN_AH or op1.type == TOKEN_AL or - op1.type == TOKEN_BH or op1.type == TOKEN_BL or - op1.type == TOKEN_CH or op1.type == TOKEN_CL or - op1.type == TOKEN_DH or op1.type == TOKEN_DL) then - if hexval(op2.raw) > 255 then - perror("hexvalue too high at line " .. token.line .. " column " .. token.column) + local nl = readhdr() + if nl.type == tk_type["\n"] then + return ret + end + + perror("can only take up to 3 operands instructions. Line " .. first.line) + + else + if first.type == tk_type.id then + local col = readhdr() + if col.type ~= tk_type[":"] then + perror("missing colon next to " .. gettokenkey(first) .. " line " .. first.line) + else + return { type = ASM_LABEL } end + perror("Unknown instruction at line " .. first.line) + else + perror("illegal first element in instruction from line " .. first.line) end end end -src:seek("set", begin) - +----------------------------------------------------------------------- +-- 1st pass (syntax check and symbol table) +----------------------------------------------------------------------- while true do - local token = readtoken() - if token == nil then + local hdr = readhdr() + if hdr == nil then break end - if token.type == TOKEN_HLT then - dst:write(string.char(asm.hlt)) - elseif token.type == TOKEN_RET then - dst:write(string.char(asm.ret)) - elseif token.type == TOKEN_CLI then - dst:write(string.char(asm.cli)) - elseif token.type == TOKEN_STI then - dst:write(string.char(asm.sti)) - elseif token.type == TOKEN_MOV then - local op1 = readtoken() - readtoken() -- Comma - local op2 = readtoken() + if parser.inasm == true then + if hdr.type == tk_type["asm"] then + perror("illegal nested asm keyword at line " .. hdr.line .. " column " .. hdr.col) + elseif hdr.type == tk_type["}"] then + parser.inasm = false + else + if hdr.type ~= tk_type["\n"] then + local inst = asmlyze(hdr) - if op1.type == TOKEN_AL then - if op2.type == TOKEN_HEXADECIMAL then - local opcode = { hexval("0hB0") + tonumber("000", 2), hexval(op2.raw) } + if inst.type == ASM_INSTRUCTION then + if hdr.type == tk_type["mov"] then + -- mov r8, r8 + if inst.details[1] == ASM_R8 and inst.details[2] == ASM_R8 then + offset = offset + 2 - dst:write(string.char(opcode[1])) - dst:write(string.char(opcode[2])) - end - elseif op1.type == TOKEN_AH then - if op2.type == TOKEN_HEXADECIMAL then - local opcode = { hexval("0hB0") + tonumber("100", 2), hexval(op2.raw) } + -- mov r8, imm8 + elseif inst.details[1] == ASM_R8 and inst.details[2] == ASM_IMM8 then + offset = offset + 2 - dst:write(string.char(opcode[1])) - dst:write(string.char(opcode[2])) + -- huh? + else + perror("invalid instruction at line " .. hdr.line) + end + end + -- Add symbol table + elseif inst.type == ASM_LABEL then + symbol_table[hdr.raw] = offset + else + perror("Unknown instruction line " .. hdr.line) + end end - elseif op1.type == TOKEN_BL then - if op2.type == TOKEN_HEXADECIMAL then - local opcode = { hexval("0hB0") + tonumber("011", 2), hexval(op2.raw) } - - dst:write(string.char(opcode[1])) - dst:write(string.char(opcode[2])) + end + else + if hdr.type == tk_type["asm"] then + local next = readhdr() + if next.type ~= tk_type["{"] then + perror("missing { after asm, line " .. hdr.line .. " column " .. hdr.col) end - elseif op1.type == TOKEN_BH then - if op2.type == TOKEN_HEXADECIMAL then - local opcode = { hexval("0hB0") + tonumber("111", 2), hexval(op2.raw) } - - dst:write(string.char(opcode[1])) - dst:write(string.char(opcode[2])) - end - elseif op1.type == TOKEN_CL then - if op2.type == TOKEN_HEXADECIMAL then - local opcode = { hexval("0hB0") + tonumber("001", 2), hexval(op2.raw) } - - dst:write(string.char(opcode[1])) - dst:write(string.char(opcode[2])) - end - elseif op1.type == TOKEN_CH then - if op2.type == TOKEN_HEXADECIMAL then - local opcode = { hexval("0hB0") + tonumber("101", 2), hexval(op2.raw) } - - dst:write(string.char(opcode[1])) - dst:write(string.char(opcode[2])) - end - elseif op1.type == TOKEN_DL then - if op2.type == TOKEN_HEXADECIMAL then - local opcode = { hexval("0hB0") + tonumber("010", 2), hexval(op2.raw) } - - dst:write(string.char(opcode[1])) - dst:write(string.char(opcode[2])) - end - elseif op1.type == TOKEN_DH then - if op2.type == TOKEN_HEXADECIMAL then - local opcode = { hexval("0hB0") + tonumber("110", 2), hexval(op2.raw) } - - dst:write(string.char(opcode[1])) - dst:write(string.char(opcode[2])) - end + parser.inasm = true end end end +----------------------------------------------------------------------- +-- 2nd pass binary generation +----------------------------------------------------------------------- + +----------------------------------------------------------------------- +-- Closing files +----------------------------------------------------------------------- src:close() dst:close() -print(arg[1] .. " -> " .. arg[2]) blob - 17dc1369b1522cc6139fcb7b47f9086f35c4d8bb (mode 644) blob + /dev/null --- tools/boot.c +++ /dev/null @@ -1,4 +0,0 @@ -asm { -_start: - ret -} blob - /dev/null blob + 846d2fda3cde69a0ef32cbed3930c60c8c5bb3da (mode 644) --- /dev/null +++ tools/boot.sc @@ -0,0 +1,9 @@ +asm { +_start: + mov al, 0h10 + mov ah, 22 + ret +main: + hlt + cli +} blob - /dev/null blob + 1a239eae8c59bda6d3eae8bd932b95f37299a372 (mode 755) --- /dev/null +++ tools/build.sh @@ -0,0 +1,18 @@ +#!/bin/ksh + +# The goal of this script is to combine lua files into +# a single executable script + +COMPILER=6c +LINKER=6l + +# compiler +echo "#!/usr/local/bin/lua51\n" > $COMPILER +cat util.lua compiler.lua >> $COMPILER + +# linker +echo "#!/usr/local/bin/lua51\n" > $LINKER +cat util.lua linker.lua >> $LINKER + +chmod +x $COMPILER +chmod +x $LINKER blob - /dev/null blob + 71f6edaaccf35a7ecad23def99747b4524d30cd9 (mode 644) --- /dev/null +++ tools/compiler.lua @@ -0,0 +1,164 @@ +----------------------------------------------------------------------- +-- Checking arguments and setting read and write file +----------------------------------------------------------------------- + +progname = "6c" +local input = arg[1] +local output = arg[2] + +if input == nil then + print("usage: 6c input output") + os.exit(1) +end + +if output == nil then + output = string.gsub(input, "%.%w+$", ".6") +end + +local src = io.open(input, "r") +local dst = io.open(output, "wb") + +----------------------------------------------------------------------- +-- Compiler functions +----------------------------------------------------------------------- +local function emittoken(line, col, type, len, raw) + writeu32(dst, line) + writeu32(dst, col) + writeu16(dst, type) + writeu16(dst, len) + + if len > 0 then + for i = 1, len do + writeu8(dst, string.byte(raw:sub(i, i))) + end + end +end + +local function readint(line, s) + local len = #line + local buf = { } + local hex = false + + for i = s, len do + local char = line:sub(i, i) + + if char == "" or char == " " or issymbol(char) then + break + end + + if isnumber(char) == false then + if hex == false and (i - s) == 1 then + if char == "h" then + local prefix = line:sub(i - 1, i - 1) + + if prefix ~= "0" then + perror("illegal token `" .. prefix .. "` next to number : " .. line) + end + + hex = true + else + perror("illegal character `" .. char .. "` line : " .. line) + end + else + if not char:match("[0-9a-fA-F]") then + perror("illegal sufix `" .. char .. "` for hexadecimal " .. line) + end + end + end + + table.insert(buf, char) + end + + return table.concat(buf) +end + +local function readid(line, s) + local len = #line + local buf = { } + + for i = s, len do + local char = line:sub(i, i) + if char == " " then + break + end + + if issymbol(char) == true then + break + end + + if i == s and isnumber(char) == true then + break + end + + table.insert(buf, char) + end + + return table.concat(buf) +end + +----------------------------------------------------------------------- +-- Tokenizing +----------------------------------------------------------------------- + +local nline = 0 +while true do + local line = src:read("*l") + if line == nil then + break + end + + local i = 1 + local ncol = 0 + + while i <= #line do + local char = line:sub(i, i) + + if issymbol(char) == true then + if tk_type[char] == nil then + perror("Unknown symbol `" .. char .. "`") + end + emittoken(nline, ncol, tk_type[char], 0, nil) + i = i + 1 + elseif isnumber(char) == true then + local data = readint(line, i) + local ishex = false + + if data:sub(2, 2) == "h" then + ishex = true + end + + if ishex == true then + emittoken(nline, ncol, tk_type.hexadecimal, #data, data) + else + emittoken(nline, ncol, tk_type.integer, #data, data) + end + i = i + #data + elseif char == " " or char == "\t" then + i = i + 1 + else + local id = readid(line, i) + if #id == 0 then + break + end + + if tk_type[id] == nil then + emittoken(nline, ncol, tk_type.id, #id, id) + else + emittoken(nline, ncol, tk_type[id], 0, nil) + end + + i = i + #id + end + + ncol = i + end + + emittoken(nline, ncol, tk_type["\n"], 0, nil) + nline = nline + 1 +end + +----------------------------------------------------------------------- +-- Close files +----------------------------------------------------------------------- +src:close() +dst:close() blob - /dev/null blob + 4fd03b00959bb3a8a3763042c0eddde8adaa01e0 (mode 644) --- /dev/null +++ tools/linker.lua @@ -0,0 +1,225 @@ +----------------------------------------------------------------------- +-- Checking arguments +----------------------------------------------------------------------- +progname = "6l" +local input = arg[1] +local output = arg[2] + +local symbol_table = { } +local offset = 0 +local parser = { + inasm = false, -- In Assembly + insec = false, -- In Section +} + +if input == nil then + print("usage: 6l input output") + os.exit(1) +end + +if output == nil then + output = string.gsub(input, "%.%w+$", "") +end + +local src = io.open(input, "rb") +if src == nil then + perror("failed to open `" .. input .. "`") +end + +local dst = io.open("6." .. output, "wb") +if dst == nil then + perror("failed to open `" .. output .. "`") +end + +----------------------------------------------------------------------- +-- Utility +----------------------------------------------------------------------- +local function readhdr() + local hdr = src:read(12) + if hdr == nil then + return nil + end + + local b = { string.byte((hdr), 1, 12) } + + local line = b[4] + blshift(b[3], 8) + blshift(b[2], 16) + blshift(b[1], 24) + local col = b[8] + blshift(b[7], 8) + blshift(b[6], 16) + blshift(b[5], 24) + local type = b[10] + blshift(b[9], 8) + local len = b[12] + blshift(b[11], 8) + local raw = nil + + if len > 0 then raw = src:read(len) end + return { line = line, col = col, type = type, len = len, raw = raw } +end + +-- This functions reads an asm line and tells if its a label or instructions +-- If the return type is instructions, it will tell if its imm8, r8, etc +-- +-- To keep the assembler simple we will only allow 0-3 operands. More is simply +-- silly and unecessary for the goal. +local ASM_UNKNOWN = 0 +local ASM_LABEL = 1 +local ASM_INSTRUCTION = 2 +local ASM_R8 = 3 +local ASM_IMM8 = 4 + +local function operlyze(op, ret) + if isregs8(op) == true then + ret.opcount = 1 + table.insert(ret.details, ASM_R8) + elseif op.type == tk_type.integer then + ret.opcount = 1 + local val = intval(op.raw) + + if val < 256 then + table.insert(ret.details, ASM_IMM8) + else + perror("operand 1 of instruction on line " .. op.line .. " is too big!") + end + elseif op.type == tk_type.hexadecimal then + ret.opcount = 1 + local val = hexval(op.raw) + + if val < 256 then + table.insert(ret.details, ASM_IMM8) + else + perror("operand 1 of instruction on line " .. op.line .. " is too big!") + end + elseif op.type == tk_type["\n"] then + ret.opcount = 0 + else + perror("unknown operand `" .. gettokenkey(op) .. "` at line " .. op.line) + end +end + +local function asmlyze(first) + if ismnemonic(first) == true then + local ret = { + type = ASM_INSTRUCTION, + opcount = 0, + details = { } + } + + local op1 = readhdr() + operlyze(op1, ret) + + if ret.opcount == 0 then + return ret + end + + local com = readhdr() + + if com.type == tk_type["\n"] then + return ret + end + + if com.type ~= tk_type[","] then + perror("missing comma next to first operand on line " .. op1.line) + end + + local op2 = readhdr() + operlyze(op2, ret) + + if ret.opcount == 0 then + return ret + end + + com = readhdr() + if com.type == tk_type["\n"] then + return ret + end + + if com.type ~= tk_type[","] then + perror("missing comma next to second operand on line " .. op1.line) + end + + local op3 = readhdr() + operlyze(op3, ret) + + if ret.opcount == 0 then + return ret + end + + local nl = readhdr() + if nl.type == tk_type["\n"] then + return ret + end + + perror("can only take up to 3 operands instructions. Line " .. first.line) + + else + if first.type == tk_type.id then + local col = readhdr() + if col.type ~= tk_type[":"] then + perror("missing colon next to " .. gettokenkey(first) .. " line " .. first.line) + else + return { type = ASM_LABEL } + end + perror("Unknown instruction at line " .. first.line) + else + perror("illegal first element in instruction from line " .. first.line) + end + end +end + +----------------------------------------------------------------------- +-- 1st pass (syntax check and symbol table) +----------------------------------------------------------------------- +while true do + local hdr = readhdr() + if hdr == nil then + break + end + + if parser.inasm == true then + if hdr.type == tk_type["asm"] then + perror("illegal nested asm keyword at line " .. hdr.line .. " column " .. hdr.col) + elseif hdr.type == tk_type["}"] then + parser.inasm = false + else + if hdr.type ~= tk_type["\n"] then + local inst = asmlyze(hdr) + + if inst.type == ASM_INSTRUCTION then + if hdr.type == tk_type["mov"] then + -- mov r8, r8 + if inst.details[1] == ASM_R8 and inst.details[2] == ASM_R8 then + offset = offset + 2 + + -- mov r8, imm8 + elseif inst.details[1] == ASM_R8 and inst.details[2] == ASM_IMM8 then + offset = offset + 2 + + -- huh? + else + perror("invalid instruction at line " .. hdr.line) + end + end + -- Add symbol table + elseif inst.type == ASM_LABEL then + symbol_table[hdr.raw] = offset + else + perror("Unknown instruction line " .. hdr.line) + end + end + end + else + if hdr.type == tk_type["asm"] then + local next = readhdr() + if next.type ~= tk_type["{"] then + perror("missing { after asm, line " .. hdr.line .. " column " .. hdr.col) + end + parser.inasm = true + end + end +end + +----------------------------------------------------------------------- +-- 2nd pass binary generation +----------------------------------------------------------------------- + +----------------------------------------------------------------------- +-- Closing files +----------------------------------------------------------------------- +src:close() +dst:close() blob - /dev/null blob + cc7115c9a8b1920867e53e25cd21892a6d296e45 (mode 644) --- /dev/null +++ tools/util.lua @@ -0,0 +1,221 @@ +----------------------------------------------------------------------- +-- TOKEN MATCHING +----------------------------------------------------------------------- +local mnemonics = { "mov", "cli", "hlt", "ret", "sti" } + +local symbols = { ",", "[", "]", "(", ")", "{", "}", "*", "/", "-", + "+", "!", "~", "#", "?", ";", ":" } + +local regs8 = { "al", "ah", "bl", "bh", "cl", "ch", "dl", "dh" } + +local asmkwd = { "asm" } + +local tk_type = { } +local count = 0 + +for i = 1, #mnemonics do + count = count + 1 + tk_type[mnemonics[i]] = count +end + +for i = 1, #symbols do + count = count + 1 + tk_type[symbols[i]] = count +end + +for i = 1, #regs8 do + count = count + 1 + tk_type[regs8[i]] = count +end + +for i = 1, #asmkwd do + count = count + 1 + tk_type[asmkwd[i]] = count +end + +local function cincadd() + count = count + 1 + return count +end +tk_type.hexadecimal = cincadd() +tk_type.integer = cincadd() +tk_type.id = cincadd() +tk_type["\n"] = cincadd() + +----------------------------------------------------------------------- +-- DEBUGING +----------------------------------------------------------------------- +local function perror(msg) + print(progname .. ": error: " .. msg) + os.exit(1) +end + +----------------------------------------------------------------------- +-- BOOLEAN check +----------------------------------------------------------------------- +local function isnumber(n) + if n:match("[0-9]") then + return true + end + return false +end + +local function issymbol(char) + for i,v in ipairs(symbols) do + if v == char then + return true + end + end + return false +end + +local function gettokenkey(m) + for i, v in pairs(tk_type) do + if tk_type[i] == m.type then + return i + end + end + return nil +end + +local function ismnemonic(m) + local key = gettokenkey(m) + + if key == nil then + return false + end + + for i,v in ipairs(mnemonics) do + if v == key then + return true + end + end + return false +end + +local function isregs8(m) + local key = gettokenkey(m) + + if key == nil then + return false + end + + for i,v in ipairs(regs8) do + if v == key then + return true + end + end + return false +end + +----------------------------------------------------------------------- +-- BITWISE OPERATIONS +----------------------------------------------------------------------- +local function brshift(value, bit) + return math.floor(value / (2 ^ bit)) +end + +local function blshift(value, bit) + return value * (2 ^ bit) +end + +local function band(a, b) + local result = 0 + local bitval = 1 + + while a > 0 and b > 0 do + if (a % 2 == 1) and (b % 2 == 1) then + result = result + bitval + end + a = math.floor(a / 2) + b = math.floor(b / 2) + bitval = bitval * 2 + end + + return result +end + +local function bor(a, b) + local result = 0 + local shift = 1 + + while a > 0 or b > 0 do + local r1 = a % 2 + local r2 = b % 2 + + if r1 == 1 or r2 == 1 then + result = result + shift + end + + a = math.floor(a / 2) + b = math.floor(b / 2) + shift = shift * 2 + end + + return result +end + +----------------------------------------------------------------------- +-- WRITE OPERATIONS +----------------------------------------------------------------------- +local function writeu8(dst, value) + local b = band(value, 0xff) + dst:write(string.char(b)) +end + +local function writeu16(dst, value) + local b2 = band(brshift(value, 8), 0xff) + local b1 = band(value, 0xff) + + dst:write(string.char(b2, b1)) -- Little Endian x86 +end + +local function writeu32(dst, value) + local b4 = band(brshift(value, 24), 0xff) + local b3 = band(brshift(value, 16), 0xff) + local b2 = band(brshift(value, 8), 0xff) + local b1 = band(value, 0xff) + + dst:write(string.char(b4, b3, b2, b1)) -- Little Endian x86 +end + + +local function intval(intstr) + local len = #intstr + local res = 0 + + for i = 1, len do + local s = intstr:sub(len - i + 1, len - i + 1) + s = s * (10 ^ (i - 1)) + res = res + s + end + return res +end + +function hexval(hex) + local dig = hex:sub(3) + local len = #dig + local result = 0 + + for i = 1,len do + local d = dig:sub(i, i) + + if d:lower() == "a" then + d = 10 + elseif d:lower() == "b" then + d = 11 + elseif d:lower() == "c" then + d = 12 + elseif d:lower() == "d" then + d = 13 + elseif d:lower() == "e" then + d = 14 + elseif d:lower() == "f" then + d = 15 + end + + result = result + d * (16 ^ (len - i)) + end + + return result +end