diff options
Diffstat (limited to 'tests/tst-psw-abi.c')
| -rw-r--r-- | tests/tst-psw-abi.c | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/tests/tst-psw-abi.c b/tests/tst-psw-abi.c new file mode 100644 index 0000000..9e34ef9 --- /dev/null +++ b/tests/tst-psw-abi.c @@ -0,0 +1,133 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <string.h> + +#include <libmcpu.h> + +#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 ); +} |
