#ifdef HAVE_CONFIG_H #include #endif #include #include #include #define CHECK(c,m) do { if(!(c)) { fprintf(stderr,"FAIL: %s\n",(m)); return 1; } } while(0) static cpu_operand reg_operand( unsigned int bits, __mpu_uint32_t address ) { cpu_operand o; memset( &o, 0, sizeof( o ) ); o.type = CPU_OPERAND_REGISTER; o.value.reg.bits = bits; o.value.reg.address = address; return( o ); } static void put_u64_reg( unsigned int address, __mpu_uint64_t value ) { memcpy( __cpu_reg_ptr( 64, address ), &value, sizeof( value ) ); } int main( void ) { cpu_instruction i; __mpu_uint32_t before, after, control_before; __mpu_uint64_t target, ret; __mpu_UNIT_t *a, *b, *d; __cpu_init(); CHECK( CPU_PSW_ABI_VOLATILE_STATUS_MASK == PSW_MPU_MASK, "volatile PSW domain is exactly PSW[15:0]"); CHECK( CPU_PSW_ABI_PERSISTENT_CONTROL_MASK == ( PSW_TF_MASK | PSW_IF_MASK | PSW_MODE_MASK ), "persistent PSW domain is TF/IF/MODE"); CHECK( CPU_PSW_ABI_EXECUTION_STATE_MASK == PSW_HF_MASK, "HF is the execution-state PSW domain"); CHECK( ( CPU_PSW_ABI_VOLATILE_STATUS_MASK & CPU_PSW_ABI_PERSISTENT_CONTROL_MASK)==0, "volatile and persistent PSW domains do not overlap"); CHECK( ( CPU_PSW_ABI_VOLATILE_STATUS_MASK | CPU_PSW_ABI_PERSISTENT_CONTROL_MASK | CPU_PSW_ABI_EXECUTION_STATE_MASK)==PSW_USED_MASK, "PSW ABI domains cover all currently used PSW bits"); CHECK( __cpu_program_alloc( 64 ) == 0, "PROGRAM allocation" ); CHECK( __cpu_stack_alloc( 128 ) == 0, "STACK allocation" ); /* CALL/RET are instructions, not implicit PSW save/restore operations. Their own execution must leave every currently used PSW bit untouched. */ before = ( PSW_AF_MASK | PSW_CF_MASK | PSW_ZF_MASK | PSW_INXF_MASK | PSW_HF_MASK | PSW_TF_MASK | PSW_IF_MASK | ( (__mpu_uint32_t)2U << PSW_MODE_SHIFT )); __cpu_set_psw( before ); before = __cpu_get_psw(); memset( &i, 0, sizeof( i ) ); i.opcode = CPU_OP_CALL; i.operand[0].type = CPU_OPERAND_CODE64; i.operand[0].value.code64 = 32; __cpu_pc = 4; __cpu_sp = 128; CHECK( __cpu_exec( &i ) == CPU_EXEC_BRANCH, "CALL executes" ); CHECK( __cpu_get_psw() == before, "CALL itself is PSW-neutral" ); CHECK( __cpu_sp == 120, "CALL pushes return PC" ); memcpy( &ret, __cpu_stack_ptr() + 120, sizeof( ret ) ); CHECK( ret == 14, "CALL raw return PC" ); memset( &i, 0, sizeof( i ) ); i.opcode = CPU_OP_RET; CHECK( __cpu_exec( &i ) == CPU_EXEC_BRANCH, "RET executes" ); CHECK( __cpu_get_psw() == before, "RET itself is PSW-neutral" ); CHECK( __cpu_pc == 14 && __cpu_sp == 128, "RET restores PC/SP call state" ); /* CALLP has the same PSW-neutral instruction semantics. */ CHECK( __cpu_ptr_make( &target, CPU_PTR_SEG_PROGRAM, 40 ) == 0, "make PROGRAM PTR64"); put_u64_reg( 0x00100, target ); memset( &i, 0, sizeof( i ) ); i.opcode = CPU_OP_CALLP; i.operand[0] = reg_operand( 64, 0x00100 ); __cpu_pc = 8; CHECK( __cpu_exec( &i ) == CPU_EXEC_BRANCH, "CALLP executes" ); CHECK( __cpu_get_psw() == before, "CALLP itself is PSW-neutral" ); memset( &i, 0, sizeof( i ) ); i.opcode = CPU_OP_RET; CHECK( __cpu_exec( &i ) == CPU_EXEC_BRANCH, "RET after CALLP executes" ); CHECK( __cpu_get_psw() == before, "RET after CALLP is PSW-neutral" ); CHECK( __cpu_pc == 13 && __cpu_sp == 128, "CALLP/RET restore PC/SP call state" ); /* Arithmetic status is caller-clobbered, while TF/IF/MODE are persistent control state. IADD is sufficient to demonstrate both domains. */ a = __cpu_reg_ptr( 8, 0x00200 ); b = __cpu_reg_ptr( 8, 0x00201 ); d = __cpu_reg_ptr( 8, 0x00202 ); CHECK( a && b && d, "IADD register views" ); *a = 1; *b = 1; *d = 0; __cpu_set_psw( PSW_CF_MASK | PSW_ZF_MASK | PSW_INXF_MASK | PSW_TF_MASK | PSW_IF_MASK | ( (__mpu_uint32_t)3U << PSW_MODE_SHIFT )); before = __cpu_get_psw(); control_before = before & CPU_PSW_ABI_PERSISTENT_CONTROL_MASK; memset( &i, 0, sizeof( i ) ); i.opcode = CPU_OP_IADD; i.operand[0] = reg_operand( 8, 0x00202 ); i.operand[1] = reg_operand( 8, 0x00200 ); i.operand[2] = reg_operand( 8, 0x00201 ); CHECK( __cpu_exec( &i ) == CPU_EXEC_SUCCESS, "IADD executes" ); after = __cpu_get_psw(); CHECK( *d == 2, "IADD result" ); CHECK( ( after & CPU_PSW_ABI_PERSISTENT_CONTROL_MASK ) == control_before, "ordinary arithmetic preserves TF/IF/MODE control domain"); CHECK( ( after & CPU_PSW_ABI_VOLATILE_STATUS_MASK ) != ( before & CPU_PSW_ABI_VOLATILE_STATUS_MASK ), "arithmetic may clobber volatile PSW[15:0]"); __cpu_free_context(); puts( "libcpu PSW calling-convention regression: PASS" ); return( 0 ); }