libc: BGI Фаза 2a — arc/ellipse/drawpoly

Дуги/эллипсы через целочисленную тригонометрию Q7 (_bgi_trig.c, ×128) +
общий рисователь полилинией (_bgi_arc_draw.c). arc(x,y,st,end,r),
ellipse(x,y,st,end,xr,yr), drawpoly(n,pts). Проверено в MAME (tests/
bgitest): окружность/эллипс/дуга/полигон рисуются корректно.

ВАЖНО: тригонометрию считаем в int (Q7), НЕ через (long)…>>8 — первая
версия на 32-бит арифметике рисовала эллипс прямоугольником (SDCC/z80
криво собирает 32-бит; см. memory/avoid_32bit_arith_z80). Q7 даёт
радиус×значение ≤ 255×128 < 32767 — всё влезает в 16 бит.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-08 15:50:00 +03:00
parent e761d21505
commit 5a48f7fafb
9 changed files with 162 additions and 29 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ banked 1056
bankedbg 1067
banklocl 4832
banktest 3767
bgitest 4158
bgitest 5043
bios_text 4470
cat 915
cblstream 6277
1 # Эталон размеров _CODE (байт); обновление: python3 toolchain/size_check.py --update
6 bankedbg
7 banklocl
8 banktest
9 bgitest
10 bios_text
11 cat
12 cblstream
+11
View File
@@ -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);
+33
View File
@@ -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;
}
}
+43
View File
@@ -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);
}
+7
View File
@@ -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);
}
+17
View File
@@ -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);
}
}
+11
View File
@@ -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);
}
+9
View File
@@ -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 (системный). Рисуется текущим цветом на фоновом. */
+30 -28
View File
@@ -1,61 +1,63 @@
/*
* bgitest — демонстрация BGI-слоя graphics.h (режим 320×256×256).
*
* Собирается с `--gfx 256` (см. Makefile). Рисует рамку, заголовок,
* палитровую полосу, веер линий, вложенные окружности и залитые бары —
* визуальная проверка ядра BGI (Фаза 1).
* Собирается с `--gfx 256` (см. Makefile). Показывает примитивы
* Фазы 1 (рамка/бары/линии/окружность/текст) и Фазы 2a
* (эллипс/дуга/полигон) — визуальная проверка в MAME.
*/
#include <graphics.h>
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;
}