Skip to content

Instantly share code, notes, and snippets.

@hbuxiaofei

hbuxiaofei/lc3.c Secret

Last active October 17, 2023 14:24
Show Gist options
  • Save hbuxiaofei/641d648b045d10a36a12e27c0da00317 to your computer and use it in GitHub Desktop.
Save hbuxiaofei/641d648b045d10a36a12e27c0da00317 to your computer and use it in GitHub Desktop.
lc-3 vmm
// lc3.c
#include <stdio.h>
#include <stdint.h>
#include <signal.h>
/* unix only */
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/termios.h>
#include <sys/mman.h>
#define MEMORY_MAX (1 << 16)
uint16_t memory[MEMORY_MAX]; /* 65536 locations */
enum
{
R_R0 = 0,
R_R1,
R_R2,
R_R3,
R_R4,
R_R5,
R_R6,
R_R7,
R_PC, /* program counter */
R_COND,
R_COUNT
};
enum
{
MR_KBSR = 0xFE00, /* keyboard status */
MR_KBDR = 0xFE02 /* keyboard data */
};
// TRAP 定义
enum
{
TRAP_GETC = 0x20,
TRAP_OUT = 0x21,
TRAP_PUTS = 0x22,
TRAP_IN = 0x23,
TRAP_PUTSP = 0x24,
TRAP_HALT = 0x25
};
uint16_t reg[R_COUNT];
enum
{
OP_BR = 0, /* 0000 branch */
OP_ADD, /* 0001 add */
OP_LD, /* 0010 load */
OP_ST, /* 0011 store */
OP_JSR, /* 0100 jump register */
OP_AND, /* 0101 bitwise and */
OP_LDR, /* 0110 load register */
OP_STR, /* 0111 store register */
OP_RTI, /* 1000 unused */
OP_NOT, /* 1001 bitwise not */
OP_LDI, /* 1010 load indirect */
OP_STI, /* 1011 store indirect */
OP_JMP, /* 1100 jump */
OP_RES, /* 1101 reserved (unused) */
OP_LEA, /* 1110 load effective address */
OP_TRAP /* 1111 execute trap */
};
enum
{
FL_POS = 1 << 0, /* P: positive (greater than zero) */
FL_ZRO = 1 << 1, /* Z: zero */
FL_NEG = 1 << 2, /* N: negative (smaller than zero) */
};
uint16_t sign_extend(uint16_t x, int bit_count)
{
if ((x >> (bit_count - 1)) & 1) {
x |= (0xFFFF << bit_count);
}
return x;
}
int is_little_endian(void) {
union {
char c;
int i;
} un;
un.i = 1;
// 如果是小端则返回 1,如果是大端则返回 0
return un.c;
}
// LC-3 程序是大端程序, 如果是小端程序则需要交换高低字节
uint16_t swap16(uint16_t x)
{
return (x << 8) | (x >> 8);
}
void update_flags(uint16_t r)
{
if (reg[r] == 0) {
reg[R_COND] = FL_ZRO;
} else if (reg[r] >> 15) { // 最左边的位为 1 表示负数
reg[R_COND] = FL_NEG;
} else {
reg[R_COND] = FL_POS;
}
}
void mem_write(uint16_t address, uint16_t val)
{
memory[address] = val;
}
struct termios original_tio;
void disable_input_buffering()
{
tcgetattr(STDIN_FILENO, &original_tio);
struct termios new_tio = original_tio;
new_tio.c_lflag &= ~ICANON & ~ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &new_tio);
}
void restore_input_buffering()
{
tcsetattr(STDIN_FILENO, TCSANOW, &original_tio);
}
uint16_t check_key()
{
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(STDIN_FILENO, &readfds);
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 0;
return select(1, &readfds, NULL, NULL, &timeout) != 0;
}
uint16_t mem_read(uint16_t address)
{
if (address == MR_KBSR) {
if (check_key()) {
memory[MR_KBSR] = (1 << 15);
memory[MR_KBDR] = getchar();
} else {
memory[MR_KBSR] = 0;
}
}
return memory[address];
}
void read_image_file(FILE* file)
{
uint16_t origin;
fread(&origin, sizeof(origin), 1, file);
if (is_little_endian()) {
origin = swap16(origin);
}
uint16_t max_read = MEMORY_MAX - origin;
uint16_t* p = memory + origin;
size_t read = fread(p, sizeof(uint16_t), max_read, file);
if (is_little_endian()) {
while (read-- > 0) {
*p = swap16(*p);
++p;
}
}
}
int read_image(const char* image_path)
{
FILE* file = fopen(image_path, "rb");
if (!file) {
return 0;
}
read_image_file(file);
fclose(file);
return 1;
}
void handle_interrupt(int signal)
{
restore_input_buffering();
printf("\n");
exit(-2);
}
int main(int argc, const char* argv[])
{
// Load Arguments
if (argc < 2) {
/* show usage string */
printf("Using: main.out [image-file1] ...\n");
exit(2);
}
if (!read_image(argv[1])) {
printf("failed to load image: %s\n", argv[1]);
exit(1);
}
signal(SIGINT, handle_interrupt);
disable_input_buffering();
// 条件标志清零, 设置 Z(zero) 标志
reg[R_COND] = FL_ZRO;
// 设置 PC 起始位置 0x3000
enum { PC_START = 0x3000 };
reg[R_PC] = PC_START;
int running = 1;
while (running) {
// FETCH 取指令
uint16_t instr = mem_read(reg[R_PC]++);
uint16_t op = instr >> 12; /* 左移12位, 取操作码 */
// printf(">>> op: 0x%x\n", op);
switch (op) {
// 两个变量相加(+)
// ADD DR,SR1,SR2 或者 ADD DR,SR1,imm
case OP_ADD:
{
// 目的寄存器 (DR)
uint16_t r0 = (instr >> 9) & 0x7;
// 源寄存器1 (SR1)
uint16_t r1 = (instr >> 6) & 0x7;
// 立即数标志
uint16_t imm_flag = (instr >> 5) & 0x1;
if (imm_flag == 0) {
uint16_t r2 = instr & 0x7;
reg[r0] = reg[r1] + reg[r2];
} else {
uint16_t imm5 = sign_extend(instr & 0x1F, 5);
reg[r0] = reg[r1] + imm5;
}
update_flags(r0);
}
break;
case OP_AND:
{
uint16_t r0 = (instr >> 9) & 0x7;
uint16_t r1 = (instr >> 6) & 0x7;
uint16_t imm_flag = (instr >> 5) & 0x1;
if (imm_flag) {
uint16_t imm5 = sign_extend(instr & 0x1F, 5);
reg[r0] = reg[r1] & imm5;
} else {
uint16_t r2 = instr & 0x7;
reg[r0] = reg[r1] & reg[r2];
}
update_flags(r0);
}
break;
case OP_NOT:
{
uint16_t r0 = (instr >> 9) & 0x7;
uint16_t r1 = (instr >> 6) & 0x7;
reg[r0] = ~reg[r1];
update_flags(r0);
}
break;
case OP_BR:
{
uint16_t n_flag = (instr >> 11) & 0x1;
uint16_t z_flag = (instr >> 10) & 0x1;
uint16_t p_flag = (instr >> 9) & 0x1;
uint16_t pc_offset = sign_extend(instr & 0x1FF, 9);
if ((n_flag && (reg[R_COND] & FL_NEG)) ||
(z_flag && (reg[R_COND] & FL_ZRO)) ||
(p_flag && (reg[R_COND] & FL_POS))) {
reg[R_PC] += pc_offset;
}
}
break;
case OP_JMP:
{
uint16_t r1 = (instr >> 6) & 0x7;
reg[R_PC] = reg[r1];
}
break;
case OP_JSR:
{
uint16_t long_flag = (instr >> 11) & 1;
if (long_flag) {
reg[R_R7] = reg[R_PC];
uint16_t long_pc_offset = sign_extend(instr & 0x7FF, 11);
reg[R_PC] += long_pc_offset; /* JSR 直接跳转 */
} else {
uint16_t tmp = reg[(instr >> 6) & 0x7];
reg[R_R7] = reg[R_PC];
reg[R_PC] = tmp; /* JSRR 寄存器间接跳转 */
}
}
break;
case OP_LD:
{
uint16_t r0 = (instr >> 9) & 0x7;
uint16_t pc_offset = sign_extend(instr & 0x1FF, 9);
reg[r0] = mem_read(reg[R_PC] + pc_offset);
update_flags(r0);
}
break;
case OP_LDI:
{
// 目的寄存器 (DR)
uint16_t r0 = (instr >> 9) & 0x7;
// PC偏移 9bit
uint16_t pc_offset = sign_extend(instr & 0x1FF, 9);
// 将 pc_offset 加到 PC 上, 然后查看该内存位置获取最终地址
reg[r0] = mem_read(mem_read(reg[R_PC] + pc_offset));
update_flags(r0);
}
break;
case OP_LDR:
{
uint16_t r0 = (instr >> 9) & 0x7;
uint16_t r1 = (instr >> 6) & 0x7;
uint16_t offset = sign_extend(instr & 0x3F, 6);
reg[r0] = mem_read(reg[r1] + offset);
update_flags(r0);
}
break;
case OP_LEA:
{
uint16_t r0 = (instr >> 9) & 0x7;
uint16_t pc_offset = sign_extend(instr & 0x1FF, 9);
reg[r0] = reg[R_PC] + pc_offset;
update_flags(r0);
}
break;
case OP_ST:
{
uint16_t r0 = (instr >> 9) & 0x7;
uint16_t pc_offset = sign_extend(instr & 0x1FF, 9);
mem_write(reg[R_PC] + pc_offset, reg[r0]);
}
break;
case OP_STI:
{
uint16_t r0 = (instr >> 9) & 0x7;
uint16_t pc_offset = sign_extend(instr & 0x1FF, 9);
mem_write(mem_read(reg[R_PC] + pc_offset), reg[r0]);
}
break;
case OP_STR:
{
uint16_t r0 = (instr >> 9) & 0x7;
uint16_t r1 = (instr >> 6) & 0x7;
uint16_t offset = sign_extend(instr & 0x3F, 6);
mem_write(reg[r1] + offset, reg[r0]);
}
break;
case OP_TRAP:
reg[R_R7] = reg[R_PC];
// printf(">>> trap: 0x%x \n", instr & 0xff);
switch (instr & 0xFF)
{
case TRAP_GETC:
reg[R_R0] = (uint16_t)getchar();
update_flags(R_R0);
break;
case TRAP_OUT:
putc((char)reg[R_R0], stdout);
fflush(stdout);
break;
case TRAP_PUTS:
{
// 16 bit 表示一个字符
uint16_t* c = memory + reg[R_R0];
while (*c) {
putc((char)*c, stdout);
++c;
}
fflush(stdout);
}
break;
case TRAP_IN:
{
char c = getchar();
putc(c, stdout);
fflush(stdout);
reg[R_R0] = (uint16_t)c;
update_flags(R_R0);
}
break;
case TRAP_PUTSP:
{
uint16_t* c = memory + reg[R_R0];
while (*c) {
char char1 = (*c) & 0xFF;
putc(char1, stdout);
char char2 = (*c) >> 8;
if (char2) {
putc(char2, stdout);
}
++c;
}
fflush(stdout);
}
break;
case TRAP_HALT:
fflush(stdout);
running = 0;
break;
}
break;
case OP_RTI:
case OP_RES:
abort(); /* RTI RES 未使用 */
default:
printf("error: bad op code\n");
break;
}
}
restore_input_buffering();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment