summaryrefslogtreecommitdiff
path: root/tests/tst-pointer-arithmetic.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/tst-pointer-arithmetic.c')
-rw-r--r--tests/tst-pointer-arithmetic.c170
1 files changed, 170 insertions, 0 deletions
diff --git a/tests/tst-pointer-arithmetic.c b/tests/tst-pointer-arithmetic.c
new file mode 100644
index 0000000..76414fa
--- /dev/null
+++ b/tests/tst-pointer-arithmetic.c
@@ -0,0 +1,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 );
+}