polygon filler, rudimentary GL
authorJohn Tsiombikas <nuclear@member.fsf.org>
Sun, 14 Mar 2021 09:08:43 +0000 (11:08 +0200)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Sun, 14 Mar 2021 09:08:43 +0000 (11:08 +0200)
Makefile
src/debug.c [new file with mode: 0644]
src/debug.h [new file with mode: 0644]
src/gfx.h
src/main.c
src/polyfill.c [new file with mode: 0644]
src/polyfill.h [new file with mode: 0644]
src/xgl.c [new file with mode: 0644]
src/xgl.h [new file with mode: 0644]

index 88f8d0e..37be9b5 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -15,12 +15,12 @@ AS = $(ARCH)as
 OBJCOPY = $(ARCH)objcopy
 EMU = vbam
 
-opt = -O1 -fomit-frame-pointer -mcpu=arm7tdmi -mtune=arm7tdmi -mthumb -mthumb-interwork
+opt = -O3 -fomit-frame-pointer -mcpu=arm7tdmi -mtune=arm7tdmi -mthumb -mthumb-interwork
 #dbg = -g
 
 CFLAGS = $(opt) $(dbg) -pedantic -Wall -MMD
 ASFLAGS = -mthumb-interwork
-LDFLAGS = -mthumb -mthumb-interwork -Wl,-print-gc-sections
+LDFLAGS = -mthumb -mthumb-interwork -lm
 EMUFLAGS = -T 100 -f 1 --agb-print
 
 
diff --git a/src/debug.c b/src/debug.c
new file mode 100644 (file)
index 0000000..d24af0a
--- /dev/null
@@ -0,0 +1,21 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include "debug.h"
+
+__attribute__((target("arm")))
+void emuprint(const char *fmt, ...)
+{
+       char buf[128];
+       va_list arg_list;
+
+       va_start(arg_list, fmt);
+       vsnprintf(buf, 128, fmt, arg_list);
+       va_end(arg_list);
+
+       __asm__ __volatile__(
+               "mov r0, %0\n\t"
+               "swi 0xff0000\n\t" :
+               : "r" (buf)
+               : "r0"
+       );
+}
diff --git a/src/debug.h b/src/debug.h
new file mode 100644 (file)
index 0000000..41f2a23
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef DEBUG_H_
+#define DEBUG_H_
+
+void emuprint(const char *fmt, ...);
+
+#endif /* DEBUG_H_ */
index 5ea579a..6ba6af5 100644 (file)
--- a/src/gfx.h
+++ b/src/gfx.h
@@ -22,11 +22,9 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 #define wait_vblank()  while(REG_VCOUNT < 160)
 
-#define swap_buffers() \
+#define present(x) \
        do { \
-               static int vis; \
-               vis ^= 0x10; \
-               REG_DISPCNT = DISPCNT_BG2 | 4 | vis; \
+               REG_DISPCNT = DISPCNT_BG2 | 4 | ((x) << 4); \
        } while(0)
 
 #define set_bg_color(idx, r, g, b) \
index 0eeaa5a..195f4a6 100644 (file)
@@ -15,23 +15,45 @@ GNU General Public License for more details.
 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
+#include <stdlib.h>
 #include <string.h>
 #include "gbaregs.h"
 #include "timer.h"
 #include "keyb.h"
 #include "intr.h"
 #include "gfx.h"
+#include "xgl.h"
+#include "polyfill.h"
+#include "debug.h"
+
+#define MENU_HEIGHT            17
+#define TRACK_HEIGHT   18
+#define VP_HEIGHT              (160 - MENU_HEIGHT - TRACK_HEIGHT)
 
 static void handle_keys(void);
 
 extern struct { unsigned char r, g, b; } bgimg_cmap[];
 extern unsigned char bgimg_pixels[];
 
+struct xvertex varr[] = {
+       {0, -0xd000},
+       {-0x8000, 0x7000},
+       {0x8000, 0x7000}
+};
+
 int main(void)
 {
        int i;
+       unsigned int nframes = 0, backbuf;
+       unsigned long tm0, tm;
        uint16_t *cptr;
        unsigned char r, g, b;
+       unsigned char *fbptr[2], *fb;
+       struct pvertex benchv[3] = {
+               {120 << 8, 8 << 8},
+               {75 << 8, 110 << 8},
+               {164 << 8, 80 << 8}
+       };
 
        intr_init();
        reset_msec_timer();
@@ -40,6 +62,11 @@ int main(void)
        /* mode 4: 240x160 8bpp */
        REG_DISPCNT = DISPCNT_BG2 | 4;
 
+       fbptr[0] = (unsigned char*)VRAM_LFB_FB0_ADDR;
+       fbptr[1] = (unsigned char*)VRAM_LFB_FB1_ADDR;
+
+       set_bg_color(0xff, 31, 31, 31);
+
        cptr = (uint16_t*)CRAM_BG_ADDR;
        for(i=0; i<128; i++) {
                r = bgimg_cmap[i].r >> 3;
@@ -47,14 +74,42 @@ int main(void)
                b = bgimg_cmap[i].b >> 3;
                *cptr++ = r | (g << 5) | (b << 10);
        }
-       memcpy((void*)VRAM_LFB_FB0_ADDR, bgimg_pixels, 240 * 160);
-       memcpy((void*)VRAM_LFB_FB1_ADDR, bgimg_pixels, 240 * 160);
+       for(i=0; i<128; i++) {
+               r = (rand() & 0xf) + 8;
+               g = (rand() & 0xf) + 8;
+               b = (rand() & 0xf) + 8;
+               *cptr++ = r | (g << 5) | (b << 10);
+       }
+       memcpy(fbptr[0], bgimg_pixels, 240 * 160);
+       memcpy(fbptr[1], bgimg_pixels, 240 * 160);
+
+       xgl_init();
+       xgl_viewport(0, MENU_HEIGHT, 240, VP_HEIGHT);
+
+       /* benchmark */
+       polyfill_framebuffer(fbptr[0] + 240 * MENU_HEIGHT, 240, VP_HEIGHT);
+       tm0 = timer_msec;
+       for(i=0; i<2048; i++) {
+               polyfill_flat(benchv, 3, 128 + (i & 0x7f));
+       }
+       tm = timer_msec - tm0;
+       emuprint("benchmark: %lu ms\n", tm);
 
        /*key_repeat(500, 75, KEY_LEFT | KEY_RIGHT | KEY_DOWN | KEY_UP);*/
 
        for(;;) {
+               backbuf = ++nframes & 1;
+
+               fb = fbptr[backbuf] + 240 * MENU_HEIGHT;
+               polyfill_framebuffer(fb, 240, VP_HEIGHT);
+               memset(fb, 14, 240 * VP_HEIGHT);
+
+               xgl_load_identity();
+               xgl_rotate_z(nframes << 8);
+               xgl_draw(XGL_TRIANGLES, varr, 3);
+
                wait_vblank();
-               swap_buffers();
+               present(backbuf);
        }
 
        return 0;
diff --git a/src/polyfill.c b/src/polyfill.c
new file mode 100644 (file)
index 0000000..2595429
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+blender for the Gameboy Advance
+Copyright (C) 2021  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <string.h>
+#include "polyfill.h"
+#include "debug.h"
+
+static unsigned char *fb;
+static int fbwidth, fbheight;
+static short scantab[2][160] __attribute__((section(".iwram")));
+
+void polyfill_framebuffer(unsigned char *ptr, int w, int h)
+{
+       fb = ptr;
+       fbwidth = w;
+       fbheight = h;
+}
+
+#define VNEXT(p)       (((p) == vlast) ? varr : (p) + 1)
+#define VPREV(p)       ((p) == varr ? vlast : (p) - 1)
+#define VSUCC(p, side) ((side) == 0 ? VNEXT(p) : VPREV(p))
+
+void polyfill_flat(struct pvertex *varr, int vnum, unsigned char col)
+{
+       int i, line, top, bot;
+       struct pvertex *vlast, *v, *vn;
+       int32_t x, y0, y1, dx, dy, slope, fx, fy;
+       short *tab, start, len;
+       unsigned char *fbptr;
+
+       vlast = varr + vnum - 1;
+       top = fbheight;
+       bot = 0;
+
+       for(i=0; i<vnum; i++) {
+               v = varr + i;
+               vn = VNEXT(v);
+
+               if(vn->y == v->y) continue;
+
+               if(vn->y > v->y) {
+                       tab = scantab[0];
+               } else {
+                       tab = scantab[1];
+                       v = vn;
+                       vn = varr + i;
+               }
+
+               dx = vn->x - v->x;
+               dy = vn->y - v->y;
+               slope = (dx << 8) / dy;
+
+               y0 = (v->y + 0x100) & 0xffffff00;       /* start from the next scanline */
+               fy = y0 - v->y;                                         /* fractional part before the next scanline */
+               fx = (fy * slope) >> 8;                         /* X adjust for the step to the next scanline */
+               x = v->x + fx;                                          /* adjust X */
+               y1 = vn->y & 0xffffff00;                        /* last scanline of the edge <= vn->y */
+
+               line = y0 >> 8;
+               if(line < top) top = line;
+               if((y1 >> 8) > bot) bot = y1 >> 8;
+
+               if(line > 0) tab += line;
+
+               while(line < (y1 >> 8) && line < fbheight) {
+                       if(line >= 0) {
+                               int val = x < 0 ? 0 : x >> 8;
+                               *tab++ = val < fbwidth ? val : fbwidth - 1;
+                       }
+                       x += slope;
+                       line++;
+               }
+       }
+
+       fbptr = fb + top * fbwidth;
+       for(i=top; i<bot; i++) {
+               start = scantab[0][i];
+               len = scantab[1][i] - start;
+
+               if(len > 0) {
+                       memset(fbptr + start, col, len);
+               }
+               fbptr += fbwidth;
+       }
+}
diff --git a/src/polyfill.h b/src/polyfill.h
new file mode 100644 (file)
index 0000000..d637046
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+blender for the Gameboy Advance
+Copyright (C) 2021  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef POLYFILL_H_
+#define POLYFILL_H_
+
+#include <stdint.h>
+
+struct pvertex {
+       int32_t x, y;
+};
+
+void polyfill_framebuffer(unsigned char *fb, int width, int height);
+void polyfill_flat(struct pvertex *v, int vnum, unsigned char col);
+
+#endif /* POLYFILL_H_ */
diff --git a/src/xgl.c b/src/xgl.c
new file mode 100644 (file)
index 0000000..376d792
--- /dev/null
+++ b/src/xgl.c
@@ -0,0 +1,179 @@
+/*
+blender for the Gameboy Advance
+Copyright (C) 2021  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <string.h>
+#include <math.h>
+#include "xgl.h"
+#include "polyfill.h"
+
+#define MAT_STACK_SIZE 4
+
+static int vp[4];
+static int32_t mat[MAT_STACK_SIZE][16];
+static int mtop;
+
+void xgl_init(void)
+{
+       xgl_viewport(0, 0, 240, 160);
+       xgl_load_identity();
+}
+
+void xgl_viewport(int x, int y, int w, int h)
+{
+       vp[0] = x;
+       vp[1] = y;
+       vp[2] = w;
+       vp[3] = h;
+}
+
+void xgl_push_matrix(void)
+{
+       int prev;
+
+       if(mtop >= MAT_STACK_SIZE - 1) return;
+
+       prev = mtop++;
+       memcpy(mat[mtop], mat[prev], sizeof mat[0]);
+}
+
+void xgl_pop_matrix(void)
+{
+       if(mtop > 0) mtop--;
+}
+
+static const int32_t id[] = {
+       0x10000, 0, 0, 0,
+       0, 0x10000, 0, 0,
+       0, 0, 0x10000, 0,
+       0, 0, 0, 0x10000
+};
+
+void xgl_load_identity(void)
+{
+       xgl_load_matrix(id);
+}
+
+void xgl_load_matrix(const int32_t *m)
+{
+       memcpy(mat[mtop], m, sizeof mat[0]);
+}
+
+#define M(i,j) (((i) << 2) + (j))
+#define XMUL(a, b)     (((a) >> 8) * ((b) >> 8))
+void xgl_mult_matrix(const int32_t *m2)
+{
+       int i, j;
+       int32_t m1[16];
+       int32_t *dest = mat[mtop];
+
+       memcpy(m1, dest, sizeof m1);
+
+       for(i=0; i<4; i++) {
+               for(j=0; j<4; j++) {
+                       *dest++ = XMUL(m1[M(0, j)], m2[M(i, 0)]) +
+                               XMUL(m1[M(1, j)], m2[M(i, 1)]) +
+                               XMUL(m1[M(2, j)], m2[M(i, 2)]) +
+                               XMUL(m1[M(3, j)], m2[M(i, 3)]);
+               }
+       }
+}
+
+#define XSIN(x)                (int32_t)(sin(x / 65536.0f) * 65536.0f)
+#define XCOS(x)                (int32_t)(cos(x / 65536.0f) * 65536.0f)
+
+void xgl_translate(int32_t x, int32_t y, int32_t z)
+{
+       int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
+       m[12] = x;
+       m[13] = y;
+       m[14] = z;
+       xgl_mult_matrix(m);
+}
+
+void xgl_rotate_x(int32_t angle)
+{
+       int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
+       int32_t sa = XSIN(angle);
+       int32_t ca = XCOS(angle);
+       m[5] = ca;
+       m[6] = sa;
+       m[9] = -sa;
+       m[10] = ca;
+       xgl_mult_matrix(m);
+}
+
+void xgl_rotate_y(int32_t angle)
+{
+       int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
+       int32_t sa = XSIN(angle);
+       int32_t ca = XCOS(angle);
+       m[0] = ca;
+       m[2] = -sa;
+       m[8] = sa;
+       m[10] = ca;
+       xgl_mult_matrix(m);
+}
+
+void xgl_rotate_z(int32_t angle)
+{
+       int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
+       int32_t sa = XSIN(angle);
+       int32_t ca = XCOS(angle);
+       m[0] = ca;
+       m[1] = sa;
+       m[4] = -sa;
+       m[5] = ca;
+       xgl_mult_matrix(m);
+}
+
+void xgl_scale(int32_t x, int32_t y, int32_t z)
+{
+       int32_t m[16] = {0};
+       m[0] = x;
+       m[5] = y;
+       m[10] = z;
+       m[15] = 0x10000;
+       xgl_mult_matrix(m);
+}
+
+static void xform(struct xvertex *out, const struct xvertex *in, const int32_t *m)
+{
+       out->x = XMUL(m[0], in->x) + XMUL(m[4], in->y) + XMUL(m[8], in->z) + m[12];
+       out->y = XMUL(m[1], in->x) + XMUL(m[5], in->y) + XMUL(m[9], in->z) + m[13];
+       out->z = XMUL(m[2], in->x) + XMUL(m[6], in->y) + XMUL(m[10], in->z) + m[14];
+}
+
+void xgl_draw(int prim, const struct xvertex *varr, int vcount)
+{
+       int i;
+       struct xvertex xv[4];
+       struct pvertex pv[4];
+
+       while(vcount >= prim) {
+               for(i=0; i<prim; i++) {
+                       xform(xv + i, varr, mat[mtop]);
+                       varr++;
+
+                       /* viewport */
+                       pv[i].x = ((((xv[i].x + 0x10000) >> 1) * vp[2]) >> 8) + (vp[0] << 8);
+                       pv[i].y = ((((xv[i].y + 0x10000) >> 1) * vp[3]) >> 8) + (vp[1] << 8);
+               }
+               vcount -= prim;
+
+               polyfill_flat(pv, prim, 0xff);
+       }
+}
diff --git a/src/xgl.h b/src/xgl.h
new file mode 100644 (file)
index 0000000..1e531bb
--- /dev/null
+++ b/src/xgl.h
@@ -0,0 +1,50 @@
+/*
+blender for the Gameboy Advance
+Copyright (C) 2021  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef XGL_H_
+#define XGL_H_
+
+enum {
+       XGL_LINES               = 2,
+       XGL_TRIANGLES   = 3,
+       XGL_QUADS               = 4
+};
+
+struct xvertex {
+       int32_t x, y, z;
+       unsigned char cidx;
+};
+
+void xgl_init(void);
+
+void xgl_viewport(int x, int y, int w, int h);
+
+void xgl_push_matrix(void);
+void xgl_pop_matrix(void);
+void xgl_load_identity(void);
+void xgl_load_matrix(const int32_t *m);
+void xgl_mult_matrix(const int32_t *m);
+
+void xgl_translate(int32_t x, int32_t y, int32_t z);
+void xgl_rotate_x(int32_t angle);
+void xgl_rotate_y(int32_t angle);
+void xgl_rotate_z(int32_t angle);
+void xgl_scale(int32_t x, int32_t y, int32_t z);
+
+void xgl_draw(int prim, const struct xvertex *varr, int vcount);
+
+#endif /* XGL_H_ */