summaryrefslogtreecommitdiff
path: root/tests/tst-psw-write.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-psw-write.c
parentd130fe27fa753be02ed76fd9b277aa3c3d928eb9 (diff)
downloadlibmcpu-0.4.0.tar.xz
Version 0.4.00.4.0
Diffstat (limited to 'tests/tst-psw-write.c')
-rw-r--r--tests/tst-psw-write.c255
1 files changed, 255 insertions, 0 deletions
diff --git a/tests/tst-psw-write.c b/tests/tst-psw-write.c
new file mode 100644
index 0000000..0410ec6
--- /dev/null
+++ b/tests/tst-psw-write.c
@@ -0,0 +1,255 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <libmcpu.h>
+#include "../utils/mcpu-asm.h"
+
+
+static int failures = 0;
+
+#define CHECK(cond,msg) \
+ do { \
+ if( !(cond) ) \
+ { \
+ fprintf( stderr, "FAIL: %s\n", (msg) ); \
+ ++failures; \
+ } \
+ } while( 0 )
+
+
+static void
+clear_instruction( cpu_instruction *instruction, cpu_opcode opcode )
+{
+ memset( instruction, 0, sizeof(*instruction) );
+ instruction->opcode = opcode;
+}
+
+
+static cpu_operand
+psw_operand( unsigned int bit )
+{
+ cpu_operand operand;
+
+ memset( &operand, 0, sizeof(operand) );
+ operand.type = CPU_OPERAND_PSWBIT8;
+ operand.value.pswbit8 = bit;
+
+ return( operand );
+}
+
+
+static cpu_operand
+reg_operand( unsigned int bits, __mpu_uint32_t address )
+{
+ cpu_operand operand;
+
+ memset( &operand, 0, sizeof(operand) );
+ operand.type = CPU_OPERAND_REGISTER;
+ operand.value.reg.bits = bits;
+ operand.value.reg.address = address;
+
+ return( operand );
+}
+
+
+static void
+check_assembler_contract( void )
+{
+ static const char good_source[] =
+ ".text\n"
+ "SETF CF\n"
+ "CLRF AF\n"
+ "CLRCPUF\n"
+ "CLRMPUF\n"
+ "HALT\n";
+ static const char *bad_source[] =
+ {
+ ".text\nSETF HF\n",
+ ".text\nSETF TF\n",
+ ".text\nSETF IF\n",
+ ".text\nCLRF HF\n",
+ ".text\nCLRF TF\n",
+ ".text\nCLRF IF\n"
+ };
+ cpu_asm_image image;
+ cpu_instruction instruction;
+ char err[256];
+ __mpu_uint64_t pc = 0;
+ unsigned int i;
+
+ memset( &image, 0, sizeof(image) );
+ CHECK( cpu_assemble_text( good_source, &image, err, sizeof(err) ) == 0,
+ "assemble writable PSW flag instructions" );
+ if( image.code )
+ {
+ CHECK( __cpu_decode_at( image.code, image.code_size, pc, &instruction ) == CPU_EXEC_SUCCESS,
+ "decode SETF" );
+ CHECK( instruction.opcode == CPU_OP_SETF, "SETF opcode" );
+ CHECK( instruction.operand[0].type == CPU_OPERAND_PSWBIT8 &&
+ instruction.operand[0].value.pswbit8 == CPU_PSWBIT_CF,
+ "SETF selector" );
+ pc += instruction.length;
+
+ CHECK( __cpu_decode_at( image.code, image.code_size, pc, &instruction ) == CPU_EXEC_SUCCESS,
+ "decode CLRF" );
+ CHECK( instruction.opcode == CPU_OP_CLRF, "CLRF opcode" );
+ CHECK( instruction.operand[0].value.pswbit8 == CPU_PSWBIT_AF,
+ "CLRF selector" );
+ pc += instruction.length;
+
+ CHECK( __cpu_decode_at( image.code, image.code_size, pc, &instruction ) == CPU_EXEC_SUCCESS,
+ "decode CLRCPUF" );
+ CHECK( instruction.opcode == CPU_OP_CLRCPUF, "CLRCPUF opcode" );
+ pc += instruction.length;
+
+ CHECK( __cpu_decode_at( image.code, image.code_size, pc, &instruction ) == CPU_EXEC_SUCCESS,
+ "decode CLRMPUF" );
+ CHECK( instruction.opcode == CPU_OP_CLRMPUF, "CLRMPUF opcode" );
+ }
+ cpu_asm_image_free( &image );
+
+ for( i = 0; i < sizeof(bad_source) / sizeof(bad_source[0]); ++i )
+ {
+ memset( &image, 0, sizeof(image) );
+ CHECK( cpu_assemble_text( bad_source[i], &image, err, sizeof(err) ) != 0,
+ "assembler rejects control/execution PSW selector write" );
+ cpu_asm_image_free( &image );
+ }
+}
+
+
+int
+main( void )
+{
+ cpu_instruction instruction;
+ __mpu_uint32_t psw, expected;
+ unsigned int bit;
+
+ __cpu_init();
+
+ CHECK( CPU_OP_SETF == 0x0010, "SETF OPCODE16" );
+ CHECK( CPU_OP_CLRF == 0x0011, "CLRF OPCODE16" );
+ CHECK( CPU_OP_CLRCPUF == 0x0012, "CLRCPUF OPCODE16" );
+ CHECK( CPU_OP_CLRMPUF == 0x0013, "CLRMPUF OPCODE16" );
+ CHECK( PSW_CPUF_MASK == __mpu_UINT32_C(0x000000ff), "CPU flag mask" );
+ CHECK( PSW_MPUF_MASK == __mpu_UINT32_C(0x0000ff00), "MPU flag mask" );
+
+ /* SETF/CLRF write exactly one arithmetic/status flag in PSW[15:0]. */
+ for( bit = CPU_PSWBIT_AF; bit <= CPU_PSWBIT_WRITE_MAX; ++bit )
+ {
+ __mpu_uint32_t mask = (__mpu_uint32_t)1U << bit;
+
+ __cpu_set_psw( PSW_TF_MASK | PSW_IF_MASK | (__mpu_uint32_t)(2U << PSW_MODE_SHIFT) );
+ psw = __cpu_get_psw();
+
+ clear_instruction( &instruction, CPU_OP_SETF );
+ instruction.operand[0] = psw_operand( bit );
+ CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "SETF return code" );
+ CHECK( __cpu_get_psw() == ( psw | mask ), "SETF changes only selected flag" );
+
+ psw = __cpu_get_psw();
+ clear_instruction( &instruction, CPU_OP_CLRF );
+ instruction.operand[0] = psw_operand( bit );
+ CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "CLRF return code" );
+ CHECK( __cpu_get_psw() == ( psw & ~mask ), "CLRF changes only selected flag" );
+ }
+
+ /* HF/TF/IF are readable by branches but are not writable through SETF/CLRF. */
+ for( bit = CPU_PSWBIT_HF; bit <= CPU_PSWBIT_IF; ++bit )
+ {
+ clear_instruction( &instruction, CPU_OP_SETF );
+ instruction.operand[0] = psw_operand( bit );
+ CHECK( __cpu_exec( &instruction ) == CPU_EXEC_OPERAND_FAULT,
+ "SETF rejects HF/TF/IF" );
+
+ clear_instruction( &instruction, CPU_OP_CLRF );
+ instruction.operand[0] = psw_operand( bit );
+ CHECK( __cpu_exec( &instruction ) == CPU_EXEC_OPERAND_FAULT,
+ "CLRF rejects HF/TF/IF" );
+ }
+
+ /* CLRCPUF clears only PSW[7:0]. */
+ __cpu_set_psw( PSW_USED_MASK );
+ psw = __cpu_get_psw();
+ expected = psw & ~PSW_CPUF_MASK;
+ clear_instruction( &instruction, CPU_OP_CLRCPUF );
+ CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "CLRCPUF return code" );
+ CHECK( __cpu_get_psw() == expected, "CLRCPUF preserves PSW[15:8] and control state" );
+
+ /* CLRMPUF clears only PSW[15:8]. */
+ __cpu_set_psw( PSW_USED_MASK );
+ psw = __cpu_get_psw();
+ expected = psw & ~PSW_MPUF_MASK;
+ clear_instruction( &instruction, CPU_OP_CLRMPUF );
+ CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "CLRMPUF return code" );
+ CHECK( __cpu_get_psw() == expected, "CLRMPUF preserves PSW[7:0] and control state" );
+
+ /* SETF/CLRF provide an explicit initial carry for LibMPU IADC. */
+ {
+ __mpu_UNIT_t *dst = __cpu_reg_ptr( 8, 0x00600 );
+ __mpu_UNIT_t *a = __cpu_reg_ptr( 8, 0x00601 );
+ __mpu_UNIT_t *b = __cpu_reg_ptr( 8, 0x00602 );
+
+ *a = 1;
+ *b = 2;
+
+ clear_instruction( &instruction, CPU_OP_SETF );
+ instruction.operand[0] = psw_operand( CPU_PSWBIT_CF );
+ CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "SETF CF before IADC" );
+
+ clear_instruction( &instruction, CPU_OP_IADC );
+ instruction.operand[0] = reg_operand( 8, 0x00600 );
+ instruction.operand[1] = reg_operand( 8, 0x00601 );
+ instruction.operand[2] = reg_operand( 8, 0x00602 );
+ CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "IADC after SETF CF" );
+ CHECK( *dst == 4, "IADC consumes SETF CF" );
+
+ clear_instruction( &instruction, CPU_OP_CLRF );
+ instruction.operand[0] = psw_operand( CPU_PSWBIT_CF );
+ CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "CLRF CF before IADC" );
+ clear_instruction( &instruction, CPU_OP_IADC );
+ instruction.operand[0] = reg_operand( 8, 0x00600 );
+ instruction.operand[1] = reg_operand( 8, 0x00601 );
+ instruction.operand[2] = reg_operand( 8, 0x00602 );
+ CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "IADC after CLRF CF" );
+ CHECK( *dst == 3, "IADC consumes cleared CF" );
+ }
+
+ /* SETF CF likewise provides the input borrow consumed by LibMPU ISBB. */
+ {
+ __mpu_UNIT_t *dst = __cpu_reg_ptr( 8, 0x00610 );
+ __mpu_UNIT_t *a = __cpu_reg_ptr( 8, 0x00611 );
+ __mpu_UNIT_t *b = __cpu_reg_ptr( 8, 0x00612 );
+
+ *a = 9;
+ *b = 3;
+
+ clear_instruction( &instruction, CPU_OP_SETF );
+ instruction.operand[0] = psw_operand( CPU_PSWBIT_CF );
+ CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "SETF CF before ISBB" );
+
+ clear_instruction( &instruction, CPU_OP_ISBB );
+ instruction.operand[0] = reg_operand( 8, 0x00610 );
+ instruction.operand[1] = reg_operand( 8, 0x00611 );
+ instruction.operand[2] = reg_operand( 8, 0x00612 );
+ CHECK( __cpu_exec( &instruction ) == CPU_EXEC_SUCCESS, "ISBB after SETF CF" );
+ CHECK( *dst == 5, "ISBB consumes SETF CF" );
+ }
+
+ check_assembler_contract();
+
+ __cpu_free_context();
+
+ if( failures )
+ {
+ fprintf( stderr, "LibMCPU PSW write regression: %d failure(s)\n", failures );
+ return( 1 );
+ }
+
+ puts( "LibMCPU PSW write regression: PASS" );
+ return( 0 );
+}