initial commit
authorJohn Tsiombikas <nuclear@member.fsf.org>
Sat, 13 Mar 2021 01:28:21 +0000 (03:28 +0200)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Sat, 13 Mar 2021 01:28:21 +0000 (03:28 +0200)
16 files changed:
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
src/data.s [new file with mode: 0644]
src/gbaregs.h [new file with mode: 0644]
src/gfx.h [new file with mode: 0644]
src/intr.c [new file with mode: 0644]
src/intr.h [new file with mode: 0644]
src/keyb.c [new file with mode: 0644]
src/keyb.h [new file with mode: 0644]
src/main.c [new file with mode: 0644]
src/timer.c [new file with mode: 0644]
src/timer.h [new file with mode: 0644]
tools/pngdump/Makefile [new file with mode: 0644]
tools/pngdump/image.c [new file with mode: 0644]
tools/pngdump/image.h [new file with mode: 0644]
tools/pngdump/main.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..100346d
--- /dev/null
@@ -0,0 +1,9 @@
+*.o
+*.d
+*.swp
+*.elf
+*.gba
+*.png
+*.raw
+*.pal
+pngdump
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..88f8d0e
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,72 @@
+src = $(wildcard src/*.c)
+ssrc = $(wildcard src/*.s)
+#dataobj = data/data.o
+obj = $(src:.c=.o) $(ssrc:.s=.o) $(dataobj)
+dep = $(src:.c=.d)
+name = blender
+elf = $(name).elf
+bin = $(name).gba
+
+ARCH = arm-none-eabi-
+
+CPP = $(ARCH)cpp
+CC = $(ARCH)gcc
+AS = $(ARCH)as
+OBJCOPY = $(ARCH)objcopy
+EMU = vbam
+
+opt = -O1 -fomit-frame-pointer -mcpu=arm7tdmi -mtune=arm7tdmi -mthumb -mthumb-interwork
+#dbg = -g
+
+CFLAGS = $(opt) $(dbg) -pedantic -Wall -MMD
+ASFLAGS = -mthumb-interwork
+LDFLAGS = -mthumb -mthumb-interwork -Wl,-print-gc-sections
+EMUFLAGS = -T 100 -f 1 --agb-print
+
+
+.PHONY: all
+all: $(bin) $(bin_mb)
+
+$(bin): $(elf)
+       $(OBJCOPY) -O binary $(elf) $(bin)
+       gbafix $(bin)
+
+$(elf): $(obj)
+       $(CC) -o $(elf) $(obj) -specs=gba.specs $(LDFLAGS)
+
+-include $(dep)
+
+src/data.o: src/data.s data/bg.raw data/bg.pal
+
+tools/pngdump/pngdump:
+       $(MAKE) -C tools/pngdump
+
+%.raw: %.png tools/pngdump/pngdump
+       tools/pngdump/pngdump -o $@ $<
+
+%.pal: %.png tools/pngdump/pngdump
+       tools/pngdump/pngdump -o $@ -c $<
+
+.PHONY: clean
+clean:
+       rm -f $(obj) $(bin) $(bin_mb) $(elf) $(elf_mb)
+
+.PHONY: cleandep
+cleandep:
+       rm -f $(dep)
+
+.PHONY: install
+install: $(bin)
+       if2a -n -f -W $<
+
+.PHONY: run
+run: $(bin_mb)
+       if2a -m $<
+
+.PHONY: simrun
+simrun: $(bin)
+       $(EMU) $(EMUFLAGS) $(bin)
+
+.PHONY: disasm
+disasm: $(elf)
+       $(ARCH)objdump -d $< >$@
diff --git a/src/data.s b/src/data.s
new file mode 100644 (file)
index 0000000..b178889
--- /dev/null
@@ -0,0 +1,9 @@
+       .section .rodata
+
+       .globl bgimg_pixels
+       .globl bgimg_cmap
+
+bgimg_pixels:
+       .incbin "data/bg.raw"
+bgimg_cmap:
+       .incbin "data/bg.pal"
diff --git a/src/gbaregs.h b/src/gbaregs.h
new file mode 100644 (file)
index 0000000..96f024a
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+blender for the Gameboy Advance
+Copyright (C) 2021  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef GBAREGS_H_
+#define GBAREGS_H_
+
+#include <stdint.h>
+
+#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
+
+/* 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_L  REG16(0xb8)
+#define REG_DMA0CNT_H  REG16(0xba)
+#define REG_DMA1SAD            REG32(0xbc)
+#define REG_DMA1DAD            REG32(0xc0)
+#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_L  REG16(0xd0)
+#define REG_DMA2CNT_H  REG16(0xd2)
+#define REG_DMA3SAD            REG32(0xd4)
+#define REG_DMA3DAD            REG32(0xd8)
+#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_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 DMACNT_DST_INC         0
+#define DMACNT_DST_DEC         0x0020
+#define DMACNT_DST_FIXED       0x0040
+#define DMACNT_SRC_INC         0
+#define DMACNT_SRC_DEC         0x0080
+#define DMACNT_SRC_FIXED       0x0100
+#define DMACNT_REPEAT          0x0200
+#define DMACNT_16BIT           0
+#define DMACNT_32BIT           0x0400
+#define DMACNT_VBLANK          0x1000
+#define DMACNT_HBLANK          0x2000
+#define DMACNT_SOUND           0x3000
+#define DMACNT_IEN                     0x4000
+#define DMACNT_EN                      0x8000
+
+
+#endif /* GBAREGS_H_ */
diff --git a/src/gfx.h b/src/gfx.h
new file mode 100644 (file)
index 0000000..5ea579a
--- /dev/null
+++ b/src/gfx.h
@@ -0,0 +1,37 @@
+/*
+blender for the Gameboy Advance
+Copyright (C) 2021  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef GFX_H_
+#define GFX_H_
+
+#include "gbaregs.h"
+
+#define wait_vblank()  while(REG_VCOUNT < 160)
+
+#define swap_buffers() \
+       do { \
+               static int vis; \
+               vis ^= 0x10; \
+               REG_DISPCNT = DISPCNT_BG2 | 4 | vis; \
+       } while(0)
+
+#define set_bg_color(idx, r, g, b) \
+       do { \
+               ((uint16_t*)CRAM_BG_ADDR)[idx] = (uint16_t)(r) | ((uint16_t)(g) << 5) | ((uint16_t)(b) << 10); \
+       } while(0)
+
+#endif /* GFX_H_ */
diff --git a/src/intr.c b/src/intr.c
new file mode 100644 (file)
index 0000000..5c2f2c9
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+blender for the Gameboy Advance
+Copyright (C) 2021  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#include "intr.h"
+
+#define MAX_INTR       14
+static void (*intr_table[MAX_INTR])(void);
+
+__attribute__ ((target("arm")))
+static void intr_handler(void)
+{
+       int i;
+       uint16_t iflags;
+
+       clr_intr();
+       iflags = REG_IF & 0x3fff;
+
+
+       for(i=0; i<MAX_INTR; i++) {
+               if((iflags & (1 << i)) && intr_table[i]) {
+                       intr_table[i]();
+               }
+       }
+
+       REG_IF = iflags;        /* ack intr */
+       set_intr();
+}
+
+void intr_init(void)
+{
+       INTR_VECTOR = (uint32_t)intr_handler;
+}
+
+void interrupt(int intr, void (*handler)(void))
+{
+       intr_table[intr] = handler;
+}
diff --git a/src/intr.h b/src/intr.h
new file mode 100644 (file)
index 0000000..38a0fd5
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+blender for the Gameboy Advance
+Copyright (C) 2021  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef _INTR_H_
+#define _INTR_H_
+
+#include "gbaregs.h"
+
+/* interrupts */
+enum {
+       INTR_VBLANK,
+       INTR_HBLANK,
+       INTR_VCOUNT,
+       INTR_TIMER0,
+       INTR_TIMER1,
+       INTR_TIMER2,
+       INTR_TIMER3,
+       INTR_COMM,
+       INTR_DMA0,
+       INTR_DMA1,
+       INTR_DMA2,
+       INTR_DMA3,
+       INTR_KEY,
+       INTR_GPAK
+};
+
+void intr_init(void);
+
+/* set/clear interrupts */
+#define set_intr()     \
+       do { REG_IME |= 0x0001; } while(0)
+#define clr_intr()     \
+       do { REG_IME &= 0xfffe; } while(0)
+
+/* set an interrupt handler */
+void interrupt(int intr, void (*handler)(void));
+
+/* mask/unmask an interrupt */
+#define mask(intr)             do {REG_IE &= ~(1 << (intr));} while(0)
+#define unmask(intr)   do {REG_IE |= 1 << (intr);} while(0)
+
+#endif /* _INTR_H_ */
diff --git a/src/keyb.c b/src/keyb.c
new file mode 100644 (file)
index 0000000..1523461
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+blender for the Gameboy Advance
+Copyright (C) 2021  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#include "keyb.h"
+#include "gbaregs.h"
+#include "timer.h"
+
+#define NUM_KEYS       10
+
+static int rep_start, rep_rep;
+static unsigned long first_press[16], last_press[16];
+static uint16_t repmask;
+
+void key_repeat(int start, int rep, uint16_t mask)
+{
+       rep_start = start;
+       rep_rep = rep;
+       repmask = mask;
+}
+
+void update_keyb(void)
+{
+       static uint16_t prevstate;
+       int i;
+       unsigned long msec = timer_msec;
+
+       keystate = ~REG_KEYINPUT;
+       keydelta = keystate ^ prevstate;
+       prevstate = keystate;
+
+       for(i=0; i<NUM_KEYS; i++) {
+               uint16_t bit = 1 << i;
+               if(!(bit & repmask)) {
+                       continue;
+               }
+
+               if(keystate & bit) {
+                       if(keydelta & bit) {
+                               first_press[i] = msec;
+                       } else {
+                               if(msec - first_press[i] >= rep_start && msec - last_press[i] >= rep_rep) {
+                                       keydelta |= bit;
+                                       last_press[i] = msec;
+                               }
+                       }
+               }
+       }
+}
diff --git a/src/keyb.h b/src/keyb.h
new file mode 100644 (file)
index 0000000..930a250
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+blender for the Gameboy Advance
+Copyright (C) 2021  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef KEYB_H_
+#define KEYB_H_
+
+#include <stdint.h>
+
+#define KEYPRESS(key)  ((keystate & (key)) && (keydelta & (key)))
+#define KEYRELEASE(key)        ((keystate & (key)) == 0 && (keydelta & (key)))
+
+uint16_t keystate, keydelta;
+
+void key_repeat(int start, int rep, uint16_t mask);
+
+void update_keyb(void);
+
+#endif /* KEYB_H_ */
diff --git a/src/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..0eeaa5a
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+blender for the Gameboy Advance
+Copyright (C) 2021  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <string.h>
+#include "gbaregs.h"
+#include "timer.h"
+#include "keyb.h"
+#include "intr.h"
+#include "gfx.h"
+
+static void handle_keys(void);
+
+extern struct { unsigned char r, g, b; } bgimg_cmap[];
+extern unsigned char bgimg_pixels[];
+
+int main(void)
+{
+       int i;
+       uint16_t *cptr;
+       unsigned char r, g, b;
+
+       intr_init();
+       reset_msec_timer();
+       set_intr();
+
+       /* mode 4: 240x160 8bpp */
+       REG_DISPCNT = DISPCNT_BG2 | 4;
+
+       cptr = (uint16_t*)CRAM_BG_ADDR;
+       for(i=0; i<128; i++) {
+               r = bgimg_cmap[i].r >> 3;
+               g = bgimg_cmap[i].g >> 3;
+               b = bgimg_cmap[i].b >> 3;
+               *cptr++ = r | (g << 5) | (b << 10);
+       }
+       memcpy((void*)VRAM_LFB_FB0_ADDR, bgimg_pixels, 240 * 160);
+       memcpy((void*)VRAM_LFB_FB1_ADDR, bgimg_pixels, 240 * 160);
+
+       /*key_repeat(500, 75, KEY_LEFT | KEY_RIGHT | KEY_DOWN | KEY_UP);*/
+
+       for(;;) {
+               wait_vblank();
+               swap_buffers();
+       }
+
+       return 0;
+}
+
+static void handle_keys(void)
+{
+       update_keyb();
+
+       if(KEYPRESS(KEY_UP)) {
+       }
+       if(KEYPRESS(KEY_DOWN)) {
+       }
+       if(KEYPRESS(KEY_LEFT)) {
+       }
+       if(KEYPRESS(KEY_RIGHT)) {
+       }
+}
diff --git a/src/timer.c b/src/timer.c
new file mode 100644 (file)
index 0000000..a00b0fc
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+blender for the Gameboy Advance
+Copyright (C) 2021  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#include "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
new file mode 100644 (file)
index 0000000..214296c
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+blender for the Gameboy Advance
+Copyright (C) 2021  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef _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/tools/pngdump/Makefile b/tools/pngdump/Makefile
new file mode 100644 (file)
index 0000000..fcd3f4b
--- /dev/null
@@ -0,0 +1,9 @@
+LDFLAGS = -lpng -lz
+
+pngdump: main.o image.o
+       $(CC) -o $@ $^ $(LDFLAGS)
+
+clean:
+       $(RM) main.o
+       $(RM) image.o
+       $(RM) pngdump
diff --git a/tools/pngdump/image.c b/tools/pngdump/image.c
new file mode 100644 (file)
index 0000000..0ae86f9
--- /dev/null
@@ -0,0 +1,237 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <png.h>
+#include "image.h"
+
+int alloc_image(struct image *img, int x, int y, int bpp)
+{
+       memset(img, 0, sizeof *img);
+       img->width = x;
+       img->height = y;
+       img->bpp = bpp;
+       img->scansz = img->pitch = x * bpp / 8;
+
+       if(!(img->pixels = malloc(y * img->scansz))) {
+               fprintf(stderr, "failed to allocate %dx%d (%dbpp) pixel buffer\n", x, y, bpp);
+               return -1;
+       }
+
+       /* just a guess, assume the user will fill the details, but set up reasonable
+        * defaults just in case...
+        */
+       if(bpp <= 8) {
+               img->nchan = 1;
+               img->cmap_ncolors = 1 << bpp;
+       } else if(bpp <= 24) {
+               img->nchan = 3;
+       } else {
+               img->nchan = 4;
+       }
+       return 0;
+}
+
+int load_image(struct image *img, const char *fname)
+{
+       int i;
+       FILE *fp;
+       png_struct *png;
+       png_info *info;
+       int chan_bits, color_type;
+       png_uint_32 xsz, ysz;
+       png_color *palette;
+       unsigned char **scanline;
+       unsigned char *dptr;
+
+       if(!(fp = fopen(fname, "rb"))) {
+               fprintf(stderr, "failed to open: %s: %s\n", fname, strerror(errno));
+               return -1;
+       }
+
+       if(!(png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0))) {
+               fclose(fp);
+               return -1;
+       }
+       if(!(info = png_create_info_struct(png))) {
+               fclose(fp);
+               png_destroy_read_struct(&png, 0, 0);
+               return -1;
+       }
+       if(setjmp(png_jmpbuf(png))) {
+               fclose(fp);
+               png_destroy_read_struct(&png, &info, 0);
+               return -1;
+       }
+
+       png_init_io(png, fp);
+       png_read_png(png, info, 0, 0);
+
+       png_get_IHDR(png, info, &xsz, &ysz, &chan_bits, &color_type, 0, 0, 0);
+       img->width = xsz;
+       img->height = ysz;
+       img->nchan = png_get_channels(png, info);
+       img->bpp = img->nchan * chan_bits;
+       img->scansz = img->pitch = xsz * img->bpp / 8;
+       img->cmap_ncolors = 0;
+
+       if(color_type == PNG_COLOR_TYPE_PALETTE) {
+               png_get_PLTE(png, info, &palette, &img->cmap_ncolors);
+               memcpy(img->cmap, palette, img->cmap_ncolors * sizeof *img->cmap);
+       }
+
+       if(!(img->pixels = malloc(ysz * img->scansz))) {
+               perror("failed to allocate pixel buffer");
+               fclose(fp);
+               png_destroy_read_struct(&png, &info, 0);
+               return -1;
+       }
+       dptr = img->pixels;
+
+       scanline = (unsigned char**)png_get_rows(png, info);
+       for(i=0; i<ysz; i++) {
+               memcpy(dptr, scanline[i], img->scansz);
+               dptr += img->pitch;
+       }
+
+       fclose(fp);
+       png_destroy_read_struct(&png, &info, 0);
+       return 0;
+}
+
+int save_image(struct image *img, const char *fname)
+{
+       int i, chan_bits, coltype;
+       FILE *fp;
+       png_struct *png;
+       png_info *info;
+       png_text txt;
+       unsigned char **scanline = 0;
+       unsigned char *pptr;
+
+       if(!(fp = fopen(fname, "wb"))) {
+               fprintf(stderr, "save_image: failed to open: %s: %s\n", fname, strerror(errno));
+               return -1;
+       }
+
+       if(!(png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0))) {
+               fclose(fp);
+               return -1;
+       }
+       if(!(info = png_create_info_struct(png))) {
+               png_destroy_write_struct(&png, 0);
+               fclose(fp);
+               return -1;
+       }
+
+       txt.compression = PNG_TEXT_COMPRESSION_NONE;
+       txt.key = "Software";
+       txt.text = "img2tiles";
+       txt.text_length = 0;
+
+       if(setjmp(png_jmpbuf(png))) {
+               png_destroy_write_struct(&png, &info);
+               free(scanline);
+               fclose(fp);
+               return -1;
+       }
+
+       switch(img->nchan) {
+       case 1:
+               if(img->cmap_ncolors > 0) {
+                       coltype = PNG_COLOR_TYPE_PALETTE;
+               } else {
+                       coltype = PNG_COLOR_TYPE_GRAY;
+               }
+               break;
+       case 2:
+               coltype = PNG_COLOR_TYPE_GRAY_ALPHA;
+               break;
+       case 3:
+               coltype = PNG_COLOR_TYPE_RGB;
+               break;
+       case 4:
+               coltype = PNG_COLOR_TYPE_RGB_ALPHA;
+               break;
+       }
+
+       chan_bits = img->bpp / img->nchan;
+       png_set_IHDR(png, info, img->width, img->height, chan_bits, coltype, PNG_INTERLACE_NONE,
+                       PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+       png_set_text(png, info, &txt, 1);
+
+       if(img->cmap_ncolors > 0) {
+               png_set_PLTE(png, info, (png_color*)img->cmap, img->cmap_ncolors);
+       }
+
+       if(!(scanline = malloc(img->height * sizeof *scanline))) {
+               png_destroy_write_struct(&png, &info);
+               fclose(fp);
+               return -1;
+       }
+
+       pptr = img->pixels;
+       for(i=0; i<img->height; i++) {
+               scanline[i] = pptr;
+               pptr += img->pitch;
+       }
+       png_set_rows(png, info, scanline);
+
+       png_init_io(png, fp);
+       png_write_png(png, info, 0, 0);
+       png_destroy_write_struct(&png, &info);
+       free(scanline);
+       fclose(fp);
+       return 0;
+}
+
+
+int cmp_image(struct image *a, struct image *b)
+{
+       int i;
+       unsigned char *aptr = a->pixels;
+       unsigned char *bptr = b->pixels;
+
+       if(a->width != b->width || a->height != b->height || a->bpp != b->bpp || a->nchan != b->nchan) {
+               return -1;
+       }
+
+       for(i=0; i<a->height; i++) {
+               if(memcmp(aptr, bptr, a->scansz) != 0) {
+                       return -1;
+               }
+               aptr += a->pitch;
+               bptr += b->pitch;
+       }
+       return 0;
+}
+
+void blit(struct image *src, int sx, int sy, int w, int h, struct image *dst, int dx, int dy)
+{
+       int i;
+       unsigned char *sptr, *dptr;
+
+       assert(src->bpp == dst->bpp);
+       assert(src->nchan == dst->nchan);
+
+       if(sx < 0) { w += sx; sx = 0; }
+       if(sy < 0) { h += sy; sy = 0; }
+       if(dx < 0) { w += dx; sx -= dx; dx = 0; }
+       if(dy < 0) { h += dy; sy -= dy; dy = 0; }
+       if(sx + w >= src->width) w = src->width - sx;
+       if(sy + h >= src->height) h = src->height - sy;
+       if(dx + w >= dst->width) w = dst->width - dx;
+       if(dy + h >= dst->height) h = dst->height - dy;
+
+       if(w <= 0 || h <= 0) return;
+
+       sptr = src->pixels + sy * src->pitch + sx * src->bpp / 8;
+       dptr = dst->pixels + dy * dst->pitch + dx * dst->bpp / 8;
+
+       for(i=0; i<h; i++) {
+               memcpy(dptr, sptr, w * dst->bpp / 8);
+               dptr += dst->pitch;
+               sptr += src->pitch;
+       }
+}
diff --git a/tools/pngdump/image.h b/tools/pngdump/image.h
new file mode 100644 (file)
index 0000000..b9f24b4
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef IMAGE_H_
+#define IMAGE_H_
+
+struct cmapent {
+       unsigned char r, g, b;
+};
+
+struct image {
+       int width, height;
+       int bpp;
+       int nchan;
+       int scansz, pitch;
+       int cmap_ncolors;
+       struct cmapent cmap[256];
+       unsigned char *pixels;
+};
+
+int alloc_image(struct image *img, int x, int y, int bpp);
+int load_image(struct image *img, const char *fname);
+int save_image(struct image *img, const char *fname);
+
+int cmp_image(struct image *a, struct image *b);
+
+void blit(struct image *src, int sx, int sy, int w, int h, struct image *dst, int dx, int dy);
+
+#endif /* IMAGE_H_ */
diff --git a/tools/pngdump/main.c b/tools/pngdump/main.c
new file mode 100644 (file)
index 0000000..a2b37fd
--- /dev/null
@@ -0,0 +1,125 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "image.h"
+
+void print_usage(const char *argv0);
+
+int main(int argc, char **argv)
+{
+       int i, mode = 0;
+       int text = 0;
+       char *fname = 0, *outfname = 0;
+       struct image img;
+       FILE *out = stdout;
+
+       for(i=1; i<argc; i++) {
+               if(argv[i][0] == '-') {
+                       if(argv[i][2] == 0) {
+                               switch(argv[i][1]) {
+                               case 'p':
+                                       mode = 0;
+                                       break;
+
+                               case 'c':
+                                       mode = 1;
+                                       break;
+
+                               case 'i':
+                                       mode = 2;
+                                       break;
+
+                               case 't':
+                                       text = 1;
+                                       break;
+
+                               case 'o':
+                                       if(!argv[++i]) {
+                                               fprintf(stderr, "%s must be followed by a filename\n", argv[i - 1]);
+                                               return 1;
+                                       }
+                                       outfname = argv[i];
+                                       break;
+
+                               case 'h':
+                                       print_usage(argv[0]);
+                                       return 0;
+
+                               default:
+                                       fprintf(stderr, "invalid option: %s\n", argv[i]);
+                                       print_usage(argv[0]);
+                                       return 1;
+                               }
+                       } else {
+                               fprintf(stderr, "invalid option: %s\n", argv[i]);
+                               print_usage(argv[0]);
+                               return 1;
+                       }
+               } else {
+                       if(fname) {
+                               fprintf(stderr, "unexpected argument: %s\n", argv[i]);
+                               print_usage(argv[0]);
+                               return 1;
+                       }
+                       fname = argv[i];
+               }
+       }
+
+       if(!fname) {
+               fprintf(stderr, "pass the filename of a PNG file\n");
+               return 1;
+       }
+       if(load_image(&img, fname) == -1) {
+               fprintf(stderr, "failed to load PNG file: %s\n", fname);
+               return 1;
+       }
+
+       if(outfname) {
+               if(!(out = fopen(outfname, "wb"))) {
+                       fprintf(stderr, "failed to open output file: %s: %s\n", outfname, strerror(errno));
+                       return 1;
+               }
+       }
+
+       switch(mode) {
+       case 0:
+               fwrite(img.pixels, 1, img.scansz * img.height, out);
+               break;
+
+       case 1:
+               if(text) {
+                       for(i=0; i<img.cmap_ncolors; i++) {
+                               printf("%d %d %d\n", img.cmap[i].r, img.cmap[i].g, img.cmap[i].b);
+                       }
+               } else {
+                       fwrite(img.cmap, sizeof img.cmap[0], img.cmap_ncolors, out);
+               }
+               break;
+
+       case 2:
+               printf("size: %dx%d\n", img.width, img.height);
+               printf("bit depth: %d\n", img.bpp);
+               printf("scanline size: %d bytes\n", img.scansz);
+               if(img.cmap_ncolors > 0) {
+                       printf("colormap entries: %d\n", img.cmap_ncolors);
+               } else {
+                       printf("color channels: %d\n", img.nchan);
+               }
+               break;
+       }
+
+       fclose(out);
+       return 0;
+}
+
+void print_usage(const char *argv0)
+{
+       printf("Usage: %s [options] <input file>\n", argv0);
+       printf("Options:\n");
+       printf(" -p: dump pixels (default)\n");
+       printf(" -c: dump colormap (palette) entries\n");
+       printf(" -i: print image information\n");
+       printf(" -t: dump as text\n");
+       printf(" -h: print usage and exit\n");
+}