summaryrefslogtreecommitdiff
path: root/tests/tst-block-copy.c
diff options
context:
space:
mode:
authorkx <kx@radix-linux.su>2026-10-09 18:00:15 +0300
committerkx <kx@radix-linux.su>2026-10-09 18:00:15 +0300
commit065da3509019b15dd0e678072e6be629e5330bef (patch)
tree69c934c9e786ac048b4fbe8f4c4c5571661521f4 /tests/tst-block-copy.c
parentd130fe27fa753be02ed76fd9b277aa3c3d928eb9 (diff)
downloadlibmcpu-0.4.0.tar.xz
Version 0.4.00.4.0
Diffstat (limited to 'tests/tst-block-copy.c')
-rw-r--r--tests/tst-block-copy.c171
1 files changed, 171 insertions, 0 deletions
diff --git a/tests/tst-block-copy.c b/tests/tst-block-copy.c
new file mode 100644
index 0000000..463633b
--- /dev/null
+++ b/tests/tst-block-copy.c
@@ -0,0 +1,171 @@
+#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 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;
+}
+
+static void
+set_reg_u64( unsigned address, __mpu_uint64_t v )
+{
+ memcpy( __cpu_reg_ptr( 64, address ), &v, sizeof( v ) );
+}
+
+static __mpu_uint64_t
+get_reg_u64( unsigned address )
+{
+ __mpu_uint64_t v = 0;
+ memcpy( &v, __cpu_reg_ptr( 64, address ), sizeof( v ) );
+ return( v );
+}
+
+int
+main( void )
+{
+ cpu_asm_image im;
+ cpu_instruction i;
+ __mpu_uint64_t p;
+ __mpu_uint32_t psw;
+ __mpu_UNIT_t before[32];
+ unsigned k;
+ char err[256];
+ static const char src[]=
+ ".data\n"
+ ".align 8\n"
+ "v:\n"
+ " .integer 64, 1\n"
+ " .integer 64, 2\n"
+ " .integer 64, 3\n"
+ " .integer 64, 4\n"
+ ".text\n"
+ " LEA r64@0x00000, v\n"
+ " ADJSP -32\n"
+ " LEASP r64@0x00008, 0\n"
+ " BMOVI r64@0x00008, r64@0x00000, 32\n"
+ " LDI r64@0x00010, 16\n"
+ " BMOV r64@0x00008, r64@0x00000, r64@0x00010\n"
+ " HALT\n";
+
+ __cpu_init();
+ CHECK( __cpu_stack_alloc( 128 ) == 0, "stack alloc" );
+ 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" );
+
+ /* Decoder knows both block-copy signatures. */
+ __cpu_pc = 0;
+ CHECK( __cpu_decode( &i ) == CPU_EXEC_SUCCESS, "decode LEA" );
+ __cpu_pc+=i.length;
+ CHECK( __cpu_decode( &i ) == CPU_EXEC_SUCCESS, "decode ADJSP" );
+ __cpu_pc+=i.length;
+ CHECK( __cpu_decode( &i ) == CPU_EXEC_SUCCESS, "decode LEASP" );
+ __cpu_pc+=i.length;
+ CHECK( __cpu_decode( &i ) == CPU_EXEC_SUCCESS, "decode BMOVI" );
+ CHECK( i.opcode == CPU_OP_BMOVI, "BMOVI opcode" );
+ CHECK( i.length == CPU_OPCODE_SIZE + 2 * CPU_REG24_SIZE + CPU_IMM64_SIZE, "BMOVI length" );
+ CHECK( i.operand[2].type == CPU_OPERAND_IMM64 && i.operand[2].value.imm64 == 32, "BMOVI count" );
+
+ __cpu_pc = 0;
+ CHECK( __cpu_run() == CPU_EXEC_SUCCESS, "assembled block-copy program" );
+ CHECK( memcmp( __cpu_stack_ptr() + 96, __cpu_data_ptr() + CPU_DATA_NULL_GUARD_SIZE, 16 ) == 0, "BMOV runtime count DATA->STACK" );
+
+ /* Direct executor: overlapping copy must have memmove semantics. */
+ for( k = 0; k < 32; k++ ) __cpu_stack_ptr()[64 + k] = (__mpu_UNIT_t)k;
+ memcpy( before, __cpu_stack_ptr() + 64, sizeof( before ) );
+ CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_STACK, 68 ) == 0, "make overlap destination" );
+ set_reg_u64( 0x00100, p );
+ CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_STACK, 64 ) == 0, "make overlap source" );
+ set_reg_u64( 0x00108, p );
+ memset( &i, 0, sizeof( i ) );
+ i.opcode = CPU_OP_BMOVI;
+ regop( &i.operand[0], 0x00100 );
+ regop( &i.operand[1], 0x00108 );
+ i.operand[2].type = CPU_OPERAND_IMM64;
+ i.operand[2].value.imm64 = 16;
+ CHECK( __cpu_exec( &i ) == CPU_EXEC_SUCCESS, "BMOVI overlap" );
+ CHECK( memcmp( __cpu_stack_ptr() + 68, before, 16 ) == 0, "BMOVI uses memmove semantics" );
+
+ /* Register-sized form uses an unsigned r64 byte count. */
+ CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_DATA, CPU_DATA_NULL_GUARD_SIZE ) == 0, "make DATA source" );
+ set_reg_u64( 0x00108, p );
+ CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_STACK, 32 ) == 0, "make STACK destination" );
+ set_reg_u64( 0x00100, p );
+ set_reg_u64( 0x00110, 24 );
+ memset( &i, 0, sizeof( i ) );
+ i.opcode = CPU_OP_BMOV;
+ regop( &i.operand[0], 0x00100 );
+ regop( &i.operand[1], 0x00108 );
+ regop( &i.operand[2], 0x00110 );
+ CHECK( __cpu_exec( &i ) == CPU_EXEC_SUCCESS, "BMOV register count" );
+ CHECK( memcmp( __cpu_stack_ptr() + 32, __cpu_data_ptr() + CPU_DATA_NULL_GUARD_SIZE, 24 ) == 0, "BMOV copied runtime byte count" );
+
+ /* Copying from PROGRAM is permitted. */
+ CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_PROGRAM, 0 ) == 0, "make PROGRAM source" );
+ set_reg_u64( 0x00108, p );
+ CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_DATA, CPU_DATA_NULL_GUARD_SIZE ) == 0, "make DATA destination" );
+ set_reg_u64( 0x00100, p );
+ set_reg_u64( 0x00110, 2 );
+ CHECK( __cpu_exec( &i ) == CPU_EXEC_SUCCESS, "BMOV PROGRAM->DATA" );
+ CHECK( __cpu_data_ptr()[CPU_DATA_NULL_GUARD_SIZE] == __cpu_program_ptr()[0] && __cpu_data_ptr()[CPU_DATA_NULL_GUARD_SIZE + 1] == __cpu_program_ptr()[1], "PROGRAM source bytes copied" );
+
+ /* PROGRAM is read-only as a destination; destination memory is unchanged. */
+ memcpy( before, __cpu_program_ptr(), 16 );
+ CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_PROGRAM, 0 ) == 0, "make PROGRAM destination" );
+ set_reg_u64( 0x00100, p );
+ CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_STACK, 32 ) == 0, "make STACK source" );
+ set_reg_u64( 0x00108, p );
+ set_reg_u64( 0x00110, 8 );
+ CHECK( __cpu_exec( &i ) == CPU_EXEC_MEMORY_FAULT, "BMOV rejects PROGRAM destination" );
+ CHECK( memcmp( before, __cpu_program_ptr(), 16 ) == 0, "PROGRAM unchanged on destination fault" );
+
+ /* Out-of-range source is detected before any destination bytes change. */
+ memset( __cpu_stack_ptr() + 16, 0xa5, 16 );
+ memcpy( before, __cpu_stack_ptr() + 16, 16 );
+ CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_STACK, 16 ) == 0, "make atomic destination" );
+ set_reg_u64( 0x00100, p );
+ CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_DATA, __cpu_data_size() - 4 ) == 0, "make short source range" );
+ set_reg_u64( 0x00108, p );
+ set_reg_u64( 0x00110, 8 );
+ CHECK( __cpu_exec( &i ) == CPU_EXEC_MEMORY_FAULT, "BMOV source range fault" );
+ CHECK( memcmp( before, __cpu_stack_ptr() + 16, 16 ) == 0, "destination unchanged on source fault" );
+
+ /* Reserved segment fails as a pointer fault. */
+ CHECK( __cpu_ptr_make( &p, 0x0f, 0 ) == 0, "make reserved pointer" );
+ set_reg_u64( 0x00108, p );
+ set_reg_u64( 0x00110, 1 );
+ CHECK( __cpu_exec( &i ) == CPU_EXEC_POINTER_FAULT, "BMOV reserved source segment" );
+
+ /* PSW is not modified. */
+ CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_DATA, CPU_DATA_NULL_GUARD_SIZE ) == 0, "make PSW source" );
+ set_reg_u64( 0x00108, p );
+ CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_STACK, 0 ) == 0, "make PSW destination" );
+ set_reg_u64( 0x00100, p );
+ set_reg_u64( 0x00110, 1 );
+ __cpu_set_psw( PSW_CF_MASK | PSW_IF_MASK );
+ psw = __cpu_get_psw();
+ CHECK( __cpu_exec( &i ) == CPU_EXEC_SUCCESS, "BMOV PSW test" );
+ CHECK( __cpu_get_psw() == psw, "BMOV preserves PSW" );
+
+ /* Zero length is a valid no-op at one-past offsets of allocated segments. */
+ CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_DATA, __cpu_data_size() ) == 0, "make DATA one-past" );
+ set_reg_u64( 0x00108, p );
+ CHECK( __cpu_ptr_make( &p, CPU_PTR_SEG_STACK, __cpu_stack_size() ) == 0, "make STACK one-past" );
+ set_reg_u64( 0x00100, p );
+ set_reg_u64( 0x00110, 0 );
+ CHECK( __cpu_exec( &i ) == CPU_EXEC_SUCCESS, "BMOV zero byte no-op" );
+ CHECK( get_reg_u64( 0x00110 ) == 0, "zero count register retained" );
+
+ cpu_asm_image_free( &im );
+ __cpu_free_context();
+ puts( "libcpu block-copy regression: PASS" );
+ return( 0 );
+}