Created
March 24, 2016 12:29
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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