Skip to content

Instantly share code, notes, and snippets.

@numinit
Created March 31, 2013 05:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save numinit/5279644 to your computer and use it in GitHub Desktop.
Save numinit/5279644 to your computer and use it in GitHub Desktop.
#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;
}
#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))
// Print
#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