diff --git a/docs/size_baseline.tsv b/docs/size_baseline.tsv index 36682a9..4c6d5e9 100644 --- a/docs/size_baseline.tsv +++ b/docs/size_baseline.tsv @@ -6,7 +6,7 @@ banked 1056 bankedbg 1067 banklocl 4832 banktest 3767 -bgitest 4158 +bgitest 5043 bios_text 4470 cat 915 cblstream 6277 diff --git a/libc/bgi/_bgi.h b/libc/bgi/_bgi.h index 11289cd..984017f 100644 --- a/libc/bgi/_bgi.h +++ b/libc/bgi/_bgi.h @@ -42,6 +42,17 @@ unsigned _bgi_read(int x, int y); void _bgi_begin(void); void _bgi_end(void); void _bgi_plot_raw(int x, int y, uint8_t color); + +/* ---- Тригонометрия для дуг (_bgi_trig.c) ------------------------- * + * Угол в градусах; результат sin/cos × 128 (Q7 — чтобы радиус×значение + * влезало в int без 32-бит арифметики). */ +int _bgi_sin128(int deg); +int _bgi_cos128(int deg); + +/* Общий рисователь дуги/контура эллипса (_bgi_arc_draw.c): полилиния + * по точкам от stangle до endangle с полуосями xr,yr вокруг (cx,cy). */ +void _bgi_ellipse_arc(int cx, int cy, int stangle, int endangle, + int xr, int yr, uint8_t color); void _bgi_hspan(int x, int y, int len, uint8_t color); void _bgi_lineseg(int x0, int y0, int x1, int y1, uint8_t color); void _bgi_fillrect(int x, int y, int w, int h, uint8_t color); diff --git a/libc/bgi/_bgi_arc_draw.c b/libc/bgi/_bgi_arc_draw.c new file mode 100644 index 0000000..5b2754d --- /dev/null +++ b/libc/bgi/_bgi_arc_draw.c @@ -0,0 +1,33 @@ +/* + * _bgi_ellipse_arc — контур дуги/эллипса полилинией. + * + * Точки параметризованы углом (BGI: 0°=восток, против часовой, ось Y + * вниз): px = cx + xr*cos(a), py = cy - yr*sin(a). Соседние выборки + * соединяются отрезками — контур сплошной при любом радиусе. + * + * Тригонометрия в Q7 (×128): xr*cos128 ≤ 255×128 < 32767, помещается в + * int — БЕЗ 32-битной арифметики (её SDCC/Z80 собирает криво: раньше + * эллипс через (long)…>>8 рисовался прямоугольником). + */ + +#include "_bgi.h" + +void _bgi_ellipse_arc(int cx, int cy, int stangle, int endangle, + int xr, int yr, uint8_t color) +{ + int a, x, y, px, py, first; + + if (endangle < stangle) endangle += 360; /* дуга через 0° */ + + px = 0; + py = 0; + first = 1; + for (a = stangle; a <= endangle; a++) { + x = cx + ((xr * _bgi_cos128(a)) >> 7); + y = cy - ((yr * _bgi_sin128(a)) >> 7); + if (!first) _bgi_lineseg(px, py, x, y, color); + px = x; + py = y; + first = 0; + } +} diff --git a/libc/bgi/_bgi_trig.c b/libc/bgi/_bgi_trig.c new file mode 100644 index 0000000..6b84c47 --- /dev/null +++ b/libc/bgi/_bgi_trig.c @@ -0,0 +1,43 @@ +/* + * _bgi_trig — целочисленная тригонометрия для дуг/эллипсов BGI. + * + * Угол в градусах (Turbo-C); sin/cos масштабированы на 128 (Q7): + * _bgi_sin128(90) = 128. Масштаб 128 (а не 256) выбран так, чтобы + * произведение радиус×значение помещалось в 16-бит int для любого + * экранного радиуса (255×128 = 32640 < 32767) — и НЕ приходилось + * трогать 32-битную арифметику, которую SDCC/Z80 собирает криво + * (см. memory/avoid_32bit_arith_z80; ловили эллипс-в-прямоугольник). + */ + +#include "_bgi.h" + +/* sin(0°..90°) × 128. */ +static const int _bgi_sintab[91] = { + 0, 2, 4, 7, 9, 11, 13, 16, 18, 20, + 22, 24, 27, 29, 31, 33, 35, 37, 40, 42, + 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, + 64, 66, 68, 70, 72, 73, 75, 77, 79, 81, + 82, 84, 86, 87, 89, 91, 92, 94, 95, 97, + 98, 99, 101, 102, 104, 105, 106, 107, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 119, + 120, 121, 122, 122, 123, 124, 124, 125, 125, 126, + 126, 126, 127, 127, 127, 128, 128, 128, 128, 128, + 128, +}; + +/* sin(угол°) × 128, угол приводится к 0..359. */ +int _bgi_sin128(int deg) +{ + deg %= 360; + if (deg < 0) deg += 360; + if (deg <= 90) return _bgi_sintab[deg]; + if (deg <= 180) return _bgi_sintab[180 - deg]; + if (deg <= 270) return -_bgi_sintab[deg - 180]; + return -_bgi_sintab[360 - deg]; +} + +/* cos(угол°) × 128 = sin(угол+90). */ +int _bgi_cos128(int deg) +{ + return _bgi_sin128(deg + 90); +} diff --git a/libc/bgi/arc.c b/libc/bgi/arc.c new file mode 100644 index 0000000..f07840c --- /dev/null +++ b/libc/bgi/arc.c @@ -0,0 +1,7 @@ +/* arc — дуга окружности радиуса r от stangle до endangle (градусы). */ +#include "_bgi.h" + +void arc(int x, int y, int stangle, int endangle, int radius) +{ + _bgi_ellipse_arc(x, y, stangle, endangle, radius, radius, _bgi_fg); +} diff --git a/libc/bgi/drawpoly.c b/libc/bgi/drawpoly.c new file mode 100644 index 0000000..fd023e5 --- /dev/null +++ b/libc/bgi/drawpoly.c @@ -0,0 +1,17 @@ +/* + * drawpoly — ломаная по numpoints точкам (массив x0,y0,x1,y1,…), + * текущим цветом. Как в Turbo-C, контур НЕ замыкается автоматически: + * чтобы получить замкнутый полигон, повторите первую точку последней. + */ +#include "_bgi.h" + +void drawpoly(int numpoints, const int *polypoints) +{ + int i; + if (numpoints < 2) return; + for (i = 0; i < numpoints - 1; i++) { + _bgi_lineseg(polypoints[2 * i], polypoints[2 * i + 1], + polypoints[2 * i + 2], polypoints[2 * i + 3], + _bgi_fg); + } +} diff --git a/libc/bgi/ellipse.c b/libc/bgi/ellipse.c new file mode 100644 index 0000000..f6fbdc9 --- /dev/null +++ b/libc/bgi/ellipse.c @@ -0,0 +1,11 @@ +/* + * ellipse — контур эллипса/эллиптической дуги с полуосями xradius, + * yradius от stangle до endangle. Полный эллипс: stangle=0,endangle=360. + */ +#include "_bgi.h" + +void ellipse(int x, int y, int stangle, int endangle, + int xradius, int yradius) +{ + _bgi_ellipse_arc(x, y, stangle, endangle, xradius, yradius, _bgi_fg); +} diff --git a/libc/include/graphics.h b/libc/include/graphics.h index 8550db5..4efdac5 100644 --- a/libc/include/graphics.h +++ b/libc/include/graphics.h @@ -84,6 +84,15 @@ void rectangle(int left, int top, int right, int bottom); /* контур */ void bar(int left, int top, int right, int bottom); /* заливка */ void circle(int x, int y, int radius); /* окружность */ +/* Дуги/эллипсы: угол в градусах, 0°=восток, против часовой стрелки. + * Полный эллипс — ellipse(x,y,0,360,xr,yr). */ +void arc(int x, int y, int stangle, int endangle, int radius); +void ellipse(int x, int y, int stangle, int endangle, + int xradius, int yradius); + +/* Ломаная по numpoints точкам {x0,y0,x1,y1,…}; НЕ замыкается сама. */ +void drawpoly(int numpoints, const int *polypoints); + /* ---- Текст ------------------------------------------------------- * * Шрифт 8×8 (системный). Рисуется текущим цветом на фоновом. */ diff --git a/tests/bgitest/bgitest.c b/tests/bgitest/bgitest.c index 19cc2d9..5736eb2 100644 --- a/tests/bgitest/bgitest.c +++ b/tests/bgitest/bgitest.c @@ -1,61 +1,63 @@ /* * bgitest — демонстрация BGI-слоя graphics.h (режим 320×256×256). * - * Собирается с `--gfx 256` (см. Makefile). Рисует рамку, заголовок, - * палитровую полосу, веер линий, вложенные окружности и залитые бары — - * визуальная проверка ядра BGI (Фаза 1). + * Собирается с `--gfx 256` (см. Makefile). Показывает примитивы + * Фазы 1 (рамка/бары/линии/окружность/текст) и Фазы 2a + * (эллипс/дуга/полигон) — визуальная проверка в MAME. */ #include int main(void) { - int i, mx, my, cx, cy; + int i, mx, my; + + /* треугольник для drawpoly (замкнут повтором первой точки). */ + static const int tri[] = { 120, 244, 210, 244, 165, 210, 120, 244 }; initgraph(); mx = getmaxx(); /* 319 */ my = getmaxy(); /* 255 */ - cx = mx / 2; - cy = my / 2; cleardevice(); - /* Рамка по краю экрана. */ setcolor(WHITE); rectangle(0, 0, mx, my); - /* Заголовок. */ setcolor(YELLOW); - outtextxy(88, 6, "SPRINTER BGI 256"); + outtextxy(96, 6, "SPRINTER BGI P2"); /* Палитровая полоса: 15 залитых баров цветами 1..15. */ for (i = 1; i <= 15; i++) { int x0 = 8 + (i - 1) * 20; setcolor(i); - bar(x0, 24, x0 + 16, 44); + bar(x0, 22, x0 + 16, 40); } - /* Веер линий из центра к верхней кромке. */ - for (i = 0; i <= mx; i += 16) { - setcolor(1 + (i / 16) % 15); - line(cx, cy, i, 52); - } + /* Окружность (Фаза 1). */ + setcolor(LIGHTRED); + circle(60, 120, 40); - /* Вложенные окружности. */ - for (i = 8; i < 90; i += 8) { - setcolor(9 + (i / 8) % 7); - circle(cx, cy, i); - } + /* Полный эллипс (Фаза 2a). */ + setcolor(LIGHTGREEN); + ellipse(165, 120, 0, 360, 70, 40); - /* Точечная диагональ (putpixel). */ - for (i = 0; i < 60; i++) - putpixel(10 + i, 200 + i, LIGHTGREEN); - - /* Подпись внизу текущей позицией (outtext). */ + /* Верхняя половина дуги (Фаза 2a). */ setcolor(LIGHTCYAN); - moveto(8, 240); - outtext("Phase 1: lines rects circles text"); + arc(270, 140, 0, 180, 35); + + /* Треугольник через drawpoly (Фаза 2a). */ + setcolor(LIGHTMAGENTA); + drawpoly(4, tri); + + /* Веер линий слева. */ + for (i = 150; i <= my - 6; i += 8) { + setcolor(1 + ((i - 150) / 8) % 15); + line(6, 175, 110, i); + } + + setcolor(YELLOW); + outtextxy(120, 190, "circle ellipse arc poly"); for (;;) { } /* держим картинку на экране */ - return 0; }