Created
March 31, 2013 05:26
-
-
Save numinit/5279644 to your computer and use it in GitHub Desktop.
This file contains 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 "lc3.h" | |
static lc3 *global_ctx = NULL; | |
static struct termios oldt, newt; | |
void lc3_printf(const char *format, ...) { | |
va_list args; | |
const char *ptr; | |
FILE *stream; | |
va_start(args, format); | |
ptr = (*format == '^' || *format == '$' || *format == '!' ? format + 1 : format); | |
if (*format == '!') { | |
stream = stderr; | |
} else { | |
stream = stdout; | |
} | |
if (*format != '^') { | |
fprintf(stream, ">> "); | |
} | |
vfprintf(stream, ptr, args); | |
if (*format != '$') { | |
fprintf(stream, "\n"); | |
} | |
va_end(args); | |
} | |
void lc3_start(struct lc3 *ctx) { | |
p("emulation started, PC=%04x", ctx->pc); | |
ctx->pc = 0x0000; | |
ctx->emulation_flags |= LC3_EMULATION_RUN; | |
} | |
void lc3_stop(struct lc3 *ctx) { | |
p("emulation stopped, cleaning up", NULL); | |
ctx->emulation_flags &= ~LC3_EMULATION_RUN; | |
} | |
void lc3_br(struct lc3 *ctx, u16 ins) { | |
if (ctx->cond & ins) { | |
ctx->pc += SEXT9(XM90(ins)); | |
} | |
} | |
void lc3_add(struct lc3 *ctx, u16 ins) { | |
register u16 result = (ctx->reg[XM36(ins)] + (ins & B(5) ? (u16)SEXT5(XM50(ins)) : ctx->reg[XM30(ins)])); | |
ctx->reg[XM39(ins)] = result; | |
COND(ctx, result); | |
} | |
void lc3_ld(struct lc3 *ctx, u16 ins) { | |
register u16 result = ctx->mem[ctx->pc + SEXT9(XM90(ins))]; | |
ctx->reg[XM39(ins)] = result; | |
COND(ctx, result); | |
} | |
void lc3_st(struct lc3 *ctx, u16 ins) { | |
ctx->mem[ctx->pc + SEXT9(XM90(ins))] = ctx->reg[XM39(ins)]; | |
} | |
void lc3_jsr(struct lc3 *ctx, u16 ins) { | |
ctx->reg[7] = ctx->pc; | |
ctx->pc = (ins & B(11) ? ctx->reg[XM36(ins)] : ctx->pc + SEXT11(XM110(ins))); | |
} | |
void lc3_and(struct lc3 *ctx, u16 ins) { | |
register u16 result = (ctx->reg[XM36(ins)] & (ins & B(5) ? (u16)SEXT5(XM50(ins)) : ctx->reg[XM30(ins)])); | |
ctx->reg[XM39(ins)] = result; | |
COND(ctx, result); | |
} | |
void lc3_ldr(struct lc3 *ctx, u16 ins) { | |
register u16 result = ctx->mem[ctx->reg[XM36(ins)] + SEXT6(XM60(ins))]; | |
ctx->reg[XM39(ins)] = result; | |
COND(ctx, result); | |
} | |
void lc3_str(struct lc3 *ctx, u16 ins) { | |
ctx->mem[ctx->reg[XM36(ins)] + SEXT6(XM60(ins))] = ctx->reg[XM90(ins)]; | |
} | |
void lc3_rti(struct lc3 *ctx, u16 ins) { | |
if (ctx->psr & B(15) == 0) { | |
register u16 r6 = ctx->reg[6]; | |
ctx->pc = ctx->mem[r6]; | |
ctx->psr = ctx->mem[r6 + 1]; | |
ctx->reg[6] = r6 + 2; | |
} else { | |
// Todo: Check your privilege | |
} | |
} | |
void lc3_not(struct lc3 *ctx, u16 ins) { | |
register u16 result = ~ctx->reg[XM36(ins)]; | |
ctx->reg[XM39(ins)] = result; | |
COND(ctx, result); | |
} | |
void lc3_ldi(struct lc3 *ctx, u16 ins) { | |
register u16 result = ctx->mem[ctx->mem[ctx->pc + SEXT9(XM90(ins))]]; | |
ctx->reg[XM39(ins)] = result; | |
COND(ctx, result); | |
} | |
void lc3_sti(struct lc3 *ctx, u16 ins) { | |
ctx->mem[ctx->mem[ctx->pc + SEXT9(XM90(ins))]] = ctx->reg[XM39(ins)]; | |
} | |
void lc3_jmp(struct lc3 *ctx, u16 ins) { | |
ctx->pc = ctx->reg[XM36(ins)]; | |
} | |
void lc3_illegal(struct lc3 *ctx, u16 ins) { | |
printf("Illegal instruction: %04x\n", ins); | |
lc3_stop(ctx); | |
} | |
void lc3_lea(struct lc3 *ctx, u16 ins) { | |
register u16 result = ctx->pc + (u16)SEXT9(XM90(ins)); | |
ctx->reg[XM39(ins)] = result; | |
COND(ctx, result); | |
} | |
void lc3_trap(struct lc3 *ctx, u16 ins) { | |
switch (XM80(ins)) { | |
case LC3_TRAP_GETC: | |
ctx->reg[0] = getchar(); | |
break; | |
case LC3_TRAP_OUTC: | |
putchar(ctx->reg[0]); | |
break; | |
case LC3_TRAP_PUTS: | |
for (u16 offset = ctx->reg[0]; ctx->mem[offset] != 0x0000; putchar((u8)ctx->mem[offset++])); | |
break; | |
case LC3_TRAP_IN: | |
ctx->reg[0] = (u16)putchar(getchar()); | |
break; | |
case LC3_TRAP_PUTSP: | |
for (u8 *ptr = (u8 *)&ctx->mem + ctx->reg[0]; *ptr != 0x00; putchar(*ptr++)); | |
break; | |
case LC3_TRAP_HALT: | |
lc3_stop(ctx); | |
break; | |
default: | |
ctx->reg[7] = ctx->pc; | |
ctx->pc = ctx->mem[XM80(ins)]; | |
break; | |
} | |
} | |
static lc3_ins lc3_isa[] = { | |
&lc3_br, | |
&lc3_add, | |
&lc3_ld, | |
&lc3_st, | |
&lc3_jsr, | |
&lc3_and, | |
&lc3_ldr, | |
&lc3_str, | |
&lc3_rti, | |
&lc3_not, | |
&lc3_ldi, | |
&lc3_sti, | |
&lc3_jmp, | |
&lc3_illegal, | |
&lc3_lea, | |
&lc3_trap | |
}; | |
static const char *lc3_isa_names[] = { | |
"br", | |
"add", | |
"ld", | |
"st", | |
"jsr", | |
"and", | |
"ldr", | |
"str", | |
"rti", | |
"not", | |
"ldi", | |
"sti", | |
"jmp", | |
"illegal", | |
"lea", | |
"trap" | |
}; | |
void lc3_step(struct lc3 *ctx) { | |
// Fetch the instruction, incrementing the PC | |
register u16 ins = ctx->mem[ctx->pc++]; | |
// Execute the instruction and store results | |
p("<%s> (%04x): PC=%04x, R[0-7]: %04x %04x %04x %04x %04x %04x %04x, N=%d, Z=%d, P=%d", lc3_isa_names[XM412(ins)], ins, ctx->pc, ctx->reg[0], ctx->reg[1], ctx->reg[2], ctx->reg[3], ctx->reg[4], ctx->reg[5], ctx->reg[6], ctx->reg[7], ctx->cond & N != 0, ctx->cond & Z != 0, ctx->cond & P != 0); | |
lc3_isa[XM412(ins)](ctx, ins); | |
} | |
void lc3_run(struct lc3 *ctx) { | |
lc3_start(ctx); | |
while (ctx->emulation_flags & LC3_EMULATION_RUN) { | |
lc3_step(ctx); | |
} | |
} | |
void lc3_signal(int signal) { | |
lc3_stop(global_ctx); | |
} | |
void lc3_init(u16 *mem, size_t size) { | |
// Allocate a LC-3 | |
global_ctx = (struct lc3 *)calloc(1, sizeof(struct lc3)); | |
// Copy over memory, but endian-swap it | |
for (u16 a = 0; a < size; global_ctx->mem[a] = (mem[a] & 0x00ff) << 8 | (mem[a] & 0xff00) >> 8, a++); | |
// Perform terminal setup | |
tcgetattr(STDIN_FILENO, &oldt); | |
memcpy(&newt, &oldt, sizeof(struct termios)); | |
newt.c_lflag &= ~(ICANON | ECHO); | |
tcsetattr(STDIN_FILENO, TCSANOW, &newt); | |
// Break on CTRL+C | |
signal(SIGINT, lc3_signal); | |
} | |
void lc3_exit() { | |
// Restore terminal | |
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); | |
// Free allocated memory | |
if (global_ctx) { | |
free(global_ctx); | |
global_ctx = NULL; | |
} | |
} | |
int main(int argc, char **argv) { | |
if (argc < 2) { | |
p("!Usage: %s <RAM image>", argv[0]); | |
return EXIT_FAILURE; | |
} | |
FILE *f = fopen(argv[argc - 1], "rb"); | |
size_t size; | |
void *ptr; | |
if (f == NULL) { | |
p("!Error opening input file %s", argv[argc - 1]); | |
return EXIT_FAILURE; | |
} | |
// Find the length of the file | |
fseek(f, 0, SEEK_END); | |
size = ftell(f) / sizeof(u16); | |
rewind(f); | |
// Read, aligning the file to 16-bit chunks | |
ptr = malloc(size); | |
fread(ptr, sizeof(u16), size, f); | |
fclose(f); | |
// Init the LC-3 | |
lc3_init((u16 *)ptr, size); | |
free(ptr); | |
// Register an atexit | |
atexit(lc3_exit); | |
// Run the code | |
lc3_run(global_ctx); | |
return EXIT_SUCCESS; | |
} |
This file contains 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 <stdlib.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdarg.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <signal.h> | |
#include <termios.h> | |
typedef uint8_t u8; | |
typedef int8_t s8; | |
typedef uint16_t u16; | |
typedef int16_t s16; | |
struct lc3 { | |
u16 emulation_flags; | |
u16 pc, cond, psr; | |
u16 reg[8]; | |
u16 mem[0xffff]; | |
}; | |
typedef void (*lc3_ins)(lc3*, u16); | |
// Handy bitmask macros | |
#define M110 0x07ff | |
#define M90 0x01ff | |
#define M80 0x00ff | |
#define M60 0x003f | |
#define M50 0x001f | |
#define M412 0xf000 | |
#define M39 0x0e07 | |
#define M36 0x01c0 | |
#define M33 0x0038 | |
#define M30 0x0007 | |
// Extracting bits from a mask | |
#define XM110(n) ((n & M110) >> 0) | |
#define XM90(n) ((n & M90) >> 0) | |
#define XM80(n) ((n & M80) >> 0) | |
#define XM60(n) ((n & M60) >> 0) | |
#define XM50(n) ((n & M50) >> 0) | |
#define XM412(n) ((n & M412) >> 12) | |
#define XM39(n) ((n & M39) >> 9) | |
#define XM36(n) ((n & M36) >> 6) | |
#define XM33(n) ((n & M33) >> 3) | |
#define XM30(n) ((n & M30) >> 0) | |
// Setting one bit | |
#define B(n) (1 << n) | |
// Sign extension | |
#define SEXT11(n) ((s16)(n | (n & B(11) ? ~((u16)M110) : 0))) | |
#define SEXT9(n) ((s16)(n | (n & B(9) ? ~((u16)M90) : 0))) | |
#define SEXT6(n) ((s16)(n | (n & B(6) ? ~((u16)M60) : 0))) | |
#define SEXT5(n) ((s16)(n | (n & B(5) ? ~((u16)M50) : 0))) | |
// Emulation flags | |
#define LC3_EMULATION_RUN B(1) | |
// Traps | |
#define LC3_TRAP_GETC 0x20 | |
#define LC3_TRAP_OUTC 0x21 | |
#define LC3_TRAP_PUTS 0x22 | |
#define LC3_TRAP_IN 0x23 | |
#define LC3_TRAP_PUTSP 0x24 | |
#define LC3_TRAP_HALT 0x25 | |
// Condition codes | |
#define P B(9) | |
#define Z B(10) | |
#define N B(11) | |
#define COND(ctx, val) ctx->cond = ((s16)val < 0 ? N : ((s16)val > 0 ? P : Z)) | |
#define p(fmt, ...) lc3_printf(fmt, __VA_ARGS__) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment