--- /dev/null
+*.o
+*.d
+*.swp
+*.map
+*.elf
+*.bin
--- /dev/null
+csrc = $(wildcard src/*.c) $(wildcard src/libc/*.c)
+asrc = $(wildcard src/*.s)
+aSsrc = $(wildcard src/*.S)
+obj = $(asrc:.s=.o) $(aSsrc:.S=.o) $(csrc:.c=.o)
+dep = $(csrc:.c=.d)
+
+name = megatris
+elf = $(name).elf
+bin = $(name).bin
+
+warn = -pedantic -Wall
+dbg = -g
+opt = -O2
+def = -DGAMENAME=\"megatris\" -DVERSTR=\"01\" -D__NO_CTYPE
+inc = -I. -Isrc -Isrc/libc
+
+tool_prefix = m68k-linux-gnu-
+
+CC = $(tool_prefix)gcc
+AS = $(tool_prefix)as
+LD = $(tool_prefix)ld
+OBJCOPY = $(tool_prefix)objcopy
+
+CFLAGS = -m68000 -ffreestanding -fno-builtin $(warn) $(dbg) $(opt) $(def) $(inc)
+CPPFLAGS = $(def)
+ASFLAGS = -m68000 $(inc)
+LDFLAGS = -T megadrive.ld -print-gc-sections -L/usr/lib/gcc-cross/m68k-linux-gnu/6 -lgcc
+
+$(bin): $(elf)
+ $(OBJCOPY) -O binary $< $@
+
+$(elf): $(obj)
+ $(LD) -o $@ $(obj) -Map link.map $(LDFLAGS)
+
+#tun_data.h: tunnel.ppm
+# cat $< | tools/ppm2md tun_ >$@
+
+-include $(dep)
+
+%.d: %.c
+ @$(CPP) $(CFLAGS) $< -MM -MT $(@:.d=.o) >$@
+
+.PHONY: clean
+clean:
+ rm -f $(obj) $(elf) $(bin)
+
+.PHONY: cleandep
+cleandep:
+ rm -f $(dep)
+
+.PHONY: run
+run: $(bin)
+ gens-sdl $<
+
+.PHONY: copy
+copy: $(bin)
+ mount /media/usbmass && cp $(bin) /media/usbmass/$(bin)
+ umount /media/usbmass
+
+.PHONY: install
+install: $(bin)
+ mount /media/usbmass
+ [ -f /media/usbmass/MEGA/MEGA.RBF ] || cp $(bin) /media/usbmass/MEGA/MEGA.BIN
+ umount /media/usbmass
--- /dev/null
+OUTPUT_ARCH(m68k)
+
+MEMORY
+{
+ rom : ORIGIN = 0x00000000, LENGTH = 0x00a00000
+ ram : ORIGIN = 0x00ff0000, LENGTH = 0x00010000
+}
+
+PROVIDE (_stacktop = 0x01000000);
+
+SECTIONS {
+ /* ---- start of ROM ---- */
+ /* .vect section is used to place the m68k exception vectors at the
+ * beginning of the address space
+ */
+ .vect : { * (.vect); } >rom
+ /* .romhdr section is used to place the SEGA ROM header at 0x100 */
+ . = 0x100;
+ .romhdr : { * (.romhdr); } >rom
+ .text : { * (.text); } >rom
+ .rodata : { * (.rodata); } >rom
+
+ /* place the load address of the .data section after .rodata */
+ . = ALIGN(4);
+ _data_lma = .;
+ _rom_end = _data_lma + _data_size;
+
+ /* ---- start of RAM ---- */
+ . = 0xff0000;
+ /* place the .data section at the start of RAM */
+ .data ALIGN(4): AT (_data_lma) {
+ _data_start = .;
+ * (.data);
+ . = ALIGN(4);
+ _data_end = .;
+ } >ram
+ _data_size = SIZEOF(.data);
+
+ /* place the .bss section at the end */
+ .bss ALIGN(4): {
+ _bss_start = .;
+ * (.bss);
+ . = ALIGN(4);
+ _bss_end = .;
+ } >ram
+ _bss_size = SIZEOF(.bss);
+}
--- /dev/null
+#ifndef INTR_H_
+#define INTR_H_
+
+void enable_intr(void);
+void disable_intr(void);
+
+#endif /* INTR_H_ */
--- /dev/null
+| vi:filetype=gas68k:
+| the following will go into the .vect section which will be placed at the very
+| begining of the binary at address 0 by the linker (see lnkscript).
+ .section .vect,"a"
+ .extern start
+| exception vectors
+ .long _stacktop | 00 reset - initial SSP
+ .long start | 01 reset - initial PC
+ .long intr_fatal | 02 bus error
+ .long intr_fatal | 03 address error
+ .long intr_fatal | 04 illegal instruction
+ .long intr_fatal | 05 zero divide
+ .long intr_fatal | 06 chk instruction
+ .long intr_fatal | 07 trapv instruction
+ .long intr_fatal | 08 privilege violation
+ .long intr_fatal | 09 trace
+ .long intr_fatal | 0a line 1010 emulator
+ .long intr_fatal | 0b line 1111 emulator
+ .long intr_fatal | 0c reserved
+ .long intr_fatal | 0d reserved
+ .long intr_fatal | 0e format error (mc68010 only)
+ .long intr_fatal | 0f uninitialized interrupt vector
+ .long intr_fatal | 10 \
+ .long intr_fatal | 11 |
+ .long intr_fatal | 12 |
+ .long intr_fatal | 13 > reserved
+ .long intr_fatal | 14 |
+ .long intr_fatal | 15 |
+ .long intr_fatal | 16 |
+ .long intr_fatal | 17 /
+ .long intr_fatal | 18 spurious interrupt
+ .long intr_fatal | 19 level 1 interrupt
+ .long intr_fatal | 1a level 2 interrupt
+ .long intr_fatal | 1b level 3 interrupt
+ .long intr_hblank | 1c level 4 interrupt (hblank in the mega drive)
+ .long intr_fatal | 1d level 5 interrupt
+ .long intr_vblank | 1e level 6 interrupt (vblank in the mega drive)
+ .long intr_fatal | 1f level 7 interrupt
+ .long intr_fatal | 20 trap 0
+ .long intr_fatal | 21 trap 1
+ .long intr_fatal | 22 trap 2
+ .long intr_fatal | 23 trap 3
+ .long intr_fatal | 24 trap 4
+ .long intr_fatal | 25 trap 5
+ .long intr_fatal | 26 trap 6
+ .long intr_fatal | 27 trap 7
+ .long intr_fatal | 28 trap 8
+ .long intr_fatal | 29 trap 9
+ .long intr_fatal | 2a trap a
+ .long intr_fatal | 2b trap b
+ .long intr_fatal | 2c trap c
+ .long intr_fatal | 2d trap d
+ .long intr_fatal | 2e trap e
+ .long intr_fatal | 2f trap f
+ .long intr_fatal | 30 \
+ .long intr_fatal | 31 |
+ .long intr_fatal | 32 |
+ .long intr_fatal | 33 |
+ .long intr_fatal | 34 |
+ .long intr_fatal | 35 |
+ .long intr_fatal | 36 |
+ .long intr_fatal | 37 |
+ .long intr_fatal | 38 > reserved
+ .long intr_fatal | 39 |
+ .long intr_fatal | 3a |
+ .long intr_fatal | 3b |
+ .long intr_fatal | 3c |
+ .long intr_fatal | 3d |
+ .long intr_fatal | 3e |
+ .long intr_fatal | 3f /
+
+| from here on we continue in the regular .text section since we don't care
+| where this code ends up.
+ .text
+
+.global enable_intr
+enable_intr:
+ andi.w #0xf8ff, %sr
+ rts
+
+.global disable_intr
+disable_intr:
+ ori.w #0x0300, %sr
+ rts
+
+| interrupt handlers
+intr_fatal:
+ stop #0x2700
+
+ .include "vdpdefs.inc"
+| .extern vblank_handler
+| .extern palval
+
+intr_hblank:
+| move.l #0xc0020000, VDP_PORT_CTL
+|
+| move.w testcol, %d0
+| move.w %d0, VDP_PORT_DATA
+| rol.b #4, %d0
+| move.w %d0, testcol
+|
+ rte
+
+intr_vblank:
+| jsr vblank_handler
+ rte
--- /dev/null
+#include "io.h"
+
+void io_init(void)
+{
+ io_setdir(0, 0x40);
+ io_setdir(1, 0x40);
+
+ IO_REG_DATA(0) = 0;
+ IO_REG_DATA(1) = 0;
+}
--- /dev/null
+#ifndef MEGADRIVE_IO_H_
+#define MEGADRIVE_IO_H_
+
+#include <stdint.h>
+
+#define IO_REG_VER *((volatile uint8_t*)0xa10001)
+#define IO_REG_DATA1 *((volatile uint8_t*)0xa10003)
+#define IO_REG_DATA2 *((volatile uint8_t*)0xa10005)
+#define IO_REG_DATA3 *((volatile uint8_t*)0xa10007)
+#define IO_REG_CTRL1 *((volatile uint8_t*)0xa10009)
+#define IO_REG_CTRL2 *((volatile uint8_t*)0xa1000b)
+#define IO_REG_CTRL3 *((volatile uint8_t*)0xa1000d)
+#define IO_REG_TXDATA1 *((volatile uint8_t*)0xa1000f)
+#define IO_REG_RXDATA1 *((volatile uint8_t*)0xa10011)
+#define IO_REG_S_CTRL1 *((volatile uint8_t*)0xa10013)
+#define IO_REG_TXDATA2 *((volatile uint8_t*)0xa10015)
+#define IO_REG_RXDATA2 *((volatile uint8_t*)0xa10017)
+#define IO_REG_S_CTRL2 *((volatile uint8_t*)0xa10013)
+#define IO_REG_TXDATA3 *((volatile uint8_t*)0xa1001b)
+#define IO_REG_RXDATA3 *((volatile uint8_t*)0xa1001d)
+#define IO_REG_S_CTRL3 *((volatile uint8_t*)0xa1001f)
+#define IO_REG_LOCK *((volatile uint8_t*)0xa14000)
+#define IO_REG_TMSS *((volatile uint8_t*)0xa14101)
+
+#define IO_REG_DATA(x) *((volatile uint8_t*)0xa10003 + ((x) << 1))
+#define IO_REG_CTRL(x) *((volatile uint8_t*)0xa10009 + ((x) << 1))
+
+
+enum {
+ IO_VER_VERSION_MASK = 0x0f,
+ IO_VER_EXP = 0x20,
+ IO_VER_PAL = 0x40,
+ IO_VER_NONJP = 0x80
+};
+
+enum {
+ IO_PAD_UP = 0x001,
+ IO_PAD_DOWN = 0x002,
+ IO_PAD_LEFT = 0x004,
+ IO_PAD_RIGHT = 0x008,
+ IO_PAD_B = 0x010,
+ IO_PAD_C = 0x020,
+ IO_PAD_A = 0x040,
+ IO_PAD_START = 0x080,
+
+ IO_PAD_Z = 0x100,
+ IO_PAD_Y = 0x200,
+ IO_PAD_X = 0x400
+};
+#define IO_PAD_LOW_MASK 0x3f
+#define IO_PAD_HIGH_MASK (IO_PAD_A | IO_PAD_START)
+
+static inline void io_setdir(int port, uint8_t dir)
+{
+ IO_REG_CTRL(port) = dir;
+}
+
+static inline uint16_t io_readpad(int port)
+{
+ uint16_t bnstate;
+
+ bnstate = ~(IO_REG_DATA(port) << 2) & IO_PAD_HIGH_MASK;
+
+ IO_REG_DATA(port) = 0x40; /* select mode 1 */
+ asm volatile("nop");
+ asm volatile("nop");
+ bnstate |= ~IO_REG_DATA(port) & IO_PAD_LOW_MASK;
+
+ IO_REG_DATA(port) = 0; /* select mode 0 */
+ return bnstate;
+}
+
+static inline uint16_t io_readpad6(int port)
+{
+ uint16_t bnstate;
+
+ IO_REG_DATA(port) = 0x40; /* start with mode 1 */
+ asm volatile("nop");
+ asm volatile("nop");
+ bnstate = ~IO_REG_DATA(port) & IO_PAD_LOW_MASK;
+
+ IO_REG_DATA(port) = 0; /* select mode 0 */
+ asm volatile("nop");
+ asm volatile("nop");
+ bnstate |= ~(IO_REG_DATA(port) << 2) & IO_PAD_HIGH_MASK;
+
+ /* then introduce two more rising edges to trigger extended mode */
+ IO_REG_DATA(port) = 0x40;
+ asm volatile("nop");
+ asm volatile("nop");
+ IO_REG_DATA(port) = 0;
+ asm volatile("nop");
+ asm volatile("nop");
+ IO_REG_DATA(port) = 0x40;
+ asm volatile("nop");
+ asm volatile("nop");
+
+ bnstate |= (uint16_t)(~IO_REG_DATA(port) & 0x7) << 8;
+ IO_REG_DATA(port) = 0; /* return mode to 0 */
+ return bnstate;
+}
+
+void io_init(void);
+/*uint16_t io_readpad(int port);*/
+
+#endif /* MEGADRIVE_IO_H_ */
--- /dev/null
+#include <stdint.h>
+#include "vdp.h"
+#include "io.h"
+#include "pad.h"
+#include "intr.h"
+
+void load_pattern(int idx, void *data);
+void set_tile(int nametab_idx, int x, int y, int tile_idx, int palidx);
+
+
+int main(void)
+{
+ int i;
+ int bgcol = 0;
+
+ vdp_init();
+ io_init();
+
+ if(IO_REG_VER & IO_VER_PAL) {
+ vdp_setreg(VDP_REG_MODE2, vdp_getreg(VDP_REG_MODE2));
+ }
+
+ vdp_begin_palette(0, 0);
+ for(i=0; i<16; i++) {
+ int r = i;
+ int g = 15 - i;
+ int b = i - 8;
+
+ if(r > 7) r = 7;
+ if(g > 7) g = 7;
+ if(b < 0) b = 0;
+
+ VDP_PORT_DATA = VDP_PACK_RGB(r, g, b);
+ }
+ vdp_set_bgcolor(0, bgcol);
+
+ for(;;) {
+ pad_update(0);
+
+ if(pad_pressed(0, IO_PAD_C)) {
+ vdp_set_bgcolor(0, ++bgcol & 0xf);
+ }
+
+ vdp_wait_vblank();
+ }
+
+ return 0;
+}
+
+void load_pattern(int idx, void *data)
+{
+ int i;
+ uint32_t *ptr = data;
+ uint16_t addr = idx << 5;
+ vdp_setup_access(addr, VDP_MEM_WRITE, VDP_MEM_VRAM);
+
+ for(i=0; i<16; i++) {
+ VDP_PORT_DATA32 = *ptr++;
+ }
+}
+
+void set_tile(int nametab_idx, int x, int y, int tile_idx, int palidx)
+{
+ uint16_t tile_ent, addr;
+
+ tile_ent = vdp_nametab_entry(tile_idx, palidx, VDP_TILE_LOW_PRIO);
+
+ addr = vdp_nametab_addr(nametab_idx) + (y * 64 + x) * 2;
+ vdp_setup_access(addr, VDP_MEM_WRITE, VDP_MEM_VRAM);
+ VDP_PORT_DATA = tile_ent;
+}
--- /dev/null
+#ifndef PAD_H_
+#define PAD_H_
+
+#include "io.h"
+
+uint16_t pad_bnstate[2], pad_prev_bnstate[2], pad_bndiff[2];
+char pad_6bn;
+
+static inline void pad_update(int pad)
+{
+ pad_prev_bnstate[pad] = pad_bnstate[pad];
+ pad_bnstate[pad] = pad_6bn ? io_readpad6(pad) : io_readpad(pad);
+ pad_bndiff[pad] = pad_bnstate[pad] ^ pad_prev_bnstate[pad];
+}
+
+static inline int pad_pressed(int pad, uint16_t mask)
+{
+ return (pad_bnstate[pad] & mask & pad_bndiff[pad]) != 0;
+}
+
+static inline int pad_released(int pad, uint16_t mask)
+{
+ return (~pad_bnstate[pad] & mask & pad_bndiff[pad]) != 0;
+}
+
+static inline uint16_t pad_getstate(int pad)
+{
+ return pad_bnstate[pad];
+}
+
+#endif /* PAD_H_ */
--- /dev/null
+| vi:filetype=gas68k:
+ .text
+ .extern main
+
+ .global start
+ .global halt_cpu
+start:
+ jsr disable_intr
+
+ | write SEGA to 0xa14000 to convience the TMSS to run us
+ move.l 0x100, %d0
+ move.l %d0, 0xa14000
+
+ | copy .data section from ROM to RAM
+ move.l #_data_lma, %a0
+ move.l #_data_start, %a1
+ move.l #_data_end, %a2
+ cmp.l %a1, %a2
+ beq.s 1f | skip data copy if the section is empty
+0: move.l (%a0)+, (%a1)+
+ cmp.l %a1, %a2
+ bne.s 0b
+1:
+
+ | zero the .bss section
+ move.l #_bss_start, %a0
+ move.l #_bss_end, %a1
+ cmp.l %a0, %a1
+ beq.s 1f | skip bss zeroing if the section is empty
+0: clr.l (%a0)+
+ cmp.l %a0, %a1
+ bne.s 0b
+1:
+
+ | setup the stack pointer stack
+ move.l #_stacktop, %sp
+ | now that we have a stack, we can enable interrupts
+ jsr enable_intr
+
+ jsr main
+halt_cpu:
+ stop #0x2700
--- /dev/null
+#include "vdp.h"
+
+void vdp_init(void)
+{
+ int i;
+
+ vdp_setreg(VDP_REG_MODE1, VDP_MODE1_BASE);
+ vdp_setreg(VDP_REG_MODE2, VDP_MODE2_BASE | VDP_MODE2_DISP);
+
+ vdp_set_nametab_addr(VDP_PLANE_A, 0xc000);
+ vdp_set_nametab_addr(VDP_PLANE_B, 0xe000);
+ vdp_disable_layer(VDP_PLANE_WIN);
+ vdp_set_scroll_table(0xd000);
+ vdp_set_sprite_table(0xd400);
+
+ vdp_setreg(VDP_REG_MODE3, VDP_MODE3_BASE);
+ vdp_setreg(VDP_REG_MODE4, VDP_MODE4_BASE | VDP_MODE4_H40CELL);
+ vdp_setreg(VDP_REG_SCROLL_SIZE, VDP_SCROLL_H64 | VDP_SCROLL_V32);
+
+ vdp_set_autoinc(2);
+ vdp_set_bgcolor(0, 0);
+
+ vdp_setreg(VDP_REG_HINTR, 0);
+ vdp_setreg(VDP_REG_WIN_XPOS, 0);
+ vdp_setreg(VDP_REG_WIN_YPOS, 0);
+
+ /* clear vmem at the planes A/B name tables, scroll table, and sprite table */
+ vdp_setup_access(0xd000, VDP_MEM_WRITE, VDP_MEM_VRAM);
+ for(i=0xd000; i<65536; i++) {
+ VDP_PORT_DATA = 0;
+ }
+}
--- /dev/null
+#ifndef VDP_H_
+#define VDP_H_
+
+#include <stdint.h>
+
+#define VDP_PORT_DATA *((volatile uint16_t*)0xc00000)
+#define VDP_PORT_DATA32 *((volatile uint32_t*)0xc00000)
+#define VDP_PORT_CTL *((volatile uint16_t*)0xc00004)
+#define VDP_PORT_CTL32 *((volatile uint32_t*)0xc00004)
+#define VDP_PORT_HVCNT *((volatile uint16_t*)0xc00008)
+#define VDP_PORT_PSG *((volatile uint16_t*)0xc00010)
+
+#define VDP_PORT_STATUS *((volatile uint16_t*)0xc00004)
+
+enum {
+ VDP_REG_MODE1 = 0,
+ VDP_REG_MODE2 = 1,
+ VDP_REG_NAMETAB_A = 2,
+ VDP_REG_NAMETAB_WIN = 3,
+ VDP_REG_NAMETAB_B = 4,
+ VDP_REG_SPRITE_TAB = 5,
+ VDP_REG_BGCOL = 7,
+ VDP_REG_HINTR = 10,
+ VDP_REG_MODE3 = 11,
+ VDP_REG_MODE4 = 12,
+ VDP_REG_SCROLL_TAB = 13,
+ VDP_REG_AUTOINC = 15,
+ VDP_REG_SCROLL_SIZE = 16,
+ VDP_REG_WIN_XPOS = 17,
+ VDP_REG_WIN_YPOS = 18,
+ VDP_REG_DMA_LEN_LOW = 19,
+ VDP_REG_DMA_LEN_HIGH = 20,
+ VDP_REG_DMA_SRC_LOW = 21,
+ VDP_REG_DMA_SRC_MID = 22,
+ VDP_REG_DMA_SRC_HIGH = 23,
+
+ VDP_NUM_REGS
+};
+
+uint16_t vdp_reg_shadow[VDP_NUM_REGS];
+
+/* access VDP memory */
+enum { VDP_MEM_READ, VDP_MEM_WRITE };
+enum {
+ VDP_MEM_VRAM = 0,
+ VDP_MEM_CRAM = 0xa, /* CD5->CD0: 0 0 r 0 w 0 */
+ VDP_MEM_VSRAM = 4 /* CD5->CD0: 0 0 0 1 0 0 */
+};
+
+enum {
+ VDP_PLANE_A,
+ VDP_PLANE_B,
+ VDP_PLANE_WIN
+};
+
+enum {
+ VDP_TILE_LOW_PRIO = 0,
+ VDP_TILE_HFLIP = 0x0800,
+ VDP_TILE_VFLIP = 0x1000,
+ VDP_TILE_HIGH_PRIO = 0x8000
+};
+
+enum {
+ VDP_STAT_PAL = 0x0001,
+ VDP_STAT_DMABUSY = 0x0002,
+ VDP_STAT_HBLANK = 0x0004,
+ VDP_STAT_VBLANK = 0x0008,
+ VDP_STAT_ODDFIELD = 0x0010,
+ VDP_STAT_SPRITE_COL = 0x0020,
+ VDP_STAT_SPRITE_OVF = 0x0040,
+ VDP_STAT_VINTR = 0x0080,
+ VDP_STAT_FIFO_FULL = 0x0100,
+ VDP_STAT_FIFO_EMPTY = 0x0200
+};
+
+
+static inline void vdp_setup_access(uint16_t addr, int rw, int memid)
+{
+ uint32_t type;
+ if(rw == VDP_MEM_WRITE) {
+ type = (memid & 7) | 1;
+ } else {
+ type = memid & 0xc;
+ }
+
+ VDP_PORT_CTL32 = (((uint32_t)addr & 0x3fff) << 16) | ((addr >> 14) & 3) |
+ ((type << 2) & 0xf0) | (type << 30);
+}
+
+
+/* mode register 1 */
+enum {
+ VDP_MODE1_BASE = 0x4,
+ VDP_MODE1_HVCNT = 0x2,
+ VDP_MODE1_HINTR = 0x10
+};
+
+/* mode register 2 */
+enum {
+ VDP_MODE2_BASE = 0x4,
+ VDP_MODE2_V30CELL = 0x8,
+ VDP_MODE2_DMA = 0x10,
+ VDP_MODE2_VINTR = 0x20,
+ VDP_MODE2_DISP = 0x40
+};
+
+/* mode register 3 */
+enum {
+ VDP_MODE3_BASE = 0,
+ VDP_MODE3_HSCROLL_CELL = 2,
+ VDP_MODE3_HSCROLL_LINE = 3,
+ VDP_MODE3_VSCROLL_2CELL = 4,
+ VDP_MODE3_EXTINTR = 8
+};
+
+/* mode register 4 */
+enum {
+ VDP_MODE4_BASE = 0,
+ VDP_MODE4_H40CELL = 0x81,
+ VDP_MODE4_ILACE = 2,
+ VDP_MODE4_ILACE_2XRES = 6,
+ VDP_MODE4_SH = 8 /* shadow/highlight enable */
+};
+
+/* scroll size register */
+enum {
+ VDP_SCROLL_H32 = 0,
+ VDP_SCROLL_H64 = 1,
+ VDP_SCROLL_H128 = 3,
+ VDP_SCROLL_V32 = 0,
+ VDP_SCROLL_V64 = 0x10,
+ VDP_SCROLL_V128 = 0x30
+};
+
+/* window X/Y position register */
+enum {
+ VDP_WIN_LEFT = 0,
+ VDP_WIN_UP = 0,
+ VDP_WIN_RIGHT = 0x80,
+ VDP_WIN_DOWN = 0x80,
+ VDP_WIN_POSMASK = 0x1f
+};
+
+#define VDP_PACK_RGB(r, g, b) \
+ ((((uint16_t)(r) & 7) << 1) | (((uint16_t)(g) & 7) << 5) | (((uint16_t)(b) & 7) << 9))
+
+
+static inline void vdp_setreg(int reg, uint8_t value)
+{
+ vdp_reg_shadow[reg] = value;
+ VDP_PORT_CTL = (uint16_t)value | (reg << 8) | (uint16_t)0x8000;
+}
+
+static inline uint16_t vdp_getreg(int reg)
+{
+ return vdp_reg_shadow[reg];
+}
+
+static inline uint16_t vdp_status(void)
+{
+ return VDP_PORT_CTL;
+}
+
+static inline void vdp_set_bgcolor(int palidx, int colidx)
+{
+ vdp_setreg(VDP_REG_BGCOL, (colidx & 0xf) | (palidx << 4));
+}
+
+static inline void vdp_set_autoinc(int stride)
+{
+ vdp_setreg(VDP_REG_AUTOINC, stride);
+}
+
+static inline void vdp_begin_palette(int pidx, int cidx)
+{
+ uint16_t paddr = (pidx << 5) | (cidx << 1);
+ vdp_setup_access(paddr, VDP_MEM_WRITE, VDP_MEM_CRAM);
+}
+
+static inline void vdp_set_pal_entry(int pidx, int cidx, int r, int g, int b)
+{
+ vdp_begin_palette(pidx, cidx);
+ VDP_PORT_DATA = VDP_PACK_RGB(r, g, b);
+}
+
+static inline void vdp_enable_vintr(void)
+{
+ vdp_setreg(VDP_REG_MODE2, vdp_getreg(VDP_REG_MODE2) | VDP_MODE2_VINTR);
+}
+
+static inline void vdp_disable_vintr(void)
+{
+ vdp_setreg(VDP_REG_MODE2, vdp_getreg(VDP_REG_MODE2) & ~VDP_MODE2_VINTR);
+}
+
+static inline void vdp_enable_hintr(int counter)
+{
+ vdp_setreg(VDP_REG_HINTR, counter);
+ vdp_setreg(VDP_REG_MODE1, vdp_getreg(VDP_REG_MODE1) | VDP_MODE1_HINTR);
+}
+
+static inline void vdp_disable_hintr(void)
+{
+ vdp_setreg(VDP_REG_MODE1, vdp_getreg(VDP_REG_MODE1) & ~VDP_MODE1_HINTR);
+}
+
+static inline void vdp_set_nametab_addr(int plane, uint16_t addr)
+{
+ switch(plane) {
+ case VDP_PLANE_A:
+ vdp_setreg(VDP_REG_NAMETAB_A, (addr >> 10) & 0x38);
+ break;
+ case VDP_PLANE_B:
+ vdp_setreg(VDP_REG_NAMETAB_B, (addr >> 13) & 7);
+ break;
+ case VDP_PLANE_WIN:
+ vdp_setreg(VDP_REG_NAMETAB_WIN, (addr >> 10) & 0x7e);
+ default:
+ break;
+ }
+}
+
+static inline uint16_t vdp_nametab_addr(int idx)
+{
+ return (uint16_t)idx << 13;
+}
+
+static inline void vdp_set_nametab_idx(int plane, int idx)
+{
+ vdp_set_nametab_addr(plane, vdp_nametab_addr(idx));
+}
+
+static inline void vdp_disable_layer(int plane)
+{
+ switch(plane) {
+ case VDP_PLANE_A:
+ vdp_setreg(VDP_REG_NAMETAB_A, 0x40);
+ break;
+ case VDP_PLANE_B:
+ vdp_setreg(VDP_REG_NAMETAB_B, 0x08);
+ break;
+ case VDP_PLANE_WIN:
+ vdp_setreg(VDP_REG_NAMETAB_WIN, 0x40);
+ default:
+ break;
+ }
+}
+
+static inline uint16_t vdp_nametab_entry(int tileidx, int palidx, uint16_t flags)
+{
+ return tileidx | (((uint16_t)palidx & 0x3) << 13) | flags;
+}
+
+static inline void vdp_set_sprite_table(uint16_t addr)
+{
+ vdp_setreg(VDP_REG_SPRITE_TAB, addr >> 9);
+}
+
+static inline void vdp_set_scroll_table(uint16_t addr)
+{
+ vdp_setreg(VDP_REG_SCROLL_TAB, addr >> 10);
+}
+
+static inline void vdp_wait_vblank_start(void)
+{
+ while(!(VDP_PORT_STATUS & VDP_STAT_VBLANK));
+}
+
+static inline void vdp_wait_vblank_end(void)
+{
+ while(VDP_PORT_STATUS & VDP_STAT_VBLANK);
+}
+
+static inline void vdp_wait_vblank(void)
+{
+ vdp_wait_vblank_start();
+ vdp_wait_vblank_end();
+}
+
+void vdp_init(void);
+
+#endif /* VDP_H_ */
--- /dev/null
+| vi:filetype=asm68k:
+ .equ VDP_PORT_DATA, 0xc00000
+ .equ VDP_PORT_CTL, 0xc00004
+
+ .equ VDP_REG_MODE1, 0
+ .equ VDP_REG_MODE2, 1
+
+ .equ VDP_WRITE_VRAM, 0x40000000
+ .equ VDP_WRITE_CRAM, 0xc0000000
+ .equ VDP_WRITE_VSRAM, 0x40000010
+ .equ VDP_READ_VRAM, 0
+ .equ VDP_READ_CRAM, 0x00000020
+ .equ VDP_READ_VSRAM, 0x00000010