Skip to content

Instantly share code, notes, and snippets.

@kythyria
Created March 24, 2016 12:29
Show Gist options
  • Save kythyria/6d108d638b489b61d4f9 to your computer and use it in GitHub Desktop.
Save kythyria/6d108d638b489b61d4f9 to your computer and use it in GitHub Desktop.
I decided to join the fun some other people were having with daft fizzbuzz solutions.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#define ADDRESS_SPACE 65536
//16 registers, like ARM. r0-r3,r12 scratch, r4-r11 preserved, r13 call stack, r14 destination of BL, r15 PC
// Hole in a context.
#define OP_HOLE 0
// End of context
#define OP_CEND 0
// End program
#define OP_HALT 1
// PRS r: Print the zero-terminated buffer starting at r to the output port
#define OP_PRS 2
// PRI r: Print the decimal representation of r to the output port
#define OP_PRI 3
// PUSH s r : copy value from register r to the memory location given by register s, then increment s
#define OP_PUSH 4
// POP r s: decrement register s, then copy from the location given by s to register r
#define OP_POP 5
// CPZ d s: d and s are registers, while(*s) {*d++ = *s++; }
// ie it won't copy the terminating zero, but s will point to it
#define OP_CPZ 6
// LD d a: d = *a
#define OP_LD 7
// LDI d a: d = immediate a
#define OP_LDI 8
// ST d r: copy from r to address given by d
#define OP_ST 9
// BL d: Jump to address in d, putting the return address in r14
#define OP_BL 10
// BZ r d: Jump to address in d, only if r is zero.
#define OP_BZ 11
// BNZ r d: Jump to address in d, only if r is nonzero.
#define OP_BNZ 12
// INC r: r++
#define OP_INC 13
// DEC r: r--
#define OP_DEC 14
// MOD r a, b: r = a % b
#define OP_MOD 15
// MOV d r: d = r
#define OP_MOV 16
// nop
#define OP_SKIP 17
/* FizzbuzzVM asm */
char *source[] = {
// global register allocation:
// r4 = number under consideration
// r5 = code gen pointer
// r6 = second half stack pointer
// r11 = start of codegen buffer
".code",
" MOV r4 r0",
" LDI r11 #0x2000",
" LDI r6 #0x1001", // Leaving 0x1000 as a zero sentinel
" MOV r5 r11",
// put the base case in.
" LDI r0 &base_c",
" PUSH r6 r0",
" LDI r3 &composeif",
" LDI r0 #3",
" LDI r1 &fizz_c",
" BL r3",
" LDI r0 #5",
" LDI r1 &buzz_c",
" BL r3",
//cork
" LDI r0 #17", // Skip is 17
" PUSH r5 r0",
//fill in the second halves
" LDI r3 &loop_done",
" LDI r12 &loop",
"loop",
" POP r0 r6",
" BZ r0 r3",
" CPZ r5 r0",
" BL r12", // don't have an unconditional jump, so we abuse BL
"loop_done",
//and run
" BL r11",
" HALT",
// register parameters
// r0 = divisor
// r1 = start of context
"composeif",
" MOD r0 r4 r0",
" LDI r2 &done_compose",
" BNZ r0 r2",
" CPZ r5 r1",
" INC r1",
" PUSH r6 r1",
"done_compose",
" MOV r15 r14",
"base_c",
" PRI r4",
" HALT",
" CEND",
"fizz_c",
" LDI r1 &fizz",
" PRS r1",
" HOLE",
" HALT",
" CEND",
"buzz_c",
" LDI r1 &buzz",
" PRS r1",
" HOLE",
" HALT",
" CEND",
".data",
"fizz",
" \"Fizz\"",
"buzz",
" \"Buzz\"",
".end"
};
struct symtab_entry {
struct symtab_entry *next;
char *symbol;
uint16_t target;
};
struct symtab_usage {
struct symtab_usage *next;
struct symtab_entry *symbol;
uint16_t location;
};
struct symtab {
struct symtab_entry *first_symbol;
struct symtab_usage *first_usage;
struct symtab_entry *last_symbol;
struct symtab_usage *last_usage;
};
struct symtab_entry *symtab_get_symbol(struct symtab *table, char *name) {
struct symtab_entry *found = table->first_symbol;
while(found) {
if (strcmp(name, found->symbol) == 0) { break; }
else { found = found->next; }
}
if(!found) {
found = malloc(sizeof(struct symtab_entry));
memset(found, 0, sizeof(struct symtab_entry));
char *symname = malloc(strlen(name) + 1);
strcpy(symname, name);
found->symbol = symname;
if(table->last_symbol) {
table->last_symbol->next = found;
table->last_symbol = found;
}
else {
table->last_symbol = found;
table->first_symbol = found;
}
}
return found;
}
void symtab_add_usage(struct symtab *table, char *name, uint16_t location) {
struct symtab_usage *item = malloc(sizeof(struct symtab_usage));
memset(item, 0, sizeof(struct symtab_usage));
item->symbol = symtab_get_symbol(table, name);
item->location = location;
if(table->last_usage) {
table->last_usage->next = item;
table->last_usage = item;
}
else {
table->first_usage = item;
table->last_usage = item;
}
}
#define MODE_CODE 0
#define MODE_DATA 1
uint16_t assemble_process_operand(struct symtab *table, uint16_t addr, char *operand) {
if(*operand == 'r' || *operand == '#') {
long int result = strtol(operand+1, NULL, 0);
return (uint16_t)result;
}
else if(*operand == '&') {
symtab_add_usage(table, operand+1, addr);
return 0;
}
return 0;
}
uint16_t *assemble(char *asm_src[]) {
struct symtab symbol_table = { 0 };
uint16_t *image = malloc(sizeof(uint16_t[ADDRESS_SPACE]));
int current_address = 0;
int current_line = 0;
int mode = MODE_CODE;
while(strcmp(asm_src[current_line], ".end") != 0) {
if(strcmp(asm_src[current_line], ".code") == 0) {
mode = MODE_CODE;
current_line++;
continue;
}
if(strcmp(asm_src[current_line], ".data") == 0) {
mode = MODE_DATA;
current_line++;
continue;
}
if(asm_src[current_line][0] != ' ') {
char *symbol;
sscanf(asm_src[current_line], "%ms", &symbol);
struct symtab_entry *entry = symtab_get_symbol(&symbol_table, symbol);
entry->target = current_address;
free(symbol);
current_line++;
continue;
}
if(mode == MODE_CODE) {
char *instruction;
char *op1;
char *op2;
char *op3;
sscanf(asm_src[current_line], " %ms %ms %ms %ms", &instruction, &op1, &op2, &op3);
if(0){}
#define OPCODE_OPERAND(n) image[current_address] = \
assemble_process_operand(&symbol_table, current_address, op##n);\
free(op##n);current_address++;
#define OPCODE_ARG0
#define OPCODE_ARG1 OPCODE_OPERAND(1)
#define OPCODE_ARG2 OPCODE_ARG1 OPCODE_OPERAND(2)
#define OPCODE_ARG3 OPCODE_ARG2 OPCODE_OPERAND(3)
#define OPCODE(mnemonic,argcount) else if(strcmp(#mnemonic, instruction) == 0) {\
image[current_address++] = OP_##mnemonic;\
OPCODE_ARG##argcount }
OPCODE(HOLE,0)
OPCODE(CEND,0)
OPCODE(HALT,0)
OPCODE(PRS,1)
OPCODE(PRI,1)
OPCODE(PUSH,2)
OPCODE(POP,2)
OPCODE(CPZ,2)
OPCODE(LD,2)
OPCODE(LDI,2)
OPCODE(ST,2)
OPCODE(BL,1)
OPCODE(BZ,2)
OPCODE(BNZ,2)
OPCODE(INC,1)
OPCODE(DEC,2)
OPCODE(MOD,3)
OPCODE(MOV,2)
OPCODE(SKIP,0)
#undef OPCODE
#undef OPCODE_OPERAND
#undef OPCODE_ARG0
#undef OPCODE_ARG1
#undef OPCODE_ARG2
#undef OPCODE_ARG3
current_line++;
continue;
}
if(mode == MODE_DATA) {
char *c = &(asm_src[current_line][4]);
if(*c == '"') {
c++;
while(*c != '"') {
image[current_address++] = *(c++);
}
image[current_address++] = 0;
}
else {
assemble_process_operand(&symbol_table, current_address, c);
}
current_line++;
continue;
}
}
// Resolve symbols and free usages
{
struct symtab_usage *curr = symbol_table.first_usage;
struct symtab_usage *next;
while(curr) {
image[curr->location] = curr->symbol->target;
next = curr->next;
free(curr);
curr = next;
}
}
//free symbols
{
struct symtab_entry *curr = symbol_table.first_symbol;
struct symtab_entry *next;
while(curr) {
next = curr->next;
free(curr->symbol);
free(curr);
curr = next;
}
}
return image;
}
struct printer {
int top_half;
};
void utf16_print(struct printer *p, uint16_t *buf) {
while(*buf) {
unsigned int chara;
uint16_t payload = *buf & 0x03FF;
uint16_t sig = *buf & 0xFC00;
if(sig == 0xD800) {
p->top_half = payload;
}
else {
if(sig == 0xDC00) {
chara = (p->top_half << 10) + payload;
}
else {
chara = *buf;
}
p->top_half = 0;
}
char out[5] = {};
if(chara > 0xFFFF) {
out[0] = ((chara >> 18 ) & 0b00000111) | 0b11110000;
out[1] = ((chara >> 12 ) & 0b00111111) | 0b10000000;
out[2] = ((chara >> 6) & 0b00111111) | 0b10000000;
out[3] = ((chara) & 0b00111111) | 0b10000000;
}
else if(chara > 0x7FF) {
out[0] = ((chara >> 12 ) & 0b00001111) | 0b11100000;
out[1] = ((chara >> 6 ) & 0b00111111) | 0b10000000;
out[2] = ((chara) & 0b00111111) | 0b10000000;
}
else if(chara > 0x7F) {
out[0] = ((chara >> 6 ) & 0b00011111) | 0b11000000;
out[1] = ((chara) & 0b00111111) | 0b10000000;
}
else {
out[0] = ((chara) & 0b01111111) | 0b00000000;
}
printf("%s", &out[0]);
buf++;
}
}
void int_print(uint16_t num) {
int val = num; //widen because there's no uint16_t specifier in printf
printf("%i",val);
}
struct machine_state {
int running;
uint16_t reg[16];
uint16_t memory[ADDRESS_SPACE];
};
void machine_run(struct machine_state *state) {
struct printer printer = {0};
state->running = 1;
while(state->running) {
int jumping = 0;
uint16_t o1,o2,o3;
switch(state->memory[state->reg[15]]) {
#define REGR(n) (state->reg[n])
#define REGW(n,v) ((jumping = n == 15),((state->reg[n]) = v))
#define OPCODE_ARGN(n) o##n = state->memory[REGR(15)+n];
#define OPCODE_ARG0
#define OPCODE_ARG1 OPCODE_ARGN(1)
#define OPCODE_ARG2 OPCODE_ARG1 OPCODE_ARGN(2)
#define OPCODE_ARG3 OPCODE_ARG2 OPCODE_ARGN(3)
#define OPCODE_BASE(code,argc,handler) case OP_##code: {\
OPCODE_ARG##argc \
handler;\
break; }
#define OPCODE(code,argc,handler) OPCODE_BASE(code,argc,handler;state->reg[15] += (jumping ? 0 : 1 + argc);)
OPCODE(HOLE,0,;)
OPCODE(HALT,0,state->running = 0)
OPCODE(PRS,1,utf16_print(&printer, &(state->memory[state->reg[o1]])))
OPCODE(PRI,1,int_print(state->reg[o1]))
OPCODE(PUSH,2,state->memory[REGR(o1)++] = REGR(o2));
OPCODE(POP,2,REGW(o1,state->memory[--state->reg[o2]]));
OPCODE(CPZ,2,while(state->memory[REGR(o2)]) {
state->memory[REGR(o1)] = state->memory[REGR(o2)];
state->reg[o1] += 1;
state->reg[o2] += 1;
})
OPCODE(LD,2,REGW(o1,state->memory[REGR(o2)]))
OPCODE(LDI,2,REGW(o1,o2))
OPCODE(ST,2,state->memory[REGR(o1)] = REGR(o2))
OPCODE_BASE(BL,1,state->reg[14] = state->reg[15]+2;state->reg[15]=state->reg[o1])
OPCODE_BASE(BZ,2,state->reg[15] = state->reg[o1] ? state->reg[15]+3 : state->reg[o2])
OPCODE_BASE(BNZ,2,state->reg[15] = state->reg[o1] ? state->reg[o2] : state->reg[15]+3)
OPCODE(INC,1,REGW(o1,REGR(o1)+1))
OPCODE(DEC,1,REGW(o1,REGR(o1)-1))
OPCODE(MOD,3,REGW(o1,REGR(o2) % REGR(o3)))
OPCODE(MOV,2,REGW(o1,REGR(o2)))
OPCODE(SKIP,0,;)
default:
state->running = 0;
printf("\nIllegal instruction\n");
}
}
}
void run_image(uint16_t num, uint16_t *image) {
struct machine_state s = { 0 };
s.reg[0] = num;
memcpy(s.memory, image, ADDRESS_SPACE);
machine_run(&s);
printf("\n");
}
int main(int argc, char *argv[]) {
setbuf(stdout, NULL);
uint16_t *image = assemble(source);
for(uint16_t i = 1; i <= 100; i++) {
run_image(i, image);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment