From 097d03fa2406fca819b27b698ffb1cfc8ac445c2 Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Wed, 5 Oct 2022 16:08:06 +0300 Subject: [PATCH 1/1] starting to separate platform-specific code to facilitate a PC build --- .gitignore | 1 + Makefile | 10 +- Makefile.pc | 61 ++ src/asmutil.s | 38 - src/debug.c | 173 ---- src/dma.c | 62 -- src/dma.h | 12 - src/gamescr.c | 2 +- src/gba/asmutil.s | 38 + src/gba/debug.c | 173 ++++ src/gba/dma.c | 62 ++ src/gba/dma.h | 12 + src/gba/gbaregs.h | 365 ++++++++ src/gba/input.c | 39 + src/gba/intr.c | 31 + src/gba/intr.h | 39 + src/gba/main.c | 59 ++ src/gba/sprite.c | 77 ++ src/gba/timer.c | 55 ++ src/gba/timer.h | 32 + src/gbaregs.h | 365 -------- src/input.c | 39 - src/intr.c | 31 - src/intr.h | 39 - src/level.c | 25 +- src/level.h | 3 + src/main.c | 59 -- src/pc/main.c | 286 ++++++ src/pc/miniglut.c | 2646 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/pc/miniglut.h | 206 +++++ src/player.c | 11 + src/player.h | 2 + src/sprite.c | 77 -- src/timer.c | 55 -- src/timer.h | 32 - src/util.c | 6 + 36 files changed, 4234 insertions(+), 989 deletions(-) create mode 100644 Makefile.pc delete mode 100644 src/asmutil.s delete mode 100644 src/debug.c delete mode 100644 src/dma.c delete mode 100644 src/dma.h create mode 100644 src/gba/asmutil.s create mode 100644 src/gba/debug.c create mode 100644 src/gba/dma.c create mode 100644 src/gba/dma.h create mode 100644 src/gba/gbaregs.h create mode 100644 src/gba/input.c create mode 100644 src/gba/intr.c create mode 100644 src/gba/intr.h create mode 100644 src/gba/main.c create mode 100644 src/gba/sprite.c create mode 100644 src/gba/timer.c create mode 100644 src/gba/timer.h delete mode 100644 src/gbaregs.h delete mode 100644 src/input.c delete mode 100644 src/intr.c delete mode 100644 src/intr.h delete mode 100644 src/main.c create mode 100644 src/pc/main.c create mode 100644 src/pc/miniglut.c create mode 100644 src/pc/miniglut.h delete mode 100644 src/sprite.c delete mode 100644 src/timer.c delete mode 100644 src/timer.h diff --git a/.gitignore b/.gitignore index 6244192..31ff1c8 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ cfg.mk pngdump mmutil lutgen +vistab *.ppm *.png *.sav diff --git a/Makefile b/Makefile index 4ac7b09..e3e1354 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -src = $(wildcard src/*.c) -ssrc = $(wildcard src/*.s) data/lut.s +src = $(wildcard src/*.c) $(wildcard src/gba/*.c) +ssrc = $(wildcard src/gba/*.s) data/lut.s obj = $(src:.c=.o) $(ssrc:.s=.o) dep = $(src:.c=.d) name = gbajam22 @@ -16,11 +16,13 @@ AS = $(TCPREFIX)as OBJCOPY = $(TCPREFIX)objcopy OBJDUMP = $(TCPREFIX)objdump +def = -DBUILD_GBA opt = -O0 -fomit-frame-pointer -mcpu=arm7tdmi -mtune=arm7tdmi -mthumb -mthumb-interwork dbg = -g -inc = -I. -Ilibs/maxmod +inc = -I. -Isrc -Isrc/gba -Ilibs/maxmod +warn = -pedantic -Wall -CFLAGS = $(opt) $(dbg) -pedantic -Wall -MMD $(def) $(inc) +CFLAGS = $(opt) $(dbg) $(warn) -MMD $(def) $(inc) ASFLAGS = -mthumb-interwork LDFLAGS = -mthumb -mthumb-interwork $(libs) -lm diff --git a/Makefile.pc b/Makefile.pc new file mode 100644 index 0000000..14c7d41 --- /dev/null +++ b/Makefile.pc @@ -0,0 +1,61 @@ +src = $(wildcard src/*.c) $(wildcard src/pc/*.c) +ssrc = data/lut.s +obj = $(src:.c=.o) $(ssrc:.s=.o) +dep = $(src:.c=.d) +bin = gbajam22 + +opt = -O0 -fno-strict-aliasing +dbg = -g +inc = -I. -Isrc -Isrc/gba +warn = -pedantic -Wall + +CFLAGS = $(opt) $(dbg) $(warn) -MMD $(def) $(inc) +LDFLAGS = $(libs) -lm + +-include cfg.mk + +sys = $(shell uname -s | sed 's/MINGW.*/mingw/') +ifeq ($(sys), mingw) + libs = -lopengl32 -lwinmm +else + libs = -lGL +endif + + +$(bin): $(obj) + $(CC) -o $@ $(obj) $(LDFLAGS) + +-include $(dep) + +src/data.o: src/data.s $(data) + +tools/pngdump/pngdump: + $(MAKE) -C tools/pngdump + +tools/lutgen: tools/lutgen.c + $(CC) -o $@ $< -lm + +tools/vistab: tools/vistab.c + $(CC) -o $@ $< -lm + +%.sraw: %.png tools/pngdump/pngdump + tools/pngdump/pngdump -o $@ -oc $(subst .sraw,.spal,$@) -os $(subst .sraw,.shade,$@) -s 8 $< + +%.raw: %.png tools/pngdump/pngdump + tools/pngdump/pngdump -o $@ -n $< + +%.pal: %.png tools/pngdump/pngdump + tools/pngdump/pngdump -o $@ -c $< + +data/lut.s: tools/lutgen + tools/lutgen >$@ + +.PHONY: clean +clean: + rm -f $(obj) $(bin) + +.PHONY: cleandep +cleandep: + rm -f $(dep) + +# vi:ft=make: diff --git a/src/asmutil.s b/src/asmutil.s deleted file mode 100644 index b88a7a1..0000000 --- a/src/asmutil.s +++ /dev/null @@ -1,38 +0,0 @@ - .text - .thumb - - .globl fillblock_16byte -fillblock_16byte: - push {r4-r6} - mov r3, r1 - mov r4, r1 - mov r5, r1 - mov r6, r1 -0: stmia r0!, {r3, r4, r5, r6} - sub r2, #1 - bne 0b - pop {r4-r6} - bx lr - - .globl get_pc -get_pc: - mov r0, lr - bx lr - - .globl get_sp -get_sp: - mov r0, sp - bx lr - - .arm - .extern panic_regs - .globl get_panic_regs - .type get_panic_regs, %function -get_panic_regs: - stmfd sp!, {sp, lr} - ldr lr, =panic_regs - stm lr, {r0-r15} - ldmfd sp!, {r0, lr} - ldr r0, =panic_regs + 13 * 4 - stm r0, {sp, lr} - bx lr diff --git a/src/debug.c b/src/debug.c deleted file mode 100644 index 9bdf5d1..0000000 --- a/src/debug.c +++ /dev/null @@ -1,173 +0,0 @@ -#include -#include -#include -#include -#include "gbaregs.h" -#include "intr.h" -#include "debug.h" -#include "util.h" - -uint16_t vblperf_color[] = { - /* grn blue cyan yellow orng red purple d.green purple ... */ - /* 60 30 20 15 12 10 8.5 7.5 ... */ - 0x3e0, 0xf863, 0xffc0, 0x3ff, 0x1ff, 0x001f, 0xf81f, 0x1e0, 0xf81f, 0xf81f, 0xf81f -}; - -void vblperf_setcolor(int palidx) -{ - vblperf_palptr = (uint16_t*)CRAM_BG_ADDR + palidx; -} - - -uint32_t panic_regs[16]; -void get_panic_regs(void); - -void panic(void *pc, const char *fmt, ...) -{ - int y; - va_list ap; - uint32_t *reg; - - get_panic_regs(); - - intr_disable(); - REG_DISPCNT = 4 | DISPCNT_BG2; - - set_bg_color(0, 31, 0, 0); - set_bg_color(0xff, 31, 31, 31); - - fillblock_16byte((void*)VRAM_LFB_FB0_ADDR, 0, 240 * 160 / 16); - - fillblock_16byte((unsigned char*)VRAM_LFB_FB0_ADDR + 240 * 3, 0xffffffff, 240 / 16); - dbg_drawstr(44, 0, " Panic at %p ", pc); - - va_start(ap, fmt); - y = dbg_vdrawstr(0, 12, fmt, ap) + 8; - va_end(ap); - - fillblock_16byte((unsigned char*)VRAM_LFB_FB0_ADDR + 240 * (y + 4), 0xffffffff, 240 / 16); - y += 8; - - reg = panic_regs; - y = dbg_drawstr(0, y, " r0 %08x r1 %08x\n r2 %08x r3 %08x\n r4 %08x r5 %08x\n r6 %08x r7 %08x\n", - reg[0], reg[1], reg[2], reg[3], reg[4], reg[5], reg[6], reg[7]); - y = dbg_drawstr(0, y, " r8 %08x r9 %08x\nr10 %08x r11 %08x\n ip %08x sp %08x\n lr %08x pc %08x\n", - reg[8], reg[9], reg[10], reg[11], reg[12], reg[13], reg[14], reg[15]); - - /* stop any sound/music playback */ - REG_SOUNDCNT_H = SCNT_DSA_CLRFIFO | SCNT_DSB_CLRFIFO; - REG_TMCNT_H(1) &= ~TMCNT_EN; - REG_DMA1CNT_H = 0; - REG_DMA2CNT_H = 0; - - for(;;); -} - -void dbg_drawglyph(int x, int y, int c) -{ - int i; - uint16_t pp; - unsigned char row; - uint16_t *ptr = (uint16_t*)VRAM_LFB_FB0_ADDR + (y << 7) - (y << 3) + (x >> 1); - unsigned char *fnt = font_8x8 + ((c & 0xff) << 3); - - for(i=0; i<8; i++) { - row = *fnt++; - pp = row & 0x80 ? 0xff : 0; - *ptr++ = pp | (row & 0x40 ? 0xff00 : 0); - pp = row & 0x20 ? 0xff : 0; - *ptr++ = pp | (row & 0x10 ? 0xff00 : 0); - pp = row & 0x08 ? 0xff : 0; - *ptr++ = pp | (row & 0x04 ? 0xff00 : 0); - pp = row & 0x02 ? 0xff : 0; - *ptr++ = pp | (row & 0x01 ? 0xff00 : 0); - ptr += 120 - 4; - } -} - -int dbg_vdrawstr(int x, int y, const char *fmt, va_list ap) -{ - int startx, c; - char buf[128]; - char *ptr = buf; - - vsnprintf(buf, sizeof buf, fmt, ap); - - startx = x; - while(*ptr) { - if(y >= 160) break; - - c = *ptr++; - switch(c) { - case '\n': - y += 8; - case '\r': - x = startx; - break; - - default: - dbg_drawglyph(x, y, c); - x += 8; - if(x >= 240 - 8) { - while(*ptr && isspace(*ptr)) ptr++; - x = 0; - y += 8; - } - } - } - - return y; -} - -int dbg_drawstr(int x, int y, const char *fmt, ...) -{ - int res; - va_list ap; - - va_start(ap, fmt); - res = dbg_vdrawstr(x, y, fmt, ap); - va_end(ap); - return res; -} - -#ifdef EMUBUILD -#define REG_DBG_ENABLE REG16(0xfff780) -#define REG_DBG_FLAGS REG16(0xfff700) -#define REG_DBG_STR REG8(0xfff600) - -/*__attribute__((target("arm")))*/ -void emuprint(const char *fmt, ...) -{ - static int opened; - char buf[128]; - va_list ap; - - if(!opened) { - REG_DBG_ENABLE = 0xc0de; - if(REG_DBG_ENABLE != 0x1dea) { - return; - } - opened = 1; - } - - va_start(ap, fmt); - vsnprintf(buf, sizeof buf, fmt, ap); - va_end(ap); - - strcpy((char*)0x4fff600, buf); - REG_DBG_FLAGS = 0x104; /* debug message */ - - /* - asm volatile( - "mov r0, %0\n\t" - "swi 0xff0000\n\t" : - : "r" (buf) - : "r0" - ); - */ -} -#else -void emuprint(const char *fmt, ...) -{ -} -#endif diff --git a/src/dma.c b/src/dma.c deleted file mode 100644 index ba23ab7..0000000 --- a/src/dma.c +++ /dev/null @@ -1,62 +0,0 @@ -#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 uint32_t *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, unsigned int flags) -{ - reg_dma[channel][DMA_SRC] = (uint32_t)src; - reg_dma[channel][DMA_DST] = (uint32_t)dst; - reg_dma[channel][DMA_CTRL] = words | flags | DMA_32 | DMA_ENABLE; -} - -void dma_copy16(int channel, void *dst, void *src, int halfwords, unsigned int flags) -{ - reg_dma[channel][DMA_SRC] = (uint32_t)src; - reg_dma[channel][DMA_DST] = (uint32_t)dst; - reg_dma[channel][DMA_CTRL] = halfwords | flags | DMA_16 | DMA_ENABLE; -} - -/* --- fill a buffer with an ammount of words and halfwords using DMA --- */ - -static uint32_t fill[4]; - -void dma_fill32(int channel, void *dst, uint32_t val, int words) -{ - fill[channel] = val; - reg_dma[channel][DMA_SRC] = (uint32_t)(fill + channel); - reg_dma[channel][DMA_DST] = (uint32_t)dst; - reg_dma[channel][DMA_CTRL] = words | DMA_SRC_FIX | DMA_TIMING_IMMED | DMA_32 | DMA_ENABLE; -} - -void dma_fill16(int channel, void *dst, uint16_t val, int halfwords) -{ - fill[channel] = val; - reg_dma[channel][DMA_SRC] = (uint32_t)(fill + channel); - reg_dma[channel][DMA_DST] = (uint32_t)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 deleted file mode 100644 index 7ff7606..0000000 --- a/src/dma.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef DMA_H_ -#define DMA_H_ - -#include - -void dma_copy32(int channel, void *dst, void *src, int words, unsigned int flags); -void dma_copy16(int channel, void *dst, void *src, int halfwords, unsigned int flags); - -void dma_fill32(int channel, void *dst, uint32_t val, int words); -void dma_fill16(int channel, void *dst, uint16_t val, int halfwords); - -#endif /* DMA_H_ */ diff --git a/src/gamescr.c b/src/gamescr.c index 2bbe408..96620f3 100644 --- a/src/gamescr.c +++ b/src/gamescr.c @@ -69,7 +69,7 @@ void gamescr(void) xgl_init(); - memset(&player, 0, sizeof player); + init_player(&player, lvl); player.phi = 0x100; cmap = (uint16_t*)CRAM_BG_ADDR; diff --git a/src/gba/asmutil.s b/src/gba/asmutil.s new file mode 100644 index 0000000..b88a7a1 --- /dev/null +++ b/src/gba/asmutil.s @@ -0,0 +1,38 @@ + .text + .thumb + + .globl fillblock_16byte +fillblock_16byte: + push {r4-r6} + mov r3, r1 + mov r4, r1 + mov r5, r1 + mov r6, r1 +0: stmia r0!, {r3, r4, r5, r6} + sub r2, #1 + bne 0b + pop {r4-r6} + bx lr + + .globl get_pc +get_pc: + mov r0, lr + bx lr + + .globl get_sp +get_sp: + mov r0, sp + bx lr + + .arm + .extern panic_regs + .globl get_panic_regs + .type get_panic_regs, %function +get_panic_regs: + stmfd sp!, {sp, lr} + ldr lr, =panic_regs + stm lr, {r0-r15} + ldmfd sp!, {r0, lr} + ldr r0, =panic_regs + 13 * 4 + stm r0, {sp, lr} + bx lr diff --git a/src/gba/debug.c b/src/gba/debug.c new file mode 100644 index 0000000..9bdf5d1 --- /dev/null +++ b/src/gba/debug.c @@ -0,0 +1,173 @@ +#include +#include +#include +#include +#include "gbaregs.h" +#include "intr.h" +#include "debug.h" +#include "util.h" + +uint16_t vblperf_color[] = { + /* grn blue cyan yellow orng red purple d.green purple ... */ + /* 60 30 20 15 12 10 8.5 7.5 ... */ + 0x3e0, 0xf863, 0xffc0, 0x3ff, 0x1ff, 0x001f, 0xf81f, 0x1e0, 0xf81f, 0xf81f, 0xf81f +}; + +void vblperf_setcolor(int palidx) +{ + vblperf_palptr = (uint16_t*)CRAM_BG_ADDR + palidx; +} + + +uint32_t panic_regs[16]; +void get_panic_regs(void); + +void panic(void *pc, const char *fmt, ...) +{ + int y; + va_list ap; + uint32_t *reg; + + get_panic_regs(); + + intr_disable(); + REG_DISPCNT = 4 | DISPCNT_BG2; + + set_bg_color(0, 31, 0, 0); + set_bg_color(0xff, 31, 31, 31); + + fillblock_16byte((void*)VRAM_LFB_FB0_ADDR, 0, 240 * 160 / 16); + + fillblock_16byte((unsigned char*)VRAM_LFB_FB0_ADDR + 240 * 3, 0xffffffff, 240 / 16); + dbg_drawstr(44, 0, " Panic at %p ", pc); + + va_start(ap, fmt); + y = dbg_vdrawstr(0, 12, fmt, ap) + 8; + va_end(ap); + + fillblock_16byte((unsigned char*)VRAM_LFB_FB0_ADDR + 240 * (y + 4), 0xffffffff, 240 / 16); + y += 8; + + reg = panic_regs; + y = dbg_drawstr(0, y, " r0 %08x r1 %08x\n r2 %08x r3 %08x\n r4 %08x r5 %08x\n r6 %08x r7 %08x\n", + reg[0], reg[1], reg[2], reg[3], reg[4], reg[5], reg[6], reg[7]); + y = dbg_drawstr(0, y, " r8 %08x r9 %08x\nr10 %08x r11 %08x\n ip %08x sp %08x\n lr %08x pc %08x\n", + reg[8], reg[9], reg[10], reg[11], reg[12], reg[13], reg[14], reg[15]); + + /* stop any sound/music playback */ + REG_SOUNDCNT_H = SCNT_DSA_CLRFIFO | SCNT_DSB_CLRFIFO; + REG_TMCNT_H(1) &= ~TMCNT_EN; + REG_DMA1CNT_H = 0; + REG_DMA2CNT_H = 0; + + for(;;); +} + +void dbg_drawglyph(int x, int y, int c) +{ + int i; + uint16_t pp; + unsigned char row; + uint16_t *ptr = (uint16_t*)VRAM_LFB_FB0_ADDR + (y << 7) - (y << 3) + (x >> 1); + unsigned char *fnt = font_8x8 + ((c & 0xff) << 3); + + for(i=0; i<8; i++) { + row = *fnt++; + pp = row & 0x80 ? 0xff : 0; + *ptr++ = pp | (row & 0x40 ? 0xff00 : 0); + pp = row & 0x20 ? 0xff : 0; + *ptr++ = pp | (row & 0x10 ? 0xff00 : 0); + pp = row & 0x08 ? 0xff : 0; + *ptr++ = pp | (row & 0x04 ? 0xff00 : 0); + pp = row & 0x02 ? 0xff : 0; + *ptr++ = pp | (row & 0x01 ? 0xff00 : 0); + ptr += 120 - 4; + } +} + +int dbg_vdrawstr(int x, int y, const char *fmt, va_list ap) +{ + int startx, c; + char buf[128]; + char *ptr = buf; + + vsnprintf(buf, sizeof buf, fmt, ap); + + startx = x; + while(*ptr) { + if(y >= 160) break; + + c = *ptr++; + switch(c) { + case '\n': + y += 8; + case '\r': + x = startx; + break; + + default: + dbg_drawglyph(x, y, c); + x += 8; + if(x >= 240 - 8) { + while(*ptr && isspace(*ptr)) ptr++; + x = 0; + y += 8; + } + } + } + + return y; +} + +int dbg_drawstr(int x, int y, const char *fmt, ...) +{ + int res; + va_list ap; + + va_start(ap, fmt); + res = dbg_vdrawstr(x, y, fmt, ap); + va_end(ap); + return res; +} + +#ifdef EMUBUILD +#define REG_DBG_ENABLE REG16(0xfff780) +#define REG_DBG_FLAGS REG16(0xfff700) +#define REG_DBG_STR REG8(0xfff600) + +/*__attribute__((target("arm")))*/ +void emuprint(const char *fmt, ...) +{ + static int opened; + char buf[128]; + va_list ap; + + if(!opened) { + REG_DBG_ENABLE = 0xc0de; + if(REG_DBG_ENABLE != 0x1dea) { + return; + } + opened = 1; + } + + va_start(ap, fmt); + vsnprintf(buf, sizeof buf, fmt, ap); + va_end(ap); + + strcpy((char*)0x4fff600, buf); + REG_DBG_FLAGS = 0x104; /* debug message */ + + /* + asm volatile( + "mov r0, %0\n\t" + "swi 0xff0000\n\t" : + : "r" (buf) + : "r0" + ); + */ +} +#else +void emuprint(const char *fmt, ...) +{ +} +#endif diff --git a/src/gba/dma.c b/src/gba/dma.c new file mode 100644 index 0000000..ba23ab7 --- /dev/null +++ b/src/gba/dma.c @@ -0,0 +1,62 @@ +#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 uint32_t *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, unsigned int flags) +{ + reg_dma[channel][DMA_SRC] = (uint32_t)src; + reg_dma[channel][DMA_DST] = (uint32_t)dst; + reg_dma[channel][DMA_CTRL] = words | flags | DMA_32 | DMA_ENABLE; +} + +void dma_copy16(int channel, void *dst, void *src, int halfwords, unsigned int flags) +{ + reg_dma[channel][DMA_SRC] = (uint32_t)src; + reg_dma[channel][DMA_DST] = (uint32_t)dst; + reg_dma[channel][DMA_CTRL] = halfwords | flags | DMA_16 | DMA_ENABLE; +} + +/* --- fill a buffer with an ammount of words and halfwords using DMA --- */ + +static uint32_t fill[4]; + +void dma_fill32(int channel, void *dst, uint32_t val, int words) +{ + fill[channel] = val; + reg_dma[channel][DMA_SRC] = (uint32_t)(fill + channel); + reg_dma[channel][DMA_DST] = (uint32_t)dst; + reg_dma[channel][DMA_CTRL] = words | DMA_SRC_FIX | DMA_TIMING_IMMED | DMA_32 | DMA_ENABLE; +} + +void dma_fill16(int channel, void *dst, uint16_t val, int halfwords) +{ + fill[channel] = val; + reg_dma[channel][DMA_SRC] = (uint32_t)(fill + channel); + reg_dma[channel][DMA_DST] = (uint32_t)dst; + reg_dma[channel][DMA_CTRL] = halfwords | DMA_SRC_FIX | DMA_TIMING_IMMED | DMA_16 | DMA_ENABLE; +} diff --git a/src/gba/dma.h b/src/gba/dma.h new file mode 100644 index 0000000..7ff7606 --- /dev/null +++ b/src/gba/dma.h @@ -0,0 +1,12 @@ +#ifndef DMA_H_ +#define DMA_H_ + +#include + +void dma_copy32(int channel, void *dst, void *src, int words, unsigned int flags); +void dma_copy16(int channel, void *dst, void *src, int halfwords, unsigned int flags); + +void dma_fill32(int channel, void *dst, uint32_t val, int words); +void dma_fill16(int channel, void *dst, uint16_t val, int halfwords); + +#endif /* DMA_H_ */ diff --git a/src/gba/gbaregs.h b/src/gba/gbaregs.h new file mode 100644 index 0000000..3396cc9 --- /dev/null +++ b/src/gba/gbaregs.h @@ -0,0 +1,365 @@ +#ifndef GBAREGS_H_ +#define GBAREGS_H_ + +#include + +#define VRAM_START_ADDR 0x6000000 +#define VRAM_BG_ADDR VRAM_START_ADDR +#define VRAM_OBJ_ADDR 0x6010000 +#define VRAM_LFB_OBJ_ADDR 0x6014000 +#define VRAM_LFB_FB0_ADDR VRAM_START_ADDR +#define VRAM_LFB_FB1_ADDR 0x600a000 + +/* address of character data block x (4 possible blocks, 16k each) */ +#define VRAM_CHR_BLOCK_ADDR(x) (VRAM_START_ADDR + ((x) << 14)) +/* address of screen data block x (32 possible blocks, 2k each) */ +#define VRAM_SCR_BLOCK_ADDR(x) (VRAM_START_ADDR + ((x) << 11)) + +/* fields of a background tile in screen memory */ +#define BGTILE_HFLIP 0x0400 +#define BGTILE_VFLIP 0x0800 +#define BGTILE_PAL(x) ((uint16_t)(x) << 12) + +/* color palette ram addresses for backgrounds and sprites */ +#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) + +/* battery backed RAM address */ +#define SRAM_ADDR 0xe000000 + +/* I/O space */ + +#define REG_BASE 0x4000000 +#define REG8(x) (*(volatile uint8_t*)(REG_BASE + (x))) +#define REG16(x) (*(volatile uint16_t*)(REG_BASE + (x))) +#define REG32(x) (*(volatile uint32_t*)(REG_BASE + (x))) + +/* ---- display registers ---- */ +#define REG_DISPCNT REG16(0x00) +#define REG_GREENSWAP REG16(0x02) +#define REG_DISPSTAT REG16(0x04) +#define REG_VCOUNT REG16(0x06) +#define REG_BG0CNT REG16(0x08) +#define REG_BG1CNT REG16(0x0a) +#define REG_BG2CNT REG16(0x0c) +#define REG_BG3CNT REG16(0x0e) +/* scrolling registers */ +#define REG_BG0HOFS REG16(0x10) +#define REG_BG0VOFS REG16(0x12) +#define REG_BG1HOFS REG16(0x14) +#define REG_BG1VOFS REG16(0x16) +#define REG_BG2HOFS REG16(0x18) +#define REG_BG2VOFS REG16(0x1a) +#define REG_BG3HOFS REG16(0x1c) +#define REG_BG3VOFS REG16(0x1e) +/* BG rotation and scaling registers */ +#define REG_BG2PA REG16(0x20) +#define REG_BG2PB REG16(0x22) +#define REG_BG2PC REG16(0x24) +#define REG_BG2PD REG16(0x26) +#define REG_BG2X REG32(0x28) +#define REG_BG2Y REG32(0x2c) +#define REG_BG3PA REG16(0x30) +#define REG_BG3PB REG16(0x32) +#define REG_BG3PC REG16(0x34) +#define REG_BG3PD REG16(0x36) +#define REG_BG3X REG32(0x38) +#define REG_BG3Y REG32(0x3c) +/* window registers */ +#define REG_WIN0H REG16(0x40) +#define REG_WIN1H REG16(0x42) +#define REG_WIN0V REG16(0x44) +#define REG_WIN1V REG16(0x46) +#define REG_WININ REG16(0x48) +#define REG_WINOUT REG16(0x4a) + +#define REG_MOSAIC REG16(0x4c) +/* color effects */ +#define REG_BLDCNT REG16(0x50) +#define REG_BLDALPHA REG16(0x52) +#define REG_BLDY REG16(0x54) + +/* ---- sound registers ---- */ +#define REG_SOUND1CNT_L REG16(0x60) +#define REG_SOUND1CNT_H REG16(0x62) +#define REG_SOUND1CNT_X REG16(0x64) +#define REG_SOUND2CNT_L REG16(0x68) +#define REG_SOUND2CNT_H REG16(0x6c) +#define REG_SOUND3CNT_L REG16(0x70) +#define REG_SOUND3CNT_H REG16(0x72) +#define REG_SOUND3CNT_X REG16(0x74) +#define REG_SOUND4CNT_L REG16(0x78) +#define REG_SOUND4CNT_H REG16(0x7c) +#define REG_SOUNDCNT_L REG16(0x80) +#define REG_SOUNDCNT_H REG16(0x82) +#define REG_SOUNDCNT_X REG16(0x84) +#define REG_SOUNDBIAS REG16(0x88) +#define WAVE_RAM_PTR ((unsigned char*)(REG_BASE + 0x90)) +#define REG_FIFO_A REG32(0xa0) +#define REG_FIFO_B REG32(0xa4) +#define FIFO_A_PTR ((unsigned char*)(REG_BASE + 0xa0)) +#define FIFO_B_PTR ((unsigned char*)(REG_BASE + 0xa4)) + +/* ---- DMA registers ---- */ +#define REG_DMA0SAD REG32(0xb0) +#define REG_DMA0DAD REG32(0xb4) +#define REG_DMA0CNT REG32(0xb8) +#define REG_DMA0CNT_L REG16(0xb8) +#define REG_DMA0CNT_H REG16(0xba) +#define REG_DMA1SAD REG32(0xbc) +#define REG_DMA1DAD REG32(0xc0) +#define REG_DMA1CNT REG32(0xc4) +#define REG_DMA1CNT_L REG16(0xc4) +#define REG_DMA1CNT_H REG16(0xc6) +#define REG_DMA2SAD REG32(0xc8) +#define REG_DMA2DAD REG32(0xcc) +#define REG_DMA2CNT REG32(0xd0) +#define REG_DMA2CNT_L REG16(0xd0) +#define REG_DMA2CNT_H REG16(0xd2) +#define REG_DMA3SAD REG32(0xd4) +#define REG_DMA3DAD REG32(0xd8) +#define REG_DMA3CNT REG32(0xdc) +#define REG_DMA3CNT_L REG16(0xdc) +#define REG_DMA3CNT_H REG16(0xde) + +/* ---- timer registers ---- */ +#define REG_TM0CNT_L REG16(0x100) +#define REG_TM0CNT_H REG16(0x102) +#define REG_TM1CNT_L REG16(0x104) +#define REG_TM1CNT_H REG16(0x106) +#define REG_TM2CNT_L REG16(0x108) +#define REG_TM2CNT_H REG16(0x10a) +#define REG_TM3CNT_L REG16(0x10c) +#define REG_TM3CNT_H REG16(0x10e) + +#define REG_TMCNT_L(x) REG16(0x100 + ((x) << 2)) +#define REG_TMCNT_H(x) REG16(0x102 + ((x) << 2)) + +/* ---- communication registers (serial/joybus/gpio) ---- */ +#define REG_SIODATA32 REG32(0x120) +#define REG_SIOMULTI0 REG16(0x120) +#define REG_SIOMULTI1 REG16(0x122) +#define REG_SIOMULTI2 REG16(0x124) +#define REG_SIOMULTI3 REG16(0x126) +#define REG_SIOCNT REG16(0x128) +#define REG_SIOMLT_SEND REG16(0x12a) +#define REG_SIODATA8 REG16(0x12a) +#define REG_RCNT REG16(0x134) +#define REG_JOYCNT REG16(0x140) +#define REG_JOY_RECV REG32(0x150) +#define REG_JOY_TRANS REG32(0x154) +#define REG_JOYSTAT REG16(0x158) + +/* ---- keypad registers ---- */ +#define REG_KEYINPUT REG16(0x130) +#define REG_KEYCNT REG16(0x132) + +/* ---- interrupts ---- */ +#define REG_IE REG16(0x200) +#define REG_IF REG16(0x202) +#define REG_WAITCNT REG16(0x204) +#define REG_IME REG16(0x208) + +#define REG_POSTFLG REG8(0x300) +#define REG_HALTCNT REG8(0x301) +#define REG_INTMEMCNT REG32(0x800) + +/* REG_DISPSTAT bits */ +#define DISPSTAT_VBLANK 0x01 +#define DISPSTAT_HBLANK 0x02 +#define DISPSTAT_VMATCH 0x04 +#define DISPSTAT_IEN_VBLANK 0x08 +#define DISPSTAT_IEN_HBLANK 0x10 +#define DISPSTAT_IEN_VMATCH 0x20 +#define DISPSTAT_VCOUNT(x) ((uint16_t)(x) << 8) + +/* REG_DISPCNT bits */ +#define DISPCNT_MODE(x) (x) +#define DISPCNT_FB1 0x0010 +#define DISPCNT_HBLANK_OBJPROC 0x0020 +#define DISPCNT_OBJMAP_1D 0x0040 +#define DISPCNT_FORCE_BLANK 0x0080 +#define DISPCNT_BG0 0x0100 +#define DISPCNT_BG1 0x0200 +#define DISPCNT_BG2 0x0400 +#define DISPCNT_BG3 0x0800 +#define DISPCNT_OBJ 0x1000 +#define DISPCNT_WIN0 0x2000 +#define DISPCNT_WIN1 0x4000 +#define DISPCNT_WINOBJ 0x8000 + +/* REG_BGXCNT bits */ +#define BGCNT_PRIO(x) ((uint16_t)(x)) +#define BGCNT_CHR_BASE(x) ((uint16_t)(x) << 2) +#define BGCNT_MOSAIC 0x0040 +#define BGCNT_256COL 0x0080 +#define BGCNT_SCR_BASE(x) ((uint16_t)(x) << 8) +#define BGCNT_WRAP 0x2000 + +#define BGCNT_SZ(x) ((uint16_t)(x) << 14) +#define BGCNT_SZ_TX_256X256 BGCNT_SZ(0) +#define BGCNT_SZ_RS_128X128 BGCNT_SZ(0) +#define BGCNT_SZ_TX_512X256 BGCNT_SZ(1) +#define BGCNT_SZ_RS_256X256 BGCNT_SZ(1) +#define BGCNT_SZ_TX_256X512 BGCNT_SZ(2) +#define BGCNT_SZ_RS_512X512 BGCNT_SZ(2) +#define BGCNT_SZ_TX_512X512 BGCNT_SZ(3) +#define BGCNT_SZ_RS_1024X1024 BGCNT_SZ(3) + +/* REG_BLDCNT bits */ +#define BLDCNT_A_BG0 0x0001 +#define BLDCNT_A_BG1 0x0002 +#define BLDCNT_A_BG2 0x0004 +#define BLDCNT_A_BG3 0x0008 +#define BLDCNT_A_OBJ 0x0010 +#define BLDCNT_A_BACKDROP 0x0020 +#define BLDCNT_B_BG0 0x0100 +#define BLDCNT_B_BG1 0x0200 +#define BLDCNT_B_BG2 0x0400 +#define BLDCNT_B_BG3 0x0800 +#define BLDCNT_B_OBJ 0x1000 +#define BLDCNT_B_BACKDROP 0x2000 + +#define BLDCNT_ALPHA 0x0040 +#define BLDCNT_BRIGHTEN 0x0080 +#define BLDCNT_DARKEN 0x00c0 + +/* REG_IF bits */ +#define IF_VBLANK 0x0001 +#define IF_HBLANK 0x0002 +#define IF_VMATCH 0x0004 +#define IF_TIMER0 0x0008 +#define IF_TIMER1 0x0010 +#define IF_TIMER2 0x0020 +#define IF_TIMER3 0x0040 +#define IF_COMM 0x0080 +#define IF_DMA0 0x0100 +#define IF_DMA1 0x0200 +#define IF_DMA2 0x0400 +#define IF_DMA3 0x0800 +#define IF_KEY 0x1000 +#define IF_GPAK 0x2000 + +/* REG_TMXCNT bits */ +#define TMCNT_PRESCL_CLK1 0 +#define TMCNT_PRESCL_CLK64 1 +#define TMCNT_PRESCL_CLK256 2 +#define TMCNT_PRESCL_CLK1024 3 + +#define TMCNT_CASCADE 0x04 +#define TMCNT_IE 0x40 +#define TMCNT_EN 0x80 + +/* REG_KEY* bits */ +#define KEY_A 0x0001 +#define KEY_B 0x0002 +#define KEY_SELECT 0x0004 +#define KEY_START 0x0008 +#define KEY_RIGHT 0x0010 +#define KEY_LEFT 0x0020 +#define KEY_UP 0x0040 +#define KEY_DOWN 0x0080 +#define KEY_RT 0x0100 +#define KEY_LT 0x0200 + +#define KEYCNT_IE 0x4000 +#define KEYCNT_IAND 0x8000 + +/* REG_SOUNDCNT_L bits */ +#define SCNT_SS_LVOL(x) ((x) & 7) +#define SCNT_SS_RVOL(x) (((x) & 7) << 4) +#define SCNT_SS_VOL(x) (SCNT_SS_LVOL(x) | SCNT_SS_RVOL(x)) +#define SCNT_SS1_EN_R 0x0100 +#define SCNT_SS2_EN_R 0x0200 +#define SCNT_SS3_EN_R 0x0400 +#define SCNT_SS4_EN_R 0x0800 +#define SCNT_SS_EN_R(x) (SCNT_SS1_EN_R << (x)) +#define SCNT_SS1_EN_L 0x1000 +#define SCNT_SS2_EN_L 0x2000 +#define SCNT_SS3_EN_L 0x4000 +#define SCNT_SS4_EN_L 0x8000 +#define SCNT_SS_EN_L(x) (SCNT_SS1_EN_L << (x)) +#define SCNT_SS1_EN (SCNT_SS1_EN_R | SCNT_SS1_EN_L) +#define SCNT_SS2_EN (SCNT_SS2_EN_R | SCNT_SS2_EN_L) +#define SCNT_SS3_EN (SCNT_SS3_EN_R | SCNT_SS3_EN_L) +#define SCNT_SS4_EN (SCNT_SS4_EN_R | SCNT_SS4_EN_L) +#define SCNT_SS_EN(x) (SCNT_SS_EN_L(x) | SCNT_SS_EN_R(x)) + +#define SCNT_SS1 0 +#define SCNT_SS2 1 +#define SCNT_SS3 2 +#define SCNT_SS4 3 + +/* REG_SOUNDCNT_X bits */ +#define SCNT_MASTER_EN 0x0080 + +/* REG_SOUNDCNT_H bits */ +#define SCNT_SS_VOL_QRT 0x0000 +#define SCNT_SS_VOL_HALF 0x0001 +#define SCNT_SS_VOL_FULL 0x0002 +#define SCNT_DSA_VOL_HALF 0 +#define SCNT_DSA_VOL_FULL 0x0004 +#define SCNT_DSB_VOL_HALF 0 +#define SCNT_DSB_VOL_FULL 0x0008 +#define SCNT_DSA_EN_R 0x0100 +#define SCNT_DSA_EN_L 0x0200 +#define SCNT_DSA_TIMER0 0 +#define SCNT_DSA_TIMER1 0x0400 +#define SCNT_DSA_CLRFIFO 0x0800 +#define SCNT_DSB_EN_R 0x1000 +#define SCNT_DSB_EN_L 0x2000 +#define SCNT_DSB_TIMER0 0 +#define SCNT_DSB_TIMER1 0x4000 +#define SCNT_DSB_CLRFIFO 0x8000 + +/* REG_DMAxCNT_H bits */ +#define DMACNTH_DST_INC 0 +#define DMACNTH_DST_DEC 0x0020 +#define DMACNTH_DST_FIXED 0x0040 +#define DMACNTH_INC_RELOAD 0x0060 +#define DMACNTH_SRC_INC 0 +#define DMACNTH_SRC_DEC 0x0080 +#define DMACNTH_SRC_FIXED 0x0100 +#define DMACNTH_REPEAT 0x0200 +#define DMACNTH_16BIT 0 +#define DMACNTH_32BIT 0x0400 +#define DMACNTH_VBLANK 0x1000 +#define DMACNTH_HBLANK 0x2000 +#define DMACNTH_SOUND 0x3000 +#define DMACNTH_IEN 0x4000 +#define DMACNTH_EN 0x8000 + +#define DMACNT_DST_INC 0 +#define DMACNT_DST_DEC 0x00200000 +#define DMACNT_DST_FIXED 0x00400000 +#define DMACNT_INC_RELOAD 0x00600000 +#define DMACNT_SRC_INC 0 +#define DMACNT_SRC_DEC 0x00800000 +#define DMACNT_SRC_FIXED 0x01000000 +#define DMACNT_REPEAT 0x02000000 +#define DMACNT_16BIT 0 +#define DMACNT_32BIT 0x04000000 +#define DMACNT_VBLANK 0x10000000 +#define DMACNT_HBLANK 0x20000000 +#define DMACNT_SOUND 0x30000000 +#define DMACNT_IEN 0x40000000 +#define DMACNT_EN 0x80000000 + +/* REG_WAITCNT bits */ +#define WAITCNT_ROM_4_2 0x0000 +#define WAITCNT_ROM_3_2 0x0004 +#define WAITCNT_ROM_2_2 0x0008 +#define WAITCNT_ROM_8_2 0x000c +#define WAITCNT_ROM_4_1 0x0010 +#define WAITCNT_ROM_3_1 0x0014 +#define WAITCNT_ROM_2_1 0x0018 +#define WAITCNT_ROM_8_1 0x001c +#define WAITCNT_PREFETCH 0x4000 + +#endif /* GBAREGS_H_ */ diff --git a/src/gba/input.c b/src/gba/input.c new file mode 100644 index 0000000..6a66dd1 --- /dev/null +++ b/src/gba/input.c @@ -0,0 +1,39 @@ +#include "input.h" +#include "gbaregs.h" +#include "intr.h" + +static void keyintr(void); + +static uint16_t bnstate; + +void select_input(uint16_t bmask) +{ + bnstate = 0; + + mask(INTR_KEY); + if(bmask) { + REG_KEYCNT = bmask | KEYCNT_IE; + interrupt(INTR_KEY, keyintr); + unmask(INTR_KEY); + } else { + REG_KEYCNT = 0; + interrupt(INTR_KEY, 0); + } +} + +uint16_t get_input(void) +{ + uint16_t s; + + mask(INTR_KEY); + s = bnstate; + bnstate = 0; + unmask(INTR_KEY); + + return s; +} + +static void keyintr(void) +{ + bnstate |= ~REG_KEYINPUT; +} diff --git a/src/gba/intr.c b/src/gba/intr.c new file mode 100644 index 0000000..5858bf1 --- /dev/null +++ b/src/gba/intr.c @@ -0,0 +1,31 @@ +#include "intr.h" + +#define MAX_INTR 14 +static void (*intr_table[MAX_INTR])(void); + +__attribute__ ((target("arm"), section(".iwram"))) +static void intr_handler(void) +{ + int i; + uint16_t iflags; + + iflags = REG_IF; + + for(i=0; i +#include "gbaregs.h" +#include "intr.h" +#include "debug.h" +#include "game.h" +#include "maxmod.h" + +static void vblank(void); +static void nopfunc(void); + +int main(void) +{ + int i; + volatile uint16_t *cptr; + + intr_init(); + + REG_WAITCNT = WAITCNT_PREFETCH | WAITCNT_ROM_2_1; + + cptr = (uint16_t*)CRAM_BG_ADDR; + for(i=0; i<256; i++) { + int c = i >> 3; + *cptr++ = c | ((c >> 1) << 10); + } + +#ifndef NOSOUND + mmInitDefault(sound_data, 8); + mmStart(MOD_POPCORN, MM_PLAY_LOOP); +#endif + + screen_vblank = nopfunc; + + intr_disable(); + interrupt(INTR_VBLANK, vblank); + REG_DISPSTAT |= DISPSTAT_IEN_VBLANK; + unmask(INTR_VBLANK); + + intr_enable(); + gamescr(); + + for(;;); + return 0; +} + +static void vblank(void) +{ + vblperf_count++; + + screen_vblank(); + +#ifndef NOSOUND + mmVBlank(); + mmFrame(); +#endif +} + +static void nopfunc(void) +{ +} diff --git a/src/gba/sprite.c b/src/gba/sprite.c new file mode 100644 index 0000000..c77ae90 --- /dev/null +++ b/src/gba/sprite.c @@ -0,0 +1,77 @@ +#include "sprite.h" +#include "gbaregs.h" + + +void spr_setup(int xtiles, int ytiles, unsigned char *pixels, unsigned char *cmap) +{ + int i, j, num_tiles; + uint16_t *cptr, *src, *dst; + + num_tiles = xtiles * ytiles; + dst = (uint16_t*)VRAM_LFB_OBJ_ADDR; + src = (uint16_t*)pixels; + for(i=0; i> 3; + unsigned char g = *cmap++ >> 3; + unsigned char b = *cmap++ >> 3; + *cptr++ = r | ((uint16_t)g << 5) | ((uint16_t)b << 10); + } +} + +void spr_clear(void) +{ + int i; + + for(i=0; i<128; i++) { + spr_oam_clear(0, i); + } +} + +void spr_oam(uint16_t *oam, int idx, int spr, int x, int y, 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); +} + +void spr_spr_oam(uint16_t *oam, int idx, struct sprite *spr) +{ + int i; + struct hwsprite *s; + + s = spr->hwspr; + for(i=0; inum_hwspr; i++) { + spr_oam(oam, idx, s->id, spr->x + s->x, spr->y + s->y, s->flags); + s++; + } +} + +void spr_transform(uint16_t *oam, int idx, int16_t *mat) +{ + if(!oam) oam = (uint16_t*)OAM_ADDR; + + oam += (idx << 4) + 3; + + oam[0] = *mat++; + oam[4] = *mat++; + oam[8] = *mat++; + oam[12] = *mat; +} diff --git a/src/gba/timer.c b/src/gba/timer.c new file mode 100644 index 0000000..f4b0c7a --- /dev/null +++ b/src/gba/timer.c @@ -0,0 +1,55 @@ +#include "intr.h" +#include "timer.h" + +#define F_CLK 16780000 +/* clock is 16.78MHz + * - no prescale: 59.595ns + * - prescale 64: 3.814us + * - prescale 256: 15.256us + * - prescale 1024: 61.025us + */ + +static void timer_intr(void); + +void init_timer(int tm, unsigned long rate_hz, void (*intr)(void)) +{ + static const unsigned long clk[] = {F_CLK, F_CLK / 64, F_CLK / 256, F_CLK / 1024}; + unsigned long count; + int pscl = 0; + + do { + count = clk[pscl] / rate_hz; + } while(count >= 65536 && ++pscl < 4); + + if(pscl >= 4) return; /* impossible rate */ + + REG_TMCNT_H(tm) = 0; + REG_TMCNT_L(tm) = 65536 - count; + if(intr) { + interrupt(INTR_TIMER0 + tm, intr); + unmask(INTR_TIMER0 + tm); + REG_TMCNT_H(tm) = TMCNT_IE; + } + REG_TMCNT_H(tm) |= TMCNT_EN | pscl; +} + +void reset_msec_timer(void) +{ + REG_TM0CNT_H &= ~TMCNT_EN; + interrupt(INTR_TIMER0, timer_intr); + timer_msec = 0; + REG_TM0CNT_L = 65535 - 16779; + REG_TM0CNT_H |= TMCNT_IE | TMCNT_EN; + unmask(INTR_TIMER0); +} + +void delay(unsigned long ms) +{ + unsigned long end = timer_msec + ms; + while(timer_msec < end); +} + +static void timer_intr(void) +{ + timer_msec++; +} diff --git a/src/gba/timer.h b/src/gba/timer.h new file mode 100644 index 0000000..3daed47 --- /dev/null +++ b/src/gba/timer.h @@ -0,0 +1,32 @@ +#ifndef TIMER_H_ +#define TIMER_H_ + +#include "gbaregs.h" + +#define enable_timer(x) \ + do { REG_TMCNT_H(x) |= TMCNT_EN; } while(0) + +#define disable_timer(x) \ + do { REG_TMCNT_H(x) &= ~TMCNT_EN; } while(0) + +volatile unsigned long timer_msec; + +void init_timer(int tm, unsigned long rate_hz, void (*intr)(void)); + +void reset_msec_timer(void); + +void delay(unsigned long ms); + +#ifdef __thumb__ +#define udelay(x) asm volatile ( \ + "0: sub %0, %0, #1\n\t" \ + "bne 0b\n\t" \ + :: "r"(x) : "cc") +#else +#define udelay(x) asm volatile ( \ + "0: subs %0, %0, #1\n\t" \ + "bne 0b\n\t" \ + :: "r"(x) : "cc") +#endif + +#endif /* TIMER_H_ */ diff --git a/src/gbaregs.h b/src/gbaregs.h deleted file mode 100644 index 3396cc9..0000000 --- a/src/gbaregs.h +++ /dev/null @@ -1,365 +0,0 @@ -#ifndef GBAREGS_H_ -#define GBAREGS_H_ - -#include - -#define VRAM_START_ADDR 0x6000000 -#define VRAM_BG_ADDR VRAM_START_ADDR -#define VRAM_OBJ_ADDR 0x6010000 -#define VRAM_LFB_OBJ_ADDR 0x6014000 -#define VRAM_LFB_FB0_ADDR VRAM_START_ADDR -#define VRAM_LFB_FB1_ADDR 0x600a000 - -/* address of character data block x (4 possible blocks, 16k each) */ -#define VRAM_CHR_BLOCK_ADDR(x) (VRAM_START_ADDR + ((x) << 14)) -/* address of screen data block x (32 possible blocks, 2k each) */ -#define VRAM_SCR_BLOCK_ADDR(x) (VRAM_START_ADDR + ((x) << 11)) - -/* fields of a background tile in screen memory */ -#define BGTILE_HFLIP 0x0400 -#define BGTILE_VFLIP 0x0800 -#define BGTILE_PAL(x) ((uint16_t)(x) << 12) - -/* color palette ram addresses for backgrounds and sprites */ -#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) - -/* battery backed RAM address */ -#define SRAM_ADDR 0xe000000 - -/* I/O space */ - -#define REG_BASE 0x4000000 -#define REG8(x) (*(volatile uint8_t*)(REG_BASE + (x))) -#define REG16(x) (*(volatile uint16_t*)(REG_BASE + (x))) -#define REG32(x) (*(volatile uint32_t*)(REG_BASE + (x))) - -/* ---- display registers ---- */ -#define REG_DISPCNT REG16(0x00) -#define REG_GREENSWAP REG16(0x02) -#define REG_DISPSTAT REG16(0x04) -#define REG_VCOUNT REG16(0x06) -#define REG_BG0CNT REG16(0x08) -#define REG_BG1CNT REG16(0x0a) -#define REG_BG2CNT REG16(0x0c) -#define REG_BG3CNT REG16(0x0e) -/* scrolling registers */ -#define REG_BG0HOFS REG16(0x10) -#define REG_BG0VOFS REG16(0x12) -#define REG_BG1HOFS REG16(0x14) -#define REG_BG1VOFS REG16(0x16) -#define REG_BG2HOFS REG16(0x18) -#define REG_BG2VOFS REG16(0x1a) -#define REG_BG3HOFS REG16(0x1c) -#define REG_BG3VOFS REG16(0x1e) -/* BG rotation and scaling registers */ -#define REG_BG2PA REG16(0x20) -#define REG_BG2PB REG16(0x22) -#define REG_BG2PC REG16(0x24) -#define REG_BG2PD REG16(0x26) -#define REG_BG2X REG32(0x28) -#define REG_BG2Y REG32(0x2c) -#define REG_BG3PA REG16(0x30) -#define REG_BG3PB REG16(0x32) -#define REG_BG3PC REG16(0x34) -#define REG_BG3PD REG16(0x36) -#define REG_BG3X REG32(0x38) -#define REG_BG3Y REG32(0x3c) -/* window registers */ -#define REG_WIN0H REG16(0x40) -#define REG_WIN1H REG16(0x42) -#define REG_WIN0V REG16(0x44) -#define REG_WIN1V REG16(0x46) -#define REG_WININ REG16(0x48) -#define REG_WINOUT REG16(0x4a) - -#define REG_MOSAIC REG16(0x4c) -/* color effects */ -#define REG_BLDCNT REG16(0x50) -#define REG_BLDALPHA REG16(0x52) -#define REG_BLDY REG16(0x54) - -/* ---- sound registers ---- */ -#define REG_SOUND1CNT_L REG16(0x60) -#define REG_SOUND1CNT_H REG16(0x62) -#define REG_SOUND1CNT_X REG16(0x64) -#define REG_SOUND2CNT_L REG16(0x68) -#define REG_SOUND2CNT_H REG16(0x6c) -#define REG_SOUND3CNT_L REG16(0x70) -#define REG_SOUND3CNT_H REG16(0x72) -#define REG_SOUND3CNT_X REG16(0x74) -#define REG_SOUND4CNT_L REG16(0x78) -#define REG_SOUND4CNT_H REG16(0x7c) -#define REG_SOUNDCNT_L REG16(0x80) -#define REG_SOUNDCNT_H REG16(0x82) -#define REG_SOUNDCNT_X REG16(0x84) -#define REG_SOUNDBIAS REG16(0x88) -#define WAVE_RAM_PTR ((unsigned char*)(REG_BASE + 0x90)) -#define REG_FIFO_A REG32(0xa0) -#define REG_FIFO_B REG32(0xa4) -#define FIFO_A_PTR ((unsigned char*)(REG_BASE + 0xa0)) -#define FIFO_B_PTR ((unsigned char*)(REG_BASE + 0xa4)) - -/* ---- DMA registers ---- */ -#define REG_DMA0SAD REG32(0xb0) -#define REG_DMA0DAD REG32(0xb4) -#define REG_DMA0CNT REG32(0xb8) -#define REG_DMA0CNT_L REG16(0xb8) -#define REG_DMA0CNT_H REG16(0xba) -#define REG_DMA1SAD REG32(0xbc) -#define REG_DMA1DAD REG32(0xc0) -#define REG_DMA1CNT REG32(0xc4) -#define REG_DMA1CNT_L REG16(0xc4) -#define REG_DMA1CNT_H REG16(0xc6) -#define REG_DMA2SAD REG32(0xc8) -#define REG_DMA2DAD REG32(0xcc) -#define REG_DMA2CNT REG32(0xd0) -#define REG_DMA2CNT_L REG16(0xd0) -#define REG_DMA2CNT_H REG16(0xd2) -#define REG_DMA3SAD REG32(0xd4) -#define REG_DMA3DAD REG32(0xd8) -#define REG_DMA3CNT REG32(0xdc) -#define REG_DMA3CNT_L REG16(0xdc) -#define REG_DMA3CNT_H REG16(0xde) - -/* ---- timer registers ---- */ -#define REG_TM0CNT_L REG16(0x100) -#define REG_TM0CNT_H REG16(0x102) -#define REG_TM1CNT_L REG16(0x104) -#define REG_TM1CNT_H REG16(0x106) -#define REG_TM2CNT_L REG16(0x108) -#define REG_TM2CNT_H REG16(0x10a) -#define REG_TM3CNT_L REG16(0x10c) -#define REG_TM3CNT_H REG16(0x10e) - -#define REG_TMCNT_L(x) REG16(0x100 + ((x) << 2)) -#define REG_TMCNT_H(x) REG16(0x102 + ((x) << 2)) - -/* ---- communication registers (serial/joybus/gpio) ---- */ -#define REG_SIODATA32 REG32(0x120) -#define REG_SIOMULTI0 REG16(0x120) -#define REG_SIOMULTI1 REG16(0x122) -#define REG_SIOMULTI2 REG16(0x124) -#define REG_SIOMULTI3 REG16(0x126) -#define REG_SIOCNT REG16(0x128) -#define REG_SIOMLT_SEND REG16(0x12a) -#define REG_SIODATA8 REG16(0x12a) -#define REG_RCNT REG16(0x134) -#define REG_JOYCNT REG16(0x140) -#define REG_JOY_RECV REG32(0x150) -#define REG_JOY_TRANS REG32(0x154) -#define REG_JOYSTAT REG16(0x158) - -/* ---- keypad registers ---- */ -#define REG_KEYINPUT REG16(0x130) -#define REG_KEYCNT REG16(0x132) - -/* ---- interrupts ---- */ -#define REG_IE REG16(0x200) -#define REG_IF REG16(0x202) -#define REG_WAITCNT REG16(0x204) -#define REG_IME REG16(0x208) - -#define REG_POSTFLG REG8(0x300) -#define REG_HALTCNT REG8(0x301) -#define REG_INTMEMCNT REG32(0x800) - -/* REG_DISPSTAT bits */ -#define DISPSTAT_VBLANK 0x01 -#define DISPSTAT_HBLANK 0x02 -#define DISPSTAT_VMATCH 0x04 -#define DISPSTAT_IEN_VBLANK 0x08 -#define DISPSTAT_IEN_HBLANK 0x10 -#define DISPSTAT_IEN_VMATCH 0x20 -#define DISPSTAT_VCOUNT(x) ((uint16_t)(x) << 8) - -/* REG_DISPCNT bits */ -#define DISPCNT_MODE(x) (x) -#define DISPCNT_FB1 0x0010 -#define DISPCNT_HBLANK_OBJPROC 0x0020 -#define DISPCNT_OBJMAP_1D 0x0040 -#define DISPCNT_FORCE_BLANK 0x0080 -#define DISPCNT_BG0 0x0100 -#define DISPCNT_BG1 0x0200 -#define DISPCNT_BG2 0x0400 -#define DISPCNT_BG3 0x0800 -#define DISPCNT_OBJ 0x1000 -#define DISPCNT_WIN0 0x2000 -#define DISPCNT_WIN1 0x4000 -#define DISPCNT_WINOBJ 0x8000 - -/* REG_BGXCNT bits */ -#define BGCNT_PRIO(x) ((uint16_t)(x)) -#define BGCNT_CHR_BASE(x) ((uint16_t)(x) << 2) -#define BGCNT_MOSAIC 0x0040 -#define BGCNT_256COL 0x0080 -#define BGCNT_SCR_BASE(x) ((uint16_t)(x) << 8) -#define BGCNT_WRAP 0x2000 - -#define BGCNT_SZ(x) ((uint16_t)(x) << 14) -#define BGCNT_SZ_TX_256X256 BGCNT_SZ(0) -#define BGCNT_SZ_RS_128X128 BGCNT_SZ(0) -#define BGCNT_SZ_TX_512X256 BGCNT_SZ(1) -#define BGCNT_SZ_RS_256X256 BGCNT_SZ(1) -#define BGCNT_SZ_TX_256X512 BGCNT_SZ(2) -#define BGCNT_SZ_RS_512X512 BGCNT_SZ(2) -#define BGCNT_SZ_TX_512X512 BGCNT_SZ(3) -#define BGCNT_SZ_RS_1024X1024 BGCNT_SZ(3) - -/* REG_BLDCNT bits */ -#define BLDCNT_A_BG0 0x0001 -#define BLDCNT_A_BG1 0x0002 -#define BLDCNT_A_BG2 0x0004 -#define BLDCNT_A_BG3 0x0008 -#define BLDCNT_A_OBJ 0x0010 -#define BLDCNT_A_BACKDROP 0x0020 -#define BLDCNT_B_BG0 0x0100 -#define BLDCNT_B_BG1 0x0200 -#define BLDCNT_B_BG2 0x0400 -#define BLDCNT_B_BG3 0x0800 -#define BLDCNT_B_OBJ 0x1000 -#define BLDCNT_B_BACKDROP 0x2000 - -#define BLDCNT_ALPHA 0x0040 -#define BLDCNT_BRIGHTEN 0x0080 -#define BLDCNT_DARKEN 0x00c0 - -/* REG_IF bits */ -#define IF_VBLANK 0x0001 -#define IF_HBLANK 0x0002 -#define IF_VMATCH 0x0004 -#define IF_TIMER0 0x0008 -#define IF_TIMER1 0x0010 -#define IF_TIMER2 0x0020 -#define IF_TIMER3 0x0040 -#define IF_COMM 0x0080 -#define IF_DMA0 0x0100 -#define IF_DMA1 0x0200 -#define IF_DMA2 0x0400 -#define IF_DMA3 0x0800 -#define IF_KEY 0x1000 -#define IF_GPAK 0x2000 - -/* REG_TMXCNT bits */ -#define TMCNT_PRESCL_CLK1 0 -#define TMCNT_PRESCL_CLK64 1 -#define TMCNT_PRESCL_CLK256 2 -#define TMCNT_PRESCL_CLK1024 3 - -#define TMCNT_CASCADE 0x04 -#define TMCNT_IE 0x40 -#define TMCNT_EN 0x80 - -/* REG_KEY* bits */ -#define KEY_A 0x0001 -#define KEY_B 0x0002 -#define KEY_SELECT 0x0004 -#define KEY_START 0x0008 -#define KEY_RIGHT 0x0010 -#define KEY_LEFT 0x0020 -#define KEY_UP 0x0040 -#define KEY_DOWN 0x0080 -#define KEY_RT 0x0100 -#define KEY_LT 0x0200 - -#define KEYCNT_IE 0x4000 -#define KEYCNT_IAND 0x8000 - -/* REG_SOUNDCNT_L bits */ -#define SCNT_SS_LVOL(x) ((x) & 7) -#define SCNT_SS_RVOL(x) (((x) & 7) << 4) -#define SCNT_SS_VOL(x) (SCNT_SS_LVOL(x) | SCNT_SS_RVOL(x)) -#define SCNT_SS1_EN_R 0x0100 -#define SCNT_SS2_EN_R 0x0200 -#define SCNT_SS3_EN_R 0x0400 -#define SCNT_SS4_EN_R 0x0800 -#define SCNT_SS_EN_R(x) (SCNT_SS1_EN_R << (x)) -#define SCNT_SS1_EN_L 0x1000 -#define SCNT_SS2_EN_L 0x2000 -#define SCNT_SS3_EN_L 0x4000 -#define SCNT_SS4_EN_L 0x8000 -#define SCNT_SS_EN_L(x) (SCNT_SS1_EN_L << (x)) -#define SCNT_SS1_EN (SCNT_SS1_EN_R | SCNT_SS1_EN_L) -#define SCNT_SS2_EN (SCNT_SS2_EN_R | SCNT_SS2_EN_L) -#define SCNT_SS3_EN (SCNT_SS3_EN_R | SCNT_SS3_EN_L) -#define SCNT_SS4_EN (SCNT_SS4_EN_R | SCNT_SS4_EN_L) -#define SCNT_SS_EN(x) (SCNT_SS_EN_L(x) | SCNT_SS_EN_R(x)) - -#define SCNT_SS1 0 -#define SCNT_SS2 1 -#define SCNT_SS3 2 -#define SCNT_SS4 3 - -/* REG_SOUNDCNT_X bits */ -#define SCNT_MASTER_EN 0x0080 - -/* REG_SOUNDCNT_H bits */ -#define SCNT_SS_VOL_QRT 0x0000 -#define SCNT_SS_VOL_HALF 0x0001 -#define SCNT_SS_VOL_FULL 0x0002 -#define SCNT_DSA_VOL_HALF 0 -#define SCNT_DSA_VOL_FULL 0x0004 -#define SCNT_DSB_VOL_HALF 0 -#define SCNT_DSB_VOL_FULL 0x0008 -#define SCNT_DSA_EN_R 0x0100 -#define SCNT_DSA_EN_L 0x0200 -#define SCNT_DSA_TIMER0 0 -#define SCNT_DSA_TIMER1 0x0400 -#define SCNT_DSA_CLRFIFO 0x0800 -#define SCNT_DSB_EN_R 0x1000 -#define SCNT_DSB_EN_L 0x2000 -#define SCNT_DSB_TIMER0 0 -#define SCNT_DSB_TIMER1 0x4000 -#define SCNT_DSB_CLRFIFO 0x8000 - -/* REG_DMAxCNT_H bits */ -#define DMACNTH_DST_INC 0 -#define DMACNTH_DST_DEC 0x0020 -#define DMACNTH_DST_FIXED 0x0040 -#define DMACNTH_INC_RELOAD 0x0060 -#define DMACNTH_SRC_INC 0 -#define DMACNTH_SRC_DEC 0x0080 -#define DMACNTH_SRC_FIXED 0x0100 -#define DMACNTH_REPEAT 0x0200 -#define DMACNTH_16BIT 0 -#define DMACNTH_32BIT 0x0400 -#define DMACNTH_VBLANK 0x1000 -#define DMACNTH_HBLANK 0x2000 -#define DMACNTH_SOUND 0x3000 -#define DMACNTH_IEN 0x4000 -#define DMACNTH_EN 0x8000 - -#define DMACNT_DST_INC 0 -#define DMACNT_DST_DEC 0x00200000 -#define DMACNT_DST_FIXED 0x00400000 -#define DMACNT_INC_RELOAD 0x00600000 -#define DMACNT_SRC_INC 0 -#define DMACNT_SRC_DEC 0x00800000 -#define DMACNT_SRC_FIXED 0x01000000 -#define DMACNT_REPEAT 0x02000000 -#define DMACNT_16BIT 0 -#define DMACNT_32BIT 0x04000000 -#define DMACNT_VBLANK 0x10000000 -#define DMACNT_HBLANK 0x20000000 -#define DMACNT_SOUND 0x30000000 -#define DMACNT_IEN 0x40000000 -#define DMACNT_EN 0x80000000 - -/* REG_WAITCNT bits */ -#define WAITCNT_ROM_4_2 0x0000 -#define WAITCNT_ROM_3_2 0x0004 -#define WAITCNT_ROM_2_2 0x0008 -#define WAITCNT_ROM_8_2 0x000c -#define WAITCNT_ROM_4_1 0x0010 -#define WAITCNT_ROM_3_1 0x0014 -#define WAITCNT_ROM_2_1 0x0018 -#define WAITCNT_ROM_8_1 0x001c -#define WAITCNT_PREFETCH 0x4000 - -#endif /* GBAREGS_H_ */ diff --git a/src/input.c b/src/input.c deleted file mode 100644 index 6a66dd1..0000000 --- a/src/input.c +++ /dev/null @@ -1,39 +0,0 @@ -#include "input.h" -#include "gbaregs.h" -#include "intr.h" - -static void keyintr(void); - -static uint16_t bnstate; - -void select_input(uint16_t bmask) -{ - bnstate = 0; - - mask(INTR_KEY); - if(bmask) { - REG_KEYCNT = bmask | KEYCNT_IE; - interrupt(INTR_KEY, keyintr); - unmask(INTR_KEY); - } else { - REG_KEYCNT = 0; - interrupt(INTR_KEY, 0); - } -} - -uint16_t get_input(void) -{ - uint16_t s; - - mask(INTR_KEY); - s = bnstate; - bnstate = 0; - unmask(INTR_KEY); - - return s; -} - -static void keyintr(void) -{ - bnstate |= ~REG_KEYINPUT; -} diff --git a/src/intr.c b/src/intr.c deleted file mode 100644 index 5858bf1..0000000 --- a/src/intr.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "intr.h" - -#define MAX_INTR 14 -static void (*intr_table[MAX_INTR])(void); - -__attribute__ ((target("arm"), section(".iwram"))) -static void intr_handler(void) -{ - int i; - uint16_t iflags; - - iflags = REG_IF; - - for(i=0; imobs = 0; lvl->items = 0; + lvl->xshift = 0; + x = ncols - 1; + while(x) { + x >>= 1; + lvl->xshift++; + } + str = descstr; cell = lvl->cells; @@ -52,6 +59,12 @@ struct level *init_level(const char *descstr) cell->type = CELL_SOLID; } else { cell->type = CELL_WALK; + switch(*str) { + case 's': + lvl->orgx = j; + lvl->orgy = i; + break; + } } cell++; while(*++str == '\n') str++; @@ -79,6 +92,12 @@ void free_level(struct level *lvl) } } +struct cell *level_cell(struct level *lvl, int cx, int cy) +{ + return lvl->cells + (cy << lvl->xshift) + cx; +} + +/* generated with tools/vistab */ struct {int dx, dy;} visoffs[8][32] = { /* dir 0 */ {{-4,-4}, {4,-4}, {-3,-4}, {3,-4}, {-2,-4}, {2,-4}, {-3,-3}, {3,-3}, {-1,-4}, @@ -137,7 +156,9 @@ void upd_vis(struct level *lvl, struct player *p) x = p->cx + visoffs[dir][idx].dx; y = p->cy + visoffs[dir][idx].dy; cptr = lvl->cells + y * lvl->width + x; - lvl->vis[lvl->numvis++] = cptr; + if(cptr->type != CELL_SOLID) { + lvl->vis[lvl->numvis++] = cptr; + } } while(visoffs[dir][idx].dx | visoffs[dir][idx].dy); } diff --git a/src/level.h b/src/level.h index ed32ce1..dc9af8d 100644 --- a/src/level.h +++ b/src/level.h @@ -40,6 +40,7 @@ struct level { int width, height; int orgx, orgy; unsigned int xmask; + int xshift; struct cell *cells; struct mob *mobs; @@ -55,6 +56,8 @@ struct player; struct level *init_level(const char *descstr); void free_level(struct level *lvl); +struct cell *level_cell(struct level *lvl, int cx, int cy); + void upd_vis(struct level *lvl, struct player *p); void cell_to_pos(struct level *lvl, int cx, int cy, int32_t *px, int32_t *py); diff --git a/src/main.c b/src/main.c deleted file mode 100644 index 068f3d1..0000000 --- a/src/main.c +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include "gbaregs.h" -#include "intr.h" -#include "debug.h" -#include "game.h" -#include "maxmod.h" - -static void vblank(void); -static void nopfunc(void); - -int main(void) -{ - int i; - volatile uint16_t *cptr; - - intr_init(); - - REG_WAITCNT = WAITCNT_PREFETCH | WAITCNT_ROM_2_1; - - cptr = (uint16_t*)CRAM_BG_ADDR; - for(i=0; i<256; i++) { - int c = i >> 3; - *cptr++ = c | ((c >> 1) << 10); - } - -#ifndef NOSOUND - mmInitDefault(sound_data, 8); - mmStart(MOD_POPCORN, MM_PLAY_LOOP); -#endif - - screen_vblank = nopfunc; - - intr_disable(); - interrupt(INTR_VBLANK, vblank); - REG_DISPSTAT |= DISPSTAT_IEN_VBLANK; - unmask(INTR_VBLANK); - - intr_enable(); - gamescr(); - - for(;;); - return 0; -} - -static void vblank(void) -{ - vblperf_count++; - - screen_vblank(); - -#ifndef NOSOUND - mmVBlank(); - mmFrame(); -#endif -} - -static void nopfunc(void) -{ -} diff --git a/src/pc/main.c b/src/pc/main.c new file mode 100644 index 0000000..7466e1b --- /dev/null +++ b/src/pc/main.c @@ -0,0 +1,286 @@ +#include +#include +#include +#include +#include "miniglut.h" +#include "game.h" + +static void display(void); +static void idle(void); +static void reshape(int x, int y); +static void keydown(unsigned char key, int x, int y); +static void keyup(unsigned char key, int x, int y); +static void skeydown(int key, int x, int y); +static void skeyup(int key, int x, int y); +static int translate_special(int skey); +static unsigned int next_pow2(unsigned int x); +static void set_fullscreen(int fs); +static void set_vsync(int vsync); + +static unsigned int num_pressed; +static unsigned char keystate[256]; + +static unsigned long start_time; +static unsigned int modkeys; + +static float win_aspect; +static unsigned int tex; + +static int tex_xsz, tex_ysz; +static uint32_t convbuf[240 * 160]; + +#ifdef __unix__ +#include +static Display *xdpy; +static Window xwin; + +static void (*glx_swap_interval_ext)(); +static void (*glx_swap_interval_sgi)(); +#endif +#ifdef _WIN32 +#include +static PROC wgl_swap_interval_ext; +#endif + +int main(int argc, char **argv) +{ + glutInit(&argc, argv); + glutInitWindowSize(800, 600); + glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); + glutCreateWindow("GBAjam22 PC build"); + + glutDisplayFunc(display); + glutIdleFunc(idle); + glutReshapeFunc(reshape); + glutKeyboardFunc(keydown); + glutKeyboardUpFunc(keyup); + glutSpecialFunc(skeydown); + glutSpecialUpFunc(skeyup); + + glutSetCursor(GLUT_CURSOR_NONE); + + glEnable(GL_TEXTURE_2D); + glEnable(GL_CULL_FACE); + +#ifdef __unix__ + xdpy = glXGetCurrentDisplay(); + xwin = glXGetCurrentDrawable(); + + if(!(glx_swap_interval_ext = glXGetProcAddress((unsigned char*)"glXSwapIntervalEXT"))) { + glx_swap_interval_sgi = glXGetProcAddress((unsigned char*)"glXSwapIntervalSGI"); + } +#endif +#ifdef _WIN32 + wgl_swap_interval_ext = wglGetProcAddress("wglSwapIntervalEXT"); +#endif + + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + + tex_xsz = next_pow2(240); + tex_ysz = next_pow2(160); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_xsz, tex_ysz, 0, GL_RGBA, + GL_UNSIGNED_BYTE, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glScalef(240.0f / tex_xsz, 160.0f / tex_ysz, 1); + + gamescr(); + return 0; +} + +void blit_frame(void *pixels, int vsync) +{ + int i, npix = fb_width * fb_height; + uint32_t *dptr = convbuf; + uint16_t *sptr = pixels; + + for(i=0; i= 1.5f) { + glScalef(1.5f / win_aspect, 1, 1); + } else { + glScalef(1, win_aspect / 1.5f, 1); + } + + glClear(GL_COLOR_BUFFER_BIT); + + glBegin(GL_QUADS); + glTexCoord2f(0, 1); + glVertex2f(-1, -1); + glTexCoord2f(1, 1); + glVertex2f(1, -1); + glTexCoord2f(1, 0); + glVertex2f(1, 1); + glTexCoord2f(0, 0); + glVertex2f(-1, 1); + glEnd(); + + glutSwapBuffers(); + assert(glGetError() == GL_NO_ERROR); +} + +int kb_isdown(int key) +{ + switch(key) { + case KB_ANY: + return num_pressed; + + case KB_ALT: + return keystate[KB_LALT] + keystate[KB_RALT]; + + case KB_CTRL: + return keystate[KB_LCTRL] + keystate[KB_RCTRL]; + } + + if(isalpha(key)) { + key = tolower(key); + } + return keystate[key]; +} + +static void display(void) +{ + inp_update(); + + time_msec = get_msec(); + draw(); +} + +static void idle(void) +{ + glutPostRedisplay(); +} + +static void reshape(int x, int y) +{ + win_aspect = (float)x / (float)y; + glViewport(0, 0, x, y); +} + +static void keydown(unsigned char key, int x, int y) +{ + modkeys = glutGetModifiers(); + + if((key == '\n' || key == '\r') && (modkeys & GLUT_ACTIVE_ALT)) { + opt.fullscreen ^= 1; + set_fullscreen(opt.fullscreen); + return; + } + keystate[key] = 1; + //game_key(key, 1); +} + +static void keyup(unsigned char key, int x, int y) +{ + keystate[key] = 0; + //game_key(key, 0); +} + +static void skeydown(int key, int x, int y) +{ + key = translate_special(key); + keystate[key] = 1; + //game_key(key, 1); +} + +static void skeyup(int key, int x, int y) +{ + key = translate_special(key); + keystate[key] = 0; + //game_key(key, 0); +} + +static int translate_special(int skey) +{ + switch(skey) { + case 127: + return 127; +/* case GLUT_KEY_LEFT: + return KB_LEFT; + case GLUT_KEY_RIGHT: + return KB_RIGHT; + case GLUT_KEY_UP: + return KB_UP; + case GLUT_KEY_DOWN: + return KB_DOWN; + case GLUT_KEY_PAGE_UP: + return KB_PGUP; + case GLUT_KEY_PAGE_DOWN: + return KB_PGDN; + case GLUT_KEY_HOME: + return KB_HOME; + case GLUT_KEY_END: + return KB_END; + default: + if(skey >= GLUT_KEY_F1 && skey <= GLUT_KEY_F12) { + return KB_F1 + skey - GLUT_KEY_F1; + } + */ + } + return 0; +} + +static unsigned int next_pow2(unsigned int x) +{ + x--; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return x + 1; +} + +static void set_fullscreen(int fs) +{ + static int win_x, win_y; + + if(fs) { + if(win_x == 0) { + win_x = glutGet(GLUT_WINDOW_WIDTH); + win_y = glutGet(GLUT_WINDOW_HEIGHT); + glutFullScreen(); + } + } else { + if(win_x) { + glutReshapeWindow(win_x, win_y); + win_x = win_y = 0; + } + } +} + +#ifdef __unix__ +static void set_vsync(int vsync) +{ + vsync = vsync ? 1 : 0; + if(glx_swap_interval_ext) { + glx_swap_interval_ext(xdpy, xwin, vsync); + } else if(glx_swap_interval_sgi) { + glx_swap_interval_sgi(vsync); + } +} +#endif +#ifdef WIN32 +static void set_vsync(int vsync) +{ + if(wgl_swap_interval_ext) { + wgl_swap_interval_ext(vsync ? 1 : 0); + } +} +#endif diff --git a/src/pc/miniglut.c b/src/pc/miniglut.c new file mode 100644 index 0000000..54838ae --- /dev/null +++ b/src/pc/miniglut.c @@ -0,0 +1,2646 @@ +/* +MiniGLUT - minimal GLUT subset without dependencies +Copyright (C) 2020-2022 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 . + */ +#if defined(unix) || defined(__unix__) + +#include +#include +#include +#include +#define BUILD_X11 + +#ifndef GLX_SAMPLE_BUFFERS_ARB +#define GLX_SAMPLE_BUFFERS_ARB 100000 +#define GLX_SAMPLES_ARB 100001 +#endif +#ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB +#define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20b2 +#endif + +static Display *dpy; +static Window win, root; +static Colormap cmap; +static int cmap_size; +static int scr; +static GLXContext ctx; +static Atom xa_wm_proto, xa_wm_del_win; +static Atom xa_net_wm_state, xa_net_wm_state_fullscr; +static Atom xa_motif_wm_hints; +static Atom xa_motion_event, xa_button_press_event, xa_button_release_event, xa_command_event; +static unsigned int evmask; +static Cursor blank_cursor; + +static int have_netwm_fullscr(void); + +#elif defined(_WIN32) + +#include +#define BUILD_WIN32 + +static LRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam); + +static HINSTANCE hinst; +static HWND win; +static HDC dc; +static HGLRC ctx; +static HPALETTE cmap; +static int cmap_size; + +#else +#error unsupported platform +#endif +#include +#include "miniglut.h" + +#ifdef _MSC_VER +#pragma warning (disable: 4244 4305) +#endif + + +struct ctx_info { + int rsize, gsize, bsize, asize; + int zsize, ssize; + int dblbuf; + int samples; + int stereo; + int srgb; +}; + +static void cleanup(void); +static void create_window(const char *title); +static void get_window_pos(int *x, int *y); +static void get_window_size(int *w, int *h); +static void get_screen_size(int *scrw, int *scrh); + +static long get_msec(void); +static void panic(const char *msg); +static void sys_exit(int status); +static int sys_write(int fd, const void *buf, int count); + + +static int init_x = -1, init_y, init_width = 256, init_height = 256; +static unsigned int init_mode; + +static struct ctx_info ctx_info; +static int cur_cursor = GLUT_CURSOR_INHERIT; +static int ignore_key_repeat; + +static glut_cb cb_display; +static glut_cb cb_idle; +static glut_cb_reshape cb_reshape; +static glut_cb_state cb_vis, cb_entry; +static glut_cb_keyb cb_keydown, cb_keyup; +static glut_cb_special cb_skeydown, cb_skeyup; +static glut_cb_mouse cb_mouse; +static glut_cb_motion cb_motion, cb_passive; +static glut_cb_sbmotion cb_sball_motion, cb_sball_rotate; +static glut_cb_sbbutton cb_sball_button; + +static int fullscreen; +static int prev_win_x, prev_win_y, prev_win_width, prev_win_height; + +static int win_width, win_height; +static int mapped; +static int quit; +static int upd_pending; +static int modstate; + +void glutInit(int *argc, char **argv) +{ +#ifdef BUILD_X11 + Pixmap blankpix = 0; + XColor xcol; + + if(!(dpy = XOpenDisplay(0))) { + panic("Failed to connect to the X server\n"); + } + scr = DefaultScreen(dpy); + root = RootWindow(dpy, scr); + xa_wm_proto = XInternAtom(dpy, "WM_PROTOCOLS", False); + xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + xa_motif_wm_hints = XInternAtom(dpy, "_MOTIF_WM_HINTS", False); + xa_net_wm_state_fullscr = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + if(have_netwm_fullscr()) { + xa_net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False); + } + + xa_motion_event = XInternAtom(dpy, "MotionEvent", True); + xa_button_press_event = XInternAtom(dpy, "ButtonPressEvent", True); + xa_button_release_event = XInternAtom(dpy, "ButtonReleaseEvent", True); + xa_command_event = XInternAtom(dpy, "CommandEvent", True); + + evmask = ExposureMask | StructureNotifyMask; + + if((blankpix = XCreateBitmapFromData(dpy, root, (char*)&blankpix, 1, 1))) { + blank_cursor = XCreatePixmapCursor(dpy, blankpix, blankpix, &xcol, &xcol, 0, 0); + XFreePixmap(dpy, blankpix); + } + +#endif +#ifdef BUILD_WIN32 + WNDCLASSEX wc = {0}; + + hinst = GetModuleHandle(0); + + wc.cbSize = sizeof wc; + wc.hbrBackground = GetStockObject(BLACK_BRUSH); + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION); + wc.hInstance = hinst; + wc.lpfnWndProc = handle_message; + wc.lpszClassName = "MiniGLUT"; + wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + if(!RegisterClassEx(&wc)) { + panic("Failed to register \"MiniGLUT\" window class\n"); + } + + if(init_x == -1) { + get_screen_size(&init_x, &init_y); + init_x >>= 3; + init_y >>= 3; + } +#endif +} + +void glutInitWindowPosition(int x, int y) +{ + init_x = x; + init_y = y; +} + +void glutInitWindowSize(int xsz, int ysz) +{ + init_width = xsz; + init_height = ysz; +} + +void glutInitDisplayMode(unsigned int mode) +{ + init_mode = mode; +} + +void glutCreateWindow(const char *title) +{ + create_window(title); +} + +void glutExit(void) +{ + quit = 1; +} + +void glutMainLoop(void) +{ + while(!quit) { + glutMainLoopEvent(); + } +} + +void glutPostRedisplay(void) +{ + upd_pending = 1; +} + +void glutIgnoreKeyRepeat(int ignore) +{ + ignore_key_repeat = ignore; +} + +#define UPD_EVMASK(x) \ + do { \ + if(func) { \ + evmask |= x; \ + } else { \ + evmask &= ~(x); \ + } \ + if(win) XSelectInput(dpy, win, evmask); \ + } while(0) + + +void glutIdleFunc(glut_cb func) +{ + cb_idle = func; +} + +void glutDisplayFunc(glut_cb func) +{ + cb_display = func; +} + +void glutReshapeFunc(glut_cb_reshape func) +{ + cb_reshape = func; +} + +void glutVisibilityFunc(glut_cb_state func) +{ + cb_vis = func; +#ifdef BUILD_X11 + UPD_EVMASK(VisibilityChangeMask); +#endif +} + +void glutEntryFunc(glut_cb_state func) +{ + cb_entry = func; +#ifdef BUILD_X11 + UPD_EVMASK(EnterWindowMask | LeaveWindowMask); +#endif +} + +void glutKeyboardFunc(glut_cb_keyb func) +{ + cb_keydown = func; +#ifdef BUILD_X11 + UPD_EVMASK(KeyPressMask); +#endif +} + +void glutKeyboardUpFunc(glut_cb_keyb func) +{ + cb_keyup = func; +#ifdef BUILD_X11 + UPD_EVMASK(KeyReleaseMask); +#endif +} + +void glutSpecialFunc(glut_cb_special func) +{ + cb_skeydown = func; +#ifdef BUILD_X11 + UPD_EVMASK(KeyPressMask); +#endif +} + +void glutSpecialUpFunc(glut_cb_special func) +{ + cb_skeyup = func; +#ifdef BUILD_X11 + UPD_EVMASK(KeyReleaseMask); +#endif +} + +void glutMouseFunc(glut_cb_mouse func) +{ + cb_mouse = func; +#ifdef BUILD_X11 + UPD_EVMASK(ButtonPressMask | ButtonReleaseMask); +#endif +} + +void glutMotionFunc(glut_cb_motion func) +{ + cb_motion = func; +#ifdef BUILD_X11 + UPD_EVMASK(ButtonMotionMask); +#endif +} + +void glutPassiveMotionFunc(glut_cb_motion func) +{ + cb_passive = func; +#ifdef BUILD_X11 + UPD_EVMASK(PointerMotionMask); +#endif +} + +void glutSpaceballMotionFunc(glut_cb_sbmotion func) +{ + cb_sball_motion = func; +} + +void glutSpaceballRotateFunc(glut_cb_sbmotion func) +{ + cb_sball_rotate = func; +} + +void glutSpaceballButtonFunc(glut_cb_sbbutton func) +{ + cb_sball_button = func; +} + +int glutGet(unsigned int s) +{ + int x, y; + switch(s) { + case GLUT_WINDOW_X: + get_window_pos(&x, &y); + return x; + case GLUT_WINDOW_Y: + get_window_pos(&x, &y); + return y; + case GLUT_WINDOW_WIDTH: + get_window_size(&x, &y); + return x; + case GLUT_WINDOW_HEIGHT: + get_window_size(&x, &y); + return y; + case GLUT_WINDOW_BUFFER_SIZE: + return ctx_info.rsize + ctx_info.gsize + ctx_info.bsize + ctx_info.asize; + case GLUT_WINDOW_STENCIL_SIZE: + return ctx_info.ssize; + case GLUT_WINDOW_DEPTH_SIZE: + return ctx_info.zsize; + case GLUT_WINDOW_RED_SIZE: + return ctx_info.rsize; + case GLUT_WINDOW_GREEN_SIZE: + return ctx_info.gsize; + case GLUT_WINDOW_BLUE_SIZE: + return ctx_info.bsize; + case GLUT_WINDOW_ALPHA_SIZE: + return ctx_info.asize; + case GLUT_WINDOW_DOUBLEBUFFER: + return ctx_info.dblbuf; + case GLUT_WINDOW_RGBA: + return 1; + case GLUT_WINDOW_NUM_SAMPLES: + return ctx_info.samples; + case GLUT_WINDOW_STEREO: + return ctx_info.stereo; + case GLUT_WINDOW_SRGB: + return ctx_info.srgb; + case GLUT_WINDOW_CURSOR: + return cur_cursor; + case GLUT_WINDOW_COLORMAP_SIZE: + return cmap_size; + case GLUT_SCREEN_WIDTH: + get_screen_size(&x, &y); + return x; + case GLUT_SCREEN_HEIGHT: + get_screen_size(&x, &y); + return y; + case GLUT_INIT_DISPLAY_MODE: + return init_mode; + case GLUT_INIT_WINDOW_X: + return init_x; + case GLUT_INIT_WINDOW_Y: + return init_y; + case GLUT_INIT_WINDOW_WIDTH: + return init_width; + case GLUT_INIT_WINDOW_HEIGHT: + return init_height; + case GLUT_ELAPSED_TIME: + return get_msec(); + default: + break; + } + return 0; +} + +int glutGetModifiers(void) +{ + return modstate; +} + +static int is_space(int c) +{ + return c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r'; +} + +static const char *skip_space(const char *s) +{ + while(*s && is_space(*s)) s++; + return s; +} + +int glutExtensionSupported(char *ext) +{ + const char *str, *eptr; + + if(!(str = (const char*)glGetString(GL_EXTENSIONS))) { + return 0; + } + + while(*str) { + str = skip_space(str); + eptr = skip_space(ext); + while(*str && !is_space(*str) && *eptr && *str == *eptr) { + str++; + eptr++; + } + if((!*str || is_space(*str)) && !*eptr) { + return 1; + } + while(*str && !is_space(*str)) str++; + } + + return 0; +} + + +/* --------------- UNIX/X11 implementation ----------------- */ +#ifdef BUILD_X11 +enum { + SPNAV_EVENT_ANY, /* used by spnav_remove_events() */ + SPNAV_EVENT_MOTION, + SPNAV_EVENT_BUTTON /* includes both press and release */ +}; + +struct spnav_event_motion { + int type; + int x, y, z; + int rx, ry, rz; + unsigned int period; + int *data; +}; + +struct spnav_event_button { + int type; + int press; + int bnum; +}; + +union spnav_event { + int type; + struct spnav_event_motion motion; + struct spnav_event_button button; +}; + + +static void handle_event(XEvent *ev); + +static int spnav_window(Window win); +static int spnav_event(const XEvent *xev, union spnav_event *event); +static int spnav_remove_events(int type); + + +void glutMainLoopEvent(void) +{ + XEvent ev; + + if(!cb_display) { + panic("display callback not set"); + } + + if(!upd_pending && !cb_idle) { + XNextEvent(dpy, &ev); + handle_event(&ev); + if(quit) goto end; + } + while(XPending(dpy)) { + XNextEvent(dpy, &ev); + handle_event(&ev); + if(quit) goto end; + } + + if(cb_idle) { + cb_idle(); + } + + if(upd_pending && mapped) { + upd_pending = 0; + cb_display(); + } + +end: + if(quit) { + cleanup(); + } +} + +static void cleanup(void) +{ + if(win) { + spnav_window(root); + glXMakeCurrent(dpy, 0, 0); + XDestroyWindow(dpy, win); + } +} + +static KeySym translate_keysym(KeySym sym) +{ + switch(sym) { + case XK_Escape: + return 27; + case XK_BackSpace: + return '\b'; + case XK_Linefeed: + return '\r'; + case XK_Return: + return '\n'; + case XK_Delete: + return 127; + case XK_Tab: + return '\t'; + default: + break; + } + return sym; +} + +static void handle_event(XEvent *ev) +{ + KeySym sym; + union spnav_event sev; + + switch(ev->type) { + case MapNotify: + mapped = 1; + break; + case UnmapNotify: + mapped = 0; + break; + case ConfigureNotify: + if(cb_reshape && (ev->xconfigure.width != win_width || ev->xconfigure.height != win_height)) { + win_width = ev->xconfigure.width; + win_height = ev->xconfigure.height; + cb_reshape(ev->xconfigure.width, ev->xconfigure.height); + } + break; + + case ClientMessage: + if(ev->xclient.message_type == xa_wm_proto) { + if(ev->xclient.data.l[0] == xa_wm_del_win) { + quit = 1; + } + } + if(spnav_event(ev, &sev)) { + switch(sev.type) { + case SPNAV_EVENT_MOTION: + if(cb_sball_motion) { + cb_sball_motion(sev.motion.x, sev.motion.y, sev.motion.z); + } + if(cb_sball_rotate) { + cb_sball_rotate(sev.motion.rx, sev.motion.ry, sev.motion.rz); + } + spnav_remove_events(SPNAV_EVENT_MOTION); + break; + + case SPNAV_EVENT_BUTTON: + if(cb_sball_button) { + cb_sball_button(sev.button.bnum + 1, sev.button.press ? GLUT_DOWN : GLUT_UP); + } + break; + + default: + break; + } + } + break; + + case Expose: + upd_pending = 1; + break; + + case KeyPress: + if(0) { + case KeyRelease: + if(ignore_key_repeat && XEventsQueued(dpy, QueuedAfterReading)) { + XEvent next; + XPeekEvent(dpy, &next); + + if(next.type == KeyPress && next.xkey.keycode == ev->xkey.keycode && + next.xkey.time == ev->xkey.time) { + /* this is a key-repeat event, ignore the release and consume + * the following press + */ + XNextEvent(dpy, &next); + break; + } + } + } + modstate = ev->xkey.state & (ShiftMask | ControlMask | Mod1Mask); + if(!(sym = XLookupKeysym(&ev->xkey, 0))) { + break; + } + sym = translate_keysym(sym); + if(sym < 256) { + if(ev->type == KeyPress) { + if(cb_keydown) cb_keydown((unsigned char)sym, ev->xkey.x, ev->xkey.y); + } else { + if(cb_keyup) cb_keyup((unsigned char)sym, ev->xkey.x, ev->xkey.y); + } + } else { + if(ev->type == KeyPress) { + if(cb_skeydown) cb_skeydown(sym, ev->xkey.x, ev->xkey.y); + } else { + if(cb_skeyup) cb_skeyup(sym, ev->xkey.x, ev->xkey.y); + } + } + break; + + case ButtonPress: + case ButtonRelease: + modstate = ev->xbutton.state & (ShiftMask | ControlMask | Mod1Mask); + if(cb_mouse) { + int bn = ev->xbutton.button - Button1; + cb_mouse(bn, ev->type == ButtonPress ? GLUT_DOWN : GLUT_UP, + ev->xbutton.x, ev->xbutton.y); + } + break; + + case MotionNotify: + if(ev->xmotion.state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)) { + if(cb_motion) cb_motion(ev->xmotion.x, ev->xmotion.y); + } else { + if(cb_passive) cb_passive(ev->xmotion.x, ev->xmotion.y); + } + break; + + case VisibilityNotify: + if(cb_vis) { + cb_vis(ev->xvisibility.state == VisibilityFullyObscured ? GLUT_NOT_VISIBLE : GLUT_VISIBLE); + } + break; + case EnterNotify: + if(cb_entry) cb_entry(GLUT_ENTERED); + break; + case LeaveNotify: + if(cb_entry) cb_entry(GLUT_LEFT); + break; + } +} + +void glutSwapBuffers(void) +{ + glXSwapBuffers(dpy, win); +} + +/* BUG: + * set_fullscreen_mwm removes the decorations with MotifWM hints, and then it + * needs to resize the window to make it fullscreen. The way it does this is by + * querying the size of the root window (see get_screen_size), which in the + * case of multi-monitor setups will be the combined size of all monitors. + * This is problematic; the way to solve it is to use the XRandR extension, or + * the Xinerama extension, to figure out the dimensions of the correct video + * output, which would add potentially two extension support libraries to our + * dependencies list. + * Moreover, any X installation modern enough to support XR&R will almost + * certainly be running a window manager supporting the EHWM + * _NET_WM_STATE_FULLSCREEN method (set_fullscreen_ewmh), which does not rely + * on manual resizing, and is used in preference if available, making this + * whole endeavor pointless. + * So I'll just leave it with set_fullscreen_mwm covering the entire + * multi-monitor area for now. + */ + +struct mwm_hints { + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long input_mode; + unsigned long status; +}; + +#define MWM_HINTS_DECORATIONS 2 +#define MWM_DECOR_ALL 1 + +static void set_fullscreen_mwm(int fs) +{ + struct mwm_hints hints; + int scr_width, scr_height; + + if(fs) { + get_window_pos(&prev_win_x, &prev_win_y); + get_window_size(&prev_win_width, &prev_win_height); + get_screen_size(&scr_width, &scr_height); + + hints.decorations = 0; + hints.flags = MWM_HINTS_DECORATIONS; + XChangeProperty(dpy, win, xa_motif_wm_hints, xa_motif_wm_hints, 32, + PropModeReplace, (unsigned char*)&hints, 5); + + XMoveResizeWindow(dpy, win, 0, 0, scr_width, scr_height); + } else { + XDeleteProperty(dpy, win, xa_motif_wm_hints); + XMoveResizeWindow(dpy, win, prev_win_x, prev_win_y, prev_win_width, prev_win_height); + } +} + +static int have_netwm_fullscr(void) +{ + int fmt; + long offs = 0; + unsigned long i, count, rem; + Atom *prop, type; + Atom xa_net_supported = XInternAtom(dpy, "_NET_SUPPORTED", False); + + do { + XGetWindowProperty(dpy, root, xa_net_supported, offs, 8, False, AnyPropertyType, + &type, &fmt, &count, &rem, (unsigned char**)&prop); + + for(i=0; i 0); + + return 0; +} + +static void set_fullscreen_ewmh(int fs) +{ + XClientMessageEvent msg = {0}; + + msg.type = ClientMessage; + msg.window = win; + msg.message_type = xa_net_wm_state; /* _NET_WM_STATE */ + msg.format = 32; + msg.data.l[0] = fs ? 1 : 0; + msg.data.l[1] = xa_net_wm_state_fullscr; /* _NET_WM_STATE_FULLSCREEN */ + msg.data.l[2] = 0; + msg.data.l[3] = 1; /* source regular application */ + XSendEvent(dpy, root, False, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent*)&msg); +} + +static void set_fullscreen(int fs) +{ + if(fullscreen == fs) return; + + if(xa_net_wm_state && xa_net_wm_state_fullscr) { + set_fullscreen_ewmh(fs); + fullscreen = fs; + } else if(xa_motif_wm_hints) { + set_fullscreen_mwm(fs); + fullscreen = fs; + } +} + +void glutPositionWindow(int x, int y) +{ + set_fullscreen(0); + XMoveWindow(dpy, win, x, y); +} + +void glutReshapeWindow(int xsz, int ysz) +{ + set_fullscreen(0); + XResizeWindow(dpy, win, xsz, ysz); +} + +void glutFullScreen(void) +{ + set_fullscreen(1); +} + +void glutSetWindowTitle(const char *title) +{ + XTextProperty tprop; + if(!XStringListToTextProperty((char**)&title, 1, &tprop)) { + return; + } + XSetWMName(dpy, win, &tprop); + XFree(tprop.value); +} + +void glutSetIconTitle(const char *title) +{ + XTextProperty tprop; + if(!XStringListToTextProperty((char**)&title, 1, &tprop)) { + return; + } + XSetWMIconName(dpy, win, &tprop); + XFree(tprop.value); +} + +void glutSetCursor(int cidx) +{ + Cursor cur = None; + + switch(cidx) { + case GLUT_CURSOR_LEFT_ARROW: + cur = XCreateFontCursor(dpy, XC_left_ptr); + break; + case GLUT_CURSOR_INHERIT: + break; + case GLUT_CURSOR_NONE: + cur = blank_cursor; + break; + default: + return; + } + + XDefineCursor(dpy, win, cur); + cur_cursor = cidx; +} + +void glutSetColor(int idx, float r, float g, float b) +{ + XColor color; + + if(idx >= 0 && idx < cmap_size) { + color.pixel = idx; + color.red = (unsigned short)(r * 65535.0f); + color.green = (unsigned short)(g * 65535.0f); + color.blue = (unsigned short)(b * 65535.0f); + color.flags = DoRed | DoGreen | DoBlue; + XStoreColor(dpy, cmap, &color); + } +} + +float glutGetColor(int idx, int comp) +{ + XColor color; + + if(idx < 0 || idx >= cmap_size) { + return -1.0f; + } + + color.pixel = idx; + XQueryColor(dpy, cmap, &color); + switch(comp) { + case GLUT_RED: + return color.red / 65535.0f; + case GLUT_GREEN: + return color.green / 65535.0f; + case GLUT_BLUE: + return color.blue / 65535.0f; + default: + break; + } + return -1.0f; +} + +void glutSetKeyRepeat(int repmode) +{ + if(repmode) { + XAutoRepeatOn(dpy); + } else { + XAutoRepeatOff(dpy); + } +} + +static XVisualInfo *choose_visual(unsigned int mode) +{ + XVisualInfo *vi; + int attr[32]; + int *aptr = attr; + int *samples = 0; + + if(mode & GLUT_DOUBLE) { + *aptr++ = GLX_DOUBLEBUFFER; + } + + if(mode & GLUT_INDEX) { + *aptr++ = GLX_BUFFER_SIZE; + *aptr++ = 1; + } else { + *aptr++ = GLX_RGBA; + *aptr++ = GLX_RED_SIZE; *aptr++ = 1; + *aptr++ = GLX_GREEN_SIZE; *aptr++ = 1; + *aptr++ = GLX_BLUE_SIZE; *aptr++ = 1; + } + if(mode & GLUT_ALPHA) { + *aptr++ = GLX_ALPHA_SIZE; + *aptr++ = 4; + } + if(mode & GLUT_DEPTH) { + *aptr++ = GLX_DEPTH_SIZE; + *aptr++ = 8; + } + if(mode & GLUT_STENCIL) { + *aptr++ = GLX_STENCIL_SIZE; + *aptr++ = 1; + } + if(mode & GLUT_ACCUM) { + *aptr++ = GLX_ACCUM_RED_SIZE; *aptr++ = 1; + *aptr++ = GLX_ACCUM_GREEN_SIZE; *aptr++ = 1; + *aptr++ = GLX_ACCUM_BLUE_SIZE; *aptr++ = 1; + } + if(mode & GLUT_STEREO) { + *aptr++ = GLX_STEREO; + } + if(mode & GLUT_SRGB) { + *aptr++ = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB; + } + if(mode & GLUT_MULTISAMPLE) { + *aptr++ = GLX_SAMPLE_BUFFERS_ARB; + *aptr++ = 1; + *aptr++ = GLX_SAMPLES_ARB; + samples = aptr; + *aptr++ = 32; + } + *aptr++ = None; + + if(!samples) { + return glXChooseVisual(dpy, scr, attr); + } + while(!(vi = glXChooseVisual(dpy, scr, attr)) && *samples) { + *samples >>= 1; + if(!*samples) { + aptr[-3] = None; + } + } + return vi; +} + +static void create_window(const char *title) +{ + XSetWindowAttributes xattr = {0}; + XVisualInfo *vi; + unsigned int xattr_mask; + unsigned int mode = init_mode; + + if(!(vi = choose_visual(mode))) { + mode &= ~GLUT_SRGB; + if(!(vi = choose_visual(mode))) { + panic("Failed to find compatible visual\n"); + } + } + + if(!(ctx = glXCreateContext(dpy, vi, 0, True))) { + XFree(vi); + panic("Failed to create OpenGL context\n"); + } + + glXGetConfig(dpy, vi, GLX_RED_SIZE, &ctx_info.rsize); + glXGetConfig(dpy, vi, GLX_GREEN_SIZE, &ctx_info.gsize); + glXGetConfig(dpy, vi, GLX_BLUE_SIZE, &ctx_info.bsize); + glXGetConfig(dpy, vi, GLX_ALPHA_SIZE, &ctx_info.asize); + glXGetConfig(dpy, vi, GLX_DEPTH_SIZE, &ctx_info.zsize); + glXGetConfig(dpy, vi, GLX_STENCIL_SIZE, &ctx_info.ssize); + glXGetConfig(dpy, vi, GLX_DOUBLEBUFFER, &ctx_info.dblbuf); + glXGetConfig(dpy, vi, GLX_STEREO, &ctx_info.stereo); + glXGetConfig(dpy, vi, GLX_SAMPLES_ARB, &ctx_info.samples); + glXGetConfig(dpy, vi, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb); + + if(!(cmap = XCreateColormap(dpy, root, vi->visual, mode & GLUT_INDEX ? AllocAll : AllocNone))) { + XFree(vi); + glXDestroyContext(dpy, ctx); + panic("Failed to create colormap\n"); + } + cmap_size = GLUT_INDEX ? vi->colormap_size : 0; + + xattr.background_pixel = BlackPixel(dpy, scr); + xattr.colormap = cmap; + xattr_mask = CWBackPixel | CWColormap | CWBackPixmap | CWBorderPixel; + if(!(win = XCreateWindow(dpy, root, init_x, init_y, init_width, init_height, 0, + vi->depth, InputOutput, vi->visual, xattr_mask, &xattr))) { + XFree(vi); + glXDestroyContext(dpy, ctx); + XFreeColormap(dpy, cmap); + panic("Failed to create window\n"); + } + XFree(vi); + + XSelectInput(dpy, win, evmask); + + spnav_window(win); + + glutSetWindowTitle(title); + glutSetIconTitle(title); + XSetWMProtocols(dpy, win, &xa_wm_del_win, 1); + XMapWindow(dpy, win); + + glXMakeCurrent(dpy, win, ctx); +} + +static void get_window_pos(int *x, int *y) +{ + Window child; + XTranslateCoordinates(dpy, win, root, 0, 0, x, y, &child); +} + +static void get_window_size(int *w, int *h) +{ + XWindowAttributes wattr; + XGetWindowAttributes(dpy, win, &wattr); + *w = wattr.width; + *h = wattr.height; +} + +static void get_screen_size(int *scrw, int *scrh) +{ + XWindowAttributes wattr; + XGetWindowAttributes(dpy, root, &wattr); + *scrw = wattr.width; + *scrh = wattr.height; +} + + +/* spaceball */ +enum { + CMD_APP_WINDOW = 27695, + CMD_APP_SENS +}; + +static Window get_daemon_window(Display *dpy); +static int catch_badwin(Display *dpy, XErrorEvent *err); + +#define SPNAV_INITIALIZED (xa_motion_event) + +static int spnav_window(Window win) +{ + int (*prev_xerr_handler)(Display*, XErrorEvent*); + XEvent xev; + Window daemon_win; + + if(!SPNAV_INITIALIZED) { + return -1; + } + + if(!(daemon_win = get_daemon_window(dpy))) { + return -1; + } + + prev_xerr_handler = XSetErrorHandler(catch_badwin); + + xev.type = ClientMessage; + xev.xclient.send_event = False; + xev.xclient.display = dpy; + xev.xclient.window = win; + xev.xclient.message_type = xa_command_event; + xev.xclient.format = 16; + xev.xclient.data.s[0] = ((unsigned int)win & 0xffff0000) >> 16; + xev.xclient.data.s[1] = (unsigned int)win & 0xffff; + xev.xclient.data.s[2] = CMD_APP_WINDOW; + + XSendEvent(dpy, daemon_win, False, 0, &xev); + XSync(dpy, False); + + XSetErrorHandler(prev_xerr_handler); + return 0; +} + +static Bool match_events(Display *dpy, XEvent *xev, char *arg) +{ + int evtype = *(int*)arg; + + if(xev->type != ClientMessage) { + return False; + } + + if(xev->xclient.message_type == xa_motion_event) { + return !evtype || evtype == SPNAV_EVENT_MOTION ? True : False; + } + if(xev->xclient.message_type == xa_button_press_event || + xev->xclient.message_type == xa_button_release_event) { + return !evtype || evtype == SPNAV_EVENT_BUTTON ? True : False; + } + return False; +} + +static int spnav_remove_events(int type) +{ + int rm_count = 0; + XEvent xev; + while(XCheckIfEvent(dpy, &xev, match_events, (char*)&type)) { + rm_count++; + } + return rm_count; +} + +static int spnav_event(const XEvent *xev, union spnav_event *event) +{ + int i; + int xmsg_type; + + xmsg_type = xev->xclient.message_type; + + if(xmsg_type != xa_motion_event && xmsg_type != xa_button_press_event && + xmsg_type != xa_button_release_event) { + return 0; + } + + if(xmsg_type == xa_motion_event) { + event->type = SPNAV_EVENT_MOTION; + event->motion.data = &event->motion.x; + + for(i=0; i<6; i++) { + event->motion.data[i] = xev->xclient.data.s[i + 2]; + } + event->motion.period = xev->xclient.data.s[8]; + } else { + event->type = SPNAV_EVENT_BUTTON; + event->button.press = xmsg_type == xa_button_press_event ? 1 : 0; + event->button.bnum = xev->xclient.data.s[2]; + } + return event->type; +} + +static int mglut_strcmp(const char *s1, const char *s2) +{ + while(*s1 && *s1 == *s2) { + s1++; + s2++; + } + return *s1 - *s2; +} + +static Window get_daemon_window(Display *dpy) +{ + Window win; + XTextProperty wname; + Atom type; + int fmt; + unsigned long nitems, bytes_after; + unsigned char *prop; + + XGetWindowProperty(dpy, root, xa_command_event, 0, 1, False, AnyPropertyType, + &type, &fmt, &nitems, &bytes_after, &prop); + if(!prop) { + return 0; + } + + win = *(Window*)prop; + XFree(prop); + + wname.value = 0; + if(!XGetWMName(dpy, win, &wname) || mglut_strcmp("Magellan Window", (char*)wname.value) != 0) { + win = 0; + } + XFree(wname.value); + + return win; +} + +static int catch_badwin(Display *dpy, XErrorEvent *err) +{ + return 0; +} + + + +#endif /* BUILD_X11 */ + + +/* --------------- windows implementation ----------------- */ +#ifdef BUILD_WIN32 +static int reshape_pending; + +static void update_modkeys(void); +static int translate_vkey(int vkey); +static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam); + +#ifdef MINIGLUT_WINMAIN +int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, char *cmdline, int showcmd) +{ + int argc = 1; + char *argv[] = { "miniglut.exe", 0 }; + return main(argc, argv); +} +#endif + +void glutMainLoopEvent(void) +{ + MSG msg; + + if(!cb_display) { + panic("display callback not set"); + } + + if(reshape_pending && cb_reshape) { + reshape_pending = 0; + get_window_size(&win_width, &win_height); + cb_reshape(win_width, win_height); + } + + if(!upd_pending && !cb_idle) { + GetMessage(&msg, 0, 0, 0); + TranslateMessage(&msg); + DispatchMessage(&msg); + if(quit) return; + } + while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + if(quit) return; + } + + if(cb_idle) { + cb_idle(); + } + + if(upd_pending && mapped) { + upd_pending = 0; + cb_display(); + } +} + +static void cleanup(void) +{ + if(win) { + wglMakeCurrent(dc, 0); + wglDeleteContext(ctx); + UnregisterClass("MiniGLUT", hinst); + } +} + +void glutSwapBuffers(void) +{ + SwapBuffers(dc); +} + +void glutPositionWindow(int x, int y) +{ + RECT rect; + unsigned int flags = SWP_SHOWWINDOW; + + if(fullscreen) { + rect.left = prev_win_x; + rect.top = prev_win_y; + rect.right = rect.left + prev_win_width; + rect.bottom = rect.top + prev_win_height; + SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW); + fullscreen = 0; + flags |= SWP_FRAMECHANGED; + } else { + GetWindowRect(win, &rect); + } + SetWindowPos(win, HWND_NOTOPMOST, x, y, rect.right - rect.left, rect.bottom - rect.top, flags); +} + +void glutReshapeWindow(int xsz, int ysz) +{ + RECT rect; + unsigned int flags = SWP_SHOWWINDOW; + + if(fullscreen) { + rect.left = prev_win_x; + rect.top = prev_win_y; + SetWindowLong(win, GWL_STYLE, WS_OVERLAPPEDWINDOW); + fullscreen = 0; + flags |= SWP_FRAMECHANGED; + } else { + GetWindowRect(win, &rect); + } + SetWindowPos(win, HWND_NOTOPMOST, rect.left, rect.top, xsz, ysz, flags); +} + +void glutFullScreen(void) +{ + RECT rect; + int scr_width, scr_height; + + if(fullscreen) return; + + GetWindowRect(win, &rect); + prev_win_x = rect.left; + prev_win_y = rect.top; + prev_win_width = rect.right - rect.left; + prev_win_height = rect.bottom - rect.top; + + get_screen_size(&scr_width, &scr_height); + + SetWindowLong(win, GWL_STYLE, 0); + SetWindowPos(win, HWND_TOPMOST, 0, 0, scr_width, scr_height, SWP_SHOWWINDOW); + + fullscreen = 1; +} + +void glutSetWindowTitle(const char *title) +{ + SetWindowText(win, title); +} + +void glutSetIconTitle(const char *title) +{ +} + +void glutSetCursor(int cidx) +{ + switch(cidx) { + case GLUT_CURSOR_NONE: + ShowCursor(0); + break; + case GLUT_CURSOR_INHERIT: + case GLUT_CURSOR_LEFT_ARROW: + default: + SetCursor(LoadCursor(0, IDC_ARROW)); + ShowCursor(1); + } +} + +void glutSetColor(int idx, float r, float g, float b) +{ + PALETTEENTRY col; + + if(idx < 0 || idx >= 256 || !cmap) { + return; + } + + col.peRed = (int)(r * 255.0f); + col.peGreen = (int)(g * 255.0f); + col.peBlue = (int)(b * 255.0f); + col.peFlags = PC_NOCOLLAPSE; + + SetPaletteEntries(cmap, idx, 1, &col); + + if(dc) { + UnrealizeObject(cmap); + SelectPalette(dc, cmap, 0); + RealizePalette(dc); + } +} + +float glutGetColor(int idx, int comp) +{ + PALETTEENTRY col; + + if(idx < 0 || idx >= 256 || !cmap) { + return -1.0f; + } + + if(!GetPaletteEntries(cmap, idx, 1, &col)) { + return -1.0f; + } + + switch(comp) { + case GLUT_RED: + return col.peRed / 255.0f; + case GLUT_GREEN: + return col.peGreen / 255.0f; + case GLUT_BLUE: + return col.peBlue / 255.0f; + default: + break; + } + return -1.0f; +} + +void glutSetKeyRepeat(int repmode) +{ +} + +#define WGL_DRAW_TO_WINDOW 0x2001 +#define WGL_ACCELERATION 0x2003 +#define WGL_SUPPORT_OPENGL 0x2010 +#define WGL_DOUBLE_BUFFER 0x2011 +#define WGL_STEREO 0x2012 +#define WGL_PIXEL_TYPE 0x2013 +#define WGL_COLOR_BITS 0x2014 +#define WGL_RED_BITS 0x2015 +#define WGL_GREEN_BITS 0x2017 +#define WGL_BLUE_BITS 0x2019 +#define WGL_ALPHA_BITS 0x201b +#define WGL_ACCUM_BITS 0x201d +#define WGL_DEPTH_BITS 0x2022 +#define WGL_STENCIL_BITS 0x2023 +#define WGL_FULL_ACCELERATION 0x2027 + +#define WGL_TYPE_RGBA 0x202b +#define WGL_TYPE_COLORINDEX 0x202c + +#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9 +#define WGL_SAMPLE_BUFFERS_ARB 0x2041 +#define WGL_SAMPLES_ARB 0x2042 + +static PROC wglChoosePixelFormat; +static PROC wglGetPixelFormatAttribiv; + +#define ATTR(a, v) \ + do { *aptr++ = (a); *aptr++ = (v); } while(0) + +static unsigned int choose_pixfmt(unsigned int mode) +{ + unsigned int num_pixfmt, pixfmt = 0; + int attr[32] = { WGL_DRAW_TO_WINDOW, 1, WGL_SUPPORT_OPENGL, 1, + WGL_ACCELERATION, WGL_FULL_ACCELERATION }; + float fattr[2] = {0, 0}; + + int *aptr = attr + 6; + int *samples = 0; + + if(mode & GLUT_DOUBLE) { + ATTR(WGL_DOUBLE_BUFFER, 1); + } + + ATTR(WGL_PIXEL_TYPE, mode & GLUT_INDEX ? WGL_TYPE_COLORINDEX : WGL_TYPE_RGBA); + ATTR(WGL_COLOR_BITS, mode & GLUT_INDEX ? 8 : 24); + if(mode & GLUT_ALPHA) { + ATTR(WGL_ALPHA_BITS, 4); + } + if(mode & GLUT_DEPTH) { + ATTR(WGL_DEPTH_BITS, 16); + } + if(mode & GLUT_STENCIL) { + ATTR(WGL_STENCIL_BITS, 1); + } + if(mode & GLUT_ACCUM) { + ATTR(WGL_ACCUM_BITS, 1); + } + if(mode & GLUT_STEREO) { + ATTR(WGL_STEREO, 1); + } + if(mode & GLUT_SRGB) { + ATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, 1); + } + if(mode & GLUT_MULTISAMPLE) { + ATTR(WGL_SAMPLE_BUFFERS_ARB, 1); + *aptr++ = WGL_SAMPLES_ARB; + samples = aptr; + *aptr++ = 32; + } + *aptr++ = 0; + + while((!wglChoosePixelFormat(dc, attr, fattr, 1, &pixfmt, &num_pixfmt) || !num_pixfmt) && samples && *samples) { + *samples >>= 1; + if(!*samples) { + aptr[-3] = 0; + } + } + return pixfmt; +} + +static PIXELFORMATDESCRIPTOR pfd; +static PIXELFORMATDESCRIPTOR tmppfd = { + sizeof tmppfd, 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, + PFD_TYPE_RGBA, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 8, 0, + PFD_MAIN_PLANE, 0, 0, 0, 0 +}; +#define TMPCLASS "TempMiniGLUT" + +#define GETATTR(attr, vptr) \ + do { \ + int gattr = attr; \ + wglGetPixelFormatAttribiv(dc, pixfmt, 0, 1, &gattr, vptr); \ + } while(0) + +static int create_window_wglext(const char *title, int width, int height) +{ + WNDCLASSEX wc = {0}; + HWND tmpwin = 0; + HDC tmpdc = 0; + HGLRC tmpctx = 0; + int pixfmt; + + /* create a temporary window and GL context, just to query and retrieve + * the wglChoosePixelFormatEXT function + */ + wc.cbSize = sizeof wc; + wc.hbrBackground = GetStockObject(BLACK_BRUSH); + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = wc.hIconSm = LoadIcon(0, IDI_APPLICATION); + wc.hInstance = hinst; + wc.lpfnWndProc = DefWindowProc; + wc.lpszClassName = TMPCLASS; + wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + if(!RegisterClassEx(&wc)) { + return 0; + } + if(!(tmpwin = CreateWindow(TMPCLASS, "temp", WS_OVERLAPPEDWINDOW, 0, 0, + width, height, 0, 0, hinst, 0))) { + goto fail; + } + tmpdc = GetDC(tmpwin); + + if(!(pixfmt = ChoosePixelFormat(tmpdc, &tmppfd)) || + !SetPixelFormat(tmpdc, pixfmt, &tmppfd) || + !(tmpctx = wglCreateContext(tmpdc))) { + goto fail; + } + wglMakeCurrent(tmpdc, tmpctx); + + if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatARB"))) { + if(!(wglChoosePixelFormat = wglGetProcAddress("wglChoosePixelFormatEXT"))) { + goto fail; + } + if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivEXT"))) { + goto fail; + } + } else { + if(!(wglGetPixelFormatAttribiv = wglGetProcAddress("wglGetPixelFormatAttribivARB"))) { + goto fail; + } + } + wglMakeCurrent(0, 0); + wglDeleteContext(tmpctx); + DestroyWindow(tmpwin); + UnregisterClass(TMPCLASS, hinst); + + /* create the real window and context */ + if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW, init_x, + init_y, width, height, 0, 0, hinst, 0))) { + panic("Failed to create window\n"); + } + dc = GetDC(win); + + if(!(pixfmt = choose_pixfmt(init_mode))) { + panic("Failed to find suitable pixel format\n"); + } + if(!SetPixelFormat(dc, pixfmt, &pfd)) { + panic("Failed to set the selected pixel format\n"); + } + if(!(ctx = wglCreateContext(dc))) { + panic("Failed to create the OpenGL context\n"); + } + wglMakeCurrent(dc, ctx); + + GETATTR(WGL_RED_BITS, &ctx_info.rsize); + GETATTR(WGL_GREEN_BITS, &ctx_info.gsize); + GETATTR(WGL_BLUE_BITS, &ctx_info.bsize); + GETATTR(WGL_ALPHA_BITS, &ctx_info.asize); + GETATTR(WGL_DEPTH_BITS, &ctx_info.zsize); + GETATTR(WGL_STENCIL_BITS, &ctx_info.ssize); + GETATTR(WGL_DOUBLE_BUFFER, &ctx_info.dblbuf); + GETATTR(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, &ctx_info.srgb); + GETATTR(WGL_SAMPLES_ARB, &ctx_info.samples); + + return 0; + +fail: + if(tmpctx) { + wglMakeCurrent(0, 0); + wglDeleteContext(tmpctx); + } + if(tmpwin) { + DestroyWindow(tmpwin); + } + UnregisterClass(TMPCLASS, hinst); + return -1; +} + + +static void create_window(const char *title) +{ + RECT rect; + int i, pixfmt, width, height; + char palbuf[sizeof(LOGPALETTE) + 255 * sizeof(PALETTEENTRY)]; + LOGPALETTE *logpal; + + + rect.left = init_x; + rect.top = init_y; + rect.right = init_x + init_width; + rect.bottom = init_y + init_height; + AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, 0); + width = rect.right - rect.left; + height = rect.bottom - rect.top; + + memset(&pfd, 0, sizeof pfd); + pfd.nSize = sizeof pfd; + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + if(init_mode & GLUT_STEREO) { + pfd.dwFlags |= PFD_STEREO; + } + if(init_mode & GLUT_INDEX) { + pfd.iPixelType = PFD_TYPE_COLORINDEX; + pfd.cColorBits = 8; + } else { + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 24; + } + if(init_mode & GLUT_ALPHA) { + pfd.cAlphaBits = 8; + } + if(init_mode & GLUT_ACCUM) { + pfd.cAccumBits = 24; + } + if(init_mode & GLUT_DEPTH) { + pfd.cDepthBits = 24; + } + if(init_mode & GLUT_STENCIL) { + pfd.cStencilBits = 8; + } + pfd.iLayerType = PFD_MAIN_PLANE; + + if(init_mode & (GLUT_SRGB | GLUT_MULTISAMPLE)) { + if(create_window_wglext(title, width, height) != -1) { + goto ctxdone; + } + } + + /* if we don't need sRGB or multisample, or if the wglChoosePixelFormat method + * failed, just use the old-style ChoosePixelFormat method instead + */ + if(!(win = CreateWindow("MiniGLUT", title, WS_OVERLAPPEDWINDOW, + rect.left, rect.top, width, height, 0, 0, hinst, 0))) { + panic("Failed to create window\n"); + } + dc = GetDC(win); + + if(!(pixfmt = ChoosePixelFormat(dc, &pfd))) { + panic("Failed to find suitable pixel format\n"); + } + if(!SetPixelFormat(dc, pixfmt, &pfd)) { + panic("Failed to set the selected pixel format\n"); + } + if(!(ctx = wglCreateContext(dc))) { + panic("Failed to create the OpenGL context\n"); + } + wglMakeCurrent(dc, ctx); + + DescribePixelFormat(dc, pixfmt, sizeof pfd, &pfd); + ctx_info.rsize = pfd.cRedBits; + ctx_info.gsize = pfd.cGreenBits; + ctx_info.bsize = pfd.cBlueBits; + ctx_info.asize = pfd.cAlphaBits; + ctx_info.zsize = pfd.cDepthBits; + ctx_info.ssize = pfd.cStencilBits; + ctx_info.dblbuf = pfd.dwFlags & PFD_DOUBLEBUFFER ? 1 : 0; + ctx_info.samples = 0; + ctx_info.srgb = 0; + +ctxdone: + ShowWindow(win, 1); + SetForegroundWindow(win); + SetFocus(win); + + if(init_mode & GLUT_INDEX) { + logpal = (LOGPALETTE*)palbuf; + + GetSystemPaletteEntries(dc, 0, 256, logpal->palPalEntry); + + logpal->palVersion = 0x300; + logpal->palNumEntries = 256; + + if(!(cmap = CreatePalette(logpal))) { + panic("Failed to create palette in indexed mode"); + } + SelectPalette(dc, cmap, 0); + RealizePalette(dc); + + cmap_size = 256; + } else { + if(GetDeviceCaps(dc, BITSPIXEL) * GetDeviceCaps(dc, PLANES) <= 8) { + /* for RGB mode in 8bpp displays we also need to set up a palette + * with RGB 332 colors + */ + logpal = (LOGPALETTE*)palbuf; + + logpal->palVersion = 0x300; + logpal->palNumEntries = 256; + + for(i=0; i<256; i++) { + int r = i & 7; + int g = (i >> 3) & 7; + int b = (i >> 5) & 3; + + logpal->palPalEntry[i].peRed = (r << 5) | (r << 2) | (r >> 1); + logpal->palPalEntry[i].peGreen = (g << 5) | (g << 2) | (g >> 1); + logpal->palPalEntry[i].peBlue = (b << 6) | (b << 4) | (b << 2) | b; + logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE; + } + + if((cmap = CreatePalette(logpal))) { + SelectPalette(dc, cmap, 0); + RealizePalette(dc); + cmap_size = 256; + } + } + } + + upd_pending = 1; + reshape_pending = 1; +} + +static LRESULT CALLBACK handle_message(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam) +{ + static int mouse_x, mouse_y; + int x, y, key; + + switch(msg) { + case WM_CLOSE: + if(win) DestroyWindow(win); + break; + + case WM_DESTROY: + cleanup(); + quit = 1; + PostQuitMessage(0); + break; + + case WM_PAINT: + upd_pending = 1; + ValidateRect(win, 0); + break; + + case WM_SIZE: + x = lparam & 0xffff; + y = lparam >> 16; + if(x != win_width && y != win_height) { + win_width = x; + win_height = y; + if(cb_reshape) { + reshape_pending = 0; + cb_reshape(win_width, win_height); + } + } + break; + + case WM_SHOWWINDOW: + mapped = wparam; + if(cb_vis) cb_vis(mapped ? GLUT_VISIBLE : GLUT_NOT_VISIBLE); + break; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + update_modkeys(); + key = translate_vkey(wparam); + if(key < 256) { + if(cb_keydown) { + cb_keydown((unsigned char)key, mouse_x, mouse_y); + } + } else { + if(cb_skeydown) { + cb_skeydown(key, mouse_x, mouse_y); + } + } + break; + + case WM_KEYUP: + case WM_SYSKEYUP: + update_modkeys(); + key = translate_vkey(wparam); + if(key < 256) { + if(cb_keyup) { + cb_keyup((unsigned char)key, mouse_x, mouse_y); + } + } else { + if(cb_skeyup) { + cb_skeyup(key, mouse_x, mouse_y); + } + } + break; + + case WM_LBUTTONDOWN: + handle_mbutton(0, 1, wparam, lparam); + break; + case WM_MBUTTONDOWN: + handle_mbutton(1, 1, wparam, lparam); + break; + case WM_RBUTTONDOWN: + handle_mbutton(2, 1, wparam, lparam); + break; + case WM_LBUTTONUP: + handle_mbutton(0, 0, wparam, lparam); + break; + case WM_MBUTTONUP: + handle_mbutton(1, 0, wparam, lparam); + break; + case WM_RBUTTONUP: + handle_mbutton(2, 0, wparam, lparam); + break; + + case WM_MOUSEMOVE: + if(wparam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) { + if(cb_motion) cb_motion(lparam & 0xffff, lparam >> 16); + } else { + if(cb_passive) cb_passive(lparam & 0xffff, lparam >> 16); + } + break; + + case WM_SYSCOMMAND: + wparam &= 0xfff0; + if(wparam == SC_KEYMENU || wparam == SC_SCREENSAVE || wparam == SC_MONITORPOWER) { + return 0; + } + default: + return DefWindowProc(win, msg, wparam, lparam); + } + + return 0; +} + +static void update_modkeys(void) +{ + if(GetKeyState(VK_SHIFT) & 0x8000) { + modstate |= GLUT_ACTIVE_SHIFT; + } else { + modstate &= ~GLUT_ACTIVE_SHIFT; + } + if(GetKeyState(VK_CONTROL) & 0x8000) { + modstate |= GLUT_ACTIVE_CTRL; + } else { + modstate &= ~GLUT_ACTIVE_CTRL; + } + if(GetKeyState(VK_MENU) & 0x8000) { + modstate |= GLUT_ACTIVE_ALT; + } else { + modstate &= ~GLUT_ACTIVE_ALT; + } +} + +#ifndef VK_OEM_1 +#define VK_OEM_1 0xba +#define VK_OEM_2 0xbf +#define VK_OEM_3 0xc0 +#define VK_OEM_4 0xdb +#define VK_OEM_5 0xdc +#define VK_OEM_6 0xdd +#define VK_OEM_7 0xde +#endif + +static int translate_vkey(int vkey) +{ + switch(vkey) { + case VK_PRIOR: return GLUT_KEY_PAGE_UP; + case VK_NEXT: return GLUT_KEY_PAGE_DOWN; + case VK_END: return GLUT_KEY_END; + case VK_HOME: return GLUT_KEY_HOME; + case VK_LEFT: return GLUT_KEY_LEFT; + case VK_UP: return GLUT_KEY_UP; + case VK_RIGHT: return GLUT_KEY_RIGHT; + case VK_DOWN: return GLUT_KEY_DOWN; + case VK_OEM_1: return ';'; + case VK_OEM_2: return '/'; + case VK_OEM_3: return '`'; + case VK_OEM_4: return '['; + case VK_OEM_5: return '\\'; + case VK_OEM_6: return ']'; + case VK_OEM_7: return '\''; + default: + break; + } + + if(vkey >= 'A' && vkey <= 'Z') { + vkey += 32; + } else if(vkey >= VK_F1 && vkey <= VK_F12) { + vkey -= VK_F1 + GLUT_KEY_F1; + } + + return vkey; +} + +static void handle_mbutton(int bn, int st, WPARAM wparam, LPARAM lparam) +{ + int x, y; + + update_modkeys(); + + if(cb_mouse) { + x = lparam & 0xffff; + y = lparam >> 16; + cb_mouse(bn, st ? GLUT_DOWN : GLUT_UP, x, y); + } +} + +static void get_window_pos(int *x, int *y) +{ + RECT rect; + GetWindowRect(win, &rect); + *x = rect.left; + *y = rect.top; +} + +static void get_window_size(int *w, int *h) +{ + RECT rect; + GetClientRect(win, &rect); + *w = rect.right - rect.left; + *h = rect.bottom - rect.top; +} + +static void get_screen_size(int *scrw, int *scrh) +{ + *scrw = GetSystemMetrics(SM_CXSCREEN); + *scrh = GetSystemMetrics(SM_CYSCREEN); +} +#endif /* BUILD_WIN32 */ + +#if defined(unix) || defined(__unix__) || defined(__APPLE__) +#include + +#ifdef MINIGLUT_USE_LIBC +#define sys_gettimeofday(tv, tz) gettimeofday(tv, tz) +#else +static int sys_gettimeofday(struct timeval *tv, struct timezone *tz); +#endif + +static long get_msec(void) +{ + static struct timeval tv0; + struct timeval tv; + + sys_gettimeofday(&tv, 0); + if(tv0.tv_sec == 0 && tv0.tv_usec == 0) { + tv0 = tv; + return 0; + } + return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000; +} +#endif /* UNIX */ +#ifdef _WIN32 +static long get_msec(void) +{ + static long t0; + long tm; + +#ifdef MINIGLUT_NO_WINMM + tm = GetTickCount(); +#else + tm = timeGetTime(); +#endif + if(!t0) { + t0 = tm; + return 0; + } + return tm - t0; +} +#endif + +static void panic(const char *msg) +{ + const char *end = msg; + while(*end) end++; + sys_write(2, msg, end - msg); + sys_exit(1); +} + + +#ifdef MINIGLUT_USE_LIBC +#include +#ifdef _WIN32 +#include +#else +#include +#endif + +static void sys_exit(int status) +{ + exit(status); +} + +static int sys_write(int fd, const void *buf, int count) +{ + return write(fd, buf, count); +} + +#else /* !MINIGLUT_USE_LIBC */ + +#ifdef __linux__ +#ifdef __x86_64__ +static void sys_exit(int status) +{ + asm volatile( + "syscall\n\t" + :: "a"(60), "D"(status)); +} +static int sys_write(int fd, const void *buf, int count) +{ + long res; + asm volatile( + "syscall\n\t" + : "=a"(res) + : "a"(1), "D"(fd), "S"(buf), "d"(count)); + return res; +} +static int sys_gettimeofday(struct timeval *tv, struct timezone *tz) +{ + int res; + asm volatile( + "syscall\n\t" + : "=a"(res) + : "a"(96), "D"(tv), "S"(tz)); + return res; +} +#endif /* __x86_64__ */ +#ifdef __i386__ +static void sys_exit(int status) +{ + asm volatile( + "int $0x80\n\t" + :: "a"(1), "b"(status)); +} +static int sys_write(int fd, const void *buf, int count) +{ + int res; + asm volatile( + "int $0x80\n\t" + : "=a"(res) + : "a"(4), "b"(fd), "c"(buf), "d"(count)); + return res; +} +static int sys_gettimeofday(struct timeval *tv, struct timezone *tz) +{ + int res; + asm volatile( + "int $0x80\n\t" + : "=a"(res) + : "a"(78), "b"(tv), "c"(tz)); + return res; +} +#endif /* __i386__ */ +#endif /* __linux__ */ + +#ifdef _WIN32 +static void sys_exit(int status) +{ + ExitProcess(status); +} +static int sys_write(int fd, const void *buf, int count) +{ + unsigned long wrsz = 0; + + HANDLE out = GetStdHandle(fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE); + if(!WriteFile(out, buf, count, &wrsz, 0)) { + return -1; + } + return wrsz; +} +#endif /* _WIN32 */ +#endif /* !MINIGLUT_USE_LIBC */ + + +/* ----------------- primitives ------------------ */ +#ifdef MINIGLUT_USE_LIBC +#include +#include + +void mglut_sincos(float angle, float *sptr, float *cptr) +{ + *sptr = sin(angle); + *cptr = cos(angle); +} + +float mglut_atan(float x) +{ + return atan(x); +} + +#else /* !MINIGLUT_USE_LIBC */ + +#ifdef __GNUC__ +void mglut_sincos(float angle, float *sptr, float *cptr) +{ + asm volatile( + "flds %2\n\t" + "fsincos\n\t" + "fstps %1\n\t" + "fstps %0\n\t" + : "=m"(*sptr), "=m"(*cptr) + : "m"(angle) + ); +} + +float mglut_atan(float x) +{ + float res; + asm volatile( + "flds %1\n\t" + "fld1\n\t" + "fpatan\n\t" + "fstps %0\n\t" + : "=m"(res) + : "m"(x) + ); + return res; +} +#endif + +#ifdef _MSC_VER +void mglut_sincos(float angle, float *sptr, float *cptr) +{ + float s, c; + __asm { + fld angle + fsincos + fstp c + fstp s + } + *sptr = s; + *cptr = c; +} + +float mglut_atan(float x) +{ + float res; + __asm { + fld x + fld1 + fpatan + fstp res + } + return res; +} +#endif + +#ifdef __WATCOMC__ +#pragma aux mglut_sincos = \ + "fsincos" \ + "fstp dword ptr [edx]" \ + "fstp dword ptr [eax]" \ + parm[8087][eax][edx] \ + modify[8087]; + +#pragma aux mglut_atan = \ + "fld1" \ + "fpatan" \ + parm[8087] \ + value[8087] \ + modify [8087]; +#endif /* __WATCOMC__ */ + +#endif /* !MINIGLUT_USE_LIBC */ + +#define PI 3.1415926536f + +void glutSolidSphere(float rad, int slices, int stacks) +{ + int i, j, k, gray; + float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi; + float du = 1.0f / (float)slices; + float dv = 1.0f / (float)stacks; + + glBegin(GL_QUADS); + for(i=0; i> 1); + s = gray & 1 ? u + du : u; + t = gray & 2 ? v + dv : v; + theta = s * PI * 2.0f; + phi = t * PI; + mglut_sincos(theta, &sintheta, &costheta); + mglut_sincos(phi, &sinphi, &cosphi); + x = sintheta * sinphi; + y = costheta * sinphi; + z = cosphi; + + glColor3f(s, t, 1); + glTexCoord2f(s, t); + glNormal3f(x, y, z); + glVertex3f(x * rad, y * rad, z * rad); + } + } + } + glEnd(); +} + +void glutWireSphere(float rad, int slices, int stacks) +{ + glPushAttrib(GL_POLYGON_BIT); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glutSolidSphere(rad, slices, stacks); + glPopAttrib(); +} + +void glutSolidCube(float sz) +{ + int i, j, idx, gray, flip, rotx; + float vpos[3], norm[3]; + float rad = sz * 0.5f; + + glBegin(GL_QUADS); + for(i=0; i<6; i++) { + flip = i & 1; + rotx = i >> 2; + idx = (~i & 2) - rotx; + norm[0] = norm[1] = norm[2] = 0.0f; + norm[idx] = flip ^ ((i >> 1) & 1) ? -1 : 1; + glNormal3fv(norm); + vpos[idx] = norm[idx] * rad; + for(j=0; j<4; j++) { + gray = j ^ (j >> 1); + vpos[i & 2] = (gray ^ flip) & 1 ? rad : -rad; + vpos[rotx + 1] = (gray ^ (rotx << 1)) & 2 ? rad : -rad; + glTexCoord2f(gray & 1, gray >> 1); + glVertex3fv(vpos); + } + } + glEnd(); +} + +void glutWireCube(float sz) +{ + glPushAttrib(GL_POLYGON_BIT); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glutSolidCube(sz); + glPopAttrib(); +} + +static void draw_cylinder(float rbot, float rtop, float height, int slices, int stacks) +{ + int i, j, k, gray; + float x, y, z, s, t, u, v, theta, phi, sintheta, costheta, sinphi, cosphi, rad; + float du = 1.0f / (float)slices; + float dv = 1.0f / (float)stacks; + + rad = rbot - rtop; + phi = mglut_atan((rad < 0 ? -rad : rad) / height); + mglut_sincos(phi, &sinphi, &cosphi); + + glBegin(GL_QUADS); + for(i=0; i> 1); + s = gray & 2 ? u + du : u; + t = gray & 1 ? v + dv : v; + rad = rbot + (rtop - rbot) * t; + theta = s * PI * 2.0f; + mglut_sincos(theta, &sintheta, &costheta); + + x = sintheta * cosphi; + y = costheta * cosphi; + z = sinphi; + + glColor3f(s, t, 1); + glTexCoord2f(s, t); + glNormal3f(x, y, z); + glVertex3f(sintheta * rad, costheta * rad, t * height); + } + } + } + glEnd(); +} + +void glutSolidCone(float base, float height, int slices, int stacks) +{ + draw_cylinder(base, 0, height, slices, stacks); +} + +void glutWireCone(float base, float height, int slices, int stacks) +{ + glPushAttrib(GL_POLYGON_BIT); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glutSolidCone(base, height, slices, stacks); + glPopAttrib(); +} + +void glutSolidCylinder(float rad, float height, int slices, int stacks) +{ + draw_cylinder(rad, rad, height, slices, stacks); +} + +void glutWireCylinder(float rad, float height, int slices, int stacks) +{ + glPushAttrib(GL_POLYGON_BIT); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glutSolidCylinder(rad, height, slices, stacks); + glPopAttrib(); +} + +void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings) +{ + int i, j, k, gray; + float x, y, z, s, t, u, v, phi, theta, sintheta, costheta, sinphi, cosphi; + float du = 1.0f / (float)rings; + float dv = 1.0f / (float)sides; + + glBegin(GL_QUADS); + for(i=0; i> 1); + s = gray & 1 ? u + du : u; + t = gray & 2 ? v + dv : v; + theta = s * PI * 2.0f; + phi = t * PI * 2.0f; + mglut_sincos(theta, &sintheta, &costheta); + mglut_sincos(phi, &sinphi, &cosphi); + x = sintheta * sinphi; + y = costheta * sinphi; + z = cosphi; + + glColor3f(s, t, 1); + glTexCoord2f(s, t); + glNormal3f(x, y, z); + + x = x * inner_rad + sintheta * outer_rad; + y = y * inner_rad + costheta * outer_rad; + z *= inner_rad; + glVertex3f(x, y, z); + } + } + } + glEnd(); +} + +void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings) +{ + glPushAttrib(GL_POLYGON_BIT); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glutSolidTorus(inner_rad, outer_rad, sides, rings); + glPopAttrib(); +} + +#define NUM_TEAPOT_INDICES (sizeof teapot_index / sizeof *teapot_index) +#define NUM_TEAPOT_VERTS (sizeof teapot_verts / sizeof *teapot_verts) + +#define NUM_TEAPOT_PATCHES (NUM_TEAPOT_INDICES / 16) + +#define PATCH_SUBDIV 7 + +static float teapot_part_flip[] = { + 1, 1, 1, 1, /* rim flip */ + 1, 1, 1, 1, /* body1 flip */ + 1, 1, 1, 1, /* body2 flip */ + 1, 1, 1, 1, /* lid patch 1 flip */ + 1, 1, 1, 1, /* lid patch 2 flip */ + 1, -1, /* handle 1 flip */ + 1, -1, /* handle 2 flip */ + 1, -1, /* spout 1 flip */ + 1, -1, /* spout 2 flip */ + 1, 1, 1, 1 /* bottom flip */ +}; + +static float teapot_part_rot[] = { + 0, 90, 180, 270, /* rim rotations */ + 0, 90, 180, 270, /* body patch 1 rotations */ + 0, 90, 180, 270, /* body patch 2 rotations */ + 0, 90, 180, 270, /* lid patch 1 rotations */ + 0, 90, 180, 270, /* lid patch 2 rotations */ + 0, 0, /* handle 1 rotations */ + 0, 0, /* handle 2 rotations */ + 0, 0, /* spout 1 rotations */ + 0, 0, /* spout 2 rotations */ + 0, 90, 180, 270 /* bottom rotations */ +}; + + +static int teapot_index[] = { + /* rim */ + 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + /* body1 */ + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + /* body 2 */ + 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + /* lid 1 */ + 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3, + 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3, + 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3, + 96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101, 101, 0, 1, 2, 3, + /* lid 2 */ + 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + /* handle 1 */ + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* handle 2 */ + 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67, + 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 28, 65, 66, 67, + /* spout 1 */ + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + /* spout 2 */ + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + /* bottom */ + 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37, + 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37, + 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37, + 118, 118, 118, 118, 124, 122, 119, 121, 123, 126, 125, 120, 40, 39, 38, 37 +}; + + +static float teapot_verts[][3] = { + { 0.2000, 0.0000, 2.70000 }, { 0.2000, -0.1120, 2.70000 }, + { 0.1120, -0.2000, 2.70000 }, { 0.0000, -0.2000, 2.70000 }, + { 1.3375, 0.0000, 2.53125 }, { 1.3375, -0.7490, 2.53125 }, + { 0.7490, -1.3375, 2.53125 }, { 0.0000, -1.3375, 2.53125 }, + { 1.4375, 0.0000, 2.53125 }, { 1.4375, -0.8050, 2.53125 }, + { 0.8050, -1.4375, 2.53125 }, { 0.0000, -1.4375, 2.53125 }, + { 1.5000, 0.0000, 2.40000 }, { 1.5000, -0.8400, 2.40000 }, + { 0.8400, -1.5000, 2.40000 }, { 0.0000, -1.5000, 2.40000 }, + { 1.7500, 0.0000, 1.87500 }, { 1.7500, -0.9800, 1.87500 }, + { 0.9800, -1.7500, 1.87500 }, { 0.0000, -1.7500, 1.87500 }, + { 2.0000, 0.0000, 1.35000 }, { 2.0000, -1.1200, 1.35000 }, + { 1.1200, -2.0000, 1.35000 }, { 0.0000, -2.0000, 1.35000 }, + { 2.0000, 0.0000, 0.90000 }, { 2.0000, -1.1200, 0.90000 }, + { 1.1200, -2.0000, 0.90000 }, { 0.0000, -2.0000, 0.90000 }, + { -2.0000, 0.0000, 0.90000 }, { 2.0000, 0.0000, 0.45000 }, + { 2.0000, -1.1200, 0.45000 }, { 1.1200, -2.0000, 0.45000 }, + { 0.0000, -2.0000, 0.45000 }, { 1.5000, 0.0000, 0.22500 }, + { 1.5000, -0.8400, 0.22500 }, { 0.8400, -1.5000, 0.22500 }, + { 0.0000, -1.5000, 0.22500 }, { 1.5000, 0.0000, 0.15000 }, + { 1.5000, -0.8400, 0.15000 }, { 0.8400, -1.5000, 0.15000 }, + { 0.0000, -1.5000, 0.15000 }, { -1.6000, 0.0000, 2.02500 }, + { -1.6000, -0.3000, 2.02500 }, { -1.5000, -0.3000, 2.25000 }, + { -1.5000, 0.0000, 2.25000 }, { -2.3000, 0.0000, 2.02500 }, + { -2.3000, -0.3000, 2.02500 }, { -2.5000, -0.3000, 2.25000 }, + { -2.5000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 2.02500 }, + { -2.7000, -0.3000, 2.02500 }, { -3.0000, -0.3000, 2.25000 }, + { -3.0000, 0.0000, 2.25000 }, { -2.7000, 0.0000, 1.80000 }, + { -2.7000, -0.3000, 1.80000 }, { -3.0000, -0.3000, 1.80000 }, + { -3.0000, 0.0000, 1.80000 }, { -2.7000, 0.0000, 1.57500 }, + { -2.7000, -0.3000, 1.57500 }, { -3.0000, -0.3000, 1.35000 }, + { -3.0000, 0.0000, 1.35000 }, { -2.5000, 0.0000, 1.12500 }, + { -2.5000, -0.3000, 1.12500 }, { -2.6500, -0.3000, 0.93750 }, + { -2.6500, 0.0000, 0.93750 }, { -2.0000, -0.3000, 0.90000 }, + { -1.9000, -0.3000, 0.60000 }, { -1.9000, 0.0000, 0.60000 }, + { 1.7000, 0.0000, 1.42500 }, { 1.7000, -0.6600, 1.42500 }, + { 1.7000, -0.6600, 0.60000 }, { 1.7000, 0.0000, 0.60000 }, + { 2.6000, 0.0000, 1.42500 }, { 2.6000, -0.6600, 1.42500 }, + { 3.1000, -0.6600, 0.82500 }, { 3.1000, 0.0000, 0.82500 }, + { 2.3000, 0.0000, 2.10000 }, { 2.3000, -0.2500, 2.10000 }, + { 2.4000, -0.2500, 2.02500 }, { 2.4000, 0.0000, 2.02500 }, + { 2.7000, 0.0000, 2.40000 }, { 2.7000, -0.2500, 2.40000 }, + { 3.3000, -0.2500, 2.40000 }, { 3.3000, 0.0000, 2.40000 }, + { 2.8000, 0.0000, 2.47500 }, { 2.8000, -0.2500, 2.47500 }, + { 3.5250, -0.2500, 2.49375 }, { 3.5250, 0.0000, 2.49375 }, + { 2.9000, 0.0000, 2.47500 }, { 2.9000, -0.1500, 2.47500 }, + { 3.4500, -0.1500, 2.51250 }, { 3.4500, 0.0000, 2.51250 }, + { 2.8000, 0.0000, 2.40000 }, { 2.8000, -0.1500, 2.40000 }, + { 3.2000, -0.1500, 2.40000 }, { 3.2000, 0.0000, 2.40000 }, + { 0.0000, 0.0000, 3.15000 }, { 0.8000, 0.0000, 3.15000 }, + { 0.8000, -0.4500, 3.15000 }, { 0.4500, -0.8000, 3.15000 }, + { 0.0000, -0.8000, 3.15000 }, { 0.0000, 0.0000, 2.85000 }, + { 1.4000, 0.0000, 2.40000 }, { 1.4000, -0.7840, 2.40000 }, + { 0.7840, -1.4000, 2.40000 }, { 0.0000, -1.4000, 2.40000 }, + { 0.4000, 0.0000, 2.55000 }, { 0.4000, -0.2240, 2.55000 }, + { 0.2240, -0.4000, 2.55000 }, { 0.0000, -0.4000, 2.55000 }, + { 1.3000, 0.0000, 2.55000 }, { 1.3000, -0.7280, 2.55000 }, + { 0.7280, -1.3000, 2.55000 }, { 0.0000, -1.3000, 2.55000 }, + { 1.3000, 0.0000, 2.40000 }, { 1.3000, -0.7280, 2.40000 }, + { 0.7280, -1.3000, 2.40000 }, { 0.0000, -1.3000, 2.40000 }, + { 0.0000, 0.0000, 0.00000 }, { 1.4250, -0.7980, 0.00000 }, + { 1.5000, 0.0000, 0.07500 }, { 1.4250, 0.0000, 0.00000 }, + { 0.7980, -1.4250, 0.00000 }, { 0.0000, -1.5000, 0.07500 }, + { 0.0000, -1.4250, 0.00000 }, { 1.5000, -0.8400, 0.07500 }, + { 0.8400, -1.5000, 0.07500 } +}; + +static void draw_patch(int *index, int flip, float scale); +static float bernstein(int i, float x); + +void glutSolidTeapot(float size) +{ + int i; + + size /= 2.0; + + for(i=0; i> 1); + x = *(float*)&i; + x = x * (1.5f - xhalf * x * x); + return x; +} + + +#define CROSS(res, a, b) \ + do { \ + (res)[0] = (a)[1] * (b)[2] - (a)[2] * (b)[1]; \ + (res)[1] = (a)[2] * (b)[0] - (a)[0] * (b)[2]; \ + (res)[2] = (a)[0] * (b)[1] - (a)[1] * (b)[0]; \ + } while(0) + +#define NORMALIZE(v) \ + do { \ + float s = rsqrt((v)[0] * (v)[0] + (v)[1] * (v)[1] + (v)[2] * (v)[2]); \ + (v)[0] *= s; \ + (v)[1] *= s; \ + (v)[2] *= s; \ + } while(0) + +#define DT 0.001 + +static void bezier_patch_norm(float *res, float *cp, float u, float v) +{ + float tang[3], bitan[3], tmp[3]; + + bezier_patch(tang, cp, u + DT, v); + bezier_patch(tmp, cp, u - DT, v); + tang[0] -= tmp[0]; + tang[1] -= tmp[1]; + tang[2] -= tmp[2]; + + bezier_patch(bitan, cp, u, v + DT); + bezier_patch(tmp, cp, u, v - DT); + bitan[0] -= tmp[0]; + bitan[1] -= tmp[1]; + bitan[2] -= tmp[2]; + + CROSS(res, tang, bitan); + NORMALIZE(res); +} + + + +static float bernstein(int i, float x) +{ + float invx = 1.0f - x; + + switch(i) { + case 0: + return invx * invx * invx; + case 1: + return 3.0f * x * invx * invx; + case 2: + return 3.0f * x * x * invx; + case 3: + return x * x * x; + default: + break; + } + return 0.0f; +} + +static void draw_patch(int *index, int flip, float scale) +{ + static const float uoffs[2][4] = {{0, 0, 1, 1}, {1, 1, 0, 0}}; + static const float voffs[4] = {0, 1, 1, 0}; + + int i, j, k; + float cp[16 * 3]; + float pt[3], n[3]; + float u, v; + float du = 1.0 / PATCH_SUBDIV; + float dv = 1.0 / PATCH_SUBDIV; + + /* collect control points */ + for(i=0; i<16; i++) { + cp[i * 3] = teapot_verts[index[i]][0]; + cp[i * 3 + 1] = teapot_verts[index[i]][1]; + cp[i * 3 + 2] = teapot_verts[index[i]][2]; + } + + glBegin(GL_QUADS); + glColor3f(1, 1, 1); + + u = 0; + for(i=0; i 3.14) { + n[0] = n[1] = 0.0f; + n[2] = 1.0f; + } else if(pt[2] < 0.00001) { + n[0] = n[1] = 0.0f; + n[2] = -1.0f; + } else { + bezier_patch_norm(n, cp, u + uoffs[flip][k] * du, v + voffs[k] * dv); + } + + glTexCoord2f(u, v); + glNormal3fv(n); + glVertex3f(pt[0] * scale, pt[1] * scale, pt[2] * scale); + } + + v += dv; + } + u += du; + } + + glEnd(); +} diff --git a/src/pc/miniglut.h b/src/pc/miniglut.h new file mode 100644 index 0000000..53bb17b --- /dev/null +++ b/src/pc/miniglut.h @@ -0,0 +1,206 @@ +/* +MiniGLUT - minimal GLUT subset without dependencies +Copyright (C) 2020-2022 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 MINIGLUT_H_ +#define MINIGLUT_H_ + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN 1 +#include + +#ifdef _MSC_VER +#pragma comment (lib, "opengl32") +#ifndef MINIGLUT_NO_WINMM +#pragma comment (lib, "winmm") +#endif +#endif /* MSVC */ + +#endif +#include + +/* mode flags for glutInitDisplayMode */ +#define GLUT_RGB 0 +#define GLUT_RGBA 0 +#define GLUT_INDEX 0x001 +#define GLUT_SINGLE 0 +#define GLUT_DOUBLE 0x002 +#define GLUT_ACCUM 0x004 +#define GLUT_ALPHA 0x008 +#define GLUT_DEPTH 0x010 +#define GLUT_STENCIL 0x020 +#define GLUT_STEREO 0x040 +#define GLUT_MULTISAMPLE 0x100 +#define GLUT_SRGB 0x200 + +enum { GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON, GLUT_RIGHT_BUTTON }; +enum { GLUT_UP, GLUT_DOWN }; +enum { GLUT_NOT_VISIBLE, GLUT_VISIBLE }; +enum { GLUT_LEFT, GLUT_ENTERED }; + +/* cursors */ +enum { + GLUT_CURSOR_INHERIT, + GLUT_CURSOR_LEFT_ARROW, + GLUT_CURSOR_NONE +}; + +/* glutGet */ +enum { + GLUT_WINDOW_X, + GLUT_WINDOW_Y, + GLUT_WINDOW_WIDTH, + GLUT_WINDOW_HEIGHT, + GLUT_WINDOW_BUFFER_SIZE, + GLUT_WINDOW_STENCIL_SIZE, + GLUT_WINDOW_DEPTH_SIZE, + GLUT_WINDOW_RED_SIZE, + GLUT_WINDOW_GREEN_SIZE, + GLUT_WINDOW_BLUE_SIZE, + GLUT_WINDOW_ALPHA_SIZE, + GLUT_WINDOW_DOUBLEBUFFER, + GLUT_WINDOW_RGBA, + GLUT_WINDOW_NUM_SAMPLES, + GLUT_WINDOW_STEREO, + GLUT_WINDOW_SRGB, + GLUT_WINDOW_CURSOR, + GLUT_SCREEN_WIDTH, + GLUT_SCREEN_HEIGHT, + GLUT_INIT_DISPLAY_MODE, + GLUT_INIT_WINDOW_X, + GLUT_INIT_WINDOW_Y, + GLUT_INIT_WINDOW_WIDTH, + GLUT_INIT_WINDOW_HEIGHT, + GLUT_ELAPSED_TIME, + GLUT_WINDOW_COLORMAP_SIZE +}; + +enum { + GLUT_RED, + GLUT_GREEN, + GLUT_BLUE +}; + +enum { + GLUT_KEY_HOME = 0xff50, + GLUT_KEY_LEFT = 0xff51, + GLUT_KEY_UP, + GLUT_KEY_RIGHT, + GLUT_KEY_DOWN, + GLUT_KEY_PAGE_UP, + GLUT_KEY_PAGE_DOWN, + GLUT_KEY_END = 0xff57, + GLUT_KEY_INSERT = 0xff63, + GLUT_KEY_F1 = 0xffbe, + GLUT_KEY_F2, + GLUT_KEY_F3, + GLUT_KEY_F4, + GLUT_KEY_F5, + GLUT_KEY_F6, + GLUT_KEY_F7, + GLUT_KEY_F8, + GLUT_KEY_F9, + GLUT_KEY_F10, + GLUT_KEY_F11, + GLUT_KEY_F12 +}; + +/* returned by glutGetModifiers */ +#define GLUT_ACTIVE_SHIFT 1 +#define GLUT_ACTIVE_CTRL 4 +#define GLUT_ACTIVE_ALT 8 + +enum { + GLUT_KEY_REPEAT_OFF, + GLUT_KEY_REPEAT_ON +}; +#define GLUT_KEY_REPEAT_DEFAULT GLUT_KEY_REPEAT_ON + +typedef void (*glut_cb)(void); +typedef void (*glut_cb_reshape)(int x, int y); +typedef void (*glut_cb_state)(int state); +typedef void (*glut_cb_keyb)(unsigned char key, int x, int y); +typedef void (*glut_cb_special)(int key, int x, int y); +typedef void (*glut_cb_mouse)(int bn, int state, int x, int y); +typedef void (*glut_cb_motion)(int x, int y); +typedef void (*glut_cb_sbmotion)(int x, int y, int z); +typedef void (*glut_cb_sbbutton)(int bn, int state); + +#ifdef __cplusplus +extern "C" { +#endif + +void glutInit(int *argc, char **argv); +void glutInitWindowPosition(int x, int y); +void glutInitWindowSize(int xsz, int ysz); +void glutInitDisplayMode(unsigned int mode); +void glutCreateWindow(const char *title); + +void glutExit(void); +void glutMainLoop(void); +void glutMainLoopEvent(void); + +void glutPostRedisplay(void); +void glutSwapBuffers(void); +void glutPositionWindow(int x, int y); +void glutReshapeWindow(int xsz, int ysz); +void glutFullScreen(void); +void glutSetWindowTitle(const char *title); +void glutSetIconTitle(const char *title); +void glutSetCursor(int cursor); +void glutSetColor(int idx, float r, float g, float b); +float glutGetColor(int idx, int comp); + +void glutIgnoreKeyRepeat(int ignore); +void glutSetKeyRepeat(int repmode); + +void glutIdleFunc(glut_cb func); +void glutDisplayFunc(glut_cb func); +void glutReshapeFunc(glut_cb_reshape func); +void glutVisibilityFunc(glut_cb_state func); +void glutEntryFunc(glut_cb_state func); +void glutKeyboardFunc(glut_cb_keyb func); +void glutKeyboardUpFunc(glut_cb_keyb func); +void glutSpecialFunc(glut_cb_special func); +void glutSpecialUpFunc(glut_cb_special func); +void glutMouseFunc(glut_cb_mouse func); +void glutMotionFunc(glut_cb_motion func); +void glutPassiveMotionFunc(glut_cb_motion func); +void glutSpaceballMotionFunc(glut_cb_sbmotion func); +void glutSpaceballRotateFunc(glut_cb_sbmotion func); +void glutSpaceballButtonFunc(glut_cb_sbbutton func); + +int glutGet(unsigned int s); +int glutGetModifiers(void); +int glutExtensionSupported(char *ext); + +void glutSolidSphere(float rad, int slices, int stacks); +void glutWireSphere(float rad, int slices, int stacks); +void glutSolidCube(float sz); +void glutWireCube(float sz); +void glutSolidCone(float base, float height, int slices, int stacks); +void glutWireCone(float base, float height, int slices, int stacks); +void glutSolidCylinder(float rad, float height, int slices, int stacks); +void glutSolidTorus(float inner_rad, float outer_rad, int sides, int rings); +void glutWireTorus(float inner_rad, float outer_rad, int sides, int rings); +void glutSolidTeapot(float size); +void glutWireTeapot(float size); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* MINIGLUT_H_ */ diff --git a/src/player.c b/src/player.c index ebaef3e..5a39077 100644 --- a/src/player.c +++ b/src/player.c @@ -1,7 +1,18 @@ +#include #include "player.h" +#include "level.h" #include "gbaregs.h" #include "xgl.h" +void init_player(struct player *p, struct level *lvl) +{ + memset(p, 0, sizeof *p); + p->cx = lvl->orgx; + p->cy = lvl->orgy; + cell_to_pos(lvl, lvl->orgx, lvl->orgy, &p->x, &p->y); + p->cell = level_cell(lvl, lvl->orgx, lvl->orgy); +} + void player_input(struct player *p, uint16_t bnstate) { if(bnstate & KEY_UP) { diff --git a/src/player.h b/src/player.h index 33abfaf..7636af5 100644 --- a/src/player.h +++ b/src/player.h @@ -4,6 +4,7 @@ #include struct cell; +struct level; struct player { int32_t x, y; @@ -12,6 +13,7 @@ struct player { struct cell *cell; }; +void init_player(struct player *p, struct level *lvl); void player_input(struct player *p, uint16_t bnstate); #endif /* PLAYER_H_ */ diff --git a/src/sprite.c b/src/sprite.c deleted file mode 100644 index c77ae90..0000000 --- a/src/sprite.c +++ /dev/null @@ -1,77 +0,0 @@ -#include "sprite.h" -#include "gbaregs.h" - - -void spr_setup(int xtiles, int ytiles, unsigned char *pixels, unsigned char *cmap) -{ - int i, j, num_tiles; - uint16_t *cptr, *src, *dst; - - num_tiles = xtiles * ytiles; - dst = (uint16_t*)VRAM_LFB_OBJ_ADDR; - src = (uint16_t*)pixels; - for(i=0; i> 3; - unsigned char g = *cmap++ >> 3; - unsigned char b = *cmap++ >> 3; - *cptr++ = r | ((uint16_t)g << 5) | ((uint16_t)b << 10); - } -} - -void spr_clear(void) -{ - int i; - - for(i=0; i<128; i++) { - spr_oam_clear(0, i); - } -} - -void spr_oam(uint16_t *oam, int idx, int spr, int x, int y, 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); -} - -void spr_spr_oam(uint16_t *oam, int idx, struct sprite *spr) -{ - int i; - struct hwsprite *s; - - s = spr->hwspr; - for(i=0; inum_hwspr; i++) { - spr_oam(oam, idx, s->id, spr->x + s->x, spr->y + s->y, s->flags); - s++; - } -} - -void spr_transform(uint16_t *oam, int idx, int16_t *mat) -{ - if(!oam) oam = (uint16_t*)OAM_ADDR; - - oam += (idx << 4) + 3; - - oam[0] = *mat++; - oam[4] = *mat++; - oam[8] = *mat++; - oam[12] = *mat; -} diff --git a/src/timer.c b/src/timer.c deleted file mode 100644 index f4b0c7a..0000000 --- a/src/timer.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "intr.h" -#include "timer.h" - -#define F_CLK 16780000 -/* clock is 16.78MHz - * - no prescale: 59.595ns - * - prescale 64: 3.814us - * - prescale 256: 15.256us - * - prescale 1024: 61.025us - */ - -static void timer_intr(void); - -void init_timer(int tm, unsigned long rate_hz, void (*intr)(void)) -{ - static const unsigned long clk[] = {F_CLK, F_CLK / 64, F_CLK / 256, F_CLK / 1024}; - unsigned long count; - int pscl = 0; - - do { - count = clk[pscl] / rate_hz; - } while(count >= 65536 && ++pscl < 4); - - if(pscl >= 4) return; /* impossible rate */ - - REG_TMCNT_H(tm) = 0; - REG_TMCNT_L(tm) = 65536 - count; - if(intr) { - interrupt(INTR_TIMER0 + tm, intr); - unmask(INTR_TIMER0 + tm); - REG_TMCNT_H(tm) = TMCNT_IE; - } - REG_TMCNT_H(tm) |= TMCNT_EN | pscl; -} - -void reset_msec_timer(void) -{ - REG_TM0CNT_H &= ~TMCNT_EN; - interrupt(INTR_TIMER0, timer_intr); - timer_msec = 0; - REG_TM0CNT_L = 65535 - 16779; - REG_TM0CNT_H |= TMCNT_IE | TMCNT_EN; - unmask(INTR_TIMER0); -} - -void delay(unsigned long ms) -{ - unsigned long end = timer_msec + ms; - while(timer_msec < end); -} - -static void timer_intr(void) -{ - timer_msec++; -} diff --git a/src/timer.h b/src/timer.h deleted file mode 100644 index 3daed47..0000000 --- a/src/timer.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef TIMER_H_ -#define TIMER_H_ - -#include "gbaregs.h" - -#define enable_timer(x) \ - do { REG_TMCNT_H(x) |= TMCNT_EN; } while(0) - -#define disable_timer(x) \ - do { REG_TMCNT_H(x) &= ~TMCNT_EN; } while(0) - -volatile unsigned long timer_msec; - -void init_timer(int tm, unsigned long rate_hz, void (*intr)(void)); - -void reset_msec_timer(void); - -void delay(unsigned long ms); - -#ifdef __thumb__ -#define udelay(x) asm volatile ( \ - "0: sub %0, %0, #1\n\t" \ - "bne 0b\n\t" \ - :: "r"(x) : "cc") -#else -#define udelay(x) asm volatile ( \ - "0: subs %0, %0, #1\n\t" \ - "bne 0b\n\t" \ - :: "r"(x) : "cc") -#endif - -#endif /* TIMER_H_ */ diff --git a/src/util.c b/src/util.c index c734f85..fa5b4b5 100644 --- a/src/util.c +++ b/src/util.c @@ -3,7 +3,13 @@ #include "util.h" #include "debug.h" +#ifdef BUILD_GBA extern char __iheap_start; +#else +#define IWRAM_POOL_SZ 32768 +#define __iheap_start iwram[0] +static char iwram[IWRAM_POOL_SZ]; +#endif static char *top = &__iheap_start; int iwram_brk(void *addr) -- 1.7.10.4