summaryrefslogtreecommitdiff
path: root/tests/tst-frame.c
blob: b94b51e5182f43ad0002558caf782b44875adf72 (plain)
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#include <stdio.h>
#include <string.h>
#include <libmcpu.h>
#include "../utils/mcpu-asm.h"

#define CHECK(c,m) do{if(!(c)){fprintf(stderr,"FAIL: %s\n",m);return 1;}}while(0)

static const char src[]=
".text\n"
"        LDI     r64@0x00000, 5\n"
"        ADJSP   -16\n"
"        STSP    0, r64@0x00000\n"
"        CALL    demo\n"
"        ADJSP   16\n"
"        MOV     r64@0x00008, r64@0x7fff8\n"
"        HALT\n"
"demo:\n"
"        PUSHFP\n"
"        SETFP\n"
"        ADJSP   -16\n"
"        LDFP    r64@0x00100, 16\n"
"        STFP    -8, r64@0x00100\n"
"        LEAFP   r64@0x00108, -8\n"
"        LEASP   r64@0x00110, 0\n"
"        LDI     r64@0x00118, 24\n"
"        PSUB    r64@0x00120, r64@0x00110, r64@0x00118\n"
"        SETSP   r64@0x00120\n"
"        LDI     r64@0x00128, 37\n"
"        LEASP   r64@0x00130, 0\n"
"        STOREP  r64@0x00130, r64@0x00128\n"
"        LDFP    r64@0x00138, -8\n"
"        LOADP   r64@0x00140, r64@0x00130\n"
"        IADD    r64@0x7fff8, r64@0x00138, r64@0x00140\n"
"        SETSP   r64@0x00110\n"
"        RESTSP\n"
"        POPFP\n"
"        RET\n";

static __mpu_uint64_t
reg_u64( unsigned address )
{
  __mpu_uint64_t v = 0;
  memcpy( &v, __cpu_reg_ptr( 64, address ), sizeof( v ) );
  return( v );
}

static __mpu_int64_t
reg_i64( unsigned address )
{
  __mpu_int64_t v = 0;
  memcpy( &v, __cpu_reg_ptr( 64, address ), sizeof( v ) );
  return( v );
}

static void
set_reg_u64( unsigned address, __mpu_uint64_t v )
{
  memcpy( __cpu_reg_ptr( 64, address ), &v, sizeof( v ) );
}

static void
regop( cpu_operand *o, unsigned address )
{
  memset( o, 0, sizeof( *o ) );
  o->type = CPU_OPERAND_REGISTER;
  o->value.reg.bits = 64;
  o->value.reg.address = address;
}

int
main( void )
{
  cpu_asm_image im;
  cpu_instruction i;
  __mpu_uint64_t ptr, saved_sp, saved_fp, badfp;
  __mpu_uint32_t psw;
  char err[256];

  __cpu_init();
  CHECK( __cpu_stack_alloc( 256 ) == 0, "stack alloc" );
  CHECK( __cpu_sp == 256, "empty stack SP" );
  CHECK( __cpu_fp == 256, "empty stack FP" );

  CHECK( cpu_assemble_text( src, &im, err, sizeof( err ) ) == 0, err );
  CHECK( __cpu_program_set( im.code, im.code_size ) == 0, "program set" );
  __cpu_pc = 0;
  __cpu_set_psw( PSW_CF_MASK | PSW_IF_MASK );
  psw = __cpu_get_psw();
  CHECK( __cpu_run() == CPU_EXEC_SUCCESS, "frame/dynamic stack program run" );
  CHECK( reg_i64( 0x00008 ) == 42, "frame example result" );
  CHECK( reg_i64( 0x7fff8 ) == 42, "canonical return value" );
  CHECK( __cpu_sp == 256, "caller cleanup restored SP" );
  CHECK( __cpu_fp == 256, "callee restored caller FP" );
  cpu_asm_image_free( &im );

  /* LEAFP is based on FP, not on the moving SP. */
  __cpu_sp = 160;
  __cpu_fp = 200;
  memset( &i, 0, sizeof( i ) );
  i.opcode = CPU_OP_LEAFP;
  regop( &i.operand[0], 0x00200 );
  i.operand[1].type = CPU_OPERAND_OFF64;
  i.operand[1].value.off64 = -24;
  __cpu_set_psw( PSW_CF_MASK | PSW_IF_MASK );
  psw = __cpu_get_psw();
  CHECK( __cpu_exec( &i ) == CPU_EXEC_SUCCESS, "LEAFP direct execution" );
  CHECK( __cpu_get_psw() == psw, "LEAFP preserves PSW" );
  ptr = reg_u64( 0x00200 );
  CHECK( __cpu_ptr_segment( ptr ) == CPU_PTR_SEG_STACK, "LEAFP STACK segment" );
  CHECK( __cpu_ptr_offset( ptr ) == 176, "LEAFP uses FP base" );
  CHECK( __cpu_sp == 160, "LEAFP leaves SP unchanged" );
  CHECK( __cpu_fp == 200, "LEAFP leaves FP unchanged" );

  /* SETSP accepts only a STACK PTR64 and is atomic on faults. */
  CHECK( __cpu_ptr_make( &ptr, CPU_PTR_SEG_STACK, 144 ) == 0, "make SETSP STACK ptr" );
  set_reg_u64( 0x00208, ptr );
  memset( &i, 0, sizeof( i ) );
  i.opcode = CPU_OP_SETSP;
  regop( &i.operand[0], 0x00208 );
  CHECK( __cpu_exec( &i ) == CPU_EXEC_SUCCESS, "SETSP valid STACK ptr" );
  CHECK( __cpu_sp == 144, "SETSP installs offset" );
  CHECK( __cpu_fp == 200, "SETSP preserves FP" );

  saved_sp = __cpu_sp;
  CHECK( __cpu_ptr_make( &ptr, CPU_PTR_SEG_DATA, 10 ) == 0, "make DATA ptr" );
  set_reg_u64( 0x00208, ptr );
  CHECK( __cpu_exec( &i ) == CPU_EXEC_POINTER_FAULT, "SETSP rejects DATA ptr" );
  CHECK( __cpu_sp == saved_sp, "SETSP pointer fault atomic" );

  CHECK( __cpu_ptr_make( &ptr, CPU_PTR_SEG_STACK, 300 ) == 0, "make out-of-range STACK ptr" );
  set_reg_u64( 0x00208, ptr );
  CHECK( __cpu_exec( &i ) == CPU_EXEC_STACK_FAULT, "SETSP checks allocated stack size" );
  CHECK( __cpu_sp == saved_sp, "SETSP stack fault atomic" );

  /* PUSHFP/POPFP preserve exact previous FP and validate a restored FP. */
  __cpu_sp = 128;
  __cpu_fp = 216;
  memset( &i, 0, sizeof( i ) );
  i.opcode = CPU_OP_PUSHFP;
  CHECK( __cpu_exec( &i ) == CPU_EXEC_SUCCESS, "PUSHFP" );
  CHECK( __cpu_sp == 120, "PUSHFP moves SP by eight bytes" );
  memcpy( &saved_fp, __cpu_stack_ptr() + 120, 8 );
  CHECK( saved_fp == 216, "PUSHFP stored FP" );

  memset( &i, 0, sizeof( i ) );
  i.opcode = CPU_OP_SETFP;
  CHECK( __cpu_exec( &i ) == CPU_EXEC_SUCCESS, "SETFP" );
  CHECK( __cpu_fp == 120, "SETFP copies SP" );

  memset( &i, 0, sizeof( i ) );
  i.opcode = CPU_OP_RESTSP;
  __cpu_sp = 80;
  CHECK( __cpu_exec( &i ) == CPU_EXEC_SUCCESS, "RESTSP" );
  CHECK( __cpu_sp == 120, "RESTSP copies FP" );

  memset( &i, 0, sizeof( i ) );
  i.opcode = CPU_OP_POPFP;
  CHECK( __cpu_exec( &i ) == CPU_EXEC_SUCCESS, "POPFP" );
  CHECK( __cpu_fp == 216, "POPFP restores caller FP" );
  CHECK( __cpu_sp == 128, "POPFP advances SP by eight bytes" );
  CHECK( __cpu_get_psw() == psw, "FP/SP management preserves PSW" );

  /* Invalid saved FP must not partially change FP or SP. */
  __cpu_sp = 64;
  __cpu_fp = 100;
  badfp = 257;
  memcpy( __cpu_stack_ptr() + 64, &badfp, 8 );
  saved_sp = __cpu_sp;
  saved_fp = __cpu_fp;
  CHECK( __cpu_exec( &i ) == CPU_EXEC_STACK_FAULT, "POPFP rejects invalid saved FP" );
  CHECK( __cpu_sp == saved_sp, "POPFP fault preserves SP" );
  CHECK( __cpu_fp == saved_fp, "POPFP fault preserves FP" );

  __cpu_free_context();
  CHECK( __cpu_fp == 0, "FP cleared by free context" );
  puts( "libcpu frame-pointer/dynamic STACK regression: PASS" );
  return( 0 );
}