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
|
#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[]=
".data\n"
".align 8\n"
"values:\n"
" .integer 64, 11\n"
" .integer 64, 22\n"
" .integer 64, 33\n"
".text\n"
" LEA r64@0x00000, values\n"
" PADDI r64@0x00008, r64@0x00000, 16\n"
" LOADP r64@0x00010, r64@0x00008\n"
" PDIFF r64@0x00018, r64@0x00008, r64@0x00000\n"
" PSUBI r64@0x00020, r64@0x00008, 8\n"
" LOADP r64@0x00028, r64@0x00020\n"
" LDI r64@0x00030, 8\n"
" PADD r64@0x00038, r64@0x00000, r64@0x00030\n"
" LOADP r64@0x00040, r64@0x00038\n"
" PSUB r64@0x00048, r64@0x00038, r64@0x00030\n"
" LOADP r64@0x00050, r64@0x00048\n"
" PADDI r64@0x00058, r64@0x00008, -16\n"
" LOADP r64@0x00060, r64@0x00058\n"
" PSUBI r64@0x00068, r64@0x00000, -16\n"
" LOADP r64@0x00070, r64@0x00068\n"
" HALT\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
set_reg_i64( unsigned address, __mpu_int64_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 p, saved, minbits;
__mpu_int64_t mindelta;
__mpu_uint32_t psw;
char err[256];
__cpu_init();
CHECK( cpu_assemble_text( src, &im, err, sizeof( err ) ) == 0, err );
CHECK( __cpu_program_set( im.code, im.code_size ) == 0, "program set" );
CHECK( __cpu_data_set( im.data, im.data_size ) == 0, "data set" );
CHECK( __cpu_run() == CPU_EXEC_SUCCESS, "pointer arithmetic program run" );
CHECK( __cpu_ptr_segment( reg_u64( 0x00008 ) ) == CPU_PTR_SEG_DATA, "PADDI preserves DATA segment" );
CHECK( __cpu_ptr_offset( reg_u64( 0x00008 ) ) == 32, "PADDI offset" );
CHECK( reg_i64( 0x00010 ) == 33, "PADDI + LOADP" );
CHECK( reg_i64( 0x00018 ) == 16, "PDIFF byte difference" );
CHECK( __cpu_ptr_offset( reg_u64( 0x00020 ) ) == 24, "PSUBI offset" );
CHECK( reg_i64( 0x00028 ) == 22, "PSUBI + LOADP" );
CHECK( __cpu_ptr_offset( reg_u64( 0x00038 ) ) == 24, "PADD register displacement" );
CHECK( reg_i64( 0x00040 ) == 22, "PADD + LOADP" );
CHECK( __cpu_ptr_offset( reg_u64( 0x00048 ) ) == 16, "PSUB register displacement" );
CHECK( reg_i64( 0x00050 ) == 11, "PSUB + LOADP" );
CHECK( __cpu_ptr_offset( reg_u64( 0x00058 ) ) == 16, "PADDI negative displacement" );
CHECK( reg_i64( 0x00060 ) == 11, "PADDI negative + LOADP" );
CHECK( __cpu_ptr_offset( reg_u64( 0x00068 ) ) == 32, "PSUBI negative displacement" );
CHECK( reg_i64( 0x00070 ) == 33, "PSUBI negative + LOADP" );
/* Arithmetic is defined on any structurally valid PTR64 segment selector. */
CHECK( __cpu_ptr_make( &p, 0x0f, 100 ) == 0, "make reserved-segment PTR64" );
set_reg_u64( 0x00100, p );
set_reg_i64( 0x00108, 23 );
memset( &i, 0, sizeof( i ) );
i.opcode = CPU_OP_PADD;
regop( &i.operand[0], 0x00110 );
regop( &i.operand[1], 0x00100 );
regop( &i.operand[2], 0x00108 );
CHECK( __cpu_exec( &i ) == CPU_EXEC_SUCCESS, "PADD reserved segment does not dereference" );
p = reg_u64( 0x00110 );
CHECK( __cpu_ptr_segment( p ) == 0x0f, "PADD preserves reserved segment selector" );
CHECK( __cpu_ptr_offset( p ) == 123, "PADD reserved segment offset" );
/* PSW is untouched by all pointer arithmetic instructions. */
__cpu_set_psw( PSW_CF_MASK | PSW_IF_MASK );
psw = __cpu_get_psw();
CHECK( __cpu_exec( &i ) == CPU_EXEC_SUCCESS, "PADD for PSW preservation" );
CHECK( __cpu_get_psw() == psw, "PADD preserves PSW" );
/* 60-bit offset overflow/underflow faults are atomic. */
CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_DATA, CPU_PTR_OFFSET_MASK ) == 0, "make max-offset PTR64" );
set_reg_u64( 0x00100, p );
set_reg_i64( 0x00108, 1 );
saved = __mpu_UINT64_C( 0x1122334455667788 );
set_reg_u64( 0x00110, saved );
CHECK( __cpu_exec( &i ) == CPU_EXEC_POINTER_FAULT, "PADD offset overflow" );
CHECK( reg_u64( 0x00110 ) == saved, "PADD overflow leaves destination unchanged" );
CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_DATA, 0 ) == 0, "make zero-offset PTR64" );
set_reg_u64( 0x00100, p );
set_reg_i64( 0x00108, -1 );
set_reg_u64( 0x00110, saved );
CHECK( __cpu_exec( &i ) == CPU_EXEC_POINTER_FAULT, "PADD offset underflow" );
CHECK( reg_u64( 0x00110 ) == saved, "PADD underflow leaves destination unchanged" );
/* INT64_MIN is handled without signed-overflow undefined behaviour. */
minbits = __mpu_UINT64_C( 0x8000000000000000 );
memcpy( &mindelta, &minbits, sizeof( mindelta ) );
set_reg_i64( 0x00108, mindelta );
CHECK( __cpu_exec( &i ) == CPU_EXEC_POINTER_FAULT, "PADD INT64_MIN displacement faults cleanly" );
/* PDIFF requires equal segment selectors and is also atomic on fault. */
CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_DATA, 64 ) == 0, "make DATA pointer for PDIFF" );
set_reg_u64( 0x00100, p );
CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_STACK, 16 ) == 0, "make STACK pointer for PDIFF" );
set_reg_u64( 0x00108, p );
set_reg_u64( 0x00110, saved );
memset( &i, 0, sizeof( i ) );
i.opcode = CPU_OP_PDIFF;
regop( &i.operand[0], 0x00110 );
regop( &i.operand[1], 0x00100 );
regop( &i.operand[2], 0x00108 );
CHECK( __cpu_exec( &i ) == CPU_EXEC_POINTER_FAULT, "PDIFF rejects different segments" );
CHECK( reg_u64( 0x00110 ) == saved, "PDIFF segment fault leaves destination unchanged" );
/* Same-segment PDIFF may be negative and still fits signed integer64. */
CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_PROGRAM, 10 ) == 0, "make PROGRAM pointer low" );
set_reg_u64( 0x00100, p );
CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_PROGRAM, 42 ) == 0, "make PROGRAM pointer high" );
set_reg_u64( 0x00108, p );
CHECK( __cpu_exec( &i ) == CPU_EXEC_SUCCESS, "PDIFF PROGRAM pointers" );
CHECK( reg_i64( 0x00110 ) == -32, "PDIFF signed negative result" );
cpu_asm_image_free( &im );
__cpu_free_context();
puts( "libcpu PTR64 arithmetic regression: PASS" );
return( 0 );
}
|