diff options
author | Andreas Baumann <mail@andreasbaumann.cc> | 2017-01-01 19:31:12 +0100 |
---|---|---|
committer | Andreas Baumann <mail@andreasbaumann.cc> | 2017-01-01 19:31:12 +0100 |
commit | 69fe7b182a1eedfb75c611f7dd35fa60200426f4 (patch) | |
tree | 329a0c6cc9b06c23d8782ece09f0f7dfa9b16b13 /miniemu | |
download | compilertests-69fe7b182a1eedfb75c611f7dd35fa60200426f4.tar.gz compilertests-69fe7b182a1eedfb75c611f7dd35fa60200426f4.tar.bz2 |
initial checkin
Diffstat (limited to 'miniemu')
-rw-r--r-- | miniemu/README | 102 | ||||
-rw-r--r-- | miniemu/TESTS | 2 | ||||
-rw-r--r-- | miniemu/const.h | 5 | ||||
-rw-r--r-- | miniemu/cpu.c | 457 | ||||
-rw-r--r-- | miniemu/cpu.h | 25 | ||||
-rw-r--r-- | miniemu/main.c | 46 | ||||
-rw-r--r-- | miniemu/memory.c | 83 | ||||
-rw-r--r-- | miniemu/memory.h | 14 | ||||
-rw-r--r-- | miniemu/opcodes.h | 148 |
9 files changed, 882 insertions, 0 deletions
diff --git a/miniemu/README b/miniemu/README new file mode 100644 index 0000000..6b27b32 --- /dev/null +++ b/miniemu/README @@ -0,0 +1,102 @@ +Design +------ + +- MISC or RISC +- load/store or allow things like adding register to memory +- make most aspect configurable at runtime because we want to use + it to stress the compiler (things like "what if we have 1k RAM + and 4 registers?") +- fixed size instruction set or not or how much do we care about + being optimized to be implementable in hardware or in FPA? +- the PDB-8 had 8 opcodes? MISC requires 32 or less opcodes +- RISC requires operands to be part of the opcode, this is not + really practicable with 8-bit words, mostly RISCs are 32 or + 64 bit wasting tons of memory for code. +- von Neumann or Harvard? + +Opcodes +------- + +Load/Store + +MOV + +Integer Arithmetic and logical operations + +ADD +SUB +MUL +DIV + +INC +DEC + +AND +OR +NOT +XOR +NEG + +SHR +SHL +ROTR +ROTL + +Floating Point Arithmetic + +FADD +FSUB +FMUL +FDIV + +Testing and Branching + +CMP + +JC +JNC +JZ +JNZ + +Subroutine + +CALL +RET + +Not sure about those + +Stack + +PUSH +POP + +I/O-ports + +GET +PUT + +Other + +HLT +NOP + +not sure about NOP, as long as we don't do branch-prediction or +code patching for relocation? + +Building +-------- + +gcc -I../minilib -g -O0 -m32 -march=i386 -ffreestanding -Werror -Wall -Wno-return-type -pedantic -std=c89 -o miniemu *.c ../minilib/*.c +clang -I../minilib -g -O0 -march=i386 -fno-builtin -std=c89 -Werror -Wall -Wno-return-type -o miniemu *.c ../minilib/*.c + +Usage +----- + +./miniemu test1.bin +./miniemu test2.bin +./miniemu test3.bin +./miniemu test4.bin +./miniemu test5.bin +./miniemu test6.bin +./miniemu test7.bin +./miniemu test8.bin diff --git a/miniemu/TESTS b/miniemu/TESTS new file mode 100644 index 0000000..b233c35 --- /dev/null +++ b/miniemu/TESTS @@ -0,0 +1,2 @@ +test1.bin - illegal opcode +test2.bin - sequence of nops and a terminating halt diff --git a/miniemu/const.h b/miniemu/const.h new file mode 100644 index 0000000..76ed52d --- /dev/null +++ b/miniemu/const.h @@ -0,0 +1,5 @@ +#pragma once + +enum { + DEFAULT_MEMORY_SIZE = 256 +}; diff --git a/miniemu/cpu.c b/miniemu/cpu.c new file mode 100644 index 0000000..26e1469 --- /dev/null +++ b/miniemu/cpu.c @@ -0,0 +1,457 @@ +#include "cpu.h" +#include "memory.h" +#include "io.h" +#include "string.h" +#include "utils.h" +#include "const.h" +#include "opcodes.h" +#include "utils.h" +#include "minilib.h" + +void cpu_init( Cpu *cpu, struct Memory *memory ) +{ + cpu->memory = memory; + cpu_reset( cpu ); +} + +void cpu_done( Cpu *cpu ) +{ +} + +void cpu_reset( Cpu *cpu ) +{ + cpu->PC = 0; + cpu->AX = 0; + cpu->BX = 0; + cpu->CX = 0; + cpu->DX = 0; + cpu->SP = cpu->memory->size - 1; + cpu->stopped = 0; + cpu->error = 0; + cpu->debug = 0; + cpu->memory->m[cpu->PC] = OPCODE_HLT; + cpu->Z = 0; + cpu->C = 0; +} + +static int read_src_value( Cpu *cpu, int instruction ) +{ + int value; + + switch( instruction & OPCODE_SRC_MASK ) { + case OPCODE_SRC_AX_REG: + value = cpu->AX; + break; + + case OPCODE_SRC_BX_REG: + value = cpu->BX; + break; + + case OPCODE_SRC_CX_REG: + value = cpu->CX; + break; + + case OPCODE_SRC_DX_REG: + value = cpu->DX; + break; + + case OPCODE_SRC_CONST: + cpu->PC++; + value = memory_read( cpu->memory, cpu->PC ); + break; + } + + return value; +} + +static int read_dst_value( Cpu *cpu, int instruction ) +{ + int value; + + switch( instruction & OPCODE_DST_MASK ) { + case OPCODE_DST_AX_REG: + value = cpu->AX; + break; + + case OPCODE_DST_BX_REG: + value = cpu->BX; + break; + + case OPCODE_DST_CX_REG: + value = cpu->CX; + break; + + case OPCODE_DST_DX_REG: + value = cpu->DX; + break; + } + + return value; +} + +static int write_dst_value( Cpu *cpu, int instruction, int value ) +{ + switch( instruction & OPCODE_DST_MASK ) { + case OPCODE_DST_AX_REG: + cpu->AX = value; + break; + + case OPCODE_DST_BX_REG: + cpu->BX = value; + break; + + case OPCODE_DST_CX_REG: + cpu->CX = value; + break; + + case OPCODE_DST_DX_REG: + cpu->DX = value; + break; + } +} + +static int write_src_value( Cpu *cpu, int instruction, int value ) +{ + switch( instruction & OPCODE_SRC_MASK ) { + case OPCODE_SRC_AX_REG: + cpu->AX = value; + break; + + case OPCODE_SRC_BX_REG: + cpu->BX = value; + break; + + case OPCODE_SRC_CX_REG: + cpu->CX = value; + break; + + case OPCODE_SRC_DX_REG: + cpu->DX = value; + break; + } +} + +static void execute_jmp( Cpu *cpu, int instruction ) +{ + int address; + int condition; + + cpu->PC++; + address = memory_read( cpu->memory, cpu->PC ); + condition = 0; + + switch( instruction & OPCODE_JMP_MASK ) { + case OPCODE_JMP_JMP: + condition = 1; + break; + + case OPCODE_JMP_JE: + condition = ( cpu->Z == 1 ); + break; + + case OPCODE_JMP_JNE: + condition = ( cpu->Z == 0 ); + break; + + case OPCODE_JMP_JB: + condition = ( cpu->C == 1 ); + break; + + case OPCODE_JMP_JBE: + condition = ( cpu->C == 1 ) || ( cpu->Z == 1 ); + break; + + case OPCODE_JMP_JA: + condition = ( cpu->C == 0 ) && ( cpu->Z == 0 ); + break; + + case OPCODE_JMP_JAE: + condition = ( cpu->C == 0 ); + break; + + case OPCODE_JMP_JSR: + condition = 1; + memory_write( cpu->memory, cpu->SP, cpu->PC + 1 ); + cpu->SP--; + break; + + default: + print( "ILLEGAL JUMP INSTRUCTION" ); + cpu->stopped = 1; + cpu->error = 1; + return; + } + + if( condition ) { + cpu->PC = address; + } else { + cpu->PC++; + } +} + +static void execute_not( Cpu *cpu, int instruction ) +{ + int value; + + value = read_src_value( cpu, instruction ); + write_src_value( cpu, instruction, (unsigned char)~value ); + + cpu->PC++; +} + +static void execute_stack( Cpu *cpu, int instruction ) +{ + int value; + + switch( instruction & OPCODE_STACK_POP ) { + case OPCODE_STACK_PUSH: + value = read_src_value( cpu, instruction & OPCODE_STACK_OPERAND_MASK ); + memory_write( cpu->memory, cpu->SP, value ); + cpu->SP--; + break; + + case OPCODE_STACK_POP: + cpu->SP++; + value = memory_read( cpu->memory, cpu->SP ); + write_src_value( cpu, instruction & OPCODE_STACK_OPERAND_MASK, value ); + break; + } + + cpu->PC++; +} + +static void execute_mov_ld( Cpu *cpu, int instruction ) +{ + int value; + + value = read_src_value( cpu, instruction ); + write_dst_value( cpu, instruction, value ); + + cpu->PC++; +} + +static void execute_or( Cpu *cpu, int instruction ) +{ + int value1; + int value2; + + value1 = read_src_value( cpu, instruction ); + value2 = read_dst_value( cpu, instruction ); + + write_dst_value( cpu, instruction, value2 | value1 ); + + cpu->PC++; +} + +static void execute_and( Cpu *cpu, int instruction ) +{ + int value1; + int value2; + + value1 = read_src_value( cpu, instruction ); + value2 = read_dst_value( cpu, instruction ); + + write_dst_value( cpu, instruction, value2 & value1 ); + + cpu->PC++; +} + +static void execute_sub( Cpu *cpu, int instruction ) +{ + int value1; + int value2; + + value1 = read_src_value( cpu, instruction ); + value2 = read_dst_value( cpu, instruction ); + + write_dst_value( cpu, instruction, value2 - value1 ); + + cpu->PC++; +} + +static void execute_cmp( Cpu *cpu, int instruction ) +{ + int value1; + int value2; + + value1 = read_src_value( cpu, instruction ); + value2 = read_dst_value( cpu, instruction ); + + cpu->Z = ( value1 - value2 == 0 ); + cpu->C = ( value1 - value2 < 0 ); + + cpu->PC++; +} + +static void execute_add( Cpu *cpu, int instruction ) +{ + int value1; + int value2; + + value1 = read_src_value( cpu, instruction ); + value2 = read_dst_value( cpu, instruction ); + + write_dst_value( cpu, instruction, value1 + value2 ); + + cpu->PC++; +} + +static void execute_ret( Cpu *cpu, int instruction ) +{ + cpu->SP++; + + if( cpu->SP > DEFAULT_MEMORY_SIZE ) { + print( "ERROR STACK OVERFLOW" ); + cpu->error = 1; + cpu->stopped = 1; + return; + } + + cpu->PC = memory_read( cpu->memory, cpu->SP ); +} + +void cpu_step( Cpu *cpu ) +{ + unsigned char instruction; + + instruction = (unsigned char)memory_read( cpu->memory, cpu->PC ); + + if( cpu->debug ) { + char s[10]; + char buf[5]; + *buf = '\0'; + inttohex( instruction, buf ); + *s = '\0'; + strcat( s, "INSTR: " ); + strcat( s, buf ); + print( s ); + } + + if( instruction & OPCODE_GROUP_TWO_OPERANDS ) { + switch( instruction & OPCODE_GROUP_TWO_OPERANDS ) { + case OPCODE_MOV_LD: + execute_mov_ld( cpu, instruction ); + break; + + case OPCODE_OR: + execute_or( cpu, instruction ); + break; + + case OPCODE_AND: + execute_and( cpu, instruction ); + break; + + case OPCODE_SUB: + execute_sub( cpu, instruction ); + break; + + case OPCODE_CMP: + execute_cmp( cpu, instruction ); + break; + + case OPCODE_ADD: + execute_add( cpu, instruction ); + break; + + default: + print( "ILLEGAL TWO OPERAND INSTRUCTION" ); + cpu->stopped = 1; + cpu->error = 1; + } + } else if( ( instruction & OPCODE_GROUP_ONE_OPERAND ) ) { + switch( instruction & OPCODE_GROUP_ONE_OPERAND ) { + case OPCODE_JMP: + execute_jmp( cpu, instruction ); + break; + + case OPCODE_NOT: + execute_not( cpu, instruction ); + break; + + case OPCODE_STACK: + execute_stack( cpu, instruction ); + break; + + default: + print( "ILLEGAL ONE OPERAND INSTRUCTION" ); + cpu->stopped = 1; + cpu->error = 1; + } + } else if( ( instruction & OPCODE_GROUP_ZERO_OPERANDS ) ) { + switch( instruction & OPCODE_GROUP_ZERO_OPERANDS ) { + case OPCODE_HLT: + print( "CPU HALTED" ); + cpu->stopped = 1; + cpu->PC++; + break; + + case OPCODE_NOP: + cpu->PC++; + break; + + case OPCODE_RET: + execute_ret( cpu, instruction ); + break; + + default: + print( "ILLEGAL ZERO OPERAND INSTRUCTION" ); + cpu->stopped = 1; + cpu->error = 1; + } + } else { + print( "ILLEGAL INSTRUCTION GROUP" ); + cpu->stopped = 1; + cpu->error = 1; + } +} + +int cpu_stopped( Cpu *cpu ) +{ + return cpu->stopped; +} + +int cpu_has_errors( Cpu *cpu ) +{ + return cpu->error; +} + +void cpu_debug( Cpu *cpu, int enable ) +{ + cpu->debug = enable; + + if( cpu->debug ) { + print( "DEBUGGING ENABLED" ); + } else { + print( "DEBUGGING DISABLED" ); + } +} + +static void print_register( char *name, int v ) +{ + char s[10]; + char buf[5]; + + *s = '\0'; + strcat( s, " " ); + strcat( s, name ); + strcat( s, ": " ); + + *buf = '\0'; + inttohex( v, buf ); + strcat( s, buf ); + + print( s ); +} + +void cpu_print_dump( Cpu *cpu ) +{ + print_register( "PC", cpu->PC ); + print_register( "AX", cpu->AX ); + print_register( "BX", cpu->BX ); + print_register( "CX", cpu->CX ); + print_register( "DX", cpu->DX ); + print_register( "SP", cpu->SP ); + print_register( "Z", cpu->Z ); + print_register( "C", cpu->C ); + print_register( "F", cpu->error ); +} diff --git a/miniemu/cpu.h b/miniemu/cpu.h new file mode 100644 index 0000000..7a3d554 --- /dev/null +++ b/miniemu/cpu.h @@ -0,0 +1,25 @@ +#pragma once + +typedef struct Cpu { + int PC; + int AX; + int BX; + int CX; + int DX; + int SP; + struct Memory *memory; + int stopped; + int error; + int debug; + int Z; + int C; +} Cpu; + +extern void cpu_init( Cpu *cpu, struct Memory *memory ); +extern void cpu_done( Cpu *cpu ); +extern void cpu_reset( Cpu *cpu ); +extern void cpu_step( Cpu *cpu ); +extern int cpu_stopped( Cpu *cpu ); +extern int cpu_has_errors( Cpu *cpu ); +extern void cpu_debug( Cpu *cpu, int enable ); +extern void cpu_print_dump( Cpu *cpu ); diff --git a/miniemu/main.c b/miniemu/main.c new file mode 100644 index 0000000..f05973e --- /dev/null +++ b/miniemu/main.c @@ -0,0 +1,46 @@ +#include "const.h" +#include "cpu.h" +#include "memory.h" +#include "io.h" +#include "opcodes.h" + +int main( int argc, char *argv[] ) +{ + Cpu cpu; + Memory memory; + int retcode; + + if( argc != 2 ) { + print( "USAGE: miniemu <memory.bin>" ); + return 1; + } + + memory_init( &memory, DEFAULT_MEMORY_SIZE ); + cpu_init( &cpu, &memory ); + + print( "EMULATOR STARTED" ); + + print( "LOADING MEMORY DUMP" ); + memory_read_from_file( &memory, argv[1] ); + + cpu_debug( &cpu, 1 ); + + while( !cpu_stopped( &cpu ) ) { + cpu_step( &cpu ); + } + + retcode = cpu_has_errors( &cpu ); + + print( "CPU" ); + cpu_print_dump( &cpu ); + + print( "MEMORY" ); + memory_print_dump( &memory ); + + cpu_done( &cpu ); + memory_done( &memory ); + + print( "EMULATOR TERMINATED" ); + + return retcode; +} diff --git a/miniemu/memory.c b/miniemu/memory.c new file mode 100644 index 0000000..2f1efb4 --- /dev/null +++ b/miniemu/memory.c @@ -0,0 +1,83 @@ +#include "memory.h" +#include "arena.h" +#include "io.h" +#include "minilib.h" +#include "string.h" +#include "utils.h" +#include "io.h" + +void memory_init( Memory *memory, int size ) +{ + memory->size = size; + memory->m = allocate( size ); + memory_reset( memory ); +} + +void memory_done( Memory *memory ) +{ + deallocate( (void **)&memory->m ); +} + +void memory_reset( Memory *memory ) +{ + memset( memory->m, 0, memory->size ); +} + +void memory_write( Memory *memory, int addr, char v ) +{ + if( addr < 0 || addr > memory->size ) { + print( "MEMORY WRITE ACCESS VIOLATION" ); + halt( ); + } + + memory->m[addr] = v; +} + +char memory_read( Memory *memory, int addr ) +{ + if( addr < 0 || addr > memory->size ) { + print( "MEMORY READ ACCESS VIOLATION" ); + halt( ); + } + + return memory->m[addr]; +} + +void memory_print_dump( Memory *memory ) +{ + int i; + char *s = allocate( memory->size * 4 ); + char buf[4]; + s[0] = '\0'; + + strcat( s, " 00: " ); + for( i = 0; i < memory->size; i++ ) { + strtohex( &memory->m[i], 1, buf ); + strcat( s, buf ); + strcat( s, " " ); + if( i % 16 == 15 ) { + print( s ); + *s = '\0'; + inttohex( i + 1, buf ); + strcat( s, " " ); + strcat( s, buf ); + strcat( s, ": " ); + } else { + if( i % 8 == 7 ) { + strcat( s, " " ); + } + } + } + + print( s ); + + deallocate( (void **)&s ); +} + +void memory_read_from_file( Memory *memory, char *filename ) +{ + if( readfile( filename, memory->m, memory->size ) < 0 ) { + print( "MEMORY DUMP LOAD ERROR" ); + halt( ); + } +} diff --git a/miniemu/memory.h b/miniemu/memory.h new file mode 100644 index 0000000..312a42f --- /dev/null +++ b/miniemu/memory.h @@ -0,0 +1,14 @@ +#pragma once + +typedef struct Memory { + int size; + char *m; +} Memory; + +extern void memory_init( Memory *memory, int size ); +extern void memory_done( Memory *memory ); +extern void memory_reset( Memory *memory ); +extern void memory_write( Memory *memory, int addr, char v ); +extern char memory_read( Memory *memory, int addr ); +extern void memory_print_dump( Memory *memory ); +extern void memory_read_from_file( Memory *memory, char *filename ); diff --git a/miniemu/opcodes.h b/miniemu/opcodes.h new file mode 100644 index 0000000..37d0922 --- /dev/null +++ b/miniemu/opcodes.h @@ -0,0 +1,148 @@ +#pragma once + +/* + * Follows the general 8-bit opcodes: + * + * See http://www.c-jump.com/CIS77/CPU/IsaDesign/lecture.html + * + * iiirrmmm (general two operand opcode format) + * + * iii instruction/opcode group + * rr register operand + * mmm second operand + * + * iii = 000 special commands (zero or one operand) + * = 001 or + * = 010 and + * = 011 cmp + * = 100 sub + * = 101 add + * = 11x mov + * = 110 mov (load) + * = 111 mov (store) + * + * rr = 00 AX + * 01 BX + * 10 CX + * 11 DX + * + * mmm = 000 AX + * 001 BX + * 010 CX + * 011 DX + * 100 illegal + * 101 illegal + * 110 direct memory + * 111 constant + * + * 000iimmm (general one operand opcode format) + * + * ii instruction/opcode group + * mmm operand + * + * ii = 00 (special commands) zero-operand commands + * = 01 jump instructions + * = 10 not + * = 11 stack (push and pop registers to/from stack) + * + * for jmp: + * + * mmm = 000 je (Z set) + * 001 jne (Z not set) + * 010 jb (C set) + * 011 jbe (C set or Z set) + * 100 ja (C not set and Z not set) + * 101 jae (C not set) + * 110 jmp (unconditional) + * 111 jsr (subroutine call, push PC on stack) + * + * for not: + * + * mmm = 000 AX + * 001 BX + * 010 CX + * 011 DX + * 100 illegal + * 101 illegal + * 110 illegal + * 111 illegal + * + * for push/pop + * + * mmm = 000 push AX + * 001 push BX + * 010 push CX + * 011 push DX + * 100 pop AX + * 101 pop BX + * 110 pop CX + * 111 pop DX + * + * 00000iii zero-operand instructions + * + * iii instruction/opcode group + * + * iii = 000 illegal + * = 001 nop + * = 010 hlt + * = 011 ret (return from subroute call, restore PC from stack) + * = 100 illegal + * = 101 illegal + * = 110 illegal + * = 111 illegal + * + */ +typedef enum Opcode { + OPCODE_GROUP_ZERO_OPERANDS = (unsigned char)0x07, + OPCODE_GROUP_ONE_OPERAND = (unsigned char)0x18, + OPCODE_GROUP_TWO_OPERANDS = (unsigned char)0xe0, + + OPCODE_MOV_MASK = (unsigned char)0xc0, + OPCODE_MOV_LD = (unsigned char)0xc0, + OPCODE_MOV_ST = (unsigned char)0xe8, + + OPCODE_DST_MASK = (unsigned char)0x18, + OPCODE_DST_AX_REG = (unsigned char)0x00, + OPCODE_DST_BX_REG = (unsigned char)0x08, + OPCODE_DST_CX_REG = (unsigned char)0x10, + OPCODE_DST_DX_REG = (unsigned char)0x18, + + OPCODE_SRC_MASK = (unsigned char)0x07, + OPCODE_SRC_AX_REG = (unsigned char)0x00, + OPCODE_SRC_BX_REG = (unsigned char)0x01, + OPCODE_SRC_CX_REG = (unsigned char)0x02, + OPCODE_SRC_DX_REG = (unsigned char)0x03, + OPCODE_SRC_CONST = (unsigned char)0x07, + OPCODE_SRC_MEMORY = (unsigned char)0x06, + + OPCODE_OR = (unsigned char)0x20, + OPCODE_AND = (unsigned char)0x40, + OPCODE_CMP = (unsigned char)0x60, + OPCODE_SUB = (unsigned char)0x80, + OPCODE_ADD = (unsigned char)0xa0, + + OPCODE_JMP = (unsigned char)0x08, + + OPCODE_JMP_MASK = (unsigned char)0x07, + OPCODE_JMP_JE = (unsigned char)0x00, + OPCODE_JMP_JNE = (unsigned char)0x01, + OPCODE_JMP_JB = (unsigned char)0x02, + OPCODE_JMP_JBE = (unsigned char)0x03, + OPCODE_JMP_JA = (unsigned char)0x04, + OPCODE_JMP_JAE = (unsigned char)0x05, + OPCODE_JMP_JMP = (unsigned char)0x06, + OPCODE_JMP_JSR = (unsigned char)0x07, + + OPCODE_NOT = (unsigned char)0x10, + + OPCODE_STACK = (unsigned char)0x18, + OPCODE_STACK_OPERAND_MASK = (unsigned char)0x3, + OPCODE_STACK_PUSH = (unsigned char)0x0, + OPCODE_STACK_POP = (unsigned char)0x4, + + OPCODE_ZERO_OPS_MASK = (unsigned char)0xf8, + + OPCODE_NOP = (unsigned char)0x01, + OPCODE_HLT = (unsigned char)0x02, + OPCODE_RET = (unsigned char)0x03 +} Opcode; |