#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->CCR = 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->CCR & Z_FLAG ) == 1 ); break; case OPCODE_JMP_JNE: condition = ( ( cpu->CCR & Z_FLAG ) == 0 ); break; case OPCODE_JMP_JB: condition = ( ( cpu->CCR & C_FLAG ) == 1 ); break; case OPCODE_JMP_JBE: condition = ( ( cpu->CCR & C_FLAG ) == 1 ) || ( ( cpu->CCR & Z_FLAG ) == 1 ); break; case OPCODE_JMP_JA: condition = ( ( cpu->CCR & C_FLAG ) == 0 ) && ( ( cpu->CCR & Z_FLAG ) == 0 ); break; case OPCODE_JMP_JAE: condition = ( ( cpu->CCR & C_FLAG ) == 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 ); if( value1 - value2 == 0 ) { cpu->CCR |= Z_FLAG; } else { cpu->CCR &= ~Z_FLAG; } 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->CCR & Z_FLAG ); print_register( "C", cpu->CCR & C_FLAG ); print_register( "V", cpu->CCR & V_FLAG ); print_register( "F", cpu->error ); }