#include #include #include #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 ); }