#ifdef HAVE_CONFIG_H #include #endif #include #include #include static int failures = 0; #define CHECK(cond,msg) \ do { \ if( !(cond) ) \ { \ fprintf( stderr, "FAIL: %s\n", (msg) ); \ ++failures; \ } \ } while( 0 ) static cpu_operand reg_operand( unsigned int bits, __mpu_uint32_t address ) { cpu_operand operand; memset( &operand, 0, sizeof(operand) ); operand.type = CPU_OPERAND_REGISTER; operand.value.reg.bits = bits; operand.value.reg.address = address; return( operand ); } static void clear_instruction( cpu_instruction *instruction, cpu_opcode opcode ) { memset( instruction, 0, sizeof(*instruction) ); instruction->opcode = opcode; } int main( void ) { cpu_instruction instruction; __mpu_uint32_t psw; __cpu_init(); /* NOP must not change the logical PSW. */ __cpu_set_psw( PSW_CF_MASK | PSW_INXF_MASK | PSW_TF_MASK | PSW_IF_MASK ); psw = __cpu_get_psw(); clear_instruction( &instruction, CPU_OP_NOP ); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "NOP return code" ); CHECK( __cpu_get_psw() == psw, "NOP preserves PSW" ); /* MOV is a raw physical-register copy and accepts overlap. */ { __mpu_UNIT_t *src = __cpu_reg_ptr( 128, 0x00100 ); __mpu_UNIT_t *dst = __cpu_reg_ptr( 128, 0x00110 ); unsigned int i; CHECK( src != 0 && dst != 0, "MOV register pointers" ); for( i = 0; i < 16; ++i ) src[i] = (__mpu_UNIT_t)( 0x20 + i ); memset( dst, 0, 16 ); clear_instruction( &instruction, CPU_OP_MOV ); instruction.operand[0] = reg_operand( 128, 0x00110 ); instruction.operand[1] = reg_operand( 128, 0x00100 ); psw = __cpu_get_psw(); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "MOV return code" ); CHECK( memcmp( dst, src, 16 ) == 0, "MOV register contents" ); CHECK( __cpu_get_psw() == psw, "MOV preserves PSW" ); /* Overlap must have memmove semantics. */ for( i = 0; i < 32; ++i ) __cpu_registers[0x00200 + i] = (__mpu_UNIT_t)i; clear_instruction( &instruction, CPU_OP_MOV ); instruction.operand[0] = reg_operand( 128, 0x00210 ); instruction.operand[1] = reg_operand( 128, 0x00200 ); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "overlapping MOV return code" ); for( i = 0; i < 16; ++i ) CHECK( __cpu_registers[0x00210 + i] == (__mpu_UNIT_t)i, "overlapping MOV contents" ); } /* CLR clears physical storage and leaves PSW unchanged. */ { __mpu_UNIT_t *dst = __cpu_reg_ptr( 256, 0x00300 ); unsigned int i; CHECK( dst != 0, "CLR register pointer" ); memset( dst, 0xa5, 32 ); clear_instruction( &instruction, CPU_OP_CLR ); instruction.operand[0] = reg_operand( 256, 0x00300 ); psw = __cpu_get_psw(); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "CLR return code" ); for( i = 0; i < 32; ++i ) CHECK( dst[i] == 0, "CLR register contents" ); CHECK( __cpu_get_psw() == psw, "CLR preserves PSW" ); } /* IADD delegates arithmetic and integer FLAGS to LibMPU. */ { __mpu_UNIT_t *dst = __cpu_reg_ptr( 8, 0x00400 ); __mpu_UNIT_t *a = __cpu_reg_ptr( 8, 0x00401 ); __mpu_UNIT_t *b = __cpu_reg_ptr( 8, 0x00402 ); *a = 0xff; *b = 0x01; *dst = 0x5a; __cpu_stt(); __cpu_sti(); CHECK( __cpu_stmode( 2 ) == 0, "set CPU status before IADD" ); clear_instruction( &instruction, CPU_OP_IADD ); instruction.operand[0] = reg_operand( 8, 0x00400 ); instruction.operand[1] = reg_operand( 8, 0x00401 ); instruction.operand[2] = reg_operand( 8, 0x00402 ); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "IADD return code" ); CHECK( *dst == 0x00, "IADD result" ); CHECK( __mpu_gtc() == 1, "IADD carry flag" ); CHECK( __mpu_gtz() == 1, "IADD zero flag" ); CHECK( __cpu_gtt() == 1, "IADD preserves TF" ); CHECK( __cpu_gti() == 1, "IADD preserves IF" ); CHECK( __cpu_gtmode() == 2, "IADD preserves MODE" ); } /* IADC consumes LibMPU carry from the preceding operation. */ { __mpu_UNIT_t *dst = __cpu_reg_ptr( 8, 0x00403 ); __mpu_UNIT_t *a = __cpu_reg_ptr( 8, 0x00404 ); __mpu_UNIT_t *b = __cpu_reg_ptr( 8, 0x00405 ); *a = 1; *b = 2; *dst = 0; __mpu_stc(); clear_instruction( &instruction, CPU_OP_IADC ); instruction.operand[0] = reg_operand( 8, 0x00403 ); instruction.operand[1] = reg_operand( 8, 0x00404 ); instruction.operand[2] = reg_operand( 8, 0x00405 ); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "IADC return code" ); CHECK( *dst == 4, "IADC result with carry" ); } /* ISUB and ISBB use the same three-register representation. */ { __mpu_UNIT_t *dst = __cpu_reg_ptr( 8, 0x00406 ); __mpu_UNIT_t *a = __cpu_reg_ptr( 8, 0x00407 ); __mpu_UNIT_t *b = __cpu_reg_ptr( 8, 0x00408 ); *a = 9; *b = 3; clear_instruction( &instruction, CPU_OP_ISUB ); instruction.operand[0] = reg_operand( 8, 0x00406 ); instruction.operand[1] = reg_operand( 8, 0x00407 ); instruction.operand[2] = reg_operand( 8, 0x00408 ); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "ISUB return code" ); CHECK( *dst == 6, "ISUB result" ); *a = 9; *b = 3; __mpu_stc(); clear_instruction( &instruction, CPU_OP_ISBB ); instruction.operand[0] = reg_operand( 8, 0x00406 ); instruction.operand[1] = reg_operand( 8, 0x00407 ); instruction.operand[2] = reg_operand( 8, 0x00408 ); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "ISBB return code" ); CHECK( *dst == 5, "ISBB result with borrow" ); } /* Operand validation belongs to the common executor. */ clear_instruction( &instruction, CPU_OP_MOV ); instruction.operand[0] = reg_operand( 16, 0x00500 ); instruction.operand[1] = reg_operand( 32, 0x00504 ); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_WIDTH_FAULT, "MOV width mismatch" ); clear_instruction( &instruction, CPU_OP_IADD ); instruction.operand[0] = reg_operand( 32, 0x00501 ); /* unaligned */ instruction.operand[1] = reg_operand( 32, 0x00504 ); instruction.operand[2] = reg_operand( 32, 0x00508 ); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_REGISTER_FAULT, "invalid register operand" ); clear_instruction( &instruction, CPU_OP_CLR ); instruction.operand[0].type = CPU_OPERAND_IMMEDIATE; CHECK( __cpu_exec( &instruction ) == CPU_EXEC_OPERAND_FAULT, "unsupported operand type" ); clear_instruction( &instruction, (cpu_opcode)0x7fff ); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_OPCODE_FAULT, "unknown opcode" ); CHECK( __cpu_exec( (const cpu_instruction *)0 ) == CPU_EXEC_INSTRUCTION_FAULT, "NULL instruction" ); /* HALT sets only the CPU-owned HF part of PSW. */ __cpu_set_psw( PSW_CF_MASK | PSW_INXF_MASK | PSW_IF_MASK ); clear_instruction( &instruction, CPU_OP_HALT ); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "HALT return code" ); psw = __cpu_get_psw(); CHECK( (psw & PSW_HF_MASK) != 0, "HALT sets HF" ); CHECK( (psw & PSW_CF_MASK) != 0, "HALT preserves CF" ); CHECK( (psw & PSW_INXF_MASK) != 0, "HALT preserves INXF" ); CHECK( (psw & PSW_IF_MASK) != 0, "HALT preserves IF" ); if( failures != 0 ) { fprintf( stderr, "libcpu instruction regression: %d failure(s)\n", failures ); return( 1 ); } printf( "libcpu 0.0.3 decoded instruction/executor regression: PASS\n" ); printf( "opcodes: NOP HALT MOV CLR IADD IADC ISUB ISBB\n" ); printf( "binary encoding and addressing modes are not defined yet\n" ); return( 0 ); }