/* * pageflip — регресс gfx_copy_page: копия области из НЕактивной страницы в * активную, прямая (GFX_COPY_DIRECT) и с переворотом по вертикали * (GFX_COPY_VFLIP, ядро _bgi_flip_rows_raw). * * ЗАЧЕМ ИМЕННО ТАКОЙ ТЕСТ. Переворот строит на квирке акселератора: между * read- и write-триггером надо СМЕНИТЬ Port_Y, а fetch операнда любой * инструкции в этом окне перезапускает read-burst и набивает буфер кодом * (memory/accel_operand_fetch_retrigger). Ядро обходит это STOP'ом перед * вторым OUT — приём взят из _bgi_scroll_cols_raw, где он уже в проде. * Ошибка здесь даёт не «немного не так», а сдвинутые/мусорные строки, * поэтому проверка ПОБАЙТНАЯ, а не на глаз. * * Что проверяется: * T1 DIRECT: строка i источника легла в строку i приёмника; * T2 VFLIP: строка i источника легла в строку (h-1-i) приёмника; * T3 рамка вокруг области цела — burst не вылез за пределы; * T4 ШИРОКАЯ копия (320 байт = два прохода по 255+65): вылезший или * потерянный второй проход виден по краям строки; * T5 ИСТОЧНИК не тронут — копия односторонняя. * * Паттерн намеренно разный и по строкам, и по колонкам: цвет строки несёт * её номер (сдвиг по Y виден сразу), а маркеры на первом/последнем пикселе * ловят сдвиг по X и потерю хвоста широкой копии. * * ВИД НА ЭКРАНЕ: сами области выглядят чёрными — палитра по умолчанию * задаёт лишь 16 первых цветов, а тест намеренно пишет индексы 0x21..0x60, * чтобы значения были различимы побайтно. Это ожидаемо: проверка идёт * getpixel'ом по ИНДЕКСАМ, а не глазами. * * getpixel читает ОЗУ-КОПИЮ экрана — ту самую, которую обязана обновить * копия (банк 0x50). Значит T1/T2 заодно подтверждают инвариант «после * gfx_copy_page heal целевой страницы восстановит НОВЫЙ фон, а не старый»: * без записи в тень тест бы их провалил. */ #include #include #include #define AX 8 /* область-образец: левый верхний угол */ #define AY 40 #define AW 64 #define AH 32 #define WX 0 /* широкая область (два прохода по ширине) */ #define WY 120 #define WW 320 #define WH 16 #define IW 16 /* картинка в памяти: ширина */ #define IH 8 /* высота */ #define IX 8 /* куда блитим (без клипа) */ #define IY 216 #define CX 8 /* клипающий вариант: частью за верхом экрана */ #define CY -3 #define ROW_C0 0x41 /* цвет строки i = ROW_C0 + i */ #define MARK_L 0x21 /* маркер левого края строки */ #define MARK_R 0x22 /* маркер правого края */ #define FRAME 0x2F /* рамка вокруг области */ #define DIRT 0x35 /* чем забиваем приёмник до копии */ /* Картинка getimage-формата: w,h (16 бит) + пиксели row-major. * Значение = 0x40 + row*IW + col: и строки, и колонки различимы. */ static uint8_t img[4 + IW * IH]; static void make_img(void) { int col, row; img[0] = IW; img[1] = 0; img[2] = IH; img[3] = 0; for (row = 0; row < IH; row++) for (col = 0; col < IW; col++) img[4 + row * IW + col] = (uint8_t)(0x40 + row * IW + col); } /* Строка row картинки обязана лежать в экранной строке (IH-1-row). */ static uint8_t img_vflip_ok(int x0, int y0, int sx, int sy, int w, int h) { int col, row; for (row = 0; row < h; row++) for (col = 0; col < w; col++) { uint8_t want = (uint8_t)(0x40 + (sy + row) * IW + sx + col); if ((uint8_t)getpixel(x0 + col, y0 + h - 1 - row) != want) return 0; } return 1; } /* Нарисовать образец в ТЕКУЩЕЙ draw-странице: каждая строка своим цветом, * по краям маркеры. */ static void draw_pattern(int x0, int y0, int w, int h) { int y; for (y = 0; y < h; y++) { setfillstyle(SOLID_FILL, (uint8_t)(ROW_C0 + y)); bar(x0, y0 + y, x0 + w - 1, y0 + y); putpixel(x0, y0 + y, MARK_L); putpixel(x0 + w - 1, y0 + y, MARK_R); } } /* Забить приёмник мусором + рамка вокруг (её никто не должен трогать). */ static void draw_dirt(int x0, int y0, int w, int h) { setfillstyle(SOLID_FILL, FRAME); bar(x0 - 1, y0 - 1, x0 + w, y0 + h); setfillstyle(SOLID_FILL, DIRT); bar(x0, y0, x0 + w - 1, y0 + h - 1); } /* Строка src_y образца обязана лежать в строке dst_y приёмника. */ static uint8_t row_matches(int x0, int y0, int w, int src_y, int dst_y) { int x; uint8_t want = (uint8_t)(ROW_C0 + src_y); if ((uint8_t)getpixel(x0, y0 + dst_y) != MARK_L) return 0; if ((uint8_t)getpixel(x0 + w - 1, y0 + dst_y) != MARK_R) return 0; for (x = 1; x < w - 1; x++) if ((uint8_t)getpixel(x0 + x, y0 + dst_y) != want) return 0; return 1; } static uint8_t check_direct(int x0, int y0, int w, int h) { int y; for (y = 0; y < h; y++) if (!row_matches(x0, y0, w, y, y)) return 0; return 1; } static uint8_t check_vflip(int x0, int y0, int w, int h) { int y; for (y = 0; y < h; y++) if (!row_matches(x0, y0, w, y, h - 1 - y)) return 0; return 1; } static uint8_t check_frame(int x0, int y0, int w, int h) { int x, y; for (x = -1; x <= w; x++) { if ((uint8_t)getpixel(x0 + x, y0 - 1) != FRAME) return 0; if ((uint8_t)getpixel(x0 + x, y0 + h) != FRAME) return 0; } for (y = 0; y < h; y++) { if ((uint8_t)getpixel(x0 - 1, y0 + y) != FRAME) return 0; if ((uint8_t)getpixel(x0 + w, y0 + y) != FRAME) return 0; } return 1; } static void say(int y, const char *s, uint8_t ok) { setcolor(WHITE); outtextxy(8, y, (char *)s); setcolor(ok ? GREEN : RED); outtextxy(200, y, ok ? (char *)"PASS" : (char *)"FAIL"); } void main(void) { gfx_rect_t area; uint8_t t[8]; uint8_t i; initgraph(); gfx_set_visible_page(0); gfx_pal_sync(); /* у страницы 1 своя палитра */ gfx_set_bank(GFX_BANK_NORMAL); /* Образец — в страницу 1 (она будет НЕактивной, то есть источником). */ gfx_set_draw_page(1); setfillstyle(SOLID_FILL, BLACK); bar(0, 0, 319, 255); draw_pattern(AX, AY, AW, AH); draw_pattern(WX, WY, WW, WH); /* Приёмник — страница 0. */ gfx_set_draw_page(0); setfillstyle(SOLID_FILL, BLACK); bar(0, 0, 319, 255); draw_dirt(AX, AY, AW, AH); /* T1: прямая копия. */ area.x = AX; area.y = AY; area.w = AW; area.h = AH; gfx_copy_page(&area, GFX_COPY_DIRECT); t[0] = check_direct(AX, AY, AW, AH); /* T2/T3: та же область с переворотом (поверх результата T1 — значит * непереписанная строка сразу видна как «не отразилось»). */ gfx_copy_page(&area, GFX_COPY_VFLIP); t[1] = check_vflip(AX, AY, AW, AH); t[2] = check_frame(AX, AY, AW, AH); /* T4: широкая копия — 320 байт не влезают в один accel-блок. */ setfillstyle(SOLID_FILL, DIRT); bar(WX, WY, WX + WW - 1, WY + WH - 1); area.x = WX; area.y = WY; area.w = WW; area.h = WH; gfx_copy_page(&area, GFX_COPY_VFLIP); t[3] = check_vflip(WX, WY, WW, WH); /* T6-T8: vflip-блиты row-major картинки (ядро то же, знак страйда). */ make_img(); setfillstyle(SOLID_FILL, BLACK); bar(0, IY - 1, 200, IY + IH); gfx_blit_noclip_vflip(IX, IY, img); t[5] = img_vflip_ok(IX, IY, 0, 0, IW, IH); gfx_blit_part_noclip_vflip(IX + 40, IY, img, 4, 2, 8, 4); t[6] = img_vflip_ok(IX + 40, IY, 4, 2, 8, 4); /* Клипающий вариант: три верхние экранные строки за краем — значит * съедены ТРИ НИЖНИЕ строки картинки (последние по порядку вывода). */ gfx_blit_part_vflip(CX + 90, CY, img, 0, 0, IW, IH); /* y = -3 => первые три вывода срезаны, экранная строка 0 получает * источник IH-1-3 = 4, дальше вверх по источнику: строка row <- (4-row). */ t[7] = 1; { int col, row; for (row = 0; row < IH - 3; row++) for (col = 0; col < IW; col++) { uint8_t want = (uint8_t)(0x40 + (IH - 1 - 3 - row) * IW + col); if ((uint8_t)getpixel(CX + 90 + col, row) != want) { t[7] = 0; break; } } } /* T5: источник обязан остаться нетронутым. */ gfx_set_draw_page(1); t[4] = check_direct(AX, AY, AW, AH); gfx_set_draw_page(0); setcolor(WHITE); outtextxy(8, 8, "gfx_copy_page test v1"); say(150, "T1 direct copy", t[0]); say(162, "T2 vflip copy", t[1]); say(174, "T3 frame intact", t[2]); say(186, "T4 vflip 320 wide", t[3]); say(198, "T5 source intact", t[4]); say(210, "T6 blit vflip", t[5]); say(222, "T7 part vflip", t[6]); say(234, "T8 part vflip clip", t[7]); for (i = 0; i < 8; i++) if (!t[i]) break; setcolor(WHITE); outtextxy(8, 246, i == 8 ? (char *)"ALL PASS" : (char *)"SOME FAILED"); for (;;) { } }