#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "../utils/mcpu-asm.h" static int failures = 0; #define CHECK(cond,msg) \ do { \ if( !(cond) ) \ { \ fprintf( stderr, "FAIL: %s\n", (msg) ); \ ++failures; \ } \ } while( 0 ) static void clear_instruction( cpu_instruction *instruction, cpu_opcode opcode ) { memset( instruction, 0, sizeof(*instruction) ); instruction->opcode = opcode; } static cpu_operand psw_operand( unsigned int bit ) { cpu_operand operand; memset( &operand, 0, sizeof(operand) ); operand.type = CPU_OPERAND_PSWBIT8; operand.value.pswbit8 = bit; return( operand ); } 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 check_assembler_contract( void ) { static const char good_source[] = ".text\n" "SETF CF\n" "CLRF AF\n" "CLRCPUF\n" "CLRMPUF\n" "HALT\n"; static const char *bad_source[] = { ".text\nSETF HF\n", ".text\nSETF TF\n", ".text\nSETF IF\n", ".text\nCLRF HF\n", ".text\nCLRF TF\n", ".text\nCLRF IF\n" }; cpu_asm_image image; cpu_instruction instruction; char err[256]; __mpu_uint64_t pc = 0; unsigned int i; memset( &image, 0, sizeof(image) ); CHECK( cpu_assemble_text( good_source, &image, err, sizeof(err) ) == 0, "assemble writable PSW flag instructions" ); if( image.code ) { CHECK( __cpu_decode_at( image.code, image.code_size, pc, &instruction ) == CPU_EXEC_SUCCESS, "decode SETF" ); CHECK( instruction.opcode == CPU_OP_SETF, "SETF opcode" ); CHECK( instruction.operand[0].type == CPU_OPERAND_PSWBIT8 && instruction.operand[0].value.pswbit8 == CPU_PSWBIT_CF, "SETF selector" ); pc += instruction.length; CHECK( __cpu_decode_at( image.code, image.code_size, pc, &instruction ) == CPU_EXEC_SUCCESS, "decode CLRF" ); CHECK( instruction.opcode == CPU_OP_CLRF, "CLRF opcode" ); CHECK( instruction.operand[0].value.pswbit8 == CPU_PSWBIT_AF, "CLRF selector" ); pc += instruction.length; CHECK( __cpu_decode_at( image.code, image.code_size, pc, &instruction ) == CPU_EXEC_SUCCESS, "decode CLRCPUF" ); CHECK( instruction.opcode == CPU_OP_CLRCPUF, "CLRCPUF opcode" ); pc += instruction.length; CHECK( __cpu_decode_at( image.code, image.code_size, pc, &instruction ) == CPU_EXEC_SUCCESS, "decode CLRMPUF" ); CHECK( instruction.opcode == CPU_OP_CLRMPUF, "CLRMPUF opcode" ); } cpu_asm_image_free( &image ); for( i = 0; i < sizeof(bad_source) / sizeof(bad_source[0]); ++i ) { memset( &image, 0, sizeof(image) ); CHECK( cpu_assemble_text( bad_source[i], &image, err, sizeof(err) ) != 0, "assembler rejects control/execution PSW selector write" ); cpu_asm_image_free( &image ); } } int main( void ) { cpu_instruction instruction; __mpu_uint32_t psw, expected; unsigned int bit; __cpu_init(); CHECK( CPU_OP_SETF == 0x0010, "SETF OPCODE16" ); CHECK( CPU_OP_CLRF == 0x0011, "CLRF OPCODE16" ); CHECK( CPU_OP_CLRCPUF == 0x0012, "CLRCPUF OPCODE16" ); CHECK( CPU_OP_CLRMPUF == 0x0013, "CLRMPUF OPCODE16" ); CHECK( PSW_CPUF_MASK == __mpu_UINT32_C(0x000000ff), "CPU flag mask" ); CHECK( PSW_MPUF_MASK == __mpu_UINT32_C(0x0000ff00), "MPU flag mask" ); /* SETF/CLRF write exactly one arithmetic/status flag in PSW[15:0]. */ for( bit = CPU_PSWBIT_AF; bit <= CPU_PSWBIT_WRITE_MAX; ++bit ) { __mpu_uint32_t mask = (__mpu_uint32_t)1U << bit; __cpu_set_psw( PSW_TF_MASK | PSW_IF_MASK | (__mpu_uint32_t)(2U << PSW_MODE_SHIFT) ); psw = __cpu_get_psw(); clear_instruction( &instruction, CPU_OP_SETF ); instruction.operand[0] = psw_operand( bit ); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "SETF return code" ); CHECK( __cpu_get_psw() == ( psw | mask ), "SETF changes only selected flag" ); psw = __cpu_get_psw(); clear_instruction( &instruction, CPU_OP_CLRF ); instruction.operand[0] = psw_operand( bit ); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "CLRF return code" ); CHECK( __cpu_get_psw() == ( psw & ~mask ), "CLRF changes only selected flag" ); } /* HF/TF/IF are readable by branches but are not writable through SETF/CLRF. */ for( bit = CPU_PSWBIT_HF; bit <= CPU_PSWBIT_IF; ++bit ) { clear_instruction( &instruction, CPU_OP_SETF ); instruction.operand[0] = psw_operand( bit ); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_OPERAND_FAULT, "SETF rejects HF/TF/IF" ); clear_instruction( &instruction, CPU_OP_CLRF ); instruction.operand[0] = psw_operand( bit ); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_OPERAND_FAULT, "CLRF rejects HF/TF/IF" ); } /* CLRCPUF clears only PSW[7:0]. */ __cpu_set_psw( PSW_USED_MASK ); psw = __cpu_get_psw(); expected = psw & ~PSW_CPUF_MASK; clear_instruction( &instruction, CPU_OP_CLRCPUF ); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "CLRCPUF return code" ); CHECK( __cpu_get_psw() == expected, "CLRCPUF preserves PSW[15:8] and control state" ); /* CLRMPUF clears only PSW[15:8]. */ __cpu_set_psw( PSW_USED_MASK ); psw = __cpu_get_psw(); expected = psw & ~PSW_MPUF_MASK; clear_instruction( &instruction, CPU_OP_CLRMPUF ); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "CLRMPUF return code" ); CHECK( __cpu_get_psw() == expected, "CLRMPUF preserves PSW[7:0] and control state" ); /* SETF/CLRF provide an explicit initial carry for LibMPU IADC. */ { __mpu_UNIT_t *dst = __cpu_reg_ptr( 8, 0x00600 ); __mpu_UNIT_t *a = __cpu_reg_ptr( 8, 0x00601 ); __mpu_UNIT_t *b = __cpu_reg_ptr( 8, 0x00602 ); *a = 1; *b = 2; clear_instruction( &instruction, CPU_OP_SETF ); instruction.operand[0] = psw_operand( CPU_PSWBIT_CF ); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "SETF CF before IADC" ); clear_instruction( &instruction, CPU_OP_IADC ); instruction.operand[0] = reg_operand( 8, 0x00600 ); instruction.operand[1] = reg_operand( 8, 0x00601 ); instruction.operand[2] = reg_operand( 8, 0x00602 ); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "IADC after SETF CF" ); CHECK( *dst == 4, "IADC consumes SETF CF" ); clear_instruction( &instruction, CPU_OP_CLRF ); instruction.operand[0] = psw_operand( CPU_PSWBIT_CF ); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "CLRF CF before IADC" ); clear_instruction( &instruction, CPU_OP_IADC ); instruction.operand[0] = reg_operand( 8, 0x00600 ); instruction.operand[1] = reg_operand( 8, 0x00601 ); instruction.operand[2] = reg_operand( 8, 0x00602 ); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "IADC after CLRF CF" ); CHECK( *dst == 3, "IADC consumes cleared CF" ); } /* SETF CF likewise provides the input borrow consumed by LibMPU ISBB. */ { __mpu_UNIT_t *dst = __cpu_reg_ptr( 8, 0x00610 ); __mpu_UNIT_t *a = __cpu_reg_ptr( 8, 0x00611 ); __mpu_UNIT_t *b = __cpu_reg_ptr( 8, 0x00612 ); *a = 9; *b = 3; clear_instruction( &instruction, CPU_OP_SETF ); instruction.operand[0] = psw_operand( CPU_PSWBIT_CF ); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "SETF CF before ISBB" ); clear_instruction( &instruction, CPU_OP_ISBB ); instruction.operand[0] = reg_operand( 8, 0x00610 ); instruction.operand[1] = reg_operand( 8, 0x00611 ); instruction.operand[2] = reg_operand( 8, 0x00612 ); CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "ISBB after SETF CF" ); CHECK( *dst == 5, "ISBB consumes SETF CF" ); } check_assembler_contract(); __cpu_free_context(); if( failures ) { fprintf( stderr, "LibMCPU PSW write regression: %d failure(s)\n", failures ); return( 1 ); } puts( "LibMCPU PSW write regression: PASS" ); return( 0 ); }