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-psw-write.c | |
| parent | d130fe27fa753be02ed76fd9b277aa3c3d928eb9 (diff) | |
| download | libmcpu-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.c | 255 |
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 ); +} |
