/* * p2screen — проверка системного координатного backend Commander P2.1. * * Экранная модель лежит в одной EMM-странице как 80x32 пар (char, attr). * Вывод: WINREST; проверка: RDCHAR; сдвиг: прямоугольный SCROLL 55h. * BIOS window descriptors и прямой доступ к VRAM не используются. */ #include #include #include #include #include #include "p2_screen.h" #include "sc_glyphs.h" #define LEFT_X 1u #define RIGHT_X 41u #define PANEL_Y 1u #define PANEL_W 38u #define PANEL_H 27u #define ATTR_UI COLOR(COLOR_BLACK, COLOR_CYAN) #define ATTR_FRAME ATTR_UI #define ATTR_TITLE ATTR_UI #define ATTR_TEXT ATTR_UI #define ATTR_DIM ATTR_UI #define ATTR_ACTIVE ATTR_UI #define ATTR_MARK1 ATTR_UI #define ATTR_MARK2 ATTR_UI #define ATTR_PASS ATTR_UI #define ATTR_FAIL ATTR_UI #define ATTR_KEYS ATTR_UI static P2Cell row_buf[P2_SCREEN_W]; static P2Cell marker_buf[40u]; static uint8_t fail_x; static uint8_t fail_y; static uint16_t fail_want; static uint16_t fail_got; static uint8_t w3_bad; static uint16_t cell_offset(uint8_t x, uint8_t y) { return (uint16_t)y * P2_ROW_BYTES + (uint16_t)x * 2u; } static void row_fill(uint8_t ch, uint8_t attr) { uint8_t x; for (x = 0; x < P2_SCREEN_W; x++) { row_buf[x].ch = ch; row_buf[x].attr = attr; } } static void cells_fill(P2Cell *cells, uint8_t count, uint8_t ch, uint8_t attr) { uint8_t i; for (i = 0; i < count; i++) { cells[i].ch = ch; cells[i].attr = attr; } } static void cells_text(P2Cell *cells, uint8_t width, uint8_t x, const char *text, uint8_t attr) { while (*text && x < width) { cells[x].ch = (uint8_t)*text++; cells[x].attr = attr; x++; } } static void cells_bytes(P2Cell *cells, uint8_t width, uint8_t x, const uint8_t *bytes, uint8_t count, uint8_t attr) { while (count-- && x < width) { cells[x].ch = *bytes++; cells[x].attr = attr; x++; } } static void cells_dec2(P2Cell *cells, uint8_t width, uint8_t x, uint8_t value, uint8_t attr) { if (x + 1u >= width) return; cells[x].ch = (uint8_t)('0' + (value / 10u) % 10u); cells[x].attr = attr; cells[x + 1u].ch = (uint8_t)('0' + value % 10u); cells[x + 1u].attr = attr; } static void cells_dec4(P2Cell *cells, uint8_t width, uint8_t x, uint16_t value, uint8_t attr) { uint16_t div = 1000u; uint8_t i; for (i = 0; i < 4u && x + i < width; i++) { cells[x + i].ch = (uint8_t)('0' + (value / div) % 10u); cells[x + i].attr = attr; div /= 10u; } } static void row_panel_frame(uint8_t left, uint8_t y) { uint8_t x; uint8_t right = (uint8_t)(left + 39u); if (y == 0u || y == 28u) { row_buf[left].ch = (y == 0u) ? SC_G_TL : SC_G_BL; row_buf[right].ch = (y == 0u) ? SC_G_TR : SC_G_BR; row_buf[left].attr = ATTR_FRAME; row_buf[right].attr = ATTR_FRAME; for (x = (uint8_t)(left + 1u); x < right; x++) { row_buf[x].ch = SC_G_H; row_buf[x].attr = ATTR_FRAME; } } else { row_buf[left].ch = SC_G_V; row_buf[right].ch = SC_G_V; row_buf[left].attr = ATTR_FRAME; row_buf[right].attr = ATTR_FRAME; } } static void build_model(uint8_t page) { static const uint8_t boxes[] = { SC_G_H, SC_G_V, SC_G_TL, SC_G_TR, SC_G_BL, SC_G_BR, SC_G_TM, SC_G_BM, SC_G_ML, SC_G_MR, SC_G_X }; static const uint8_t arrows[] = { SC_G_ARROW_UP, SC_G_ARROW_DOWN, SC_G_ARROW_LEFT, SC_G_ARROW_RIGHT }; static const uint8_t blocks[] = { SC_G_SHADE_LIGHT, SC_G_SHADE_MEDIUM, SC_G_SHADE_DARK, SC_G_BLOCK, SC_G_HALF_UP, SC_G_HALF_DOWN, SC_G_HALF_LEFT, SC_G_HALF_RIGHT, SC_G_BULLET, SC_G_CHECK, SC_G_SQUARE }; uint8_t y; for (y = 0; y < P2_SCREEN_H; y++) { row_fill(' ', (y < 29u) ? ATTR_TEXT : ATTR_KEYS); if (y <= 28u) { row_panel_frame(0u, y); row_panel_frame(40u, y); } if (y == 0u) { cells_text(row_buf, P2_SCREEN_W, 11u, " LEFT PANEL ", ATTR_TITLE); cells_text(row_buf, P2_SCREEN_W, 50u, " RIGHT PANEL ", ATTR_TITLE); } else if (y >= 1u && y <= 27u) { cells_text(row_buf, P2_SCREEN_W, 2u, "L ROW", ATTR_TEXT); cells_dec2(row_buf, P2_SCREEN_W, 8u, y, ATTR_ACTIVE); cells_text(row_buf, P2_SCREEN_W, 42u, "R ROW", ATTR_DIM); cells_dec2(row_buf, P2_SCREEN_W, 48u, y, ATTR_DIM); } if (y == 20u) { cells_text(row_buf, P2_SCREEN_W, 2u, "BOX", ATTR_TITLE); cells_bytes(row_buf, P2_SCREEN_W, 6u, boxes, (uint8_t)sizeof(boxes), ATTR_FRAME); } else if (y == 21u) { cells_text(row_buf, P2_SCREEN_W, 2u, "ARROWS", ATTR_TITLE); cells_bytes(row_buf, P2_SCREEN_W, 10u, arrows, (uint8_t)sizeof(arrows), ATTR_FRAME); } else if (y == 22u) { cells_text(row_buf, P2_SCREEN_W, 2u, "BLOCKS", ATTR_TITLE); cells_bytes(row_buf, P2_SCREEN_W, 10u, blocks, (uint8_t)sizeof(blocks), ATTR_FRAME); } else if (y == 28u) { cells_text(row_buf, P2_SCREEN_W, 13u, " 27 rows ", ATTR_TITLE); cells_text(row_buf, P2_SCREEN_W, 53u, " 27 rows ", ATTR_TITLE); } else if (y == 29u) { cells_text(row_buf, P2_SCREEN_W, 1u, "P2.1 SYSTEM COORDINATE BACKEND - NO BIOS WINDOWS, NO VRAM ACCESS", ATTR_KEYS); } else if (y == 30u) { cells_text(row_buf, P2_SCREEN_W, 1u, "STAGE 1: FULL WINREST + RDCHAR VERIFY", ATTR_KEYS); } else if (y == 31u) { cells_text(row_buf, P2_SCREEN_W, 1u, "SYSTEM ATTR 0x30: NO PALETTE WRITES KEY: next stage", ATTR_KEYS); } bank_write(page, (uint16_t)y * P2_ROW_BYTES, row_buf, P2_ROW_BYTES); } } static void build_marker(uint8_t page) { cells_fill(marker_buf, 20u, ' ', ATTR_MARK1); cells_fill(marker_buf + 20u, 20u, ' ', ATTR_MARK2); cells_text(marker_buf, 20u, 0u, "PARTIAL RECT PASS", ATTR_MARK1); cells_text(marker_buf + 20u, 20u, 0u, "OFFSET 0x2000 OK", ATTR_MARK2); bank_write(page, P2_MARKER_OFF, marker_buf, sizeof(marker_buf)); } static void model_apply_marker(uint8_t page, uint8_t x, uint8_t y) { uint8_t saved = _io_page_w3; P2Cell *model; sprinter_page_w3(page); model = (P2Cell *)0xC000u; memcpy(model + (uint16_t)y * P2_SCREEN_W + x, marker_buf, 20u * sizeof(P2Cell)); memcpy(model + (uint16_t)(y + 1u) * P2_SCREEN_W + x, marker_buf + 20u, 20u * sizeof(P2Cell)); sprinter_page_w3(saved); } static void model_status(uint8_t page, const char *text, uint8_t ok) { bank_read(page, 30u * P2_ROW_BYTES, row_buf, P2_ROW_BYTES); cells_fill(row_buf, P2_SCREEN_W, ' ', ATTR_KEYS); cells_text(row_buf, P2_SCREEN_W, 1u, text, ATTR_KEYS); cells_text(row_buf, P2_SCREEN_W, 62u, ok ? "PASS" : "FAIL", ok ? ATTR_PASS : ATTR_FAIL); if (!ok) { cells_text(row_buf, P2_SCREEN_W, 68u, "AT", ATTR_FAIL); cells_dec2(row_buf, P2_SCREEN_W, 71u, fail_x, ATTR_FAIL); row_buf[73u].ch = ','; row_buf[73u].attr = ATTR_FAIL; cells_dec2(row_buf, P2_SCREEN_W, 74u, fail_y, ATTR_FAIL); } bank_write(page, 30u * P2_ROW_BYTES, row_buf, P2_ROW_BYTES); } static void present_rect(uint8_t page, uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint16_t offset) { uint8_t saved = _io_page_w3; p2_winrest(y, x, height, width, page, offset); if (_io_page_w3 != saved) w3_bad = 1u; } static void present_model_row(uint8_t page, uint8_t x, uint8_t y, uint8_t width) { present_rect(page, x, y, width, 1u, cell_offset(x, y)); } static uint8_t verify_model(uint8_t page) { uint8_t saved = _io_page_w3; uint8_t x; uint8_t y; for (y = 0; y < P2_SCREEN_H; y++) { bank_read(page, (uint16_t)y * P2_ROW_BYTES, row_buf, P2_ROW_BYTES); for (x = 0; x < P2_SCREEN_W; x++) { uint16_t got = rdchar(x, y); uint16_t want = ((uint16_t)row_buf[x].attr << 8) | row_buf[x].ch; if (got != want) { fail_x = x; fail_y = y; fail_want = want; fail_got = got; return 0u; } } } if (_io_page_w3 != saved) w3_bad = 1u; return w3_bad ? 0u : 1u; } static uint8_t report_verify(uint8_t page, const char *stage) { uint8_t ok = verify_model(page); model_status(page, stage, ok); present_model_row(page, 0u, 30u, P2_SCREEN_W); return ok; } static void mapped_new_row(P2Cell *line, uint8_t width, uint8_t direction, uint16_t sequence) { cells_fill(line, width, ' ', ATTR_ACTIVE); cells_text(line, width, 1u, (direction == 1u) ? "NEW BOTTOM ROW" : "NEW TOP ROW", ATTR_ACTIVE); cells_dec4(line, width, 17u, sequence, ATTR_ACTIVE); } static void model_scroll(uint8_t page, uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t direction, uint16_t sequence) { uint8_t saved = _io_page_w3; P2Cell *model; uint8_t i; sprinter_page_w3(page); model = (P2Cell *)0xC000u; if (direction == 1u) { for (i = 0; i + 1u < height; i++) { memmove(model + (uint16_t)(y + i) * P2_SCREEN_W + x, model + (uint16_t)(y + i + 1u) * P2_SCREEN_W + x, (uint16_t)width * sizeof(P2Cell)); } mapped_new_row(model + (uint16_t)(y + height - 1u) * P2_SCREEN_W + x, width, direction, sequence); } else { i = (uint8_t)(height - 1u); while (i != 0u) { memmove(model + (uint16_t)(y + i) * P2_SCREEN_W + x, model + (uint16_t)(y + i - 1u) * P2_SCREEN_W + x, (uint16_t)width * sizeof(P2Cell)); i--; } mapped_new_row(model + (uint16_t)y * P2_SCREEN_W + x, width, direction, sequence); } sprinter_page_w3(saved); } static void system_scroll(uint8_t page, uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t direction, uint16_t sequence) { uint8_t saved; uint8_t open_y; model_scroll(page, x, y, width, height, direction, sequence); saved = _io_page_w3; scroll(x, y, width, height, direction, 0u); if (_io_page_w3 != saved) w3_bad = 1u; open_y = (direction == 1u) ? (uint8_t)(y + height - 1u) : y; present_model_row(page, x, open_y, width); } static uint8_t wait_stage(void) { uint16_t key = getkey(); return ((uint8_t)key == 0x1Bu) ? 0u : 1u; } int main(void) { uint8_t previous_mode = gettextmode(); uint8_t block; uint8_t page; uint8_t ok = 1u; uint16_t i; (void)fail_want; (void)fail_got; if (settextmode(TEXT_MODE_80x32) != 0) return 1; block = mem_alloc_pages(1u); if (block == 0u) { if (previous_mode == TEXT_MODE_40x32 || previous_mode == TEXT_MODE_80x32) { (void)settextmode(previous_mode); } return 2; } page = mem_get_page(block, 0u); if (page == 0u) { mem_free_block(block); if (previous_mode == TEXT_MODE_40x32 || previous_mode == TEXT_MODE_80x32) { (void)settextmode(previous_mode); } return 3; } build_model(page); build_marker(page); present_rect(page, 0u, 0u, P2_SCREEN_W, P2_SCREEN_H, 0u); if (!report_verify(page, "STAGE 1: FULL WINREST + RDCHAR")) ok = 0u; if (!wait_stage()) goto done; present_rect(page, 30u, 14u, 20u, 2u, P2_MARKER_OFF); model_apply_marker(page, 30u, 14u); if (!report_verify(page, "STAGE 2: PARTIAL OFFSET 0x2000")) ok = 0u; if (!wait_stage()) goto done; build_model(page); present_rect(page, 0u, 0u, P2_SCREEN_W, P2_SCREEN_H, 0u); system_scroll(page, LEFT_X, PANEL_Y, PANEL_W, PANEL_H, 1u, 1u); if (!report_verify(page, "STAGE 3: LEFT RECT SCROLL UP")) ok = 0u; if (!wait_stage()) goto done; system_scroll(page, RIGHT_X, PANEL_Y, PANEL_W, PANEL_H, 2u, 2u); if (!report_verify(page, "STAGE 4: RIGHT RECT SCROLL DOWN")) ok = 0u; if (!wait_stage()) goto done; model_status(page, "STAGE 5: 1000 RECT SCROLL OPERATIONS", 1u); present_model_row(page, 0u, 30u, P2_SCREEN_W); for (i = 0u; i < 1000u; i++) { uint8_t direction = (i & 1u) ? 2u : 1u; system_scroll(page, LEFT_X, PANEL_Y, PANEL_W, PANEL_H, direction, (uint16_t)(i + 1u)); } if (!report_verify(page, "FINAL: MODEL/RDCHAR/W3 AFTER 1000")) ok = 0u; if (!ok) { model_status(page, "FINAL P2.1 RESULT", 0u); present_model_row(page, 0u, 30u, P2_SCREEN_W); } (void)wait_stage(); done: mem_free_block(block); textattr(COLOR(COLOR_WHITE, COLOR_BLACK)); clrscr(); if (previous_mode == TEXT_MODE_40x32 || previous_mode == TEXT_MODE_80x32) { (void)settextmode(previous_mode); } return ok ? 0 : 1; }