From 678874cd42289b5a873b4c9a7dd7c2b97adb26c9 Mon Sep 17 00:00:00 2001 From: GreaseMonkey Date: Sun, 7 Jan 2024 22:24:32 +1300 Subject: [PATCH] unittester: Implement 0x02 "Read Screen Snapshot Rectangle" This will need some extra testing but it does appear to be at least somewhat functional. --- doc/specifications/86box-unit-tester.md | 4 +- src/device/unittester.c | 94 ++++++++++++++++++++++++- 2 files changed, 93 insertions(+), 5 deletions(-) diff --git a/doc/specifications/86box-unit-tester.md b/doc/specifications/86box-unit-tester.md index c58b15960..ee77de8d4 100644 --- a/doc/specifications/86box-unit-tester.md +++ b/doc/specifications/86box-unit-tester.md @@ -126,9 +126,7 @@ If there is no screen snapshot, then all values will be 0 as per the initial scr ### 0x02: Read Screen Snapshot Rectangle -**TODO: IMPLEMENT ME!** - -Returns a rectangular snapshot of the screen snapshot buffer. +Returns a rectangular snapshot of the screen snapshot buffer as an array of 32bpp 8:8:8:8 B:G:R:X pixels. Input: diff --git a/src/device/unittester.c b/src/device/unittester.c index c020a81ee..fc864b965 100644 --- a/src/device/unittester.c +++ b/src/device/unittester.c @@ -91,6 +91,13 @@ struct unittester_state { uint16_t snap_img_yoffs; /* Command-specific state */ + /* 0x02: Read Screen Snapshot Rectangle */ + /* 0x03: Verify Screen Snapshot Rectangle */ + uint16_t read_snap_width; + uint16_t read_snap_height; + int16_t read_snap_xoffs; + int16_t read_snap_yoffs; + /* 0x04: Exit */ uint8_t exit_code; }; @@ -157,6 +164,13 @@ unittester_write(uint16_t port, uint8_t val, UNUSED(void *priv)) unittester.write_len = 1; break; + /* 0x02: Read Screen Snapshot Rectangle */ + case UT_CMD_READ_SCREEN_SNAPSHOT_RECTANGLE: + unittester.cmd_id = UT_CMD_READ_SCREEN_SNAPSHOT_RECTANGLE; + unittester.status = UT_STATUS_AWAITING_WRITE; + unittester.write_len = 8; + break; + /* 0x04: Exit */ case UT_CMD_EXIT: unittester.cmd_id = UT_CMD_EXIT; @@ -200,6 +214,37 @@ unittester_write(uint16_t port, uint8_t val, UNUSED(void *priv)) } break; + case UT_CMD_READ_SCREEN_SNAPSHOT_RECTANGLE: + switch(unittester.write_offs) { + case 0: + unittester.read_snap_width = (uint16_t)val; + break; + case 1: + unittester.read_snap_width |= ((uint16_t)val) << 8; + break; + case 2: + unittester.read_snap_height = (uint16_t)val; + break; + case 3: + unittester.read_snap_height |= ((uint16_t)val) << 8; + break; + case 4: + unittester.read_snap_xoffs = (uint16_t)val; + break; + case 5: + unittester.read_snap_xoffs |= ((uint16_t)val) << 8; + break; + case 6: + unittester.read_snap_yoffs = (uint16_t)val; + break; + case 7: + unittester.read_snap_yoffs |= ((uint16_t)val) << 8; + break; + default: + break; + } + break; + /* This should not be reachable, but just in case... */ default: break; @@ -257,8 +302,8 @@ unittester_write(uint16_t port, uint8_t val, UNUSED(void *priv)) unittester.snap_img_xoffs = (m->mon_overscan_x >> 1); unittester.snap_img_yoffs = (m->mon_overscan_y >> 1); /* Take snapshot */ - for (size_t y = 0; y < unittester.snap_img_height; y++) { - for (size_t x = 0; x < unittester.snap_img_width; x++) { + for (size_t y = 0; y < unittester.snap_overscan_height; y++) { + for (size_t x = 0; x < unittester.snap_overscan_width; x++) { unittester_screen_buffer->line[y][x] = m->target_buffer->line[y][x]; } } @@ -276,6 +321,32 @@ unittester_write(uint16_t port, uint8_t val, UNUSED(void *priv)) unittester.read_len = 12; break; + case UT_CMD_READ_SCREEN_SNAPSHOT_RECTANGLE: + /* Offset the X,Y offsets by the overscan offsets. */ + unittester.read_snap_xoffs += (int16_t)unittester.snap_img_xoffs; + unittester.read_snap_yoffs += (int16_t)unittester.snap_img_yoffs; + /*BUG: If the width and height are too large, this ends up with a multiplication overflow. + In practice, this means we end up sending less than we would want to. + However: + - A width and height of that size is obscene, and goes beyond what one would reasonably want. + - The consequences of triggering this bug are that one ends up with a bunch of FF FF FF FF pixels. + - Special-casing this would add unnecessary complexity. + So, this bug is kept here. + If there is any need to fix this bug... then go and make the variables 64-bit :P + */ + unittester.read_len = ((uint32_t)unittester.read_snap_width) * ((uint32_t)unittester.read_snap_height) * 4; + + /* Do we have anything to read? */ + if (unittester.read_len >= 1) { + /* Yes - start reads! */ + unittester.status = UT_STATUS_AWAITING_READ; + } else { + /* No - stop here. */ + unittester.cmd_id = UT_CMD_NOOP; + unittester.status = UT_STATUS_IDLE; + } + break; + default: /* Nothing to write? Stop here. */ unittester.cmd_id = UT_CMD_NOOP; @@ -350,6 +421,25 @@ unittester_read(uint16_t port, UNUSED(void *priv)) } break; + case UT_CMD_READ_SCREEN_SNAPSHOT_RECTANGLE: + /* WARNING: If the width is somehow 0 and wasn't caught earlier, you'll probably get a divide by zero crash. */ + { + uint32_t idx = unittester.read_offs & 0x3; + int32_t x = (unittester.read_offs >> 2) % unittester.read_snap_width; + int32_t y = (unittester.read_offs >> 2) / unittester.read_snap_width; + x += unittester.read_snap_xoffs; + y += unittester.read_snap_yoffs; + + if (x < 0 || y < 0 || x >= unittester.snap_overscan_width || y >= unittester.snap_overscan_height) { + /* Out of range! */ + outval = (idx == 3 ? 0xFF : 0x00); + } else { + /* In range */ + outval = (unittester_screen_buffer->line[y][x] & 0x00FFFFFF)>>(idx*8); + } + } + break; + /* This should not be reachable, but just in case... */ default: break;