Compare commits
No commits in common. "main" and "clang-format" have entirely different histories.
main
...
clang-form
19
.github/workflows/c-cpp.yml
vendored
19
.github/workflows/c-cpp.yml
vendored
|
@ -1,19 +0,0 @@
|
||||||
name: Cee-lang CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "main" ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ "main" ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: make
|
|
||||||
run: make
|
|
||||||
- name: example
|
|
||||||
run: make example
|
|
9
Makefile
9
Makefile
|
@ -7,7 +7,6 @@ CLANG_FORMAT_STYLE = '{ BasedOnStyle: Google, IndentWidth: 4 }'
|
||||||
|
|
||||||
hbas: build/hbas
|
hbas: build/hbas
|
||||||
example: build/example.hbf
|
example: build/example.hbf
|
||||||
hello: build/hello.hbf
|
|
||||||
|
|
||||||
format:
|
format:
|
||||||
clang-format --style=${CLANG_FORMAT_STYLE} -i src/*
|
clang-format --style=${CLANG_FORMAT_STYLE} -i src/*
|
||||||
|
@ -18,19 +17,15 @@ check-format:
|
||||||
build:
|
build:
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
|
|
||||||
build/hbas: build $(wildcard src/*.h src/*.c)
|
build/hbas: build src/hbas.c
|
||||||
${CC} ${CFLAGS} ${CFLAGS_EXTRA} src/hbas.c -o build/hbas
|
${CC} ${CFLAGS} ${CFLAGS_EXTRA} src/hbas.c -o build/hbas
|
||||||
|
|
||||||
build/example.hbf: build build/hbas examples/example.S
|
build/example.hbf: build build/hbas examples/example.S
|
||||||
./build/hbas < examples/example.S > build/example.hbf
|
./build/hbas < examples/example.S > build/example.hbf
|
||||||
xxd build/example.hbf
|
xxd build/example.hbf
|
||||||
|
|
||||||
build/hello.hbf: build build/hbas examples/hello.S
|
|
||||||
./build/hbas < examples/hello.S > build/hello.hbf
|
|
||||||
xxd build/hello.hbf
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf build
|
rm -rf build
|
||||||
|
|
||||||
all:
|
all:
|
||||||
hbas
|
hbas
|
|
@ -2,25 +2,16 @@
|
||||||
; https://git.ablecorp.us/AbleOS/holey-bytes/src/branch/trunk/spec.md
|
; https://git.ablecorp.us/AbleOS/holey-bytes/src/branch/trunk/spec.md
|
||||||
; TODO:
|
; TODO:
|
||||||
; .origin 0x1000
|
; .origin 0x1000
|
||||||
; 'c' char literals
|
; .align 0x100
|
||||||
|
; .db "hello"
|
||||||
; .struct
|
; .struct
|
||||||
start:
|
start:
|
||||||
jmp end
|
jmp end
|
||||||
un
|
un
|
||||||
|
; .db "hello world\n"
|
||||||
add16 r1, r2, r255
|
add16 r1, r2, r255
|
||||||
addi8 r1, r2, -128
|
addi8 r1, r2, -128
|
||||||
lra r1, r0, start
|
lra r1, r0, start
|
||||||
jmp start
|
jmp start
|
||||||
end:
|
end:
|
||||||
tx
|
tx
|
||||||
hello_string:
|
|
||||||
.db "Hello, w\x6frld\n", 0
|
|
||||||
hello_string_end:
|
|
||||||
.db "hi"
|
|
||||||
; TODO .db 'H', 'e', 'l', 'l', 'o', '\0'
|
|
||||||
.align 2
|
|
||||||
.dw 0x4546
|
|
||||||
.align 4
|
|
||||||
.dd 0x4748494a
|
|
||||||
.align 8
|
|
||||||
.dq 0x5051525354555657
|
|
||||||
|
|
|
@ -1,103 +0,0 @@
|
||||||
jmp entry
|
|
||||||
|
|
||||||
puts:
|
|
||||||
; Write string to console
|
|
||||||
; r2: [IN] *const u8 String pointer
|
|
||||||
; r3: [IN] usize String length
|
|
||||||
|
|
||||||
li8 r1, 0x1 ; Write syscall
|
|
||||||
brc r2, r3, 2 ; Copy parameters
|
|
||||||
li8 r2, 0x1 ; STDOUT
|
|
||||||
eca
|
|
||||||
|
|
||||||
jal r0, r31, 0
|
|
||||||
|
|
||||||
gets:
|
|
||||||
; Read string until end of buffer or LF
|
|
||||||
; r2: [IN] *mut u8 Buffer
|
|
||||||
; r3: [IN] usize Buffer length
|
|
||||||
|
|
||||||
; Register allocations:
|
|
||||||
; r33: *mut u8 Buffer end
|
|
||||||
; r34: u8 Immediate char
|
|
||||||
; r35: u8 Const [0x0A = LF]
|
|
||||||
|
|
||||||
li8 r35, 0x0A
|
|
||||||
add64 r33, r2, r3
|
|
||||||
|
|
||||||
; Setup syscall
|
|
||||||
li8 r2, 0x1 ; Stdin
|
|
||||||
cp r3, r2
|
|
||||||
li8 r4, 0x1 ; Read one char
|
|
||||||
|
|
||||||
jeq r3, r33, end
|
|
||||||
loop:
|
|
||||||
li8 r1, 0x1 ; Read syscall
|
|
||||||
eca
|
|
||||||
addi64 r3, r3, 1
|
|
||||||
ld r34, r3, 0, 1
|
|
||||||
jeq r34, r35, end
|
|
||||||
jne r3, r33, loop
|
|
||||||
|
|
||||||
end:
|
|
||||||
; Set copied amount
|
|
||||||
sub64 r1, r33, r3
|
|
||||||
addi64 r1, r1, -1
|
|
||||||
jal r0, r31, 0
|
|
||||||
|
|
||||||
alloc_pages:
|
|
||||||
; Allocate pages
|
|
||||||
; r1: [OUT] *mut u8 Pointer to page
|
|
||||||
; r2: [IN] u16 Page count
|
|
||||||
|
|
||||||
muli16 r3, r2, 4096 ; page count
|
|
||||||
li8 r1, 0x9 ; mmap syscall
|
|
||||||
li8 r2, 0x0 ; no address set, kernel chosen
|
|
||||||
li8 r4, 0x2 ; PROT_WRITE
|
|
||||||
li8 r5, 0x20 ; MAP_ANONYMOUS
|
|
||||||
li64 r6, -1 ; Doesn't map file
|
|
||||||
li8 r7, 0x0 ; Doesn't map file
|
|
||||||
eca
|
|
||||||
|
|
||||||
jal r0, r31, 0
|
|
||||||
|
|
||||||
entry:
|
|
||||||
; Program entrypoint
|
|
||||||
|
|
||||||
; Register allocations:
|
|
||||||
; r32: *mut u8 Buffer
|
|
||||||
; r36: usize Read buffer length
|
|
||||||
|
|
||||||
; Allocate one page (4096 KiB)
|
|
||||||
li8 r2, 1
|
|
||||||
jal r31, r0, alloc_pages
|
|
||||||
cp r32, r1
|
|
||||||
|
|
||||||
; Print message
|
|
||||||
lra16 r2, r0, enter_your_name
|
|
||||||
li8 r3, 17
|
|
||||||
jal r31, r0, puts
|
|
||||||
|
|
||||||
; Read name
|
|
||||||
cp r2, r32
|
|
||||||
li16 r3, 4096
|
|
||||||
jal r31, r0, gets
|
|
||||||
cp r36, r1
|
|
||||||
|
|
||||||
; Print your name is
|
|
||||||
lra16 r2, r0, your_name_is
|
|
||||||
li8 r3, 15
|
|
||||||
jal r31, r0, puts
|
|
||||||
|
|
||||||
; And now print the name
|
|
||||||
cp r2, r32
|
|
||||||
cp r3, r36
|
|
||||||
jal r31, r0, puts
|
|
||||||
|
|
||||||
tx
|
|
||||||
|
|
||||||
|
|
||||||
enter_your_name:
|
|
||||||
.db "Enter your name: "
|
|
||||||
your_name_is:
|
|
||||||
.db "\nYour name is: "
|
|
|
@ -1,8 +0,0 @@
|
||||||
li8 r1, 1 ; 1->sys::write
|
|
||||||
li8 r2, 1 ; fildes=stdout
|
|
||||||
lra16 r3, r0, hello_string ; buf=hello_string
|
|
||||||
li8 r4, 0x11 ; nbyte=0x11
|
|
||||||
eca ; sys::write(stdout, hello_string, 0x11)
|
|
||||||
tx
|
|
||||||
hello_string:
|
|
||||||
.db "Hello, AbleCorp!\n"
|
|
|
@ -56,7 +56,8 @@ const char *TYPE_STR[] = {
|
||||||
|
|
||||||
const size_t NARGS = sizeof(ARGS) / sizeof(ARGS[0]);
|
const size_t NARGS = sizeof(ARGS) / sizeof(ARGS[0]);
|
||||||
|
|
||||||
static ArgMeta arg_meta(char arg) {
|
static
|
||||||
|
ArgMeta arg_meta(char arg) {
|
||||||
for (size_t ii = 0; ii < NARGS; ii += 1) {
|
for (size_t ii = 0; ii < NARGS; ii += 1) {
|
||||||
ArgMeta meta = ARGS[ii];
|
ArgMeta meta = ARGS[ii];
|
||||||
if (meta.chr == arg) {
|
if (meta.chr == arg) {
|
||||||
|
|
|
@ -6,7 +6,8 @@ typedef struct ByteVec_s {
|
||||||
size_t len;
|
size_t len;
|
||||||
} ByteVec;
|
} ByteVec;
|
||||||
|
|
||||||
static AsmError ensure_push(ByteVec *vec, size_t el_size, size_t extra) {
|
static
|
||||||
|
AsmError ensure_push(ByteVec *vec, size_t el_size, size_t extra) {
|
||||||
if (vec->len + extra < vec->len) {
|
if (vec->len + extra < vec->len) {
|
||||||
return ErrOutOfMemory;
|
return ErrOutOfMemory;
|
||||||
}
|
}
|
||||||
|
|
138
src/directive.c
138
src/directive.c
|
@ -1,138 +0,0 @@
|
||||||
AsmError push_string(char *buf, char *input, size_t len) {
|
|
||||||
size_t ndata = 0;
|
|
||||||
for (size_t pos = 0; pos < len; pos += 1) {
|
|
||||||
char chr = input[pos];
|
|
||||||
if (chr == '\\') {
|
|
||||||
if (pos + 1 >= len) {
|
|
||||||
return ErrDanglingEscape;
|
|
||||||
}
|
|
||||||
pos += 1;
|
|
||||||
chr = input[pos];
|
|
||||||
switch (chr) {
|
|
||||||
case '\\':
|
|
||||||
chr = '\\';
|
|
||||||
break;
|
|
||||||
case '"':
|
|
||||||
chr = '"';
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
chr = '\r';
|
|
||||||
break;
|
|
||||||
case 'n':
|
|
||||||
chr = '\n';
|
|
||||||
break;
|
|
||||||
case '0':
|
|
||||||
chr = '\0';
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
chr = '\t';
|
|
||||||
break;
|
|
||||||
case 'x':
|
|
||||||
if (pos + 2 >= len) {
|
|
||||||
return ErrDanglingEscape;
|
|
||||||
}
|
|
||||||
char high = get_hex(input[pos + 1]);
|
|
||||||
char low = get_hex(input[pos + 2]);
|
|
||||||
if (high > 15 || low > 15) {
|
|
||||||
return ErrStringBadHex;
|
|
||||||
}
|
|
||||||
pos += 2;
|
|
||||||
chr = high << 4 | low;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return ErrBadStringEscape;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buf[ndata] = chr;
|
|
||||||
ndata += 1;
|
|
||||||
}
|
|
||||||
return ErrOk;
|
|
||||||
}
|
|
||||||
|
|
||||||
static AsmError push_data(char *input, size_t len, ByteVec *out, Token *tok,
|
|
||||||
size_t word_size) {
|
|
||||||
while (1) {
|
|
||||||
*tok = token(input, len, tok->start + tok->len);
|
|
||||||
if (tok->kind == TokNumber) {
|
|
||||||
if (ensure_push(out, 1, word_size) != 0) {
|
|
||||||
return ErrOutOfMemory;
|
|
||||||
}
|
|
||||||
push_int_le(&out->buf[out->len], tok->num, word_size, 3);
|
|
||||||
out->len += word_size;
|
|
||||||
} else if (tok->kind == TokString) {
|
|
||||||
if (word_size != 1) {
|
|
||||||
return ErrStringDataNotByte;
|
|
||||||
}
|
|
||||||
if (ensure_push(out, 1, tok->num) != 0) {
|
|
||||||
return ErrOutOfMemory;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *str = &input[tok->start + 1];
|
|
||||||
AsmError err = push_string(&out->buf[out->len], str, tok->len - 2);
|
|
||||||
if (err != ErrOk) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
out->len += tok->num;
|
|
||||||
} else {
|
|
||||||
return ErrNeedsDataLiteral;
|
|
||||||
}
|
|
||||||
*tok = token(input, len, tok->start + tok->len);
|
|
||||||
if (tok->kind == TokNewline || tok->kind == TokEOF) {
|
|
||||||
return ErrOk;
|
|
||||||
}
|
|
||||||
if (tok->kind == TokComma) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
return ErrNeedCommaOrNewline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AsmError assemble_directive(char *input, size_t len, ByteVec *out, Token *tok) {
|
|
||||||
if (tok->len < 2) {
|
|
||||||
return ErrInvalidDirective;
|
|
||||||
}
|
|
||||||
size_t pos = tok->start;
|
|
||||||
char byte0 = input[pos];
|
|
||||||
char byte1 = input[pos + 1];
|
|
||||||
if (tok->len == 2 && byte0 == 'd') {
|
|
||||||
size_t word_size;
|
|
||||||
switch (byte1) {
|
|
||||||
case 'b':
|
|
||||||
word_size = 1;
|
|
||||||
break;
|
|
||||||
case 'w':
|
|
||||||
word_size = 2;
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
word_size = 4;
|
|
||||||
break;
|
|
||||||
case 'q':
|
|
||||||
word_size = 8;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return ErrInvalidDirective;
|
|
||||||
}
|
|
||||||
return push_data(input, len, out, tok, word_size);
|
|
||||||
}
|
|
||||||
if (tok->len == 5 && strncmp("align", &input[pos], 5) == 0) {
|
|
||||||
*tok = token(input, len, tok->start + tok->len);
|
|
||||||
if (tok->kind != TokNumber) {
|
|
||||||
return ErrAlignNeedsNumber;
|
|
||||||
}
|
|
||||||
size_t mask = tok->num - 1;
|
|
||||||
if (tok->num == 0 || (tok->num & mask) != 0) {
|
|
||||||
return ErrAlignNeedsPow2;
|
|
||||||
}
|
|
||||||
if ((~(size_t)0) - mask < out->len) {
|
|
||||||
return ErrOutOfMemory;
|
|
||||||
}
|
|
||||||
size_t aligned = (out->len + mask) & ~mask;
|
|
||||||
if (ensure_push(out, 1, aligned - out->len) != 0) {
|
|
||||||
return ErrOutOfMemory;
|
|
||||||
}
|
|
||||||
// TODO: zero-fill?
|
|
||||||
out->len = aligned;
|
|
||||||
return ErrOk;
|
|
||||||
}
|
|
||||||
return ErrInvalidDirective;
|
|
||||||
}
|
|
20
src/error.h
20
src/error.h
|
@ -18,16 +18,6 @@ typedef enum AsmError_e {
|
||||||
ErrDirectiveNotImplemented,
|
ErrDirectiveNotImplemented,
|
||||||
ErrUnexpectedToken,
|
ErrUnexpectedToken,
|
||||||
ErrTriedNegateNonNumber,
|
ErrTriedNegateNonNumber,
|
||||||
ErrInvalidDirective,
|
|
||||||
ErrStringNewLine,
|
|
||||||
ErrDanglingEscape,
|
|
||||||
ErrStringBadHex,
|
|
||||||
ErrBadStringEscape,
|
|
||||||
ErrStringDataNotByte,
|
|
||||||
ErrAlignNeedsNumber,
|
|
||||||
ErrAlignNeedsPow2,
|
|
||||||
ErrNeedCommaOrNewline,
|
|
||||||
ErrNeedsDataLiteral,
|
|
||||||
} AsmError;
|
} AsmError;
|
||||||
char *ERRORS[] = {
|
char *ERRORS[] = {
|
||||||
"Success",
|
"Success",
|
||||||
|
@ -49,14 +39,4 @@ char *ERRORS[] = {
|
||||||
"Directive is not implemented",
|
"Directive is not implemented",
|
||||||
"Unexpected token",
|
"Unexpected token",
|
||||||
"Negation only works on numbers",
|
"Negation only works on numbers",
|
||||||
"Invalid directive",
|
|
||||||
"String contains a raw newline (did you forget to close the quote?)",
|
|
||||||
"Dangling escape in string literal",
|
|
||||||
"Bad hex in string literal",
|
|
||||||
"Bad escape sequence in string literal",
|
|
||||||
"String literals can be used only in .db directive",
|
|
||||||
".align requires a number",
|
|
||||||
".align requires a power of two as an argument",
|
|
||||||
"Need comma or newline after data literal",
|
|
||||||
"Data literal expects a number or a string",
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,8 @@ typedef struct InstHtNode_s {
|
||||||
} InstHtNode;
|
} InstHtNode;
|
||||||
typedef InstHtNode *InstHt;
|
typedef InstHtNode *InstHt;
|
||||||
|
|
||||||
static uint32_t inst_hash(const char *s, size_t len) {
|
static
|
||||||
|
uint32_t inst_hash(const char *s, size_t len) {
|
||||||
uint32_t hash = 0;
|
uint32_t hash = 0;
|
||||||
uint32_t mul = 75;
|
uint32_t mul = 75;
|
||||||
for (size_t ii = 0; ii < len; ii += 1) {
|
for (size_t ii = 0; ii < len; ii += 1) {
|
||||||
|
@ -15,7 +16,8 @@ static uint32_t inst_hash(const char *s, size_t len) {
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
static InstHt build_lookup(void) {
|
static
|
||||||
|
InstHt build_lookup(void) {
|
||||||
const size_t size = 256;
|
const size_t size = 256;
|
||||||
InstHt table = (InstHt)malloc(size * sizeof(InstHtNode));
|
InstHt table = (InstHt)malloc(size * sizeof(InstHtNode));
|
||||||
if (table == NULL) {
|
if (table == NULL) {
|
||||||
|
@ -40,7 +42,8 @@ static InstHt build_lookup(void) {
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t inst_lookup(InstHt ht, const char *s, size_t len) {
|
static
|
||||||
|
size_t inst_lookup(InstHt ht, const char *s, size_t len) {
|
||||||
uint32_t hash = inst_hash(s, len);
|
uint32_t hash = inst_hash(s, len);
|
||||||
uint8_t *node = (uint8_t *)&ht[(size_t)(hash & 0xff)];
|
uint8_t *node = (uint8_t *)&ht[(size_t)(hash & 0xff)];
|
||||||
for (size_t ii = 0; ii < 2; ii += 1) {
|
for (size_t ii = 0; ii < 2; ii += 1) {
|
||||||
|
|
91
src/hbas.c
91
src/hbas.c
|
@ -20,8 +20,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -35,16 +35,15 @@ SOFTWARE.
|
||||||
//
|
//
|
||||||
#include "hash.c"
|
#include "hash.c"
|
||||||
//
|
//
|
||||||
#include "push_int.c"
|
|
||||||
#include "register.c"
|
#include "register.c"
|
||||||
#include "token.c"
|
#include "token.c"
|
||||||
//
|
//
|
||||||
#include "directive.c"
|
|
||||||
#include "einfo.h"
|
#include "einfo.h"
|
||||||
|
|
||||||
// Print space-separated hex dump of each byte, 16 bytes per line.
|
// Print space-separated hex dump of each byte, 16 bytes per line.
|
||||||
// Can be reversed with `xxd -p -r`.
|
// Can be reversed with `xxd -p -r`.
|
||||||
static void hex_dump(char *data, size_t len) {
|
static
|
||||||
|
void hex_dump(char *data, size_t len) {
|
||||||
char buf[48];
|
char buf[48];
|
||||||
const char *alphabet = "0123456789abcdef";
|
const char *alphabet = "0123456789abcdef";
|
||||||
for (size_t ii = 0; ii < len; ii += 1) {
|
for (size_t ii = 0; ii < len; ii += 1) {
|
||||||
|
@ -62,7 +61,8 @@ static void hex_dump(char *data, size_t len) {
|
||||||
|
|
||||||
#define MIN_SIZE 4096
|
#define MIN_SIZE 4096
|
||||||
|
|
||||||
static int slurp(FILE *fd, ByteVec *out) {
|
static
|
||||||
|
int slurp(FILE *fd, ByteVec *out) {
|
||||||
ByteVec rv = {malloc(MIN_SIZE), MIN_SIZE, 0};
|
ByteVec rv = {malloc(MIN_SIZE), MIN_SIZE, 0};
|
||||||
size_t bread = 1;
|
size_t bread = 1;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
@ -109,7 +109,8 @@ typedef struct LabelVec_s {
|
||||||
size_t len;
|
size_t len;
|
||||||
} LabelVec;
|
} LabelVec;
|
||||||
|
|
||||||
static size_t label_lookup(LabelVec *labels, char *name, size_t len) {
|
static
|
||||||
|
size_t label_lookup(LabelVec *labels, char *name, size_t len) {
|
||||||
size_t nlabels = labels->len;
|
size_t nlabels = labels->len;
|
||||||
Label *buf = labels->buf;
|
Label *buf = labels->buf;
|
||||||
for (size_t ii = 0; ii < nlabels; ii += 1) {
|
for (size_t ii = 0; ii < nlabels; ii += 1) {
|
||||||
|
@ -121,8 +122,65 @@ static size_t label_lookup(LabelVec *labels, char *name, size_t len) {
|
||||||
return INVALID;
|
return INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
static AsmError assemble_instr(InstHt ht, char *input, size_t len, Token *tok,
|
static
|
||||||
ByteVec *rv, HoleVec *holes) {
|
bool check_valid_int(uint64_t val, size_t size, uint8_t sign) {
|
||||||
|
// All 64-bit values are considered valid.
|
||||||
|
if (size == 8) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Unsigned integers must have all upper bits set to zero. To check this,
|
||||||
|
// we shift the value right by the integer size and verify it equals zero.
|
||||||
|
int valid_uint = (val >> (size * 8)) == 0;
|
||||||
|
|
||||||
|
// For signed integers, the sign-extended high bits must match the sign bit.
|
||||||
|
// By shifting right by one less than the total bit size (size * 8 - 1),
|
||||||
|
// we isolate the sign bit and any sign-extended bits. For a value fitting
|
||||||
|
// in the signed range, this operation results in either 0 (for non-negative
|
||||||
|
// values) or -1 (for negative values due to sign extension).
|
||||||
|
int64_t int_shifted = ((int64_t)val) >> (size * 8 - 1);
|
||||||
|
|
||||||
|
// To unify the check for both positive and negative cases, we adjust
|
||||||
|
// non-zero values (-1) by incrementing by 1. This turns -1 into 0,
|
||||||
|
// enabling a single check for 0 to validate both cases. This adjustment
|
||||||
|
// simplifies the validation logic, allowing us to use a single condition to
|
||||||
|
// check for proper sign extension or zero extension in the original value.
|
||||||
|
int_shifted += int_shifted != 0;
|
||||||
|
|
||||||
|
// A valid signed integer will have `int_shifted` equal to 0
|
||||||
|
// after adjustment, indicating proper sign extension.
|
||||||
|
int valid_int = int_shifted == 0;
|
||||||
|
|
||||||
|
// Validity bitmask to represents whether the value
|
||||||
|
// fits as signed, unsigned, or both.
|
||||||
|
int validity = valid_int | (valid_uint << 1);
|
||||||
|
|
||||||
|
// If the value's validity doesn't match the `sign` requirements,
|
||||||
|
// we report an overflow.
|
||||||
|
return (validity & sign) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// safety: assumes the buffer has enough place for specified integer size.
|
||||||
|
// `sign` is a bitset, where bit `1` indicates that value accepts a signed int,
|
||||||
|
// and bit `2` indicates that value accepts an unsigned int.
|
||||||
|
static
|
||||||
|
AsmError push_int_le(char *buf, uint64_t val, size_t size, uint8_t sign) {
|
||||||
|
if (!check_valid_int(val, size, sign)) {
|
||||||
|
return ErrImmediateOverflow;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write out the bytes of the integer to the buffer in little-endian order,
|
||||||
|
// starting with the lowest byte first.
|
||||||
|
for (size_t ii = 0; ii < size; ii += 1) {
|
||||||
|
buf[ii] = val & 0xff;
|
||||||
|
val >>= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
AsmError assemble_instr(InstHt ht, char *input, size_t len, Token *tok,
|
||||||
|
ByteVec *rv, HoleVec *holes) {
|
||||||
const InstDesc *inst;
|
const InstDesc *inst;
|
||||||
const char *type_str;
|
const char *type_str;
|
||||||
size_t nargs;
|
size_t nargs;
|
||||||
|
@ -207,8 +265,6 @@ static AsmError assemble_instr(InstHt ht, char *input, size_t len, Token *tok,
|
||||||
return ErrBadNumOverflow;
|
return ErrBadNumOverflow;
|
||||||
}
|
}
|
||||||
num_to_write = (uint64_t)tmp;
|
num_to_write = (uint64_t)tmp;
|
||||||
} else if (meta.sign == 2 && (int64_t)num_to_write < 0) {
|
|
||||||
return ErrBadNumOverflow;
|
|
||||||
}
|
}
|
||||||
AsmError err = push_int_le(&rv->buf[rv->len], num_to_write,
|
AsmError err = push_int_le(&rv->buf[rv->len], num_to_write,
|
||||||
meta.size, meta.sign);
|
meta.size, meta.sign);
|
||||||
|
@ -227,9 +283,6 @@ AsmError assemble(InstHt ht, char *input, size_t len, ByteVec *out,
|
||||||
ByteVec rv = {malloc(MIN_SIZE), MIN_SIZE, 0};
|
ByteVec rv = {malloc(MIN_SIZE), MIN_SIZE, 0};
|
||||||
HoleVec holes = {malloc(MIN_SIZE * sizeof(Hole)), MIN_SIZE, 0};
|
HoleVec holes = {malloc(MIN_SIZE * sizeof(Hole)), MIN_SIZE, 0};
|
||||||
LabelVec labels = {malloc(MIN_SIZE * sizeof(Label)), MIN_SIZE, 0};
|
LabelVec labels = {malloc(MIN_SIZE * sizeof(Label)), MIN_SIZE, 0};
|
||||||
if (rv.buf == NULL || holes.buf == NULL || labels.buf == NULL) {
|
|
||||||
return ErrOutOfMemory;
|
|
||||||
}
|
|
||||||
size_t line = 0;
|
size_t line = 0;
|
||||||
size_t line_start = 0;
|
size_t line_start = 0;
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
|
@ -264,15 +317,11 @@ AsmError assemble(InstHt ht, char *input, size_t len, ByteVec *out,
|
||||||
}
|
}
|
||||||
if (tok.kind == TokDot) {
|
if (tok.kind == TokDot) {
|
||||||
Token next = token(input, len, pos);
|
Token next = token(input, len, pos);
|
||||||
einfo->token = next;
|
if (next.kind == TokIdent) {
|
||||||
if (next.kind != TokIdent) {
|
err = ErrDirectiveNotImplemented;
|
||||||
err = ErrNeedDirectiveAfterDot;
|
|
||||||
goto end;
|
goto end;
|
||||||
}
|
} else {
|
||||||
err = assemble_directive(input, len, &rv, &next);
|
err = ErrNeedDirectiveAfterDot;
|
||||||
pos = next.start + next.len;
|
|
||||||
einfo->token = next;
|
|
||||||
if (err != ErrOk) {
|
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
|
|
||||||
static bool check_valid_int(uint64_t val, size_t size, uint8_t sign) {
|
|
||||||
// All 64-bit values are considered valid.
|
|
||||||
if (size == 8) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Unsigned integers must have all upper bits set to zero. To check this,
|
|
||||||
// we shift the value right by the integer size and verify it equals zero.
|
|
||||||
int valid_uint = (val >> (size * 8)) == 0;
|
|
||||||
|
|
||||||
// For signed integers, the sign-extended high bits must match the sign bit.
|
|
||||||
// By shifting right by one less than the total bit size (size * 8 - 1),
|
|
||||||
// we isolate the sign bit and any sign-extended bits. For a value fitting
|
|
||||||
// in the signed range, this operation results in either 0 (for non-negative
|
|
||||||
// values) or -1 (for negative values due to sign extension).
|
|
||||||
int64_t int_shifted = ((int64_t)val) >> (size * 8 - 1);
|
|
||||||
|
|
||||||
// To unify the check for both positive and negative cases, we adjust
|
|
||||||
// non-zero values (-1) by incrementing by 1. This turns -1 into 0,
|
|
||||||
// enabling a single check for 0 to validate both cases. This adjustment
|
|
||||||
// simplifies the validation logic, allowing us to use a single condition to
|
|
||||||
// check for proper sign extension or zero extension in the original value.
|
|
||||||
int_shifted += int_shifted != 0;
|
|
||||||
|
|
||||||
// A valid signed integer will have `int_shifted` equal to 0
|
|
||||||
// after adjustment, indicating proper sign extension.
|
|
||||||
int valid_int = int_shifted == 0;
|
|
||||||
|
|
||||||
// Validity bitmask to represents whether the value
|
|
||||||
// fits as signed, unsigned, or both.
|
|
||||||
int validity = valid_int | (valid_uint << 1);
|
|
||||||
|
|
||||||
// If the value's validity doesn't match the `sign` requirements,
|
|
||||||
// we report an overflow.
|
|
||||||
return (validity & sign) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// safety: assumes the buffer has enough place for specified integer size.
|
|
||||||
// `sign` is a bitset, where bit `1` indicates that value accepts a signed int,
|
|
||||||
// and bit `2` indicates that value accepts an unsigned int.
|
|
||||||
static AsmError push_int_le(char *buf, uint64_t val, size_t size,
|
|
||||||
uint8_t sign) {
|
|
||||||
if (!check_valid_int(val, size, sign)) {
|
|
||||||
return ErrImmediateOverflow;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write out the bytes of the integer to the buffer in little-endian order,
|
|
||||||
// starting with the lowest byte first.
|
|
||||||
for (size_t ii = 0; ii < size; ii += 1) {
|
|
||||||
buf[ii] = val & 0xff;
|
|
||||||
val >>= 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ErrOk;
|
|
||||||
}
|
|
|
@ -1,4 +1,5 @@
|
||||||
static int parse_register(char *name, size_t len) {
|
static
|
||||||
|
int parse_register(char *name, size_t len) {
|
||||||
if (name[0] != 'r') {
|
if (name[0] != 'r') {
|
||||||
return 256; // Register name should start with 'r'
|
return 256; // Register name should start with 'r'
|
||||||
}
|
}
|
||||||
|
|
71
src/token.c
71
src/token.c
|
@ -10,7 +10,6 @@ typedef enum TokenKind_e {
|
||||||
TokColon = ':',
|
TokColon = ':',
|
||||||
TokComment = ';',
|
TokComment = ';',
|
||||||
TokNewline = 'n',
|
TokNewline = 'n',
|
||||||
TokString = 's',
|
|
||||||
} TokenKind;
|
} TokenKind;
|
||||||
typedef struct Token_s {
|
typedef struct Token_s {
|
||||||
TokenKind kind;
|
TokenKind kind;
|
||||||
|
@ -19,7 +18,8 @@ typedef struct Token_s {
|
||||||
uint64_t num;
|
uint64_t num;
|
||||||
} Token;
|
} Token;
|
||||||
|
|
||||||
static Token token_ident(char *input, size_t len, size_t pos) {
|
static
|
||||||
|
Token token_ident(char *input, size_t len, size_t pos) {
|
||||||
size_t start = pos;
|
size_t start = pos;
|
||||||
while (pos < len) {
|
while (pos < len) {
|
||||||
char chr = input[pos];
|
char chr = input[pos];
|
||||||
|
@ -34,7 +34,8 @@ static Token token_ident(char *input, size_t len, size_t pos) {
|
||||||
return (Token){TokIdent, start, pos - start, 0};
|
return (Token){TokIdent, start, pos - start, 0};
|
||||||
}
|
}
|
||||||
|
|
||||||
static Token token_number(char *input, size_t len, size_t pos) {
|
static
|
||||||
|
Token token_number(char *input, size_t len, size_t pos) {
|
||||||
char *ptr = &input[pos];
|
char *ptr = &input[pos];
|
||||||
char next = '\0';
|
char next = '\0';
|
||||||
size_t start = pos;
|
size_t start = pos;
|
||||||
|
@ -110,65 +111,8 @@ static Token token_number(char *input, size_t len, size_t pos) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static char get_hex(char chr) {
|
static
|
||||||
char chru = chr & ~0x20;
|
Token token(char *input, size_t len, size_t pos) {
|
||||||
if (chr >= '0' && chr <= '9') {
|
|
||||||
return chr - '0';
|
|
||||||
}
|
|
||||||
if (chru >= 'A' && chru <= 'F') {
|
|
||||||
return chru - ('A' - 10);
|
|
||||||
}
|
|
||||||
return 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Token token_string(char *input, size_t len, size_t pos) {
|
|
||||||
size_t start = pos;
|
|
||||||
size_t ndata = 0;
|
|
||||||
for (pos += 1; pos < len; pos += 1) {
|
|
||||||
if (input[pos] == '"') {
|
|
||||||
return (Token){TokString, start, pos + 1 - start, ndata};
|
|
||||||
}
|
|
||||||
if (input[pos] == '\n' || input[pos] == '\r') {
|
|
||||||
return (Token){TokInvalid, start, pos + 1 - start,
|
|
||||||
ErrStringNewLine};
|
|
||||||
}
|
|
||||||
if (input[pos] == '\\') {
|
|
||||||
if (pos + 1 >= len) {
|
|
||||||
return (Token){TokInvalid, start, pos - start,
|
|
||||||
ErrDanglingEscape};
|
|
||||||
}
|
|
||||||
pos += 1;
|
|
||||||
switch (input[pos]) {
|
|
||||||
case '\\':
|
|
||||||
case '"':
|
|
||||||
case 'r':
|
|
||||||
case 'n':
|
|
||||||
case '0':
|
|
||||||
case 't':
|
|
||||||
break;
|
|
||||||
case 'x':
|
|
||||||
if (pos + 2 >= len) {
|
|
||||||
return (Token){TokInvalid, start, pos - start,
|
|
||||||
ErrDanglingEscape};
|
|
||||||
}
|
|
||||||
if (get_hex(input[pos + 1]) > 15 ||
|
|
||||||
get_hex(input[pos + 2]) > 15) {
|
|
||||||
return (Token){TokInvalid, start, pos - start,
|
|
||||||
ErrStringBadHex};
|
|
||||||
}
|
|
||||||
pos += 2;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return (Token){TokInvalid, start, pos - start,
|
|
||||||
ErrBadStringEscape};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ndata += 1;
|
|
||||||
}
|
|
||||||
return (Token){TokString, start, pos - start, ndata};
|
|
||||||
}
|
|
||||||
|
|
||||||
static Token token(char *input, size_t len, size_t pos) {
|
|
||||||
char chr, chru;
|
char chr, chru;
|
||||||
char *ptr = &input[pos];
|
char *ptr = &input[pos];
|
||||||
while (pos < len && (input[pos] == ' ' || input[pos] == '\t')) {
|
while (pos < len && (input[pos] == ' ' || input[pos] == '\t')) {
|
||||||
|
@ -198,9 +142,6 @@ static Token token(char *input, size_t len, size_t pos) {
|
||||||
}
|
}
|
||||||
return (Token){TokComment, pos, clen, 0};
|
return (Token){TokComment, pos, clen, 0};
|
||||||
}
|
}
|
||||||
if (chr == '"') {
|
|
||||||
return token_string(input, len, pos);
|
|
||||||
}
|
|
||||||
if (chr >= '0' && chr <= '9') {
|
if (chr >= '0' && chr <= '9') {
|
||||||
return token_number(input, len, pos);
|
return token_number(input, len, pos);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue