1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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 );
}
|