summaryrefslogtreecommitdiff
path: root/tests/tst-stack.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/tst-stack.c')
-rw-r--r--tests/tst-stack.c104
1 files changed, 104 insertions, 0 deletions
diff --git a/tests/tst-stack.c b/tests/tst-stack.c
new file mode 100644
index 0000000..9c719ed
--- /dev/null
+++ b/tests/tst-stack.c
@@ -0,0 +1,104 @@
+#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[]=
+".text\n"
+" LDI r64@0x00000, 11\n"
+" LDI r64@0x00008, 22\n"
+" PUSH r64@0x00000\n"
+" PUSH r64@0x00008\n"
+" LDSP r64@0x00010, 8\n"
+" STSP 0, r64@0x00000\n"
+" POP r64@0x00018\n"
+" POP r64@0x00020\n"
+" ADJSP -16\n"
+" STSP 0, r64@0x00008\n"
+" LDSP r64@0x00028, 0\n"
+" ADJSP 16\n"
+" HALT\n";
+
+static int
+eq64( unsigned address, const char *text )
+{
+ mpu_int64_t x;
+ iatoi( ( mpu_int * ) x, (__mpu_char8_t *)text, NB_I64 );
+ return( !memcmp( __cpu_reg_ptr( 64, address ), x, NB_I64 ) );
+}
+
+int
+main( void )
+{
+ cpu_asm_image im;
+ cpu_instruction i;
+ cpu_operand *o;
+ char err[256];
+ __mpu_uint64_t sp;
+ __mpu_UNIT_t before[8];
+ int rc;
+
+ __cpu_init();
+ CHECK( __cpu_stack_alloc( 128 ) == 0, "allocate STACK" );
+ CHECK( __cpu_stack_ptr() != 0, "STACK pointer" );
+ CHECK( __cpu_stack_size() == 128, "STACK size" );
+ CHECK( __cpu_sp == 128, "empty STACK starts at stack_size" );
+
+ CHECK( cpu_assemble_text( src, &im, err, sizeof( err ) ) == 0, err );
+ CHECK( im.code_size > 0, "stack bytecode produced" );
+ CHECK( __cpu_program_set( im.code, im.code_size ) == 0, "program set" );
+
+ rc = __cpu_run();
+ CHECK( rc == CPU_EXEC_SUCCESS, "stack program run" );
+ CHECK( __cpu_sp == 128, "SP restored after balanced stack operations" );
+ CHECK( eq64( 16, "11" ), "LDSP reads SP+8" );
+ CHECK( eq64( 24, "11" ), "STSP overwrites top before POP" );
+ CHECK( eq64( 32, "11" ), "second POP value" );
+ CHECK( eq64( 40, "22" ), "ADJSP frame with STSP/LDSP" );
+
+ /* PUSH overflow/underflow faults are atomic. */
+ memset( &i, 0, sizeof( i ) );
+ i.opcode = CPU_OP_PUSH;
+ i.operand[0].type = CPU_OPERAND_REGISTER;
+ i.operand[0].value.reg.bits = 64;
+ i.operand[0].value.reg.address = 0;
+ __cpu_sp = 4;
+ memcpy( before, __cpu_stack_ptr(), sizeof( before ) );
+ sp = __cpu_sp;
+ CHECK( __cpu_exec( &i ) == CPU_EXEC_STACK_FAULT, "PUSH stack overflow fault" );
+ CHECK( __cpu_sp == sp, "PUSH fault preserves SP" );
+ CHECK( !memcmp( before, __cpu_stack_ptr(), sizeof( before ) ), "PUSH fault preserves STACK" );
+
+ memset( &i, 0, sizeof( i ) );
+ i.opcode = CPU_OP_POP;
+ o = &i.operand[0];
+ o->type = CPU_OPERAND_REGISTER;
+ o->value.reg.bits = 64;
+ o->value.reg.address = 48;
+ memset( __cpu_reg_ptr( 64, 48 ), 0xa5, 8 );
+ __cpu_sp = 128;
+ sp = __cpu_sp;
+ CHECK( __cpu_exec( &i ) == CPU_EXEC_STACK_FAULT, "POP empty stack fault" );
+ CHECK( __cpu_sp == sp, "POP fault preserves SP" );
+ {
+ __mpu_UNIT_t a5[8];
+ memset( a5, 0xa5, sizeof( a5 ) );
+ CHECK( !memcmp( __cpu_reg_ptr( 64, 48 ), a5, 8 ), "POP fault preserves destination" );
+ }
+
+ memset( &i, 0, sizeof( i ) );
+ i.opcode = CPU_OP_ADJSP;
+ i.operand[0].type = CPU_OPERAND_OFF64;
+ i.operand[0].value.off64 = 1;
+ __cpu_sp = 128;
+ CHECK( __cpu_exec( &i ) == CPU_EXEC_STACK_FAULT, "ADJSP beyond stack end" );
+ CHECK( __cpu_sp == 128, "ADJSP fault preserves SP" );
+
+ cpu_asm_image_free( &im );
+ __cpu_free_context();
+ CHECK( __cpu_sp == 0, "SP cleared by free context" );
+ puts( "libcpu stack/SP regression: PASS" );
+ return( 0 );
+}