summaryrefslogtreecommitdiff
path: root/tests/tst-psw-abi.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/tst-psw-abi.c')
-rw-r--r--tests/tst-psw-abi.c133
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 );
+}