From 3eebc6b38f3ad1fd082ae9d4d3263c42c384951c Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Tue, 16 Mar 2021 03:48:35 +0200 Subject: [PATCH] fixed 8bit writes to vmem, unaligned data buffers, and started on sprites --- Makefile | 2 +- src/data.s | 13 +++++++++ src/dma.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/dma.h | 27 ++++++++++++++++++ src/gbaregs.h | 3 ++ src/gfx.h | 2 +- src/main.c | 13 +++++---- src/polyfill.c | 30 ++++++++++++++++++-- src/sprites.c | 68 ++++++++++++++++++++++++++++++++++++++++++++ src/sprites.h | 45 +++++++++++++++++++++++++++++ tools/pngdump/main.c | 12 +++++++- 11 files changed, 281 insertions(+), 11 deletions(-) create mode 100644 src/dma.c create mode 100644 src/dma.h create mode 100644 src/sprites.c create mode 100644 src/sprites.h diff --git a/Makefile b/Makefile index 37be9b5..9bb5113 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,7 @@ $(elf): $(obj) -include $(dep) -src/data.o: src/data.s data/bg.raw data/bg.pal +src/data.o: src/data.s data/bg.raw data/bg.pal data/sprites.raw data/sprites.pal tools/pngdump/pngdump: $(MAKE) -C tools/pngdump diff --git a/src/data.s b/src/data.s index b178889..e6fc992 100644 --- a/src/data.s +++ b/src/data.s @@ -2,8 +2,21 @@ .globl bgimg_pixels .globl bgimg_cmap + .globl sprites_pixels + .globl sprites_cmap + .align 1 bgimg_pixels: .incbin "data/bg.raw" + + .align 1 bgimg_cmap: .incbin "data/bg.pal" + + .align 1 +sprites_pixels: + .incbin "data/sprites.raw" + + .align 1 +sprites_cmap: + .incbin "data/sprites.pal" diff --git a/src/dma.c b/src/dma.c new file mode 100644 index 0000000..78b516f --- /dev/null +++ b/src/dma.c @@ -0,0 +1,77 @@ +/* +gbasys - a gameboy advance hardware abstraction library +Copyright (C) 2004-2014 John Tsiombikas + +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 . +*/ +#include "dma.h" + +/* DMA Options */ +#define DMA_ENABLE 0x80000000 +#define DMA_INT_ENABLE 0x40000000 +#define DMA_TIMING_IMMED 0x00000000 +#define DMA_TIMING_VBLANK 0x10000000 +#define DMA_TIMING_HBLANK 0x20000000 +#define DMA_TIMING_DISPSYNC 0x30000000 +#define DMA_16 0x00000000 +#define DMA_32 0x04000000 +#define DMA_REPEAT 0x02000000 +#define DMA_SRC_INC 0x00000000 +#define DMA_SRC_DEC 0x00800000 +#define DMA_SRC_FIX 0x01000000 +#define DMA_DST_INC 0x00000000 +#define DMA_DST_DEC 0x00200000 +#define DMA_DST_FIX1 0x00400000 +#define DMA_DST_RELOAD 0x00600000 + +/* DMA Register Parts */ +#define DMA_SRC 0 +#define DMA_DST 1 +#define DMA_CTRL 2 + +static volatile unsigned long *reg_dma[4] = {(void*)0x040000b0, (void*)0x040000bc, (void*)0x040000c8, (void*)0x040000d4 }; + +/* --- perform a copy of words or halfwords using DMA --- */ + +void dma_copy32(int channel, void *dst, void *src, int words) +{ + reg_dma[channel][DMA_SRC] = (unsigned long)src; + reg_dma[channel][DMA_DST] = (unsigned long)dst; + reg_dma[channel][DMA_CTRL] = words | DMA_TIMING_IMMED | DMA_32 | DMA_ENABLE; +} + +void dma_copy16(int channel, void *dst, void *src, int halfwords) +{ + reg_dma[channel][DMA_SRC] = (unsigned long)src; + reg_dma[channel][DMA_DST] = (unsigned long)dst; + reg_dma[channel][DMA_CTRL] = halfwords | DMA_TIMING_IMMED | DMA_16 | DMA_ENABLE; +} + +/* --- fill a buffer with an ammount of words and halfwords using DMA --- */ + +void dma_fill32(int channel, void *dst, unsigned long val, int words) +{ + unsigned long valmem = val; + reg_dma[channel][DMA_SRC] = (unsigned long)&valmem; + reg_dma[channel][DMA_DST] = (unsigned long)dst; + reg_dma[channel][DMA_CTRL] = words | DMA_SRC_FIX | DMA_TIMING_IMMED | DMA_32 | DMA_ENABLE; +} + +void dma_fill16(int channel, void *dst, unsigned short val, int halfwords) +{ + unsigned short valmem = val; + reg_dma[channel][DMA_SRC] = (unsigned long)&valmem; + reg_dma[channel][DMA_DST] = (unsigned long)dst; + reg_dma[channel][DMA_CTRL] = halfwords | DMA_SRC_FIX | DMA_TIMING_IMMED | DMA_16 | DMA_ENABLE; +} diff --git a/src/dma.h b/src/dma.h new file mode 100644 index 0000000..96b1806 --- /dev/null +++ b/src/dma.h @@ -0,0 +1,27 @@ +/* +gbasys - a gameboy advance hardware abstraction library +Copyright (C) 2004-2014 John Tsiombikas + +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 . +*/ +#ifndef _DMA_H_ +#define _DMA_H_ + +void dma_copy32(int channel, void *dst, void *src, int words); +void dma_copy16(int channel, void *dst, void *src, int halfwords); + +void dma_fill32(int channel, void *dst, unsigned long val, int words); +void dma_fill16(int channel, void *dst, unsigned short val, int halfwords); + +#endif /* _DMA_H_ */ diff --git a/src/gbaregs.h b/src/gbaregs.h index 96f024a..762937f 100644 --- a/src/gbaregs.h +++ b/src/gbaregs.h @@ -41,6 +41,9 @@ along with this program. If not, see . #define CRAM_BG_ADDR 0x5000000 #define CRAM_OBJ_ADDR 0x5000200 +/* OAM table */ +#define OAM_ADDR 0x7000000 + /* interrupt handler */ #define INTR_VECTOR (*(volatile uint32_t*)0x3007ffc) diff --git a/src/gfx.h b/src/gfx.h index 6ba6af5..430b980 100644 --- a/src/gfx.h +++ b/src/gfx.h @@ -24,7 +24,7 @@ along with this program. If not, see . #define present(x) \ do { \ - REG_DISPCNT = DISPCNT_BG2 | 4 | ((x) << 4); \ + REG_DISPCNT = DISPCNT_BG2 | DISPCNT_OBJ | 4 | ((x) << 4); \ } while(0) #define set_bg_color(idx, r, g, b) \ diff --git a/src/main.c b/src/main.c index a519dc0..30d92c2 100644 --- a/src/main.c +++ b/src/main.c @@ -26,6 +26,8 @@ along with this program. If not, see . #include "polyfill.h" #include "debug.h" #include "meshdata.h" +#include "sprites.h" +#include "dma.h" #define MENU_HEIGHT 17 #define TRACK_HEIGHT 18 @@ -76,7 +78,7 @@ int main(void) set_intr(); /* mode 4: 240x160 8bpp */ - REG_DISPCNT = DISPCNT_BG2 | 4; + REG_DISPCNT = DISPCNT_BG2 | DISPCNT_OBJ | 4; fbptr[0] = (unsigned char*)VRAM_LFB_FB0_ADDR; fbptr[1] = (unsigned char*)VRAM_LFB_FB1_ADDR; @@ -94,8 +96,11 @@ int main(void) r = i / 5 + 6; *cptr++ = r | (r << 5) | (r << 10); } - memcpy(fbptr[0], bgimg_pixels, 240 * 160); - memcpy(fbptr[1], bgimg_pixels, 240 * 160); + dma_copy16(3, fbptr[0], bgimg_pixels, 240 * 160 / 2); + dma_copy16(3, fbptr[1], bgimg_pixels, 240 * 160 / 2); + + init_sprites(); + set_sprite(0, 0, 512, 42, 42, 0, SPR_SZ64); xgl_init(); xgl_viewport(0, 0, 240, VP_HEIGHT); @@ -128,13 +133,11 @@ int main(void) } else { xgl_draw(XGL_LINES, gridaxes + 6, 2); /* -Z */ } - if(show_obj == 1) { xgl_draw(XGL_QUADS, cube, sizeof cube / sizeof *cube); } else { xgl_draw(XGL_TRIANGLES, suzanne, sizeof suzanne / sizeof *suzanne); } - if(cam_theta < X_PI) { xgl_draw(XGL_LINES, gridaxes, 2); /* +X */ } else { diff --git a/src/polyfill.c b/src/polyfill.c index 11e70fd..856ea5f 100644 --- a/src/polyfill.c +++ b/src/polyfill.c @@ -41,6 +41,7 @@ void polyfill_flat(struct pvertex *varr, int vnum, unsigned char col) int32_t x, y0, y1, dx, dy, slope, fx, fy; short *tab, start, len; unsigned char *fbptr; + uint16_t *pptr, pcol = ((uint16_t)col << 8) | (uint16_t)col; vlast = varr + vnum - 1; top = fbheight; @@ -92,7 +93,20 @@ void polyfill_flat(struct pvertex *varr, int vnum, unsigned char col) len = scantab[1][i] - start; if(len > 0) { - memset(fbptr + start, col, len); + if(start & 1) { + pptr = (uint16_t*)(fbptr + (start & 0xfffe)); + *pptr = (*pptr & 0xff) | ((uint16_t)col << 8); + len--; + start++; + } + pptr = (uint16_t*)(fbptr + start); + while(len > 1) { + *pptr++ = pcol; + len -= 2; + } + if(len) { + *pptr = (*pptr & 0xff00) | col; + } } fbptr += fbwidth; } @@ -194,6 +208,16 @@ int clip_line(int *x0, int *y0, int *x1, int *y1, int xmin, int ymin, int xmax, return 1; } +#define PUTPIXEL(ptr) \ + do { \ + uint16_t *pptr = (uint16_t*)((uint32_t)ptr & 0xfffffffe); \ + if((uint32_t)ptr & 1) { \ + *pptr = (*pptr & 0xff) | (color << 8); \ + } else { \ + *pptr = (*pptr & 0xff00) | color; \ + } \ + } while(0) + void draw_line(int x0, int y0, int x1, int y1, unsigned short color) { int i, dx, dy, x_inc, y_inc, error; @@ -220,7 +244,7 @@ void draw_line(int x0, int y0, int x1, int y1, unsigned short color) if(dx > dy) { error = dy * 2 - dx; for(i=0; i<=dx; i++) { - *fbptr = color; + PUTPIXEL(fbptr); if(error >= 0) { error -= dx * 2; fbptr += y_inc; @@ -231,7 +255,7 @@ void draw_line(int x0, int y0, int x1, int y1, unsigned short color) } else { error = dx * 2 - dy; for(i=0; i<=dy; i++) { - *fbptr = color; + PUTPIXEL(fbptr); if(error >= 0) { error -= dy * 2; fbptr += x_inc; diff --git a/src/sprites.c b/src/sprites.c new file mode 100644 index 0000000..1986758 --- /dev/null +++ b/src/sprites.c @@ -0,0 +1,68 @@ +/* +blender for the Gameboy Advance +Copyright (C) 2021 John Tsiombikas + +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 . +*/ +#include +#include "gbaregs.h" +#include "sprites.h" +#include "debug.h" + +extern unsigned char sprites_pixels[]; +extern struct { unsigned char r, g, b; } sprites_cmap[]; + +void init_sprites(void) +{ + int i, j; + uint16_t *cptr; + uint16_t *dst, *src; + + /* copy from cartridge to OBJ RAM */ + dst = (uint16_t*)VRAM_LFB_OBJ_ADDR; + src = (uint16_t*)sprites_pixels; + for(i=0; i<256; i++) { /* 256 tiles */ + for(j=0; j<8; j++) { + *dst++ = src[j * 64]; + *dst++ = src[j * 64 + 1]; + } + + src += 2; + + if((i & 31) == 31) { + src += 7 * 128; /* skip to the next row of tiles */ + } + } + + + /* setup OBJ colormap 0 */ + cptr = (uint16_t*)CRAM_OBJ_ADDR; + for(i=0; i<16; i++) { + unsigned char r = sprites_cmap[i].r >> 3; + unsigned char g = sprites_cmap[i].g >> 3; + unsigned char b = sprites_cmap[i].b >> 3; + *cptr++ = r | (g << 5) | (b << 10); + } +} + +void set_sprite(uint16_t *oam, int idx, int spr, int x, int y, int pal, unsigned int flags) +{ + if(!oam) oam = (uint16_t*)OAM_ADDR; + + oam += idx << 2; + + oam[0] = (y & 0xff) | (flags & 0xff00); + oam[1] = (x & 0x1ff) | ((flags >> 8) & 0xfe00); + oam[2] = (spr & 0x3ff) | ((flags & 3) << 10) | ((pal & 0xf) << 12); +} diff --git a/src/sprites.h b/src/sprites.h new file mode 100644 index 0000000..3712aba --- /dev/null +++ b/src/sprites.h @@ -0,0 +1,45 @@ +/* +blender for the Gameboy Advance +Copyright (C) 2021 John Tsiombikas + +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 . +*/ +#ifndef SPRITES_H_ +#define SPRITES_H_ + +#include + +enum { + SPR_ROTSCL = 0x0100, + SPR_DBLSZ = 0x0200, + SPR_BLEND = 0x0400, + SPR_OBJWIN = 0x0800, + SPR_MOSAIC = 0x1000, + SPR_256COL = 0x2000, + SPR_HRECT = 0x4000, + SPR_VRECT = 0x8000, + + SPR_HFLIP = 0x100000, + SPR_VFLIP = 0x200000, + SPR_SZ16 = 0x400000, + SPR_SZ32 = 0x800000, + SPR_SZ64 = 0xc00000 +}; +#define SPR_ROTSCL_PARAM(x) ((unsigned int)(x) << 17) +#define SPR_PRIO(x) ((unsigned int)(x) & 3) + +void init_sprites(void); +void set_sprite(uint16_t *oam, int idx, int spr, int x, int y, int pal, unsigned int flags); + +#endif /* SPRITES_H_ */ diff --git a/tools/pngdump/main.c b/tools/pngdump/main.c index a2b37fd..908818d 100644 --- a/tools/pngdump/main.c +++ b/tools/pngdump/main.c @@ -84,7 +84,17 @@ int main(int argc, char **argv) switch(mode) { case 0: - fwrite(img.pixels, 1, img.scansz * img.height, out); + if(img.bpp > 8 || img.cmap_ncolors == 0 || img.cmap_ncolors > 16) { + fwrite(img.pixels, 1, img.scansz * img.height, out); + } else { + /* pack into nibbles */ + unsigned char *ptr = img.pixels; + for(i=0; i