diff options
| author | kx <kx@radix-linux.su> | 2026-10-09 18:00:15 +0300 |
|---|---|---|
| committer | kx <kx@radix-linux.su> | 2026-10-09 18:00:15 +0300 |
| commit | 065da3509019b15dd0e678072e6be629e5330bef (patch) | |
| tree | 69c934c9e786ac048b4fbe8f4c4c5571661521f4 /tests/tst-pointer-arithmetic.c | |
| parent | d130fe27fa753be02ed76fd9b277aa3c3d928eb9 (diff) | |
| download | libmcpu-0.4.0.tar.xz | |
Version 0.4.00.4.0
Diffstat (limited to 'tests/tst-pointer-arithmetic.c')
| -rw-r--r-- | tests/tst-pointer-arithmetic.c | 170 |
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 ); +} |
