├── samples ├── .gitignore ├── mdsdrv-usage │ ├── res │ │ ├── mdspcm.bin.cfg │ │ ├── mdsdrv.bin │ │ ├── mdspcm.bin │ │ ├── mdsseq.bin │ │ ├── font_gfx.bin │ │ └── font_pal.bin │ ├── Makefile │ └── src │ │ ├── main.c │ │ └── mdsdrv │ │ ├── mdsdrv.h │ │ └── mdsdrv.c ├── cpp-direct-sprite-demo │ ├── res │ │ └── ball.bin │ ├── src │ │ ├── bg_gradient.h │ │ ├── dot.h │ │ ├── main.cpp │ │ ├── dot.cpp │ │ └── bg_gradient.cpp │ └── Makefile ├── cspr-demo │ ├── png │ │ ├── cirno.png │ │ ├── count.png │ │ └── sonic.png │ ├── res │ │ ├── font_gfx.bin │ │ └── font_pal.bin │ ├── Makefile │ └── src │ │ └── main.c ├── move-podge │ ├── png │ │ ├── font.png │ │ ├── PodgeH24.png │ │ └── PodgeH24_gfx.bin │ ├── Makefile │ └── src │ │ └── main.c ├── controller │ ├── res │ │ ├── gfx │ │ │ └── font.bin │ │ └── pal │ │ │ └── font.bin │ ├── Makefile │ └── src │ │ └── main.c ├── hello-world │ ├── res │ │ ├── font_gfx.bin │ │ └── font_pal.bin │ ├── Makefile │ └── src │ │ └── main.c ├── system-c2-demo │ ├── adpcm │ │ └── puyo2.bin │ ├── res │ │ ├── font_gfx.bin │ │ ├── font_pal.bin │ │ └── color_blocks.bin │ ├── Makefile │ └── src │ │ └── main.c ├── audio-simple-psg │ ├── res │ │ ├── font_gfx.bin │ │ └── font_pal.bin │ ├── Makefile │ └── src │ │ └── main.c ├── cd-mode-1 │ └── src │ │ ├── cd_mode1.h │ │ ├── cd_set_sr_sub.a68 │ │ ├── main.c │ │ ├── cdaudio.h │ │ ├── kos.a68 │ │ └── cd_sub_cpu_program.a68 └── Makefile ├── blank-project ├── res │ └── res.txt ├── .gitignore ├── src │ └── main.c ├── setup.txt └── Makefile ├── mdk ├── misc │ ├── font.png │ └── font-export.png ├── src │ ├── md │ │ ├── font.bin │ │ ├── sysc_vctrl.c │ │ ├── vdp_defs.inc │ │ ├── top.inc │ │ ├── vdp_debug.a68 │ │ ├── io.c │ │ ├── z80_macros.inc │ │ ├── vram_clear.a68 │ │ ├── vdp_default_vram_map.h │ │ ├── cspr_types.inc │ │ ├── macro.h │ │ ├── irq.h │ │ ├── cspr_put_common.a68 │ │ ├── vdp_macros.inc │ │ ├── opn.h │ │ ├── sram.h │ │ ├── irq.c │ │ ├── crt0.a68 │ │ ├── sys.c │ │ ├── adpcm.h │ │ ├── adpcm.c │ │ ├── sram.a68 │ │ ├── io.h │ │ ├── irq_handlers.a68 │ │ ├── sysc_vctrl.h │ │ ├── cspr.h │ │ ├── megadrive.c │ │ ├── vdp_enums.h │ │ ├── pal.h │ │ ├── dma_process.a68 │ │ ├── errors.h │ │ ├── megadrive.h │ │ ├── cspr.c │ │ ├── dma.h │ │ ├── psg.h │ │ ├── spr.c │ │ ├── vdp.c │ │ ├── io_pad_read.a68 │ │ ├── cspr_put_fast.a68 │ │ ├── mmio.h │ │ ├── vdp_min_init.a68 │ │ ├── spr_put.a68 │ │ ├── cspr_put.a68 │ │ ├── ioc.h │ │ ├── sys.h │ │ └── pal.c │ ├── util │ │ ├── kosinski.h │ │ ├── rand.c │ │ ├── plane.h │ │ ├── beepcheck.h │ │ ├── trig_tab.h │ │ ├── plane.c │ │ ├── trig.h │ │ ├── rand.h │ │ ├── text.h │ │ ├── fixed.h │ │ ├── trig.c │ │ ├── text.c │ │ └── kosinski.a68 │ └── header.inc ├── util │ ├── png2csp │ │ ├── sample │ │ │ ├── test.ase │ │ │ ├── test.png │ │ │ ├── cirnolaugh.ase │ │ │ ├── cirnolaugh.png │ │ │ ├── countanim.png │ │ │ ├── smalltile.png │ │ │ └── countanim.aseprite │ │ ├── src │ │ │ ├── constants.h │ │ │ ├── endian.h │ │ │ ├── types.h │ │ │ ├── records.h │ │ │ ├── claim.h │ │ │ ├── util.h │ │ │ ├── util.c │ │ │ ├── types.c │ │ │ └── records.c │ │ └── Makefile │ ├── .gitignore │ ├── image │ │ └── pngto │ │ │ ├── musl_getopt.h │ │ │ ├── pngtochr-README.md │ │ │ ├── indexedimage.h │ │ │ └── indexedimage.c │ └── core │ │ ├── binpad.c │ │ ├── bin2h.c │ │ ├── bin2arr.c │ │ ├── bsplit.c │ │ └── bin2s.c └── md.ld ├── .gitignore └── notes └── c2.txt /samples/.gitignore: -------------------------------------------------------------------------------- 1 | res.h 2 | *.map 3 | -------------------------------------------------------------------------------- /blank-project/res/res.txt: -------------------------------------------------------------------------------- 1 | Include resources here! 2 | -------------------------------------------------------------------------------- /samples/mdsdrv-usage/res/mdspcm.bin.cfg: -------------------------------------------------------------------------------- 1 | align 32768 2 | 3 | -------------------------------------------------------------------------------- /samples/cpp-direct-sprite-demo/res/ball.bin: -------------------------------------------------------------------------------- 1 | #34T15e14T1#3!"" -------------------------------------------------------------------------------- /blank-project/.gitignore: -------------------------------------------------------------------------------- 1 | */*.swp 2 | res.h 3 | obj/ 4 | *.map 5 | *.gen 6 | history/ 7 | -------------------------------------------------------------------------------- /mdk/misc/font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/mdk/misc/font.png -------------------------------------------------------------------------------- /mdk/src/md/font.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/mdk/src/md/font.bin -------------------------------------------------------------------------------- /mdk/misc/font-export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/mdk/misc/font-export.png -------------------------------------------------------------------------------- /mdk/src/util/kosinski.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void kosinski_decomp(const void *in, const void *out); 4 | -------------------------------------------------------------------------------- /mdk/util/png2csp/sample/test.ase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/mdk/util/png2csp/sample/test.ase -------------------------------------------------------------------------------- /mdk/util/png2csp/sample/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/mdk/util/png2csp/sample/test.png -------------------------------------------------------------------------------- /samples/cspr-demo/png/cirno.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/samples/cspr-demo/png/cirno.png -------------------------------------------------------------------------------- /samples/cspr-demo/png/count.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/samples/cspr-demo/png/count.png -------------------------------------------------------------------------------- /samples/cspr-demo/png/sonic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/samples/cspr-demo/png/sonic.png -------------------------------------------------------------------------------- /samples/move-podge/png/font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/samples/move-podge/png/font.png -------------------------------------------------------------------------------- /mdk/src/md/sysc_vctrl.c: -------------------------------------------------------------------------------- 1 | #include "md/sysc_vctrl.h" 2 | 3 | #include 4 | 5 | uint8_t g_c2_vctrl = 0x06; 6 | -------------------------------------------------------------------------------- /samples/controller/res/gfx/font.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/samples/controller/res/gfx/font.bin -------------------------------------------------------------------------------- /samples/controller/res/pal/font.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/samples/controller/res/pal/font.bin -------------------------------------------------------------------------------- /samples/cspr-demo/res/font_gfx.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/samples/cspr-demo/res/font_gfx.bin -------------------------------------------------------------------------------- /samples/cspr-demo/res/font_pal.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/samples/cspr-demo/res/font_pal.bin -------------------------------------------------------------------------------- /samples/mdsdrv-usage/res/mdsdrv.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/samples/mdsdrv-usage/res/mdsdrv.bin -------------------------------------------------------------------------------- /samples/mdsdrv-usage/res/mdspcm.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/samples/mdsdrv-usage/res/mdspcm.bin -------------------------------------------------------------------------------- /samples/mdsdrv-usage/res/mdsseq.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/samples/mdsdrv-usage/res/mdsseq.bin -------------------------------------------------------------------------------- /samples/move-podge/png/PodgeH24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/samples/move-podge/png/PodgeH24.png -------------------------------------------------------------------------------- /mdk/util/png2csp/sample/cirnolaugh.ase: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/mdk/util/png2csp/sample/cirnolaugh.ase -------------------------------------------------------------------------------- /mdk/util/png2csp/sample/cirnolaugh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/mdk/util/png2csp/sample/cirnolaugh.png -------------------------------------------------------------------------------- /mdk/util/png2csp/sample/countanim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/mdk/util/png2csp/sample/countanim.png -------------------------------------------------------------------------------- /mdk/util/png2csp/sample/smalltile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/mdk/util/png2csp/sample/smalltile.png -------------------------------------------------------------------------------- /samples/hello-world/res/font_gfx.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/samples/hello-world/res/font_gfx.bin -------------------------------------------------------------------------------- /samples/hello-world/res/font_pal.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/samples/hello-world/res/font_pal.bin -------------------------------------------------------------------------------- /samples/mdsdrv-usage/res/font_gfx.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/samples/mdsdrv-usage/res/font_gfx.bin -------------------------------------------------------------------------------- /samples/mdsdrv-usage/res/font_pal.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/samples/mdsdrv-usage/res/font_pal.bin -------------------------------------------------------------------------------- /samples/system-c2-demo/adpcm/puyo2.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/samples/system-c2-demo/adpcm/puyo2.bin -------------------------------------------------------------------------------- /samples/move-podge/png/PodgeH24_gfx.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/samples/move-podge/png/PodgeH24_gfx.bin -------------------------------------------------------------------------------- /samples/system-c2-demo/res/font_gfx.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/samples/system-c2-demo/res/font_gfx.bin -------------------------------------------------------------------------------- /samples/system-c2-demo/res/font_pal.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/samples/system-c2-demo/res/font_pal.bin -------------------------------------------------------------------------------- /mdk/util/png2csp/sample/countanim.aseprite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/mdk/util/png2csp/sample/countanim.aseprite -------------------------------------------------------------------------------- /samples/audio-simple-psg/res/font_gfx.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/samples/audio-simple-psg/res/font_gfx.bin -------------------------------------------------------------------------------- /samples/audio-simple-psg/res/font_pal.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/samples/audio-simple-psg/res/font_pal.bin -------------------------------------------------------------------------------- /samples/system-c2-demo/res/color_blocks.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mikejmoffitt/mdk/HEAD/samples/system-c2-demo/res/color_blocks.bin -------------------------------------------------------------------------------- /mdk/src/md/vdp_defs.inc: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | .extern g_md_vdp_regs /* uint8_t [0x18] */ 4 | .extern g_md_vdp_debug_regs /* uint16_t [0x10] */ 5 | -------------------------------------------------------------------------------- /samples/cd-mode-1/src/cd_mode1.h: -------------------------------------------------------------------------------- 1 | #ifndef CD_MODE1_H 2 | #define CD_MODE1_H 3 | 4 | void cd_mode1_start(void); 5 | 6 | #endif // CD_MODE1_H 7 | -------------------------------------------------------------------------------- /samples/Makefile: -------------------------------------------------------------------------------- 1 | MAKE := make 2 | PROJECTS := $(wildcard */.) 3 | 4 | .PHONY: all $(PROJECTS) 5 | 6 | all: $(PROJECTS) 7 | $(PROJECTS): 8 | $(MAKE) -C $@ 9 | -------------------------------------------------------------------------------- /blank-project/src/main.c: -------------------------------------------------------------------------------- 1 | #include "md/megadrive.h" 2 | 3 | void main(void) 4 | { 5 | megadrive_init(); 6 | 7 | while(1) 8 | { 9 | megadrive_finish(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /mdk/src/md/top.inc: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "md/mmio.h" 3 | #include "md/vdp_regs.h" 4 | #include "md/vdp_default_vram_map.h" 5 | #include "md/vdp_defs.inc" 6 | #include "md/vdp_macros.inc" 7 | #include "md/z80_macros.inc" 8 | -------------------------------------------------------------------------------- /mdk/util/.gitignore: -------------------------------------------------------------------------------- 1 | core/bin2h 2 | core/bin2s 3 | core/binpad 4 | core/bsplit 5 | 6 | debug/megaloader/megaloader 7 | 8 | emu/blastem/blastem64-*/ 9 | emu/blastem/blastem-win64-*/ 10 | 11 | image/pngto/pngto 12 | -------------------------------------------------------------------------------- /mdk/util/png2csp/src/constants.h: -------------------------------------------------------------------------------- 1 | #ifndef CONSTANTS_H 2 | #define CONSTANTS_H 3 | 4 | #define TSIZE 8 5 | #define TBYTES 32 6 | #define PALSIZE 16 7 | #define SPR_STATIC_OFFS 128 8 | 9 | #endif // CONSTANTS_H 10 | -------------------------------------------------------------------------------- /samples/cpp-direct-sprite-demo/src/bg_gradient.h: -------------------------------------------------------------------------------- 1 | #ifndef BG_GRADIENT_H 2 | #define BG_GRADIENT_H 3 | 4 | // Register int handlers for the background gradient. 5 | void bg_gradient_init(); 6 | 7 | #endif // BG_GRADIENT_H 8 | -------------------------------------------------------------------------------- /mdk/src/util/rand.c: -------------------------------------------------------------------------------- 1 | #include "rand.h" 2 | #include "md/megadrive.h" 3 | 4 | uint16_t g_rand_value = 0x6502; 5 | 6 | void srand(uint16_t seed) 7 | { 8 | if (seed == 0) seed = md_vdp_get_hv_count(); 9 | g_rand_value = seed; 10 | } 11 | -------------------------------------------------------------------------------- /mdk/src/util/plane.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" 5 | { 6 | #endif // __cplusplus 7 | 8 | #include "md/megadrive.h" 9 | 10 | void plane_clear(VdpPlane plane); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif // __cplusplus 15 | -------------------------------------------------------------------------------- /mdk/src/md/vdp_debug.a68: -------------------------------------------------------------------------------- 1 | #include "md/top.inc" 2 | 3 | .global md_vdp_debug_port_sel 4 | ; void md_vdp_debug_port_sel(uint32_t num); 5 | md_vdp_debug_port_sel: 6 | move.l 4(sp), d0 ; num 7 | lsl.w #8, d0 8 | move.w d0, (VDP_LOC_BASE+VDP_OFFS_DBG_DATA).l 9 | ori.b #0, d0 10 | rts 11 | -------------------------------------------------------------------------------- /mdk/src/util/beepcheck.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef MDK_BOOT_DEBUG 6 | extern void md_test_beep_c(uint32_t, uint16_t); 7 | #define BEEPCHECK(x) \ 8 | md_test_beep_c(0xF | (x << 4), 8-1) 9 | #else 10 | #define BEEPCHECK(x) (void)x 11 | #endif // BEEPCHECK 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gen 2 | *.o 3 | *.o.s 4 | */obj/res.s 5 | */obj/*.gen 6 | */obj/*.elf 7 | */obj/*.bin 8 | */obj/*.o 9 | */obj/*.o.s 10 | *.swp 11 | mdk/util/megaloader 12 | mdk/util/bin2s 13 | mdk/util/bin2h 14 | mdk/util/binpad 15 | mdk/util/bsplit 16 | mdk/util/pngto 17 | mdk/util/blastem64-0.6.2/* 18 | mdk/util/mdtools 19 | -------------------------------------------------------------------------------- /samples/cd-mode-1/src/cd_set_sr_sub.a68: -------------------------------------------------------------------------------- 1 | .data 2 | .align 2 3 | 4 | ; unt16_t set_sr(uint16_t new_sr); 5 | ; set SR, return previous SR 6 | ; entry: arg = SR value 7 | ; exit: d0 = previous SR value 8 | .global set_sr 9 | set_sr: 10 | moveq #0, d0 11 | move.w sr, d0 12 | move.l 4(sp), d1 13 | move.w d1, sr 14 | rts 15 | 16 | -------------------------------------------------------------------------------- /mdk/src/md/io.c: -------------------------------------------------------------------------------- 1 | /* mdk I/O peripheral support 2 | Michael Moffitt 2018 */ 3 | #include "md/io.h" 4 | #include "md/mmio.h" 5 | #include "md/sys.h" 6 | 7 | // Even bytes are state, odd bytes are previous frame's state. 8 | uint16_t g_md_pad[2]; 9 | uint16_t g_md_pad_prev[2]; 10 | uint16_t g_md_pad_pos[2]; 11 | uint16_t g_md_pad_neg[2]; 12 | -------------------------------------------------------------------------------- /samples/controller/Makefile: -------------------------------------------------------------------------------- 1 | # Project meta configuration 2 | PROJECT_NAME := controller 3 | MDKROOT := ../../mdk 4 | 5 | # Project directories. 6 | SRCDIR := src 7 | RESDIR := res 8 | OBJDIR := obj 9 | 10 | # Sources. 11 | SOURCES_C := $(shell find $(SRCDIR) -type f -name '*.c') 12 | 13 | include $(MDKROOT)/md-rules.mk 14 | -------------------------------------------------------------------------------- /samples/hello-world/Makefile: -------------------------------------------------------------------------------- 1 | # Project meta configuration 2 | PROJECT_NAME := hello-world 3 | MDKROOT := ../../mdk 4 | 5 | # Project directories. 6 | SRCDIR := src 7 | RESDIR := res 8 | OBJDIR := obj 9 | 10 | # Sources. 11 | SOURCES_C := $(shell find $(SRCDIR) -type f -name '*.c') 12 | 13 | include $(MDKROOT)/md-rules.mk 14 | -------------------------------------------------------------------------------- /samples/audio-simple-psg/Makefile: -------------------------------------------------------------------------------- 1 | # Project meta configuration 2 | PROJECT_NAME := audio-simple-psg 3 | MDKROOT := ../../mdk 4 | 5 | # Project directories. 6 | SRCDIR := src 7 | RESDIR := res 8 | OBJDIR := obj 9 | 10 | # Sources. 11 | SOURCES_C := $(shell find $(SRCDIR) -type f -name '*.c') 12 | 13 | include $(MDKROOT)/md-rules.mk 14 | -------------------------------------------------------------------------------- /mdk/src/md/z80_macros.inc: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "md/mmio.h" 3 | 4 | # bus req (no wait) 5 | .macro zbusreq 6 | move.b #0x01, SYS_Z80_PORT_BUS_LOC 7 | .endm 8 | 9 | # bus release 10 | .macro zbusrel 11 | sf SYS_Z80_PORT_BUS_LOC 12 | .endm 13 | 14 | # bus wait 15 | .macro zbuswt 16 | 9: btst #0, SYS_Z80_PORT_BUS_LOC 17 | bne 9b 18 | .endm 19 | -------------------------------------------------------------------------------- /mdk/src/util/trig_tab.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "util/fixed.h" 3 | #define TRIG_TAB_ATAN_INPUT_RANGE 64 4 | 5 | // Range 0 to 2 * pi 6 | extern const fix16_t trig_tab_sin[256]; 7 | extern const fix16_t trig_tab_cos[256]; 8 | extern const fix16_t trig_tab_tan[256]; 9 | // Range 0 to 64 (one quadrant) 10 | extern const fix16_t trig_tab_atan[4096]; 11 | -------------------------------------------------------------------------------- /samples/cpp-direct-sprite-demo/Makefile: -------------------------------------------------------------------------------- 1 | # Project meta configuration 2 | PROJECT_NAME := cpp-direct-sprite-demo 3 | MDKROOT := ../../mdk 4 | 5 | # Project directories. 6 | SRCDIR := src 7 | RESDIR := res 8 | OBJDIR := obj 9 | 10 | # Sources. 11 | SOURCES_CPP := $(shell find $(SRCDIR) -type f -name '*.cpp') 12 | 13 | include $(MDKROOT)/md-rules.mk 14 | -------------------------------------------------------------------------------- /mdk/src/md/vram_clear.a68: -------------------------------------------------------------------------------- 1 | #include "md/top.inc" 2 | .global md_vdp_vram_clear 3 | md_vdp_vram_clear: 4 | lea (VDP_LOC_BASE).l, a0 5 | move.w #VDP_REGST(VDP_AUTOINC, 2), VDP_OFFS_CTRL(a0) 6 | moveq #0, d0 7 | md_set_vram_addr 0 8 | move.w #(VRAM_SIZE/(4*4)) - 1, d1 9 | .clear_top: 10 | .rept 4 11 | move.l d0, (a0) 12 | .endr 13 | dbra d1, .clear_top 14 | rts 15 | -------------------------------------------------------------------------------- /mdk/src/util/plane.c: -------------------------------------------------------------------------------- 1 | #include "util/plane.h" 2 | 3 | void plane_clear(VdpPlane plane) 4 | { 5 | uint16_t plane_dest = md_vdp_get_plane_base(plane); 6 | uint16_t plane_size = md_vdp_get_reg(VDP_PLANESIZE); 7 | 8 | plane_size = (md_vdp_get_reg(VDP_PLANESIZE) & 0x03) * 64; 9 | plane_size *= (md_vdp_get_reg(VDP_PLANESIZE) & 0x30) * 4; 10 | 11 | md_dma_fill_vram(plane_dest, 0, plane_size, 1); 12 | } 13 | -------------------------------------------------------------------------------- /samples/mdsdrv-usage/Makefile: -------------------------------------------------------------------------------- 1 | # Project meta configuration 2 | PROJECT_NAME := mdsdrv-usage 3 | MDKROOT := ../../mdk 4 | 5 | # Project directories. 6 | SRCDIR := src 7 | RESDIR := res 8 | OBJDIR := obj 9 | PCMDIR := pcm 10 | 11 | # Sources. 12 | SOURCES_C := $(shell find $(SRCDIR) -type f -name '*.c') 13 | 14 | # TARGET_SYSTEM := MDK_TARGET_C2 15 | # MDK_C2_ADPCM := ../system-c2-demo/adpcm.bin 16 | 17 | include $(MDKROOT)/md-rules.mk 18 | -------------------------------------------------------------------------------- /samples/cpp-direct-sprite-demo/src/dot.h: -------------------------------------------------------------------------------- 1 | #ifndef DOT_H 2 | #define DOT_H 3 | 4 | #include "util/fixed.h" 5 | #include "md/megadrive.h" 6 | 7 | class Dot 8 | { 9 | public: 10 | Dot(); 11 | void Move(); 12 | void Render(); 13 | private: 14 | void ResetVelocity(); 15 | 16 | fix32_t m_x = INTTOFIX32(160); 17 | fix32_t m_y = INTTOFIX32(200); 18 | fix16_t m_dx = 0; 19 | fix16_t m_dy = 0; 20 | 21 | SprSlot *m_spr; // Initialized in Dot(). 22 | 23 | }; 24 | 25 | #endif // DOT_H 26 | -------------------------------------------------------------------------------- /mdk/src/md/vdp_default_vram_map.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | Default VRAM layout - Optimized for 64x32 cell planes. 5 | These are used during init, but you are not obligated to stick to them. 6 | */ 7 | 8 | /* 0000-BFFF (1536 tiles) free */ 9 | #define VRAM_SCRA_BASE_DEFAULT 0xC000 10 | #define VRAM_SCRW_BASE_DEFAULT 0xD000 11 | #define VRAM_SCRB_BASE_DEFAULT 0xE000 12 | /* F000-F7FF (64 tiles) free */ 13 | #define VRAM_HSCR_BASE_DEFAULT 0xF800 14 | #define VRAM_SPR_BASE_DEFAULT 0xFC00 15 | -------------------------------------------------------------------------------- /mdk/src/md/cspr_types.inc: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | # A1 - CSPR blob 4 | .set CSPR_NAME, 0x00 5 | .set CSPR_PALETTE, 0x10 6 | .set CSPR_REF_COUNT, 0x30 7 | .set CSPR_SPR_LIST_OFFSET, 0x32 8 | .set CSPR_TILE_DATA_OFFSET, 0x36 9 | .set CSPR_REFS, 0x40 10 | 11 | # A2 - Frame ref 12 | .set REF_SPR_COUNT, 0 13 | .set REF_SPR_LIST_OFFSET, 2 14 | .set REF_TILE_SRC_OFFSET, 4 15 | .set REF_TILE_WORDS, 6 16 | 17 | # A1 - Sprite data 18 | .set SPR_DY, 0 19 | .set SPR_SIZE, 2 20 | .set SPR_TILE, 4 21 | .set SPR_DX, 6 22 | .set SPR_FDY, 8 23 | .set SPR_RESERVED1, 10 24 | .set SPR_RESERVED2, 12 25 | .set SPR_FDX, 14 26 | -------------------------------------------------------------------------------- /mdk/src/md/macro.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define MDK_NUM_IS_POW2(x) ((x & (x - 1)) == 0) 3 | 4 | #define MDK_ARRAYSIZE(x) (sizeof(x) / sizeof(x[0])) 5 | 6 | #define MDK_BITVAL(x) (1 << x) 7 | #define MDK_BTST(x, n) ((x) & (MDK_BITVAL(n))) 8 | #define MDK_BCLR(x, n) ((x) &= (~(MDK_BITVAL(n)))) 9 | #define MDK_BSET(x, n) ((x) |= (MDK_BITVAL(n))) 10 | 11 | #define MDK_MAX(a, b) \ 12 | ({ __typeof__ (a) _a = (a); \ 13 | __typeof__ (b) _b = (b); \ 14 | _a > _b ? _a : _b; }) 15 | 16 | #define MDK_MIN(a, b) \ 17 | ({ __typeof__ (a) _a = (a); \ 18 | __typeof__ (b) _b = (b); \ 19 | _a < _b ? _a : _b; }) 20 | -------------------------------------------------------------------------------- /mdk/util/png2csp/src/endian.h: -------------------------------------------------------------------------------- 1 | #ifndef ENDIAN_H 2 | #define ENDIAN_H 3 | 4 | // Motorola 68000 uses big-endian data. 5 | static void fwrite_uint16be(uint16_t val, FILE *f) 6 | { 7 | uint8_t buf[2]; 8 | buf[0] = (val >> 8) & 0xFF; 9 | buf[1] = val & 0xFF; 10 | fwrite(buf, 1, sizeof(buf), f); 11 | fflush(f); 12 | } 13 | 14 | static void fwrite_int16be(int16_t val, FILE *f) 15 | { 16 | fputc((val >> 8) & 0xFF, f); 17 | fputc(val & 0xFF, f); 18 | } 19 | 20 | static void fwrite_uint32be(uint32_t val, FILE *f) 21 | { 22 | fwrite_uint16be((val >> 16) & 0xFFFF, f); 23 | fwrite_uint16be(val & 0xFFFF, f); 24 | } 25 | 26 | #endif // ENDIAN_H 27 | -------------------------------------------------------------------------------- /mdk/src/md/irq.h: -------------------------------------------------------------------------------- 1 | // mdk IRQ and exception handler registration 2 | // Michael Moffitt 2018-2024 3 | #pragma once 4 | 5 | #ifdef __cplusplus 6 | extern "C" 7 | { 8 | #endif // __cplusplus 9 | 10 | typedef enum MdIrqType 11 | { 12 | MD_IRQ_VBLANK = 0, // Vertical raster interrupt. 13 | MD_IRQ_HBLANK = 1, // Horizontal raster interrupt. 14 | MD_IRQ_IO = 2, // TH pin transitions for controller port. 15 | } MdIrqType; 16 | 17 | // Registers a handler for an interrupt type. NULL may be passed to remove any 18 | // pre-existing handler. 19 | void md_irq_register(MdIrqType type, void (*function_ptr)(void)); 20 | 21 | #ifdef __cplusplus 22 | } 23 | #endif // __cplusplus 24 | -------------------------------------------------------------------------------- /mdk/src/md/cspr_put_common.a68: -------------------------------------------------------------------------------- 1 | .include "md/cspr_types.inc" 2 | 3 | .global cspr_dma_setup_sub 4 | cspr_dma_setup_sub: 5 | move.w d1, -(sp) 6 | movem.l a0-a1, -(sp) 7 | moveq #2, d0 8 | move.l d0, -(sp) ; stride 9 | 10 | move.w REF_TILE_WORDS(a2), d0 11 | move.l d0, -(sp) ; words 12 | 13 | adda.l CSPR_TILE_DATA_OFFSET(a1), a1 14 | moveq #0, d0 15 | move.w REF_TILE_SRC_OFFSET(a2), d0 16 | lsl.l #5, d0 ; 32 bytes per tile 17 | adda.l d0, a1 18 | move.l a1, -(sp) ; source data 19 | 20 | moveq #0, d0 21 | move.w d1, d0 22 | move.l d0, -(sp) ; dest vram 23 | 24 | jsr (pc, md_dma_transfer_vram) 25 | lea $10(sp), sp 26 | movem.l (sp)+, a0-a1 27 | move.w (sp)+, d1 28 | rts 29 | -------------------------------------------------------------------------------- /samples/system-c2-demo/Makefile: -------------------------------------------------------------------------------- 1 | # Project meta configuration 2 | PROJECT_NAME := system-c2-demo 3 | MDKROOT := ../../mdk 4 | 5 | # Project directories. 6 | SRCDIR := src 7 | RESDIR := res 8 | OBJDIR := obj 9 | 10 | # Sources. 11 | SOURCES_C := $(shell find $(SRCDIR) -type f -name '*.c') 12 | 13 | # Target configuration. 14 | # TARGET_SYSTEM: 15 | # * MDK_TARGET_MD 16 | # The default - use this for Mega Drive. 17 | # * MDK_TARGET_C2 18 | # Use this to target the Sega System C arcade PCB. 19 | # In addition, you may define MDK_C2_ADPCM with the filename of the ADPCM ROM. 20 | TARGET_SYSTEM := MDK_TARGET_C2 21 | 22 | MDK_C2_ADPCM := adpcm/puyo2.bin 23 | 24 | include $(MDKROOT)/md-rules.mk 25 | -------------------------------------------------------------------------------- /blank-project/setup.txt: -------------------------------------------------------------------------------- 1 | This is a blank project. To use it, you'll want to do the following: 2 | 3 | 1) Copy the contents of blank-project to your own repo or directory for your project. 4 | 5 | 2) Clone `mdk` as a submodule in your new repo: 6 | 7 | $ git submodule add git@github.com:mikejmoffitt/mdk 8 | 9 | (it's also acceptable but not recommended to simply copy the `mdk` directory) 10 | 11 | 3) Try building. 12 | 13 | $ make 14 | 15 | From then on, if you want to update `mdk`, you just need to do the following: 16 | 17 | $ git submodule update --remote --merge 18 | $ git add mdk && git commit -m "Updated MDK." 19 | 20 | It is also acceptable to just download the latest and drop the `mdk` directory in. 21 | -------------------------------------------------------------------------------- /mdk/src/md/vdp_macros.inc: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "md/vdp_regs.h" 3 | 4 | /* Macro for VDP address calculation (https://plutiedef.com/writing-video) */ 5 | .macro md_set_xram_addr addr, cmd 6 | move.l #(((\addr)&0x3FFF)<<16) | (((\addr)&0xC000)>>14) | (\cmd), (VDP_LOC_BASE+VDP_OFFS_CTRL) 7 | .endm 8 | 9 | .macro md_set_vram_addr addr 10 | md_set_xram_addr \addr, VDP_CTRL_VRAM_WRITE 11 | .endm 12 | 13 | .macro md_set_cram_addr addr 14 | md_set_xram_addr \addr, VDP_CTRL_CRAM_WRITE 15 | .endm 16 | 17 | .macro md_set_vsram_addr addr 18 | md_set_xram_addr \addr, VDP_CTRL_VSRAM_WRITE 19 | .endm 20 | 21 | /* Macro to wait for DMA completion. */ 22 | .macro dma_wait 23 | 9: btst #1, VDP_LOC_BASE+VDP_OFFS_DATA+1 24 | bne 9b 25 | .endm 26 | -------------------------------------------------------------------------------- /mdk/src/util/trig.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Fixed point trigonometry. 4 | 5 | #include "util/fixed.h" 6 | #include "trig_tab.h" 7 | 8 | #define TRIG_FIX16_PI INTTOFIX16((3.1415926535897)) 9 | 10 | #define DEGTOUINT8(deg) ((int)(((deg) / 360.0) * 256) % 256) 11 | #define RADTOUINT8(rad) ((int)(((rad) / 2 * 3.14159) * 256) % 256) 12 | 13 | // sin, cos, and tan accept an angle from 0 - 255, representing 0 - 2 * pi. 14 | static inline fix16_t trig_sin(uint8_t angle) 15 | { 16 | return trig_tab_sin[angle]; 17 | } 18 | 19 | static inline fix16_t trig_cos(uint8_t angle) 20 | { 21 | return trig_tab_cos[angle]; 22 | } 23 | 24 | static inline fix16_t trig_tan(uint8_t angle) 25 | { 26 | return trig_tab_tan[angle]; 27 | } 28 | 29 | uint8_t trig_atan(int y, int x); 30 | -------------------------------------------------------------------------------- /mdk/src/util/rand.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" 5 | { 6 | #endif // __cplusplus 7 | 8 | #include 9 | 10 | extern uint16_t g_rand_value; 11 | 12 | void srand(uint16_t seed); 13 | static inline uint16_t rand(void); 14 | 15 | // ----------------------------------------------------------------------------- 16 | 17 | static inline void rand_step(void) 18 | { 19 | const uint16_t feedback = (g_rand_value & 0x0001) ^ 20 | ((g_rand_value >> 9) & 0x0001); 21 | g_rand_value = g_rand_value >> 1; 22 | g_rand_value |= (feedback << 15); 23 | } 24 | 25 | static inline uint16_t rand(void) 26 | { 27 | rand_step(); 28 | return g_rand_value; 29 | } 30 | 31 | #ifdef __cplusplus 32 | } 33 | #endif // __cplusplus 34 | -------------------------------------------------------------------------------- /mdk/util/png2csp/src/types.h: -------------------------------------------------------------------------------- 1 | #ifndef TYPES_H 2 | #define TYPES_H 3 | 4 | #define PCG_TILE_PX 16 5 | 6 | // Enum for the origin point of an image. 7 | typedef enum ConvOrigin 8 | { 9 | CONV_ORIGIN_LEFT_TOP, 10 | CONV_ORIGIN_CENTER_TOP, 11 | CONV_ORIGIN_RIGHT_TOP, 12 | CONV_ORIGIN_LEFT_CENTER, 13 | CONV_ORIGIN_CENTER_CENTER, 14 | CONV_ORIGIN_RIGHT_CENTER, 15 | CONV_ORIGIN_LEFT_BOTTOM, 16 | CONV_ORIGIN_CENTER_BOTTOM, 17 | CONV_ORIGIN_RIGHT_BOTTOM, 18 | CONV_ORIGIN_DEFAULT = CONV_ORIGIN_CENTER_CENTER 19 | } ConvOrigin; 20 | 21 | // Enum for the conversion mode. 22 | typedef enum ConvMode 23 | { 24 | CONV_MODE_AUTO, 25 | CONV_MODE_XOBJ, 26 | CONV_MODE_SP, 27 | CONV_MODE_DEFAULT = CONV_MODE_AUTO 28 | } ConvMode; 29 | 30 | ConvOrigin conv_origin_from_args(int argc, char **argv); 31 | 32 | #endif // TYPES_H 33 | -------------------------------------------------------------------------------- /samples/cspr-demo/Makefile: -------------------------------------------------------------------------------- 1 | # Project meta configuration 2 | PROJECT_NAME := cspr-demo 3 | MDKROOT := ../../mdk 4 | 5 | # Project directories. 6 | SRCDIR := src 7 | RESDIR := res 8 | OBJDIR := obj 9 | PNGDIR := png 10 | 11 | # Sources. 12 | SOURCES_C := $(shell find $(SRCDIR) -type f -name '*.c') 13 | 14 | EXTERNAL_DEPS := convert_graphics 15 | EXTERNAL_ARTIFACTS := $(RESDIR)/cirno.csp $(RESDIR)/count.csp $(RESDIR)/sonic.csp 16 | 17 | include $(MDKROOT)/md-rules.mk 18 | 19 | convert_graphics: $(EXTERNAL_ARTIFACTS) 20 | 21 | $(RESDIR)/cirno.csp: $(PNG2CSP) 22 | $(PNG2CSP) png/cirno.png 80 80 $@ "Cirno Laugh" 23 | 24 | $(RESDIR)/sonic.csp: $(PNG2CSP) 25 | $(PNG2CSP) png/sonic.png 64 64 $@ "Sonic" 26 | 27 | $(RESDIR)/count.csp: $(PNG2CSP) 28 | $(PNG2CSP) png/count.png 64 64 $@ "Count Anim" 29 | -------------------------------------------------------------------------------- /mdk/src/md/opn.h: -------------------------------------------------------------------------------- 1 | // mdk YM2612 support functions 2 | // Michael Moffitt 2018-2022 3 | #pragma once 4 | 5 | #ifdef __cplusplus 6 | extern "C" 7 | { 8 | #endif // __cplusplus 9 | 10 | #define OPN_PORT_ADDR0 *(volatile uint8_t *)(OPN_BASE+0) 11 | #define OPN_PORT_DATA0 *(volatile uint8_t *)(OPN_BASE+1) 12 | #define OPN_PORT_ADDR1 *(volatile uint8_t *)(OPN_BASE+2) 13 | #define OPN_PORT_DATA1 *(volatile uint8_t *)(OPN_BASE+3) 14 | 15 | #define OPN_WAIT while (OPN_PORT_ADDR0 & 0x80) { continue; } 16 | 17 | static inline void md_opn_write(uint8_t part, uint8_t addr, uint8_t data) 18 | { 19 | if (part) 20 | { 21 | OPN_PORT_ADDR1 = addr; 22 | OPN_WAIT 23 | OPN_PORT_DATA1 = data; 24 | } 25 | else 26 | { 27 | OPN_PORT_ADDR0 = addr; 28 | OPN_WAIT 29 | OPN_PORT_DATA0 = data; 30 | } 31 | } 32 | 33 | #ifdef __cplusplus 34 | } 35 | #endif // __cplusplus 36 | -------------------------------------------------------------------------------- /mdk/src/md/sram.h: -------------------------------------------------------------------------------- 1 | /* mdk SRAM functions 2 | Michael Moffitt 2018-2023 3 | 4 | SRAM access functions. These functions exist in low ROM and handle SRAM 5 | enable/disable automatically, so the caller does not need to be conscious 6 | of code location. 7 | 8 | The intended use case is to allow the user to copy to and from a struct 9 | representing save data. 10 | */ 11 | 12 | #pragma once 13 | 14 | #ifdef __cplusplus 15 | extern "C" 16 | { 17 | #endif // __cplusplus 18 | 19 | // Copies n bytes from 68000 memory space src_addr to dest_offset within SRAM. 20 | void md_sram_write(uint32_t dest_offset, const void *src_addr, uint32_t n); 21 | 22 | // Copies n bytes from src_offset in SRAM to dest_addr in 68000 memory space. 23 | void md_sram_read(uint32_t src_offset, void *dest_addr, uint32_t n); 24 | 25 | #ifdef __cplusplus 26 | } 27 | #endif // __cplusplus 28 | -------------------------------------------------------------------------------- /mdk/util/png2csp/Makefile: -------------------------------------------------------------------------------- 1 | APPNAME := png2csp 2 | 3 | CP := cp 4 | MKDIR := mkdir 5 | RM := rm 6 | CC := gcc 7 | CFLAGS := -O3 -Wall 8 | INSTALL_PREFIX := /usr/bin 9 | ifdef SYSTEMROOT 10 | APPEXT := .exe 11 | endif 12 | 13 | SRCDIR := src 14 | 15 | SOURCES_C := $(shell find $(SRCDIR)/ -name '*.c' -print) 16 | SOURCES_H := $(shell find $(SRCDIR)/ -name '*.h' -print) 17 | OBJECTS_C_DIR := cobj 18 | OBJECTS_C := $(addprefix $(OBJECTS_C_DIR)/, $(SOURCES_C:.c=.o)) 19 | 20 | EXECNAME := $(APPNAME)$(APPEXT) 21 | 22 | .PHONY: all clean 23 | 24 | all: $(EXECNAME) 25 | 26 | $(EXECNAME): $(OBJECTS_C) 27 | $(CC) $(CFLAGS) $(OBJECTS_C) -o $@ 28 | 29 | $(OBJECTS_C_DIR)/%.o: %.c $(SOURCES_H) 30 | $(MKDIR) -p $(OBJECTS_C_DIR)/$( 5 | #include 6 | #include 7 | 8 | void (*g_irq_h_func)(void) = NULL; 9 | void (*g_irq_io_func)(void) = NULL; 10 | void (*g_irq_v_func)(void) = NULL; 11 | 12 | #define IRQ_NO_CLOBBER_FLAG_MASK 0x80000000 13 | 14 | // Registers a handler for an interrupt type. NULL may be passed to remove any 15 | // pre-existing handler. 16 | void md_irq_register(MdIrqType type, void (*function_ptr)(void)) 17 | { 18 | const bool ints_were_enabled = md_sys_di(); 19 | switch (type) 20 | { 21 | default: 22 | break; 23 | case MD_IRQ_VBLANK: 24 | g_irq_v_func = function_ptr; 25 | break; 26 | case MD_IRQ_IO: 27 | g_irq_io_func = function_ptr; 28 | break; 29 | case MD_IRQ_HBLANK: 30 | g_irq_h_func = function_ptr; 31 | break; 32 | } 33 | 34 | if (ints_were_enabled) md_sys_ei(); 35 | } 36 | -------------------------------------------------------------------------------- /notes/c2.txt: -------------------------------------------------------------------------------- 1 | Some little notes from probing a Sega C2 board. 2 | 3 | ## CPU IPL 4 | IPL0 is hooked up to a GAL16V8 IC26 "315-5395" on pin 19. 5 | IPL1 comes from pin 13 of the same IC. 6 | IPL0 is on pin 12. 7 | 8 | If we decompile this GAL's logic the pins are as follows: 9 | 10 | /f15 and /f14 are always set to GND, so the state is 1, while OE is unset. 11 | 12 | /ipl0 = (/f15 * /f14) + (i2 * i4 * /i5 * /i11) 13 | 14 | IC26 315-5395 15 | 16 | 1 17 | 2 VDP pin 98 /ZINT, interrupt for Z80 on MD 18 | 3 19 | 4 OPN2 IRQ (!) 20 | 21 | 22 | ## DTACK generation 23 | 24 | IC24 315-5452 generates enable signals and DTACK. 25 | 26 | Pins: 27 | 28 | 1 A23 29 | 2 A22 30 | 3 A21 31 | 4 A19 32 | 5 A20 33 | 6 AS 34 | 7 BGACK 35 | 8 IC23 pin 3 36 | 9 R34 pullup (not populated) 37 | 10 GND 38 | 11 ? 39 | 12 DTACK 40 | 13 ROM1 /CE (upper 1MiB) 41 | 14 ROM0 /CE (lower 1MiB) 42 | 15 ? 43 | 16 ? 44 | 17 ? 45 | 18 ? 46 | 19 ? 47 | 20 VCC 48 | -------------------------------------------------------------------------------- /blank-project/Makefile: -------------------------------------------------------------------------------- 1 | # Project meta configuration 2 | PROJECT_NAME := blank-mdk-project 3 | MDKROOT := mdk/mdk 4 | 5 | # Project directories. 6 | SRCDIR := src 7 | RESDIR := res 8 | OBJDIR := obj 9 | 10 | # Sources. 11 | SOURCES_C := $(shell find $(SRCDIR) -type f -name '*.c') 12 | SOURCES_CPP := $(shell find $(SRCDIR) -type f -name '*.cpp') 13 | SOURCES_ASM := $(shell find $(SRCDIR) -type f -name '*.s') 14 | 15 | # Target configuration. 16 | # TARGET_SYSTEM: 17 | # * MDK_TARGET_MD ; Default. Use this for megadrive 18 | # * MDK_TARGET_C2 ; Use this to target the Sega System C/C2 PCB. 19 | # In addition, you may define MDK_C2_ADPCM with the filename of the ADPCM ROM. 20 | TARGET_SYSTEM := MDK_TARGET_MD 21 | 22 | # Any additional CFLAGS/CPPFLAGS/ASFLAGS additions or build targets may go here. 23 | # Please read about EXTERNAL_DEPS and EXTERNAL_ARTIFACTS in readme.md. 24 | 25 | include $(MDKROOT)/md-rules.mk 26 | -------------------------------------------------------------------------------- /mdk/util/png2csp/src/records.h: -------------------------------------------------------------------------------- 1 | // Functions and data related to recording the XSP structures to disk. 2 | #ifndef RECORDS_H 3 | #define RECORDS_H 4 | 5 | #include 6 | #include 7 | #include "types.h" 8 | 9 | #define RECORD_MAX_TILE_COUNT 0x10000 10 | #define RECORD_MAX_SPR_COUNT 0x1000 11 | #define RECORD_MAX_REF_COUNT 0x1000 12 | 13 | // Returns true if initialization was successful. 14 | bool record_init(void); 15 | 16 | // Writes to disk. 17 | void record_complete(const char *spr_name, const char *fname); 18 | 19 | void record_palette(const uint16_t *pal_data); 20 | void record_spr(int dx, int dy, int w, int h, int tile, int flip_dx, int flip_dy); 21 | void record_ref(int spr_count, int spr_index, int tile_index, int tile_count); 22 | void record_tiles(const uint8_t *src, int count); 23 | 24 | int record_get_spr_count(void); 25 | int record_get_ref_count(void); 26 | int record_get_tile_count(void); 27 | 28 | #endif // RECORDS_H 29 | -------------------------------------------------------------------------------- /mdk/src/md/crt0.a68: -------------------------------------------------------------------------------- 1 | #include "md/top.inc" 2 | .global md_crt0_begin 3 | .extern main 4 | ; 5 | ; Other routines kept explicitly in low ROM 6 | ; 7 | 8 | md_crt0_begin: 9 | ; clear WRAM 10 | lea WRAM_BASE, a0 11 | move.w #(WRAM_SIZE/(4*4))-1, d7 12 | moveq #0, d0 13 | .clr_loop: 14 | .rept 4 15 | move.l d0, (a0)+ 16 | .endr 17 | dbra d7, .clr_loop 18 | 19 | ; copy data to work RAM 20 | lea _stext, a0 ; Data follows text section 21 | lea WRAM_BASE, a1 ; .data is at start of WRAM 22 | move.w #_sdata, d0 23 | beq.s .no_data_copy 24 | lsr.w #1, d0 25 | subq.w #1, d0 26 | 27 | .copy_data: 28 | move.w (a0)+, (a1)+ 29 | dbra d0, .copy_data 30 | 31 | .no_data_copy: 32 | moveq #0, d0 33 | move.l d0, d1 34 | move.l d0, d2 35 | move.l d0, d3 36 | move.l d0, d4 37 | move.l d0, d5 38 | move.l d0, d6 39 | move.l d0, d7 40 | move.l d0, a0 41 | move.l d0, a1 42 | move.l d0, a2 43 | move.l d0, a3 44 | move.l d0, a4 45 | move.l d0, a5 46 | move.l d0, a6 47 | jmp (main).l 48 | -------------------------------------------------------------------------------- /mdk/md.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH(m68k) 2 | SEARCH_DIR(.) 3 | __DYNAMIC = 0; 4 | 5 | MEMORY 6 | { 7 | rom : ORIGIN = 0x00000000, LENGTH = 0x00400000 8 | ram : ORIGIN = 0xFFFF0000, LENGTH = 0x00010000 9 | } 10 | 11 | PROVIDE (__stack = 0x00000000); 12 | 13 | /* TODO: work banked SRAM into the ROM area for games that support saving. */ 14 | SECTIONS 15 | { 16 | .text 0x00000000 : 17 | { 18 | KEEP(*(.text.keepboot)) *(.text .text.*) 19 | . = ALIGN(0x2); 20 | _etext = .; 21 | . = ALIGN(0x2); 22 | *(.rodata .rodata.*) 23 | . = ALIGN(0x2); 24 | *(.lit) 25 | } > rom 26 | 27 | _stext = SIZEOF (.text); 28 | 29 | .data 0xFFFF0000 : 30 | AT ( ADDR (.text) + SIZEOF (.text) ) 31 | { 32 | . = ALIGN(0x2); 33 | *(.data .data.*) 34 | . = ALIGN(0x2); 35 | _edata = .; 36 | } > ram 37 | _sdata = SIZEOF (.data); 38 | 39 | .bss 0xFFFF0000 + SIZEOF (.data) : 40 | { 41 | . = ALIGN(0x2); 42 | _start = . ; 43 | *(.bss .bss.*) 44 | . = ALIGN(0x2); 45 | _bend = . ; 46 | end = . ; 47 | } > ram 48 | } 49 | -------------------------------------------------------------------------------- /mdk/util/image/pngto/musl_getopt.h: -------------------------------------------------------------------------------- 1 | /* 2 | 2005-2012 Rich Felker 3 | 4 | The following files are trivial, in my opinion not copyrightable in 5 | the first place, and hereby explicitly released to the Public Domain: 6 | 7 | All public headers within include/ 8 | */ 9 | #ifndef MUSL_GETOPT_H 10 | #define MUSL_GETOPT_H 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | int musl_getopt(int, char * const [], const char *); 17 | extern char *musl_optarg; 18 | extern int musl_optind, musl_opterr, musl_optopt, musl_optreset; 19 | 20 | struct musl_option 21 | { 22 | const char *name; 23 | int has_arg; 24 | int *flag; 25 | int val; 26 | }; 27 | 28 | int musl_getopt_long(int, char *const *, const char *, const struct musl_option *, int *); 29 | int musl_getopt_long_only(int, char *const *, const char *, const struct musl_option *, int *); 30 | 31 | #define musl_no_arg 0 32 | #define musl_required_arg 1 33 | #define musl_optional_arg 2 34 | 35 | #ifdef __cplusplus 36 | } 37 | #endif 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /mdk/util/png2csp/src/claim.h: -------------------------------------------------------------------------------- 1 | // Function to claim and erase a region from a sprite. 2 | 3 | #ifndef CLAIM_H 4 | #define CLAIM_H 5 | 6 | #include 7 | 8 | typedef enum ClaimSize 9 | { 10 | CLAIM_SIZE_NONE = 0, 11 | CLAIM_SIZE_1x1, 12 | CLAIM_SIZE_1x2, 13 | CLAIM_SIZE_1x3, 14 | CLAIM_SIZE_1x4, 15 | CLAIM_SIZE_2x1, 16 | CLAIM_SIZE_2x2, 17 | CLAIM_SIZE_2x3, 18 | CLAIM_SIZE_2x4, 19 | CLAIM_SIZE_3x1, 20 | CLAIM_SIZE_3x2, 21 | CLAIM_SIZE_3x3, 22 | CLAIM_SIZE_3x4, 23 | CLAIM_SIZE_4x1, 24 | CLAIM_SIZE_4x2, 25 | CLAIM_SIZE_4x3, 26 | CLAIM_SIZE_4x4, 27 | } ClaimSize; 28 | 29 | int tiles_for_claim_size(ClaimSize size); 30 | int tile_w_for_claim_size(ClaimSize size); 31 | int tile_h_for_claim_size(ClaimSize size); 32 | 33 | // Finds a sprite to clip out of imgdat. 34 | // Returns CLAIM_SIZE_NONE if imgdat is empty. 35 | ClaimSize claim(const uint8_t *imgdat, 36 | int iw, int ih, 37 | int sx, int sy, int sw, int sh, 38 | int *col, int *row); 39 | 40 | #endif // CLAIM_H 41 | -------------------------------------------------------------------------------- /samples/cd-mode-1/src/main.c: -------------------------------------------------------------------------------- 1 | // mdk CD mode 1 sample 2 | // Michael Moffitt 2022 3 | #include "md/megadrive.h" 4 | 5 | #include "util/text.h" 6 | #include "util/plane.h" 7 | 8 | #include "cdaudio.h" 9 | 10 | #include "res.h" 11 | 12 | void main(void) 13 | { 14 | megadrive_init(); 15 | text_init(res_font_gfx_bin, sizeof(res_font_gfx_bin), 0x400, res_font_pal_bin, 0); 16 | text_puts(VDP_PLANE_A, 1, 1, "Initializing CD..."); 17 | megadrive_finish(); 18 | 19 | const uint16_t init_result = cdaudio_init(); 20 | if (init_result == 0) 21 | { 22 | text_puts(VDP_PLANE_A, 1, 1, "Init failed. "); 23 | while (1) megadrive_finish(); 24 | } 25 | 26 | text_puts(VDP_PLANE_A, 1, 1, "Waiting... "); 27 | 28 | for (int i = 0; i < 60; i++) 29 | { 30 | megadrive_finish(); 31 | } 32 | text_puts(VDP_PLANE_A, 1, 1, "Playing track 5..."); 33 | 34 | cdaudio_play_loop(5); 35 | 36 | text_puts(VDP_PLANE_A, 1, 1, "Now playing. "); 37 | // Wait forever. 38 | while (1) 39 | { 40 | megadrive_finish(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /mdk/src/md/sys.c: -------------------------------------------------------------------------------- 1 | // mdk system functions 2 | // Michael Moffitt 2018-2024 3 | #include "md/mmio.h" 4 | #include "md/sys.h" 5 | 6 | bool g_md_sys_ints_enabled __attribute__ ((aligned(2))); 7 | 8 | void md_sys_init(void) 9 | { 10 | #ifndef MDK_TARGET_C2 11 | static const uint8_t s_z80_stub_program[] = 12 | { 13 | 0xF3, // di 14 | 0x18, 0xFE // jr $ 15 | }; 16 | md_sys_z80_init(s_z80_stub_program, sizeof(s_z80_stub_program)); 17 | #endif 18 | } 19 | 20 | void md_sys_z80_init(const uint8_t *src, uint16_t size) 21 | { 22 | #ifndef MDK_TARGET_C2 23 | md_sys_z80_reset_assert(); 24 | md_sys_z80_bus_req(/*wait=*/false); 25 | md_sys_z80_reset_deassert(); 26 | 27 | volatile uint8_t *z80_ram = (volatile uint8_t *)SYS_Z80_PRG_LOC; 28 | for (uint16_t i = 0; i < size; i++) *z80_ram++ = *src++; 29 | 30 | md_sys_z80_reset_assert(); 31 | // Long reset for the YM2612. 32 | for (uint16_t i = 0; i < 32; i++) __asm__("nop"); 33 | 34 | md_sys_z80_reset_deassert(); 35 | md_sys_z80_bus_release(); 36 | #else 37 | (void)src; 38 | (void)size; 39 | #endif 40 | } 41 | -------------------------------------------------------------------------------- /mdk/util/image/pngto/pngtochr-README.md: -------------------------------------------------------------------------------- 1 | pngtochr 2 | ======== 3 | 4 | This is a program to convert indexed PNG images to the character 5 | (CHR) data that 8- and 16-bit video game consoles use. It describes 6 | each data format using a string called a "plane map," which allows 7 | one program to cover multiple consoles. 8 | 9 | This is a rewrite of pilbmp2nes.py, one of the tools included in 10 | my NES and Super NES project templates, in the C language. It has 11 | some advantages over the Python version: 12 | 13 | - Faster on large images 14 | - Does not need the Python 3 interpreter installed 15 | - Smaller, especially on platforms where Python is uncommon 16 | 17 | Disadvantages: 18 | 19 | - Cannot read non-PNG images 20 | - Cannot be imported as a library 21 | - Not tested as much 22 | 23 | Legal 24 | ----- 25 | Copyright 2019 Damian Yerrick 26 | 27 | The main program and the LodePNG library are under the zlib License 28 | as set forth in `lodepng.cpp`. The option parser from the musl library 29 | is under the MIT (Expat) License as set forth in `musl_getopt.c`. 30 | -------------------------------------------------------------------------------- /mdk/util/core/binpad.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char **argv) 6 | { 7 | if (argc < 2) 8 | { 9 | printf("Pads a file to the next highest power of two.\n" 10 | "Specification of a minimum size, in bytes, is optional.\n"); 11 | printf("Usage: %s unpadded_file [min_bytes]\n", argv[0]); 12 | return 0; 13 | } 14 | 15 | FILE *f = fopen(argv[1], "r+"); 16 | if (!f) 17 | { 18 | fprintf(stderr, "Couldn't open \"%s\"\n", argv[1]); 19 | return -1; 20 | } 21 | 22 | const size_t min_bytes = (argc >= 3) ? strtoul(argv[2], NULL, 0) : 0; 23 | 24 | const uint8_t pad_value = 0xFF; 25 | 26 | fseek(f, 0, SEEK_END); 27 | const size_t original_size = ftell(f); 28 | size_t next_size = 1; 29 | do 30 | { 31 | next_size = next_size * 2; 32 | } while (next_size < original_size); 33 | 34 | if (next_size < min_bytes) next_size = min_bytes; 35 | 36 | for (size_t i = 0; i < next_size - original_size; i++) 37 | { 38 | fputc(pad_value, f); 39 | } 40 | 41 | fclose(f); 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /mdk/src/util/text.h: -------------------------------------------------------------------------------- 1 | // mdk print utility functions 2 | // Michael Moffitt 2018-2022 3 | // 4 | // These functions are meant to be more instructional than practical. They are 5 | // not particularly flexible, so depending on your game's needs, it may be 6 | // sensible to not use these. 7 | 8 | #pragma once 9 | 10 | #ifdef __cplusplus 11 | extern "C" 12 | { 13 | #endif // __cplusplus 14 | 15 | #include "md/megadrive.h" 16 | 17 | // Load the font graphics and palette. 18 | // Pass 0 as font_chr to skip uploading the text graphics to 19 | // VRAM and only redirect the base tile index. 20 | // Pass 0 as font_pal to redirect the base palette without 21 | // overwriting the contents of that palette line. 22 | void text_init(const unsigned char *font_chr, uint16_t font_len, 23 | uint16_t vram_pos, 24 | const unsigned char *font_pal, uint16_t pal_line); 25 | 26 | // Print a string s on a specified plane at coordinates x, y 27 | void text_puts(VdpPlane plane, uint16_t x, uint16_t y, const char *s); 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif // __cplusplus 32 | -------------------------------------------------------------------------------- /samples/hello-world/src/main.c: -------------------------------------------------------------------------------- 1 | // md-toolchain example main.c 2 | // Michael Moffitt 2018 3 | // 4 | // This main shows a simple "hello world" demo. 5 | 6 | // megadrive.h is an umbrella for all headers in src/md. Specific modules like 7 | // md/vdp.h do not need to be individually included. However, utility funcitons 8 | // are not included, as they are not core support functions. 9 | #include "md/megadrive.h" 10 | 11 | #include "util/text.h" 12 | #include "util/plane.h" 13 | 14 | #include "res.h" 15 | 16 | void main(void) 17 | { 18 | // Get the megadrive ready to go! (See md/megadrive.h) 19 | megadrive_init(); 20 | 21 | // Set up text graphics at VRAM address 0x400 palette 0 22 | // This lines it up nicely with the actual ASCII values, which 23 | // a later demo will take advantage of. 24 | text_init(res_font_gfx_bin, sizeof(res_font_gfx_bin), 0x400, res_font_pal_bin, 0); 25 | 26 | // Print a simple message in the center of plane A 27 | text_puts(VDP_PLANE_A, 14, 11, "Hello World"); 28 | 29 | // Wait forever. 30 | while (1) 31 | { 32 | megadrive_finish(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /mdk/src/util/fixed.h: -------------------------------------------------------------------------------- 1 | // mdk fixed point 2 | // Michael Moffitt 2018-2025 3 | #pragma once 4 | 5 | #include 6 | 7 | // Fixed point types 8 | typedef int32_t fix32_t; 9 | typedef int16_t fix16_t; 10 | 11 | #ifndef MD_FIXED_BITS 12 | #define MD_FIXED_BITS 4 13 | #endif // MD_FIXED_BITS 14 | 15 | // Precision configuration 16 | #define FIX32_PRECISION_BITS MD_FIXED_BITS 17 | #define FIX32_COEF (1 << FIX32_PRECISION_BITS) 18 | 19 | #define FIX16_PRECISION_BITS MD_FIXED_BITS 20 | #define FIX16_COEF (1 << FIX16_PRECISION_BITS) 21 | 22 | // TODO: Unify these into storage-ambiguous macros. 23 | 24 | // Fixed point conversions 25 | #define INTTOFIX32(x) ((fix32_t)((x) * FIX32_COEF)) 26 | #define FIX32TOINT(x) ((int)((x) / FIX32_COEF)) 27 | #define INTTOFIX16(x) ((fix16_t)((x) * FIX16_COEF)) 28 | #define FIX16TOINT(x) ((int)((x) / FIX16_COEF)) 29 | 30 | // Fixed point multiplication and division 31 | #define FIX16MUL(x, y) (((x) * (y)) >> FIX16_PRECISION_BITS) 32 | #define FIX16DIV(x, y) (((x) << FIX16_PRECISION_BITS) / (y)) 33 | #define FIX32MUL(x, y) (((x) * (y)) >> FIX32_PRECISION_BITS) 34 | #define FIX32DIV(x, y) (((x) << FIX32_PRECISION_BITS) / (y)) 35 | -------------------------------------------------------------------------------- /samples/cpp-direct-sprite-demo/src/main.cpp: -------------------------------------------------------------------------------- 1 | // mdk C++ and DIRECT sprite mode example 2 | // Michael Moffitt 2022 3 | 4 | #include "md/megadrive.h" 5 | 6 | #include "util/rand.h" 7 | #include "bg_gradient.h" 8 | 9 | #include "res.h" 10 | #include "dot.h" 11 | 12 | // Palette for the Dots (line 0) 13 | static constexpr uint16_t kPalette[16] = 14 | { 15 | PALRGB(0, 0, 0), 16 | PALRGB(0, 0, 5), 17 | PALRGB(0, 1, 7), 18 | PALRGB(0, 2, 6), 19 | PALRGB(1, 3, 5), 20 | PALRGB(1, 4, 4), 21 | PALRGB(2, 5, 3), 22 | PALRGB(3, 5, 2), 23 | PALRGB(0, 0, 1), 24 | PALRGB(0, 0, 0), 25 | PALRGB(0, 0, 0), 26 | PALRGB(0, 0, 0), 27 | PALRGB(0, 0, 0), 28 | PALRGB(0, 0, 0), 29 | PALRGB(0, 0, 0), 30 | PALRGB(0, 0, 0), 31 | }; 32 | 33 | int main(void) 34 | { 35 | megadrive_init(); 36 | 37 | md_spr_init(SPR_MODE_DIRECT); 38 | srand(0); 39 | md_dma_transfer_vram(32, res_ball_bin, sizeof(res_ball_bin) / 2, 2); 40 | md_pal_upload(0, kPalette, 16); 41 | 42 | Dot dots[MD_SPR_MAX]; 43 | 44 | bg_gradient_init(); 45 | 46 | while (1) 47 | { 48 | for (auto &dot : dots) 49 | { 50 | dot.Move(); 51 | dot.Render(); 52 | } 53 | megadrive_finish(); 54 | } 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /mdk/src/md/adpcm.h: -------------------------------------------------------------------------------- 1 | // mdk uPD7759 support (for System C2) 2 | // Michael Moffitt 2018-2024 3 | #pragma once 4 | #ifdef MDK_TARGET_C2 5 | 6 | #ifdef __cplusplus 7 | extern "C" 8 | { 9 | #endif // __cplusplus 10 | 11 | #include 12 | #include 13 | 14 | // Despite the naming convention of md_*, these functions are only useful for 15 | // System C2, where a uPD7759 is mapped into the 68000 address space. 16 | 17 | // Reset the uPD7759. This must be done after a bank switch for it to 18 | // properly register the change in the voice table (if applicable). 19 | // Reset line is asserted (brought to 0) when reset = true. 20 | // This function wraps md_ioc_set_upd7759_reset(). 21 | void md_adpcm_set_reset(bool reset); 22 | 23 | // Set a phrase request value for the uPD7759. 24 | void md_adpcm_play(uint8_t value); 25 | 26 | // Select bank 0-3. Should call md_adpcm_reset() afterwards, unless the banks 27 | // don't change the voice table contents. 28 | // This function wraps md_ioc_set_udp7759_bank(). 29 | void md_adpcm_set_bank(uint16_t bank); 30 | 31 | // Returns true if the uPD7759 IC is busy. Wraps md_ioc_get_upd7759_busy(). 32 | bool md_adpcm_busy(void); 33 | 34 | #ifdef __cplusplus 35 | } 36 | #endif // __cplusplus 37 | #endif // MD_TARGET_C2 38 | -------------------------------------------------------------------------------- /mdk/src/util/trig.c: -------------------------------------------------------------------------------- 1 | #include "trig.h" 2 | #include "util/fixed.h" 3 | #include "md/megadrive.h" 4 | 5 | static uint8_t atan_int(fix32_t ratio) 6 | { 7 | if (ratio >= INTTOFIX16(TRIG_TAB_ATAN_INPUT_RANGE)) 8 | { 9 | return trig_tab_atan[MDK_ARRAYSIZE(trig_tab_atan) - 1]; 10 | } 11 | if (ratio < 0) return 0; 12 | ratio = ratio / 4; 13 | return trig_tab_atan[ratio]; 14 | } 15 | 16 | uint8_t trig_atan(int y, int x) 17 | { 18 | y = -y; 19 | if (x == 0) 20 | { 21 | return y < 0 ? DEGTOUINT8(270) : DEGTOUINT8(90); 22 | } 23 | else 24 | { 25 | if (x >= 0) 26 | { 27 | if (y >= 0) 28 | { 29 | const fix32_t ratio = FIX32DIV(y, x); 30 | return atan_int(ratio); 31 | } 32 | else 33 | { 34 | const fix32_t ratio = FIX32DIV(-y, x); 35 | const uint8_t angle = atan_int(ratio); 36 | return DEGTOUINT8(270) + (DEGTOUINT8(90) - angle); 37 | } 38 | } 39 | else 40 | { 41 | if (y >= 0) 42 | { 43 | const fix32_t ratio = FIX32DIV(y, -x); 44 | const uint8_t angle = atan_int(ratio); 45 | return DEGTOUINT8(180) - angle; 46 | } 47 | else 48 | { 49 | const fix32_t ratio = FIX32DIV(-y, -x); 50 | const uint8_t angle = atan_int(ratio); 51 | return DEGTOUINT8(180) + angle; 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /mdk/src/md/adpcm.c: -------------------------------------------------------------------------------- 1 | #ifdef MDK_TARGET_C2 2 | 3 | #include "md/adpcm.h" 4 | #include "md/mmio.h" 5 | #include "md/ioc.h" 6 | 7 | // Despite the naming convention of md_*, these functions are only useful for 8 | // System C2, where a uPD7759 is mapped into the 68000 address space. 9 | 10 | 11 | // Reset the uPD7759. This must be done after a bank switch for it to 12 | // properly register the change in the voice table (if applicable). 13 | // Reset line is asserted (brought to 0) when reset = 1. 14 | // This function wraps md_ioc_set_upd7759_reset(). 15 | void md_adpcm_set_reset(bool reset) 16 | { 17 | md_ioc_set_upd7759_reset(reset); 18 | } 19 | 20 | // Set a phrase request value for the uPD7759. 21 | void md_adpcm_play(uint8_t value) 22 | { 23 | volatile uint8_t *phrase = (volatile uint8_t *)SYSC_IO_UPD7759_PHRASE_LOC; 24 | *phrase = value; 25 | } 26 | 27 | // Select bank 0-3. Should call md_adpcm_reset() afterwards, unless the banks 28 | // don't change the voice table contents. 29 | // This function wraps md_ioc_set_udp7759_bank(). 30 | void md_adpcm_set_bank(uint16_t bank) 31 | { 32 | md_ioc_set_udp7759_bank(bank); 33 | } 34 | 35 | // Returns 1 if the uPD7759 IC is busy. 36 | bool md_adpcm_busy(void) 37 | { 38 | return md_ioc_get_upd7759_busy(); 39 | } 40 | 41 | #endif // MD_TARGET_C2 42 | -------------------------------------------------------------------------------- /mdk/util/png2csp/src/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H 2 | #define UTIL_H 3 | 4 | #include 5 | 6 | #include "types.h" 7 | 8 | // Small utility functions used in sprite extraction. Arguments are concise, 9 | // so a generalized description has been provided below: 10 | // imgdat: uint8_t array of sprite sheet bitmap data; one pixel is one byte. 11 | // iw, ih: width and height of source image data (the sprite sheet bitmap). 12 | // sw, sh: sprite frame size to be extracted. 13 | // sx, sy: top-left origin point of sprite frame being extracted from imgdat. 14 | // ox, oy: origin point considered the "center" of the extracted sprite. 15 | 16 | // Sets ox and oy based on the origin mode and sprite frame size. 17 | void origin_for_sp(ConvOrigin origin, int sw, int sh, int *ox, int *oy); 18 | 19 | // (debug) Renders a region of imgdat as terminal output. 20 | void render_region(const uint8_t *imgdat, int iw, int ih, 21 | int sx, int sy, int sw, int sh); 22 | 23 | // Takes the 8x8 tile from imgdat and places it in the appropriate 4bpp format 24 | // into out. *** The data is erased from imgdat as it is taken. *** 25 | // It is a given that imgdat is large enough for the indicated region. 26 | // Data exceeding sw and sh is excluded. 27 | void clip_8x8_tile(uint8_t *imgdat, int iw, int sx, int sy, 28 | int limx, int limy, uint8_t *out); 29 | #endif // UTIL_H 30 | -------------------------------------------------------------------------------- /mdk/src/md/sram.a68: -------------------------------------------------------------------------------- 1 | ; mdk SRAM routiness 2 | ; Michael Moffitt 2024 3 | #include "md/top.inc" 4 | 5 | .globl md_sram_write 6 | md_sram_write: 7 | ; Enable SRAM for writing. 8 | move.b #$01, (SRAM_CTRL).l 9 | 10 | ; Set up a0 with the destination address. 11 | lea SRAM_BASE, a0 12 | move.l 4(sp), d0 ; dest_offset 13 | add.l d0, d0 14 | lea (a0, d0.l), a0 15 | 16 | ; a1 gets the 68000-side source address, and d1 gets the byte count. 17 | move.l 8(sp), a1 ; src_addr 18 | move.w 14(sp), d1 ; n 19 | subq.w #1, d1 20 | 21 | ; Copy n bytes (d1 as loop counter). 22 | moveq #0, d0 23 | 0: 24 | move.b (a1)+, d0 25 | move.w d0, (a0)+ 26 | dbf d1, 0b 27 | 28 | ; Disable SRAM. 29 | move.b #$00, (SRAM_CTRL).l 30 | rts 31 | 32 | ; Copies n bytes from 68000 memory space src_addr to dest_offset within SRAM. 33 | 34 | ; Copies n bytes from src_offset in SRAM to dest_addr in 68000 memory space. 35 | .globl md_sram_read 36 | md_sram_read: 37 | ; Enable SRAM (read-only). 38 | move.b #$03, (SRAM_CTRL).l 39 | 40 | ; Set up a0 with the source address. 41 | lea SRAM_BASE, a0 42 | move.l 4(sp), d0 ; src_offset 43 | add.l d0, d0 44 | lea (a0, d0.l), a0 45 | 46 | ; a1 gets the 68000-side dest address, and d1 gets the byte count. 47 | move.l 8(sp), a1 ; dest_addr 48 | move.w 14(sp), d1 ; n 49 | subq.w #1, d1 50 | 51 | ; Copy n bytes. 52 | moveq #0, d0 53 | 0: 54 | move.w (a0)+, d0 55 | move.b d0, (a1)+ 56 | dbf d1, 0b 57 | 58 | ; Disable SRAM. 59 | move.b #$00, (SRAM_CTRL).l 60 | rts 61 | -------------------------------------------------------------------------------- /samples/move-podge/Makefile: -------------------------------------------------------------------------------- 1 | # Configuration 2 | PROJECT_NAME := move_podge 3 | 4 | MDKROOT := ../../mdk 5 | 6 | SRCDIR := src 7 | RESDIR := res 8 | OBJDIR := obj 9 | PNGDIR := png 10 | 11 | SOURCES_C := $(shell find $(SRCDIR) -type f -name '*.c') 12 | 13 | # Experimental PNG rules - in this example, the `res` directory contains 14 | # artifacts from automatic image conversion. 15 | IMAGES_PNG := $(shell find $(PNGDIR) -type f -name '*.png') 16 | CONVERTED_CHR := $(patsubst $(PNGDIR)/%.png,$(RESDIR)/%_gfx.bin,$(IMAGES_PNG)) 17 | CONVERTED_PAL := $(patsubst $(PNGDIR)/%.png,$(RESDIR)/%_pal.bin,$(IMAGES_PNG)) 18 | 19 | EXTERNAL_DEPS := gen_graphics 20 | EXTERNAL_ARTIFACTS := $(CONVERTED_CHR) $(CONVERTED_PAL) 21 | 22 | include $(MDKROOT)/md-rules.mk 23 | 24 | gen_graphics: $(CONVERTED_CHR) $(CONVERTED_PAL) 25 | 26 | $(RESDIR)/%_gfx.bin: $(PNGTO) $(PNGDIR)/%.png 27 | @mkdir -p $(dir $@) 28 | $(PNGTO) -p "3210" -i $(filter-out $<,$^) -o $@ 29 | 30 | $(RESDIR)/%H16_gfx.bin: $(PNGTO) $(PNGDIR)/%H16.png 31 | @mkdir -p $(dir $@) 32 | $(PNGTO) -p "3210" -H 16 -i $(filter-out $<,$^) -o $@ 33 | 34 | $(RESDIR)/%H24_gfx.bin: $(PNGTO) $(PNGDIR)/%H24.png 35 | @mkdir -p $(dir $@) 36 | $(PNGTO) -p "3210" -H 24 -i $(filter-out $<,$^) -o $@ 37 | 38 | $(RESDIR)/%H32_gfx.bin: $(PNGTO) $(PNGDIR)/%H32.png 39 | @mkdir -p $(dir $@) 40 | $(PNGTO) -p "3210" -H 32 -i $(filter-out $<,$^) -o $@ 41 | 42 | $(RESDIR)/%_pal.bin: $(PNGTO) $(PNGDIR)/%.png 43 | @mkdir -p $(dir $@) 44 | $(PNGTO) -c 0000BBB0GGG0RRR0 --num-colors=16 -i $(filter-out $<,$^) -o $@ 45 | 46 | -------------------------------------------------------------------------------- /mdk/util/core/bin2h.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | bin2h: generate an extern definition for a bin2s symbol 4 | for gfx/foo3.bin it'll write `extern const uint8_t gfx_foo_bin[x];` and 5 | `extern const unsigned int gfx_foo_bin_size` to stdout, so that a header 6 | may be collated. 7 | 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | int main(int argc, char **argv) 16 | { 17 | FILE *fin; 18 | long int filelen; 19 | int linelen; 20 | int arg; 21 | 22 | if (argc < 2) 23 | { 24 | printf("Usage: %s\n"); 25 | fprintf(stderr, "%s: not enough arguments", argv[0]); 26 | return EXIT_FAILURE; 27 | } 28 | 29 | for (arg = 1; arg < argc; arg++) 30 | { 31 | // Omit optional ".cfg" files 32 | if (strstr(argv[arg], ".cfg")) continue; 33 | fin = fopen(argv[arg], "rb"); 34 | if (!fin) 35 | { 36 | fprintf(stderr, "%s: could not open ", argv[0]); 37 | perror(argv[arg]); 38 | return 1; 39 | } 40 | 41 | char *filter = argv[arg]; 42 | while (*filter != '\0') 43 | { 44 | if (*filter == '/' || *filter == '\\' || 45 | *filter == '.') 46 | { 47 | *filter = '_'; 48 | } 49 | filter++; 50 | } 51 | 52 | fseek(fin, 0, SEEK_END); 53 | filelen = ftell(fin); 54 | if (filelen == 0) 55 | { 56 | fclose(fin); 57 | fprintf(stderr, "bin2s: warning: skipping empty file %s\n", 58 | argv[arg]); 59 | continue; 60 | } 61 | fclose(fin); 62 | 63 | printf("extern const uint8_t %s[%d];\n", argv[arg], filelen); 64 | } 65 | return 0; 66 | } 67 | 68 | -------------------------------------------------------------------------------- /mdk/src/md/io.h: -------------------------------------------------------------------------------- 1 | // mdk gamepad support 2 | // Michael Moffitt 2018-2024 3 | #pragma once 4 | 5 | #include 6 | 7 | // Serial port speed configurations 8 | #define IO_BAUD_4800 0x00 9 | #define IO_BAUD_2800 0x80 10 | #define IO_BAUD_1200 0x40 11 | #define IO_BAUD_300 0x20 12 | 13 | typedef uint16_t MdButton; 14 | // Button masks. 15 | #define BTN_UP 0x0001 16 | #define BTN_DOWN 0x0002 17 | #define BTN_LEFT 0x0004 18 | #define BTN_RIGHT 0x0008 19 | #define BTN_B 0x0010 20 | #define BTN_C 0x0020 21 | #define BTN_A 0x0040 22 | #define BTN_START 0x0080 23 | #define BTN_Z 0x0100 24 | #define BTN_Y 0x0200 25 | #define BTN_X 0x0400 26 | #define BTN_MODE 0x0800 27 | // Special status bits. 28 | #define MD_PAD_UNPLUGGED 0x4000 // This may also be set for an SMS controller. 29 | #define MD_PAD_6B 0x8000 30 | 31 | // Input state data for player 1 and 2. Test these variables with the BTN_* 32 | // constants above to read button data. 33 | extern uint16_t g_md_pad[2]; // Button data for the current frame. 34 | extern uint16_t g_md_pad_pos[2]; // Buttons pressed on the current frame. 35 | extern uint16_t g_md_pad_neg[2]; // Buttons released on the current frame. 36 | extern uint16_t g_md_pad_prev[2]; // The state of buttons from the last frame. 37 | 38 | // Configures control registers for gamepad reading. 39 | // th interrupts are disabled for each port. 40 | void md_io_init(void); 41 | 42 | // Internal Use --------------------------------------------------------------- 43 | 44 | // Poll controller inputs. 45 | void md_io_poll(void); 46 | 47 | // Generates g_md_pad_pos and g_md_pad_neg from g_md_pad. 48 | void md_io_generate_edges(void); 49 | -------------------------------------------------------------------------------- /mdk/src/md/irq_handlers.a68: -------------------------------------------------------------------------------- 1 | #include "md/top.inc" 2 | #include "md/errors.h" 3 | ; md-toolchain interrupt handlers 4 | ; Michael Moffitt 2018-2024 5 | 6 | .global _v_irq1 7 | .global _v_irq2 8 | .global _v_irq3 9 | .global _v_irq4 10 | .global _v_irq5 11 | .global _v_irq6 12 | .global _v_irq7 13 | 14 | ; This is a flag used for vblank waiting. 15 | .extern g_vblank_wait 16 | 17 | ; Pointer to interrupt routines. Calling convention is just a regular 18 | ; function which takes no arguments (e.g. void my_irq_handler(void)). 19 | .extern g_irq_h_func 20 | .extern g_irq_io_func 21 | .extern g_irq_v_func 22 | 23 | .macro irq_func_handle symbol_name:req 24 | ; Disable interrupts. Don't touch the global flag for it, though.*/ 25 | move.w sr, -(sp) 26 | ori.w #$0700, sr 27 | ; Load handler, and nope out if nothing has been registered. 28 | move.l d0, -(sp) 29 | move.l \symbol_name, d0 30 | beq.s 1f 31 | 32 | movem.l d1/a0-a1, -(sp) 33 | move.l d0, a0 34 | jsr (a0) 35 | movem.l (sp)+, d1/a0-a1 36 | 1: 37 | move.l (sp)+, d0 38 | move.w (sp)+, sr 39 | .endm 40 | 41 | ; IRQ vectors unused 42 | _v_irq1: 43 | showerr MD_ERRMSG_IRQ1 44 | _v_irq3: 45 | showerr MD_ERRMSG_IRQ3 46 | _v_irq5: 47 | showerr MD_ERRMSG_IRQ5 48 | _v_irq7: 49 | showerr MD_ERRMSG_IRQ7 50 | 51 | ; /TH pin change, from controller port 52 | _v_irq2: 53 | irq_func_handle g_irq_io_func 54 | rte 55 | 56 | ; H-blank Interrupt 57 | _v_irq4: 58 | irq_func_handle g_irq_h_func 59 | rte 60 | 61 | ; V-blank Interrupt 62 | _v_irq6: 63 | ; This flag is cleared in vblank so that software may set it high, 64 | ; and then block on it transitioning low in order to synchronize. 65 | clr.w g_vblank_wait 66 | irq_func_handle g_irq_v_func 67 | rte 68 | -------------------------------------------------------------------------------- /samples/cpp-direct-sprite-demo/src/dot.cpp: -------------------------------------------------------------------------------- 1 | #include "dot.h" 2 | 3 | #include "md/megadrive.h" 4 | 5 | #include "util/rand.h" 6 | 7 | namespace 8 | { 9 | static constexpr fix16_t kGravity = INTTOFIX16(0.08); 10 | static constexpr fix32_t kBoundLeft = INTTOFIX32(4); 11 | static constexpr fix32_t kBoundRight = INTTOFIX32(320 - 4); 12 | static constexpr fix32_t kGroundY = INTTOFIX32(224 - 7); 13 | static constexpr fix16_t kMinimumDy = kGravity * 16; 14 | static constexpr fix16_t kBounceDyCoef = INTTOFIX16(0.75); 15 | 16 | static int s_next_slot; 17 | } // namespace 18 | 19 | Dot::Dot() 20 | { 21 | // The Dot objects use a static int to assign themselves sprite slots. 22 | if (s_next_slot >= MD_SPR_MAX) s_next_slot = 0; 23 | m_spr = &g_sprite_table[s_next_slot]; 24 | s_next_slot++; 25 | 26 | // The sprite attributes that will not change are assigned values once. 27 | m_spr->link = s_next_slot; 28 | m_spr->attr = SPR_ATTR(1, 0, 0, 0, 0); 29 | m_spr->size = SPR_SIZE(1, 1); 30 | 31 | ResetVelocity(); 32 | } 33 | 34 | void Dot::ResetVelocity() 35 | { 36 | m_dx = INTTOFIX16((rand() % 64) - 32) / 16; 37 | m_dy = INTTOFIX16((rand() % 32) - 48) / 8; 38 | } 39 | 40 | void Dot::Move() 41 | { 42 | m_x += m_dx; 43 | m_y += m_dy; 44 | m_dy += kGravity; 45 | 46 | if (m_y > kGroundY && m_dy > 0) 47 | { 48 | m_y = kGroundY; 49 | if (m_dy < kMinimumDy) ResetVelocity(); 50 | else m_dy = FIX16MUL(m_dy, -kBounceDyCoef); 51 | } 52 | 53 | if (m_x > kBoundRight && m_dx > 0) 54 | { 55 | m_dx = -m_dx; 56 | m_x = kBoundRight; 57 | } 58 | else if (m_x < kBoundLeft && m_dx < 0) 59 | { 60 | m_dx = -m_dx; 61 | m_x = kBoundLeft; 62 | } 63 | } 64 | 65 | void Dot::Render() 66 | { 67 | m_spr->xpos = FIX16TOINT(m_x) + 128; 68 | m_spr->ypos = FIX16TOINT(m_y) + 128; 69 | } 70 | -------------------------------------------------------------------------------- /mdk/src/md/sysc_vctrl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef MDK_TARGET_C2 4 | 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" 9 | { 10 | #endif // __cplusplus 11 | 12 | #include 13 | #include "md/mmio.h" 14 | 15 | 16 | extern uint8_t g_c2_vctrl; 17 | 18 | // When blank is true, the output from the video DAC is blanked. 19 | static inline void md_sysc_vctrl_set_blank(bool blank); 20 | 21 | // Drives the protection reset line low. 22 | static inline void md_sysc_vctrl_set_protection_reset(bool reset); 23 | 24 | // When compat is true, the palette is arranged in a way that is similar to the 25 | // Megadrive's color RAM, with one nybble per color channel, plus an upper 26 | // nybble to provide the LSB of color data. 27 | // When compat is false, RGB data is directly packed in order in a 16-bit word. 28 | static inline void md_sysc_vctrl_set_md_color_compat(bool compat); 29 | 30 | // ----------------------------------------------------------------------------- 31 | 32 | static inline void md_sysc_vctrl_write(uint8_t data) 33 | { 34 | volatile uint8_t *vctrl = (volatile uint8_t *)SYSC_PROTECTION_LOC_VCTRL; 35 | *vctrl = data; 36 | } 37 | 38 | static inline void md_sysc_vctrl_set_blank(bool blank) 39 | { 40 | g_c2_vctrl &= ~0x01; 41 | g_c2_vctrl |= blank ? 0x01 : 0x00; 42 | md_sysc_vctrl_write(g_c2_vctrl); 43 | } 44 | 45 | static inline void md_sysc_vctrl_set_protection_reset(bool reset) 46 | { 47 | g_c2_vctrl &= ~0x02; 48 | g_c2_vctrl |= reset ? 0x02 : 0x00; 49 | md_sysc_vctrl_write(g_c2_vctrl); 50 | } 51 | 52 | static inline void md_sysc_vctrl_set_md_color_compat(bool compat) 53 | { 54 | g_c2_vctrl &= ~0x04; 55 | g_c2_vctrl |= compat ? 0x04 : 0x00; 56 | md_sysc_vctrl_write(g_c2_vctrl); 57 | } 58 | 59 | #ifdef __cplusplus 60 | } 61 | #endif // __cplusplus 62 | 63 | #endif // MDK_TARGET_C2 64 | -------------------------------------------------------------------------------- /mdk/src/md/cspr.h: -------------------------------------------------------------------------------- 1 | // mdk composite sprite support 2 | // 2023-2024 Michael Moffitt 3 | // These functions may be used in SIMPLE mode. 4 | #pragma once 5 | 6 | #include 7 | 8 | // Struct for composite sprite drawing parameters. 9 | typedef struct CSprParam 10 | { 11 | const void *cspr_data; // Composite sprite binary data struct 12 | uint16_t vram_base; // Base VRAM address used for tile data. 13 | uint16_t frame; // Frame within cspr struct to display. 14 | int16_t x, y; // Screen coordinates. 15 | uint16_t attr; // Use SPR_ATTR macro with tile parameter set to 0. 16 | uint16_t use_dma; 17 | } CSprParam; 18 | 19 | // Draws a composite sprite using screen position coordinates. 20 | // `attr` should only have flip flags and palette set. 21 | // You may use the SPR_ATTR macro with the tile set to 0. 22 | void md_cspr_put_st(const CSprParam *s); 23 | 24 | // "Fast" version with various safety checks removed for speed. Use this 25 | // for faster drawing of objects that you are already culling when off-screen 26 | // and will absolutely not be accessing animation frames outside what the data 27 | // offers. 28 | // 29 | // Checks removed: 30 | // * Screen boundary checks 31 | // * Frame bound checks 32 | void md_cspr_put_st_fast(const CSprParam *s); 33 | 34 | // Loads all tile data from cspr_data to VRAM, for non-DMA usage. 35 | void md_cspr_upload_tiles(const void *cspr_data, uint16_t vram_addr); 36 | 37 | // Loads the palette from cspr_data to a palette line. 38 | void md_cspr_upload_pal(const void *cspr_data, uint16_t pal_line); 39 | 40 | // Gets the quantity of animation frames defined in cspr_data. 41 | uint16_t md_cspr_get_frame_count(const void *cspr_data); 42 | 43 | // Returns a pointer to a C-string of the name of the composite sprite. 44 | const char *md_cspr_get_name(const void *cspr_data); 45 | -------------------------------------------------------------------------------- /mdk/src/md/megadrive.c: -------------------------------------------------------------------------------- 1 | // mdk top-level file 2 | // 2018-2024 Michael Moffitt 3 | // =========================================================================== 4 | #include "md/megadrive.h" 5 | #include "md/sys.h" 6 | #include "md/vdp.h" 7 | #include "md/io.h" 8 | #include "md/spr.h" 9 | #include "md/sysc_vctrl.h" 10 | #include 11 | 12 | void megadrive_init(void) 13 | { 14 | md_sys_di(); 15 | md_sys_init(); 16 | md_vdp_init(); 17 | md_dma_init(); 18 | md_spr_init(SPR_MODE_SIMPLE); 19 | md_dma_process(); 20 | static const uint16_t scroll_default = 0; 21 | md_dma_transfer_vram(VRAM_HSCR_BASE_DEFAULT, &scroll_default, 1, 2); 22 | md_dma_transfer_vsram(0, &scroll_default, 1, 2); 23 | md_dma_process(); 24 | 25 | #ifdef MDK_TARGET_C2 26 | md_ioc_init(); 27 | md_ioc_set_upd7759_reset(true); 28 | md_sysc_vctrl_set_blank(false); 29 | md_sysc_vctrl_set_protection_reset(true); 30 | md_sysc_vctrl_set_md_color_compat(true); 31 | md_ioc_set_udp7759_bank(0); 32 | md_ioc_set_upd7759_reset(false); 33 | #else 34 | md_io_init(); 35 | #endif 36 | md_pal_init(); 37 | md_vdp_set_display_en(true); 38 | 39 | md_sys_ei(); 40 | } 41 | 42 | // Run after completing the logic in one game tick loop. 43 | void megadrive_finish(void) 44 | { 45 | const bool irq_en = md_sys_ei(); 46 | md_spr_finish(); 47 | #ifndef MDK_TARGET_C2 48 | md_pal_poll(); 49 | #endif // MDK_TARGET_C2 50 | md_vdp_wait_vblank(); 51 | #ifdef MDK_TARGET_C2 52 | md_pal_poll(); 53 | #endif // MDK_TARGET_C2 54 | 55 | // C2-specific screen blanking. 56 | #ifdef MDK_TARGET_C2 57 | md_sysc_vctrl_set_blank(true); 58 | #endif // MDK_TARGET_C2 59 | 60 | #ifndef MDK_TARGET_C2 61 | md_io_poll(); 62 | #else 63 | md_ioc_poll(); 64 | #endif // MDK_TARGET_C2 65 | md_dma_process(); 66 | 67 | md_spr_start(); 68 | 69 | // C2-specific screen blanking. 70 | #ifdef MDK_TARGET_C2 71 | md_sysc_vctrl_set_blank(false); 72 | #endif // MDK_TARGET_C2 73 | if (!irq_en) md_sys_di(); 74 | } 75 | -------------------------------------------------------------------------------- /mdk/src/md/vdp_enums.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define MD_VDP_WIDTH_H40_PX 320 4 | #define MD_VDP_WIDTH_H32_PX 256 5 | 6 | #define MD_VDP_HEIGHT_V28_PX 224 7 | #define MD_VDP_HEIGHT_V30_PX 240 8 | 9 | // Enums for register access functions. 10 | typedef enum VdpHscrollMode 11 | { 12 | VDP_HSCROLL_PLANE = 0, 13 | VDP_HSCROLL_UNDEF = VDP_MODESET3_HS0, 14 | VDP_HSCROLL_CELL = VDP_MODESET3_HS1, 15 | VDP_HSCROLL_LINE = VDP_MODESET3_HS0 | VDP_MODESET3_HS1 16 | } VdpHscrollMode; 17 | 18 | typedef enum VdpVscrollMode 19 | { 20 | VDP_VSCROLL_PLANE, 21 | VDP_VSCROLL_CELL 22 | } VdpVscrollMode; 23 | 24 | typedef enum VdpHmode 25 | { 26 | VDP_HMODE_H40, 27 | VDP_HMODE_H32 28 | } VdpHmode; 29 | 30 | typedef enum VdpVmode 31 | { 32 | VDP_VMODE_V28, 33 | VDP_VMODE_V30 34 | } VdpVmode; 35 | 36 | typedef enum VdpPlaneSize 37 | { 38 | VDP_PLANESIZE_32x32 = 0x00, 39 | VDP_PLANESIZE_64x32 = 0x01, 40 | VDP_PLANESIZE_UNDx32 = 0x02, 41 | VDP_PLANESIZE_128x32 = 0x03, 42 | 43 | VDP_PLANESIZE_32x64 = 0x10, 44 | VDP_PLANESIZE_64x64 = 0x11, 45 | VDP_PLANESIZE_UNDx64 = 0x12, 46 | VDP_PLANESIZE_128x64 = 0x13, 47 | 48 | VDP_PLANESIZE_32xUND = 0x20, 49 | VDP_PLANESIZE_64xUND = 0x21, 50 | VDP_PLANESIZE_UNDxUND = 0x22, 51 | VDP_PLANESIZE_128xUND = 0x23, 52 | 53 | VDP_PLANESIZE_32x128 = 0x30, 54 | VDP_PLANESIZE_64x128 = 0x31, 55 | VDP_PLANESIZE_UNDx128 = 0x32, 56 | VDP_PLANESIZE_128x128 = 0x33, 57 | } VdpPlaneSize; 58 | 59 | typedef enum VdpPlane 60 | { 61 | VDP_PLANE_A = 0x00, 62 | VDP_PLANE_B = 0x01, 63 | VDP_PLANE_WINDOW = 0x02 64 | } VdpPlane; 65 | 66 | typedef enum VdpInterlaceMode 67 | { 68 | VDP_INTERLACE_NONE, 69 | VDP_INTERLACE_NORMAL, 70 | VDP_INTERLACE_DOUBLE 71 | } VdpInterlaceMode; 72 | 73 | // Enum for LYSEL values. 74 | typedef enum VdpDebugLayerSel 75 | { 76 | VDP_DEBUG_LYSEL_NONE = 0x0, 77 | VDP_DEBUG_LYSEL_SPRITE = 0x1, 78 | VDP_DEBUG_LYSEL_PLANE_A = 0x2, 79 | VDP_DEBUG_LYSEL_PLANE_B = 0x3 80 | } VdpDebugLayerSel; 81 | -------------------------------------------------------------------------------- /mdk/src/md/pal.h: -------------------------------------------------------------------------------- 1 | // mdk palette support functions 2 | // Michael Moffitt 2018-2024 3 | #pragma once 4 | 5 | #ifdef __cplusplus 6 | extern "C" 7 | { 8 | #endif // __cplusplus 9 | 10 | // Functions to read and write to the palette. Color data is cached in a small 11 | // buffer that is queued for transfer during vblank if needed. 12 | 13 | #include 14 | 15 | // Note: these are useful for debugging, but I would suggest using external 16 | // binary data for palettes, and uploading them via md_pal_upload(). 17 | #ifdef MDK_TARGET_C2 18 | #define PALRGB(r, g, b) (((r)) | ((g) << 4) | ((b) << 8)) 19 | #else 20 | #define PALRGB(r, g, b) (((r) << 1) | ((g) << 5) | ((b) << 9)) 21 | #endif // MDK_TARGET_C2 22 | 23 | // Set a single palette color. 24 | void md_pal_set(uint16_t idx, uint16_t val); 25 | 26 | // Caches and schedules a transfer to palette data. 27 | // Dest: palette index (0 - 63 on MD, 0 - 255 on C/C2). 28 | // Source: Pointer to data to copy from. Data is copied immediately to a cache. 29 | // Count: Number of palette entries to copy (1 = one word = one color). 30 | void md_pal_upload(uint16_t dest, const void *source, uint16_t count); 31 | 32 | // Select between banks 0 - 3 for sprites and backgrounds. (System C/C2 only). 33 | void md_pal_set_bg_bank(uint16_t bank); 34 | void md_pal_set_spr_bank(uint16_t bank); 35 | 36 | // Direct palette cache access. After modifying the palette, make sure to mark 37 | // the range as dirty! Otherwise, it may not be uploaded. 38 | void md_pal_mark_dirty(uint16_t first_index, uint16_t count); 39 | 40 | #ifdef MDK_TARGET_C2 41 | extern uint16_t g_palette[16 * 4 * 4 * 2]; 42 | #else 43 | extern uint16_t g_palette[64]; 44 | #endif // MDK_TARGET_C2 45 | 46 | 47 | // Internal Use --------------------------------------------------------------- 48 | 49 | // Schedules DMA transfers. Called by megadrive_finish(). 50 | void md_pal_poll(void); 51 | 52 | void md_pal_init(void); 53 | 54 | #ifdef __cplusplus 55 | } 56 | #endif // __cplusplus 57 | -------------------------------------------------------------------------------- /mdk/src/md/dma_process.a68: -------------------------------------------------------------------------------- 1 | #include "md/top.inc" 2 | ; 3 | ; void md_dma_process_cmd(DmaCmd *cmd) 4 | ; 5 | .global md_dma_process_cmd 6 | 7 | .set SPID, 4 8 | .set ARG_CMD, SPID ; pointer to DmaCmd 9 | ; Parameters in DmaCmd struct 10 | .set PRM_STRIDE, 0 ; uint16_t 11 | .set PRM_LEN1, 2 ; uint16_t 12 | .set PRM_LEN2, 4 ; uint16_t 13 | .set PRM_SRC1, 6 ; uint16_t 14 | .set PRM_SRC2, 8 ; uint16_t 15 | .set PRM_SRC3, 10 ; uint16_t 16 | .set PRM_CTRL32, 12 ; uint32_t, cache of ctrl longword for dest addr 17 | 18 | md_dma_process_cmd: 19 | lea VDP_LOC_BASE+VDP_OFFS_CTRL, a1 20 | ; A0 holds the DMA command, with precalculated register values. 21 | movea.l ARG_CMD(sp), a0 22 | move.w PRM_SRC3(a0), d0 23 | move.w (a0)+, (a1) ; PRM_STRIDE 24 | move.l (a0)+, (a1) ; PRM_LEN1/2 25 | tst.b d0 26 | bmi.s dma_fill 27 | move.l (a0)+, (a1) ; PRM_SRC1/2 28 | addq.w #2, a0 29 | move.w d0, (a1) ; PRM_SRC3 30 | #ifndef MDK_TARGET_C2 31 | zbusreq 32 | zbuswt 33 | #endif 34 | move.l (a0), (a1) ; PRM_CTRL32 35 | #ifndef MDK_TARGET_C2 36 | zbusrel 37 | #endif 38 | rts 39 | dma_fill: 40 | subq #6, a0 41 | move.w d0, (a1) ; PRM_SRC3 42 | #ifndef MDK_TARGET_C2 43 | zbusreq 44 | zbuswt 45 | #endif 46 | move.l PRM_CTRL32(a0), (a1) ; PRM_CTRL32 47 | move.w PRM_SRC1(a0), -4(a1) ; data 48 | #ifndef MDK_TARGET_C2 49 | zbusrel 50 | #endif 51 | dma_fill_vdp_wait: 52 | btst #1, 1(a1) 53 | bne.s dma_fill_vdp_wait 54 | rts 55 | 56 | ; 57 | ; void md_dma_process(void) 58 | ; 59 | .global md_dma_process 60 | 61 | .extern g_md_sys_ints_enabled ; bool 62 | md_dma_process_wip: 63 | dma_wait 64 | ; Disable ints, storing previous enablement status to restore later. 65 | move.b g_md_sys_ints_enabled, d0 66 | move.w d0, -(sp) 67 | clr.b g_md_sys_ints_enabled 68 | andi.w #$F8FF, sr 69 | 70 | ; Handle the two sprite slots first. 71 | 72 | 73 | ; Restore int enablement and exit. 74 | move.w (sp)+, d0 75 | move.b d0, g_md_sys_ints_enabled 76 | beq.s reenable_ints 77 | rts 78 | 79 | reenable_ints: 80 | ori.w #$0700, sr 81 | rts 82 | -------------------------------------------------------------------------------- /samples/cd-mode-1/src/cdaudio.h: -------------------------------------------------------------------------------- 1 | // Sega CD Mode 1 support code for SGDK 2 | // Michael Moffitt (https://github.com/mikejmoffitt) 3 | // Adapted from Chilly Willy's Mode 1 CD Player 4 | 5 | #ifndef CDAUDIO_H 6 | #define CDAUDIO_H 7 | 8 | #define SEGACD_REG_CPU 0xA12000 9 | #define SEGACD_REG_MEM 0xA12002 10 | #define SEGACD_REG_HINT 0xA12006 11 | #define SEGACD_REG_STOPWATCH 0xA1200C 12 | #define SEGACD_REG_COMM 0xA1200E 13 | 14 | #define SEGACD_TRACK_W 0xA12010 15 | #define SEGACD_PMODE_W 0xA12012 16 | 17 | #define SEGACD_PROGRAM_ADDR 0x420000 18 | #define SEGACD_PROGRAM_OFF 0x426000 19 | #define SEGACD_PROGRAM_LEN 0x20000 20 | 21 | #define SEGACD_CPU_IEN_MASK 0x8000 22 | 23 | #define SEGACD_BIOS_STAT 0xA12020 24 | #define SEGACD_FIRST_TNO 0xA12022 25 | #define SEGACD_LAST_TNO 0xA12023 26 | #define SEGACD_DRV_VERS 0xA12024 27 | #define SEGACD_FLAG 0xA12025 28 | 29 | #define SCD_OFFSET 0x6D 30 | 31 | #define SCD_BIOSLOC_1 0x415800 32 | #define SCD_BIOSLOC_2 0x416000 33 | #define SCD_BIOSLOC_3 0x41AD00 34 | 35 | #define SEGACD_CMD_ACK 0x00 36 | 37 | #define SEGACD_PLAYMODE_ONCE 0x00 38 | #define SEGACD_PLAYMODE_LOOP 0x01 39 | 40 | #define SEGACD_STAT_STOPPED 0 41 | #define SEGACD_STAT_PLAYING 1 42 | #define SEGACD_STAT_SCANNING 3 43 | #define SEGACD_STAT_PAUSED 5 44 | #define SEGACD_STAT_SEEKING 8 45 | #define SEGACD_STAT_NODISC 16 46 | #define SEGACD_STAT_READINGTOC 32 47 | #define SEGACD_STAT_OPEN 64 48 | 49 | #define DBCOL(c) VDP_setPaletteColor(0,c) 50 | 51 | #include 52 | 53 | extern volatile uint8_t *segacd_bios_addr; 54 | 55 | int16_t cdaudio_init(void); 56 | 57 | // Playback functions. Returns non-zero when the play command worked. 58 | // Will return zero if there is no disc, or the tray is open. 59 | int16_t cdaudio_play_once(uint16_t trk); 60 | int16_t cdaudio_play_loop(uint16_t trk); 61 | 62 | // Stop playing. If there is nothing playing it is a no-op. 63 | void cdaudio_stop(void); 64 | 65 | // Pause / restart. 66 | void cdaudio_pause(void); 67 | void cdaudio_resume(void); 68 | int16_t cdaudio_is_active(void); 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /mdk/src/md/errors.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Macro forfailing out to an error 4 | #ifdef __ASSEMBLER__ 5 | .macro showerr msgid 6 | movem.l d0-d7/a0-a7, -(sp) 7 | moveq #\msgid, d0 8 | jmp (md_error_dispatch) 9 | .endm 10 | #endif 11 | 12 | 13 | #define MD_ERRMSG_BUS_ERROR 0 14 | #define MD_ERRMSG_ADDRESS_ERROR 1 15 | #define MD_ERRMSG_ILLEGAL_INSTRUCTION 2 16 | #define MD_ERRMSG_DIV_ZERO 3 17 | #define MD_ERRMSG_CHK 4 18 | #define MD_ERRMSG_TRAPV 5 19 | #define MD_ERRMSG_PRIVELEGE 6 20 | #define MD_ERRMSG_TRACE 7 21 | #define MD_ERRMSG_UNUSED_IRQ 8 22 | #define MD_ERRMSG_ALINE_EMU 9 23 | #define MD_ERRMSG_FLINE_EMU 10 24 | #define MD_ERRMSG_RESERVED 11 25 | #define MD_ERRMSG_COPROC_VIOLATION 12 26 | #define MD_ERRMSG_FORMAT 13 27 | #define MD_ERRMSG_UNINIT 14 28 | #define MD_ERRMSG_SPURIOUS 15 29 | #define MD_ERRMSG_TRAP0X0 16 30 | #define MD_ERRMSG_TRAP0X1 17 31 | #define MD_ERRMSG_TRAP0X2 18 32 | #define MD_ERRMSG_TRAP0X3 19 33 | #define MD_ERRMSG_TRAP0X4 20 34 | #define MD_ERRMSG_TRAP0X5 21 35 | #define MD_ERRMSG_TRAP0X6 22 36 | #define MD_ERRMSG_TRAP0X7 23 37 | #define MD_ERRMSG_TRAP0X8 24 38 | #define MD_ERRMSG_TRAP0X9 25 39 | #define MD_ERRMSG_TRAP0XA 26 40 | #define MD_ERRMSG_TRAP0XB 27 41 | #define MD_ERRMSG_TRAP0XC 28 42 | #define MD_ERRMSG_TRAP0XD 29 43 | #define MD_ERRMSG_TRAP0XE 30 44 | #define MD_ERRMSG_TRAP0XF 31 45 | #define MD_ERRMSG_UNIMP 32 46 | #define MD_ERRMSG_IRQ1 33 47 | #define MD_ERRMSG_IRQ2 34 48 | #define MD_ERRMSG_IRQ3 35 49 | #define MD_ERRMSG_IRQ4 36 50 | #define MD_ERRMSG_IRQ5 37 51 | #define MD_ERRMSG_IRQ6 38 52 | #define MD_ERRMSG_IRQ7 39 53 | -------------------------------------------------------------------------------- /mdk/src/util/text.c: -------------------------------------------------------------------------------- 1 | // mdk print utility functions 2 | // Michael Moffitt 2018-2022 3 | #include "util/text.h" 4 | 5 | static uint16_t s_font_vram_pos; 6 | static uint16_t s_font_pal_line; 7 | 8 | void text_init(const unsigned char *font_chr, uint16_t font_len, 9 | uint16_t vram_pos, 10 | const unsigned char *font_pal, uint16_t pal_line) 11 | { 12 | s_font_vram_pos = vram_pos; 13 | s_font_pal_line = pal_line & 0x3; 14 | if (font_chr) 15 | { 16 | md_dma_transfer_vram(s_font_vram_pos, font_chr, font_len/2, 2); 17 | } 18 | s_font_vram_pos /= 32; // convert to tile number 19 | if (font_pal) 20 | { 21 | md_pal_upload(s_font_pal_line * 16, font_pal, 16); 22 | } 23 | } 24 | 25 | void text_puts(VdpPlane plane, uint16_t x, uint16_t y, const char *s) 26 | { 27 | if (plane > VDP_PLANE_WINDOW) 28 | { 29 | return; 30 | } 31 | uint16_t dest_base = md_vdp_get_plane_base(plane); 32 | uint8_t plane_size = md_vdp_get_reg(VDP_PLANESIZE); 33 | uint16_t line_inc = 64; 34 | md_vdp_wait_dma(); 35 | md_vdp_set_autoinc(2); 36 | switch (plane_size) 37 | { 38 | default: 39 | return; 40 | case VDP_PLANESIZE_32x32: 41 | case VDP_PLANESIZE_32x64: 42 | case VDP_PLANESIZE_32x128: 43 | line_inc = 64; 44 | break; 45 | case VDP_PLANESIZE_64x32: 46 | case VDP_PLANESIZE_64x64: 47 | line_inc = 128; 48 | break; 49 | case VDP_PLANESIZE_128x32: 50 | line_inc = 256; 51 | break; 52 | } 53 | 54 | dest_base += x * 2; 55 | dest_base += y * line_inc; 56 | 57 | VDPPORT_CTRL32 = VDP_CTRL_VRAM_WRITE | VDP_CTRL_ADDR(dest_base); 58 | 59 | while (*s) 60 | { 61 | if (*s == '\n') 62 | { 63 | y++; 64 | dest_base = md_vdp_get_plane_base(plane); 65 | dest_base += x * 2; 66 | dest_base += y * line_inc; 67 | 68 | VDPPORT_CTRL32 = VDP_CTRL_VRAM_WRITE | VDP_CTRL_ADDR(dest_base); 69 | } 70 | else 71 | { 72 | VDPPORT_DATA = VDP_ATTR(((s_font_vram_pos + *s) - 0x20), 73 | 0, 0, s_font_pal_line, 1); 74 | } 75 | s++; 76 | } 77 | } 78 | 79 | uint16_t text_get_vram_pos(void) 80 | { 81 | return s_font_vram_pos; 82 | } 83 | -------------------------------------------------------------------------------- /mdk/src/md/megadrive.h: -------------------------------------------------------------------------------- 1 | // mdk Top-level Sega Megadrive support routines and structures 2 | // 2018-2024 Michael Moffitt 3 | // =========================================================================== 4 | #pragma once 5 | 6 | #ifdef __cplusplus 7 | extern "C" 8 | { 9 | #endif // __cplusplus 10 | 11 | #include "md/macro.h" // Helpful macros. 12 | #include "md/vdp.h" // VDP control: all things graphics and some more 13 | #include "md/sys.h" // System control: interrupts, sub-CPU control 14 | #include "md/io.h" // Controller port I/O 15 | #include "md/ioc.h" // System C/C2 I/O 16 | #include "md/spr.h" // Sprite support 17 | #include "md/cspr.h" // Composite sprite functions 18 | #include "md/dma.h" // DMA control and scheduling 19 | #include "md/pal.h" // Palette read/write via ports or DMA 20 | #include "md/opn.h" // YM2610 FM sound chip 21 | #include "md/psg.h" // SN76489-compatible PSG sound chip 22 | #include "md/sram.h" // Support for battery-backed SRAM 23 | #include "md/irq.h" // Interrupt handler registration 24 | #include "md/sysc_vctrl.h" // System C/C2 Video Control 25 | 26 | // Internal use --------------------------------------------------------------- 27 | 28 | // * Initializes VDP with VDP defaults (see vdp.c): 29 | // * Vertical blank interrupt enabled 30 | // * 320 x 224 (40x28 cell) video mode 31 | // * Clear VRAM 32 | // * Display enabled 33 | // * DMA enabled 34 | // * HV counter enabled 35 | // * VRAM bases: SCRL A SCRL B WINDOW SPRITES H-SCROLL 36 | // 0xB000 0xD000 0xF000 0xFC00 0xFA00 37 | // * Enables Plane Scrolling 38 | // * Sets Palette 0 Color 0 for the backdrop / border 39 | // * VRAM auto-inc set to 2 40 | // * 64 x 32 cell plane size 41 | // * Clears sprites 42 | // * Configures IO ports for gamepad reads 43 | // * Enables interrupts 44 | void megadrive_init(void); 45 | 46 | // Run after completing the logic in one game tick loop. 47 | void megadrive_finish(void); 48 | 49 | #ifdef __cplusplus 50 | } 51 | #endif // __cplusplus 52 | -------------------------------------------------------------------------------- /samples/audio-simple-psg/src/main.c: -------------------------------------------------------------------------------- 1 | // md-toolchain example "audio-simple-psg" main.c 2 | // Damian Yerrick 2019 3 | // Michael Moffitt 2024 4 | // 5 | // This demo plays two chords through the programmable sound 6 | // generator (PSG) that the MD inherits from the Master System. 7 | #include "md/megadrive.h" 8 | 9 | #include "util/text.h" 10 | #include "util/plane.h" 11 | 12 | #include "res.h" 13 | 14 | struct Chord 15 | { 16 | unsigned short period[3]; 17 | unsigned short duration; 18 | }; 19 | 20 | // Notes about tuning: 21 | // NTSC defines the color burst frequency as 35/44 of the 4.5 MHz 22 | // audio carrier offset, that is, 315/88 MHz or about 3.58 MHz. 23 | // The PSG divides this by 32 to form the basic frequency for its 24 | // three square waves. 25 | // 315/88 MHz / 32 = 111860.8 Hz 26 | // Thus the following hold: 27 | // period_value = 111860.8/frequency 28 | // frequency = 111860.8/period_value 29 | 30 | static const struct Chord s_sega_chords[] = 31 | { 32 | // E flat, B flat, G, half a second 33 | {{(111860.8/155.5635), (111860.8/233.0819), (111860.8/391.9954)}, 50}, 34 | // C, G, E, one second 35 | {{(111860.8/130.8128), (111860.8/195.9977), (111860.8/329.6276)}, 60}, 36 | {{0, 0, 0}, 0} 37 | }; 38 | 39 | void main(void) 40 | { 41 | megadrive_init(); 42 | text_init(res_font_gfx_bin, 3072, 0x400, res_font_pal_bin, 0); 43 | text_puts(VDP_PLANE_A, 15, 11, "PSG Chords"); 44 | 45 | // Play a sequence of chords. 46 | for (const struct Chord *playpos = s_sega_chords; 47 | playpos->duration > 0; 48 | playpos++) 49 | { 50 | // Set pitch 51 | for (unsigned int ch = 0; ch < 3; ch++) 52 | { 53 | md_psg_pitch(ch, playpos->period[ch]); 54 | } 55 | 56 | // Fade volume 57 | for (unsigned int t = 0; t < playpos->duration; t++) 58 | { 59 | for (unsigned int ch = 0; ch < 3; ch++) 60 | { 61 | md_psg_vol(ch, t >> 2); 62 | } 63 | megadrive_finish(); // Wait for next frame 64 | } 65 | } 66 | 67 | // Silence all channels with max attenuation (15). 68 | for (unsigned int ch = 0; ch < 3; ch++) md_psg_vol(ch, 15); 69 | 70 | while (1) 71 | { 72 | megadrive_finish(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /mdk/src/md/cspr.c: -------------------------------------------------------------------------------- 1 | #include "md/cspr.h" 2 | #include "md/spr.h" 3 | 4 | #include "md/dma.h" 5 | #include "md/pal.h" 6 | 7 | typedef struct CSprRef 8 | { 9 | uint16_t spr_count; 10 | uint16_t spr_list_offset; // Added to spr_list_offset. 11 | uint16_t tile_src_offset; // Added to tile_data_offset. 12 | uint16_t tile_words; 13 | } CSprRef; 14 | 15 | typedef struct CSprSprite 16 | { 17 | int16_t dy; 18 | uint16_t size; 19 | uint16_t tile; 20 | int16_t dx; 21 | int16_t flip_dy; 22 | uint16_t reserved[2]; 23 | int16_t flip_dx; 24 | } CSprSprite; 25 | 26 | #define MD_CSPR_NAME 0x00 27 | #define MD_CSPR_PAL 0x10 28 | #define MD_CSPR_REF_COUNT 0x30 29 | #define MD_CSPR_TILE_DATA_OFFSET 0x36 30 | #define MD_CSPR_TILE_COUNT 0x3A 31 | 32 | // Gets the quantity of animation frames defined in cspr_data. 33 | uint16_t md_cspr_get_frame_count(const void *cspr_data) 34 | { 35 | const uint8_t *cspr = (const uint8_t *)cspr_data; 36 | const uint16_t ref_count = *(uint16_t *)(&cspr[MD_CSPR_REF_COUNT]); 37 | return ref_count; 38 | } 39 | 40 | // Loads all tile data from cspr_data to VRAM, for non-DMA usage. 41 | void md_cspr_upload_tiles(const void *cspr_data, uint16_t vram_addr) 42 | { 43 | const uint8_t *cspr = (const uint8_t *)cspr_data; 44 | const uint32_t tile_data_offset = *(uint32_t *)(&cspr[MD_CSPR_TILE_DATA_OFFSET]); 45 | const uint8_t *tile_data = cspr + tile_data_offset; 46 | const uint16_t tile_count = *(uint16_t *)(&cspr[MD_CSPR_TILE_COUNT]); 47 | if (tile_count == 0) return; 48 | md_dma_transfer_vram(vram_addr, tile_data, tile_count * 16, 2); 49 | } 50 | 51 | // Loads the palette from cspr_data to a palette line. 52 | void md_cspr_upload_pal(const void *cspr_data, uint16_t pal_line) 53 | { 54 | const uint8_t *cspr = (const uint8_t *)cspr_data; 55 | const uint16_t *pal_data = (const uint16_t *)(&cspr[MD_CSPR_PAL]); 56 | md_pal_upload(pal_line * 16, pal_data, 16); 57 | } 58 | 59 | // Returns a pointer to a C-string of the name of the composite sprite. 60 | const char *md_cspr_get_name(const void *cspr_data) 61 | { 62 | const uint8_t *cspr = (const uint8_t *)cspr_data; 63 | return (const char *)(&cspr[MD_CSPR_NAME]); 64 | } 65 | 66 | 67 | -------------------------------------------------------------------------------- /mdk/util/core/bin2arr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static const int MAX_ITEMS_PER_LINE = 16; 7 | 8 | int main(int argc, char **argv) 9 | { 10 | int max_items_per_line = MAX_ITEMS_PER_LINE; 11 | size_t bytes_written = 0; 12 | int out_count_per_line = 0; 13 | char out_fpath[512]; 14 | char *symbol_name; 15 | FILE *fin, *fout; 16 | if (argc < 3) 17 | { 18 | fprintf(stderr, "Usage:\n%s " 19 | "[items per line]\n", argv[0]); 20 | return -1; 21 | } 22 | 23 | if (argc > 3) 24 | { 25 | max_items_per_line = atoi(argv[3]); 26 | if (max_items_per_line < 1) 27 | { 28 | fprintf(stderr, "Warning: Invalid value \"%s\" provided for \ 29 | max items per line. Defaulting to %d.\n", 30 | argv[3], MAX_ITEMS_PER_LINE); 31 | max_items_per_line = MAX_ITEMS_PER_LINE; 32 | } 33 | } 34 | 35 | symbol_name = argv[2]; 36 | 37 | fin = fopen(argv[1], "rb"); 38 | if (!fin) 39 | { 40 | fprintf(stderr, "Couldn't open %s for reading.\n", argv[1]); 41 | return -1; 42 | } 43 | 44 | snprintf(out_fpath, sizeof(out_fpath) - 1, "%s.c", symbol_name); 45 | out_fpath[sizeof(out_fpath) - 1] = 0; 46 | fout = fopen(out_fpath, "w"); 47 | if (!fout) 48 | { 49 | fprintf(stderr, "Couldn't open %s for writing.\n", out_fpath); 50 | fclose(fin); 51 | return -1; 52 | } 53 | 54 | // Write const array to C file 55 | fseek(fin, 0L, SEEK_END); 56 | const size_t binsize = ftell(fin); 57 | fseek(fin, 0L, SEEK_SET); 58 | fprintf(fout, "#include \nconst uint8_t %s[%d] =\n{", 59 | symbol_name, (int)binsize); 60 | 61 | while (!feof(fin)) 62 | { 63 | if (out_count_per_line == 0) 64 | { 65 | fprintf(fout, "\n"); 66 | out_count_per_line = max_items_per_line; 67 | } 68 | 69 | out_count_per_line--; 70 | 71 | int fetch = fgetc(fin); 72 | if (fetch == EOF) 73 | { 74 | break; 75 | } 76 | if (out_count_per_line >= max_items_per_line - 1) 77 | { 78 | fprintf(fout, "\t"); 79 | } 80 | fprintf(fout, "0x%02X,", fetch); 81 | bytes_written++; 82 | } 83 | fprintf(fout, "\n};\n\n"); 84 | fclose(fout); 85 | fclose(fin); 86 | 87 | return 0; 88 | } 89 | -------------------------------------------------------------------------------- /samples/cpp-direct-sprite-demo/src/bg_gradient.cpp: -------------------------------------------------------------------------------- 1 | #include "bg_gradient.h" 2 | #include "md/megadrive.h" 3 | 4 | namespace 5 | { 6 | static constexpr uint16_t kColorTable[] = 7 | { 8 | PALRGB(0, 0, 4), 9 | PALRGB(0, 0, 3), 10 | PALRGB(0, 0, 2), 11 | PALRGB(0, 0, 1), 12 | PALRGB(1, 0, 0), 13 | PALRGB(2, 0, 0), 14 | PALRGB(3, 0, 0), 15 | PALRGB(4, 0, 0), 16 | PALRGB(5, 0, 0), 17 | PALRGB(6, 0, 0), 18 | PALRGB(7, 1, 0), 19 | PALRGB(7, 2, 0), 20 | PALRGB(7, 3, 0), 21 | PALRGB(7, 4, 0), 22 | PALRGB(7, 5, 0), 23 | PALRGB(7, 6, 0), 24 | PALRGB(7, 7, 1), 25 | PALRGB(7, 7, 2), 26 | PALRGB(7, 7, 4), 27 | PALRGB(7, 7, 5), 28 | PALRGB(7, 7, 6), 29 | PALRGB(7, 7, 7), 30 | PALRGB(6, 7, 7), 31 | PALRGB(5, 7, 7), 32 | PALRGB(4, 7, 7), 33 | PALRGB(3, 7, 7), 34 | PALRGB(2, 7, 6), 35 | PALRGB(1, 7, 5), 36 | PALRGB(0, 7, 4), 37 | PALRGB(0, 7, 3), 38 | PALRGB(0, 6, 2), 39 | PALRGB(0, 5, 1), 40 | PALRGB(0, 4, 0), 41 | PALRGB(0, 3, 0), 42 | PALRGB(0, 2, 0), 43 | PALRGB(0, 1, 0), 44 | PALRGB(0, 0, 0), 45 | }; 46 | 47 | static uint16_t s_color_index; 48 | } // namespace 49 | 50 | void bg_gradient_on_hbl(void) 51 | { 52 | if (s_color_index >= MDK_ARRAYSIZE(kColorTable)) s_color_index--; 53 | VDPPORT_CTRL32 = VDP_CTRL_CRAM_WRITE | VDP_CTRL_ADDR(0); 54 | // This ugly delay was tuned to move the CRAM write into HBlank. 55 | asm("nop"); 56 | asm("nop"); 57 | asm("nop"); 58 | asm("nop"); 59 | asm("nop"); 60 | asm("nop"); 61 | asm("nop"); 62 | asm("nop"); 63 | asm("nop"); 64 | asm("nop"); 65 | asm("nop"); 66 | asm("nop"); 67 | asm("nop"); 68 | asm("nop"); 69 | asm("nop"); 70 | asm("nop"); 71 | asm("nop"); 72 | asm("nop"); 73 | asm("nop"); 74 | asm("nop"); 75 | asm("nop"); 76 | asm("nop"); 77 | asm("nop"); 78 | asm("nop"); 79 | asm("nop"); 80 | asm("nop"); 81 | VDPPORT_DATA = kColorTable[s_color_index++]; 82 | 83 | } 84 | 85 | void bg_gradient_on_vbl(void) 86 | { 87 | s_color_index = 0; 88 | bg_gradient_on_hbl(); 89 | } 90 | 91 | void bg_gradient_init() 92 | { 93 | s_color_index = 0; 94 | 95 | md_irq_register(MD_IRQ_VBLANK, bg_gradient_on_vbl); 96 | md_irq_register(MD_IRQ_HBLANK, bg_gradient_on_hbl); 97 | 98 | md_vdp_set_hint_line(5); 99 | md_vdp_set_hint_en(1); 100 | } 101 | -------------------------------------------------------------------------------- /mdk/src/header.inc: -------------------------------------------------------------------------------- 1 | /* Sega Megadrive / Genesis header 2 | 3 | The header provides metadata to the system and some emulators. At minimum, it 4 | must begin with "SEGA" for TMSS-equipped systems to run it. In addition, at 5 | times the extra memory data is used to determine whether or not save data 6 | will be stored. 7 | 8 | Do be sure that the fields remain at their correct length. Extraneous 9 | characters will be truncated. A legend has been placed placed above every 10 | .ascii statement to give a visual reference for the correct length. */ 11 | 12 | /* Console name - must begin with SEGA.*/ 13 | .org 0x000100 14 | /* 16: ________________ */ 15 | .ascii "SEGA MEGA DRIVE " 16 | /* Copyright / author information. Sixteen characters. */ 17 | .org 0x000110 18 | /* 16: ________________ */ 19 | .ascii " " 20 | 21 | /* Game name (Japanese/Domestic). 48 characters. */ 22 | .org 0x000120 23 | /* 48: ________________________________________________ */ 24 | .ascii " " 25 | 26 | /* Game name (Overseas/World). 48 characters. */ 27 | .org 0x000150 28 | /* 48: ________________________________________________ */ 29 | .ascii " " 30 | 31 | /* Game serial number. Sixteen characters. */ 32 | .org 0x000180 33 | /* Serial number */ 34 | /* 16: _______________*/ 35 | .ascii "GM 00000000-00" 36 | 37 | /* Checksum (2 bytes) */ 38 | .org 0x00018E 39 | .short 0x0000 40 | 41 | /* I/O Support */ 42 | /* 16: ________________ */ 43 | .org 0x000190 44 | .ascii "JD " 45 | 46 | /* ROM start and end */ 47 | .org 0x0001A0 48 | .long 0 49 | .long 0x001FFFFF 50 | 51 | /* Work RAM start and end */ 52 | .org 0x0001A8 53 | .long 0x000FF000 54 | .long 0x000FFFFF 55 | 56 | /* Backup memory */ 57 | .org 0x0001B0 58 | .ascii "RA" 59 | .byte 0xF8 60 | .byte 0x20 61 | 62 | /* Backup RAM start and end*/ 63 | .long 0x200001 64 | .long 0x20FFFF 65 | 66 | /* Modem */ 67 | .org 0x0001BC 68 | /* 12: ____________ */ 69 | .ascii " " 70 | 71 | /* Reserved */ 72 | .org 0x0001C8 73 | /* 40: ________________________________________ */ 74 | .ascii " " 75 | 76 | /* Country codes */ 77 | .org 0x0001F0 78 | .ascii "JUE" 79 | 80 | /* Reserved */ 81 | .org 0x0001F3 82 | /* 13: _____________ */ 83 | .ascii " " 84 | 85 | -------------------------------------------------------------------------------- /mdk/src/md/dma.h: -------------------------------------------------------------------------------- 1 | /* mdk DMA control functions 2 | Michael Moffitt 2018-2021 3 | 4 | For large lengths of data, like character graphics, mappings, scroll tables, 5 | and palettes, a DMA is the best way to manipulate the VDP's VRAM. DMA 6 | operations are fastest during the vertical vblanking interval (vblank), or 7 | when the display has been disabled (see vdp_set_display_en). 8 | 9 | A DMA operation may be scheduled at any time during active display, and placed 10 | in a queue of pending operations, using the following functions: 11 | 12 | md_dma_transfer_vram md_dma_transfer_cram md_dma_transfer_vsram 13 | md_dma_fill_vram 14 | md_dma_copy_vram 15 | 16 | At the start of every vblank, md_dma_process() should be called, which will 17 | execute all scheduled DMA operations. DMA transfer operations hog the main CPU 18 | bus, while fill and copy operations do not. Try to ensure that your queued VRAM 19 | transfer operations complete within the blanking period. 20 | 21 | */ 22 | #pragma once 23 | 24 | #ifdef __cplusplus 25 | extern "C" 26 | { 27 | #endif // __cplusplus 28 | 29 | #include "md/vdp.h" 30 | 31 | void md_dma_init(void); 32 | 33 | // Schedule a DMA for next vblank from 68K mem to VRAM 34 | void md_dma_transfer_vram(uint16_t dest, const void *src, uint16_t words, 35 | uint16_t stride); 36 | void md_dma_transfer_cram(uint16_t dest, const void *src, uint16_t words, 37 | uint16_t stride); 38 | void md_dma_transfer_vsram(uint16_t dest, const void *src, uint16_t words, 39 | uint16_t stride); 40 | // Special high-priority DMA for sprite tables. Schedules transfers that will 41 | // run before any others. 42 | void md_dma_transfer_spr_vram(uint16_t dest, const void *src, uint16_t words, 43 | uint16_t stride); 44 | 45 | // Schedule a DMA for next vblank to fill n words at dest with val. 46 | void md_dma_fill_vram(uint16_t dest, uint16_t val, uint16_t bytes, uint16_t stride); 47 | 48 | // Schedule a DMA for next vblank to copy n words from VRAM src to VRAM dest. 49 | void md_dma_copy_vram(uint16_t dest, uint16_t src, uint16_t bytes, uint16_t stride); 50 | 51 | // Internal Use --------------------------------------------------------------- 52 | 53 | // Process any queued DMA requests. 54 | void md_dma_process(void); 55 | 56 | #ifdef __cplusplus 57 | } 58 | #endif // __cplusplus 59 | -------------------------------------------------------------------------------- /mdk/util/png2csp/src/util.c: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | 3 | #include 4 | #include 5 | 6 | void origin_for_sp(ConvOrigin origin, int sw, int sh, int *ox, int *oy) 7 | { 8 | switch (origin) 9 | { 10 | case CONV_ORIGIN_LEFT_TOP: *oy = 0; *ox = 0; break; 11 | case CONV_ORIGIN_CENTER_TOP: *oy = 0; *ox = sw / 2; break; 12 | case CONV_ORIGIN_RIGHT_TOP: *oy = 0; *ox = sw - 1; break; 13 | case CONV_ORIGIN_LEFT_CENTER: *oy = sh / 2; *ox = 0; break; 14 | case CONV_ORIGIN_CENTER_CENTER: *oy = sh / 2; *ox = sw / 2; break; 15 | case CONV_ORIGIN_RIGHT_CENTER: *oy = sh / 2; *ox = sw - 1; break; 16 | case CONV_ORIGIN_LEFT_BOTTOM: *oy = sh - 1; *ox = 0; break; 17 | case CONV_ORIGIN_CENTER_BOTTOM: *oy = sh - 1; *ox = sw / 2; break; 18 | case CONV_ORIGIN_RIGHT_BOTTOM: *oy = sh - 1; *ox = sw - 1; break; 19 | } 20 | } 21 | 22 | void render_region(const uint8_t *imgdat, int iw, int ih, 23 | int sx, int sy, int sw, int sh) 24 | { 25 | static const char k_hex[0x10] = 26 | { 27 | '0', '1', '2', '3', 28 | '4', '5', '6', '7', 29 | '8', '9', 'A', 'B', 30 | 'C', 'D', 'E', 'F' 31 | }; 32 | 33 | puts("\n"); 34 | for (int y = sy; y < sy + sh; y++) 35 | { 36 | for (int x = sx; x < sx + sw; x++) 37 | { 38 | const uint8_t px = imgdat[x + (y * iw)] & 0xF; 39 | const bool grid = ((x % 8) == 0 || (y % 8) == 0); 40 | const char transparent = (grid) ? '.' : ' '; 41 | printf("%c ", px ? k_hex[px] : transparent); 42 | } 43 | puts("|\n"); 44 | } 45 | puts("\n"); 46 | } 47 | 48 | 49 | void clip_8x8_tile(uint8_t *imgdat, int iw, int sx, int sy, 50 | int limx, int limy, uint8_t *out) 51 | { 52 | // 8x8 tile, row by row. 53 | for (int y = 0; y < 8; y++) 54 | { 55 | // Source eight pixels from imgdat. 56 | const int source_y = sy + y; 57 | uint8_t *line = &imgdat[sx + (source_y * iw)]; 58 | // Walk through two bytes at a time, as the destination data is 4bpp and 59 | // packs two pixels into one byte. 60 | for (int x = 0; x < 8; x += 2) 61 | { 62 | const int source_x = sx + x; 63 | uint8_t px[2] = {0, 0}; 64 | // Don't take pixels that exceed boundaries of the sprite clipping 65 | // region nor the source image data. 66 | if (source_y < limy) 67 | { 68 | if (source_x < limx) 69 | { 70 | px[0] = (line[x] & 0xF); 71 | line[x] = 0; 72 | } 73 | if (source_x + 1 < limx) 74 | { 75 | px[1] = (line[x + 1] & 0xF); 76 | line[x + 1] = 0; 77 | } 78 | } 79 | // Write the two pixels as a byte. 80 | *out++ = (px[0] << 4) | (px[1]); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /samples/controller/src/main.c: -------------------------------------------------------------------------------- 1 | // Michael Moffitt 2018-2022 2 | // 3 | // This main shows a simple "hello world" demo with some button interaction. 4 | #include "md/megadrive.h" 5 | 6 | #include "util/text.h" 7 | #include "util/plane.h" 8 | 9 | #include "res.h" 10 | 11 | void draw_inputs(void) 12 | { 13 | for (uint16_t j = 0; j < MDK_ARRAYSIZE(g_md_pad); j++) 14 | { 15 | uint16_t plot_x = 12 * 8; 16 | const uint16_t plot_y = (12 + j) * 8; 17 | 18 | // Player number indicator. 19 | md_spr_put(plot_x, plot_y, SPR_ATTR((j == 0 ? '1' : '2'), 0, 0, 0, 0), 20 | SPR_SIZE(1, 1)); 21 | plot_x += 16; 22 | 23 | // If the pad isn't plugged in, indicate as such and continue. 24 | if (g_md_pad[j] & MD_PAD_UNPLUGGED) 25 | { 26 | const char msg[] = "No Controller"; 27 | for (uint16_t i = 0; i < sizeof(msg); i++) 28 | { 29 | md_spr_put(plot_x, plot_y, SPR_ATTR(msg[i], 0, 0, 0, 0), SPR_SIZE(1, 1)); 30 | plot_x += 8; 31 | } 32 | continue; 33 | } 34 | 35 | // The button_meta struct is used to associate button masks (e.g. 36 | // BTN_A) with a char (e.g. 'a'). Each button is checked in a loop. 37 | // If the corresponding button is pressed, the character is printed. 38 | struct ButtonMeta 39 | { 40 | uint16_t mask; 41 | char chara; 42 | } button_meta[] = 43 | { 44 | {BTN_UP, 'u'}, 45 | {BTN_DOWN, 'd'}, 46 | {BTN_LEFT, 'l'}, 47 | {BTN_RIGHT, 'r'}, 48 | {BTN_A, 'a'}, 49 | {BTN_B, 'b'}, 50 | {BTN_C, 'c'}, 51 | {BTN_X, 'x'}, 52 | {BTN_Y, 'y'}, 53 | {BTN_Z, 'z'}, 54 | {BTN_START, 's'}, 55 | {BTN_MODE, 'm'}, 56 | }; 57 | 58 | for (uint16_t i = 0; i < MDK_ARRAYSIZE(button_meta); i++) 59 | { 60 | char chara = (g_md_pad[j] & button_meta[i].mask) ? 61 | button_meta[i].chara : '.'; 62 | // Make letters uppercase if they are a positive edge. 63 | if (g_md_pad_pos[j] & button_meta[i].mask) chara &= ~0x20; 64 | md_spr_put(plot_x, plot_y, SPR_ATTR(chara, 0, 0, 0, 0), SPR_SIZE(1, 1)); 65 | plot_x += 8; 66 | } 67 | } 68 | } 69 | 70 | void main(void) 71 | { 72 | // Get the megadrive ready to go! (See md/megadrive.h) 73 | megadrive_init(); 74 | 75 | // Set up text graphics at VRAM address 0x400 palette 0 76 | // This lines it up nicely with the actual ASCII values, which 77 | // we will use to draw letters with sprites. 78 | text_init(res_gfx_font_bin, sizeof(res_gfx_font_bin), 0x400, res_pal_font_bin, 0); 79 | 80 | // Print a simple message in the center of plane A 81 | text_puts(VDP_PLANE_A, 10, 8, "Input Demonstration"); 82 | 83 | while (1) 84 | { 85 | draw_inputs(); 86 | 87 | megadrive_finish(); // Terminate the sprite list and wait for vblank 88 | // Controller polling and DMA queue process is handled in VBL ISR 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /samples/mdsdrv-usage/src/main.c: -------------------------------------------------------------------------------- 1 | // mdk MDSDRV integration example. 2 | // Michael Moffitt 2022 3 | // This sample project illustrates integration of the MDSDRV sound driver. 4 | // Sample music is property of the respective creators. 5 | // https://github.com/superctr/MDSDRV 6 | 7 | #include "md/megadrive.h" 8 | 9 | #include "util/text.h" 10 | #include "util/plane.h" 11 | 12 | #include "mdsdrv/mdsdrv.h" 13 | #include "res.h" 14 | 15 | static void vbl_callback(void) 16 | { 17 | mds_update(); 18 | } 19 | 20 | void main(void) 21 | { 22 | megadrive_init(); 23 | 24 | // MDS engine initialization. 25 | mds_init(res_mdsseq_bin, res_mdspcm_bin); 26 | md_irq_register(MD_IRQ_VBLANK, vbl_callback); 27 | 28 | text_init(res_font_gfx_bin, sizeof(res_font_gfx_bin), 0x400, res_font_pal_bin, 0); 29 | 30 | // Version string and button legend. 31 | int text_y = 1; 32 | text_puts(VDP_PLANE_A, 1, text_y++, mds_get_version_str()); 33 | text_y++; 34 | text_puts(VDP_PLANE_A, 1, text_y++, "C - Play"); 35 | text_puts(VDP_PLANE_A, 1, text_y++, "B - Pause"); 36 | text_puts(VDP_PLANE_A, 1, text_y++, "A - Stop"); 37 | text_y++; 38 | 39 | MdsSlot sfx_slot = MDS_SLOT_SE1; 40 | uint8_t track_id = 0; 41 | uint8_t paused = 0; 42 | 43 | // Play a sample track by default. 44 | mds_request(MDS_SLOT_BGM, 6); 45 | 46 | while (1) 47 | { 48 | // Accept inputs to control the driver. 49 | if (g_md_pad_pos[0] & BTN_UP) 50 | { 51 | track_id--; 52 | } 53 | else if (g_md_pad_pos[0] & BTN_DOWN) 54 | { 55 | track_id++; 56 | } 57 | else if (g_md_pad_pos[0] & BTN_LEFT) 58 | { 59 | track_id -= 0x10; 60 | } 61 | else if (g_md_pad_pos[0] & BTN_RIGHT) 62 | { 63 | track_id += 0x10; 64 | } 65 | else if (g_md_pad_pos[0] & BTN_C) 66 | { 67 | if (track_id <= 6) 68 | { 69 | mds_request(MDS_SLOT_BGM, track_id); 70 | } 71 | else 72 | { 73 | mds_request(sfx_slot, track_id); 74 | sfx_slot++; 75 | if (sfx_slot > MDS_SLOT_SE3) sfx_slot = MDS_SLOT_SE1; 76 | } 77 | } 78 | else if (g_md_pad_pos[0] & BTN_B) 79 | { 80 | paused = !paused; 81 | mds_pause(MDS_SLOT_BGM, paused); 82 | } 83 | else if (g_md_pad_pos[0] & BTN_A) 84 | { 85 | mds_request(MDS_SLOT_BGM, 0); 86 | } 87 | 88 | // Print the track number. 89 | text_puts(VDP_PLANE_A, 1, text_y, "Track $"); 90 | char hex_str[3]; 91 | for (int16_t i = 0; i < 2; i++) 92 | { 93 | const uint8_t nybble = (i == 0) ? 94 | ((track_id & 0xF0) >> 4) : 95 | (track_id & 0x0F); 96 | if (nybble >= 0x0A) hex_str[i] = 'A' + (nybble - 0xA); 97 | else hex_str[i] = '0' + nybble; 98 | } 99 | hex_str[2] = '\0'; 100 | text_puts(VDP_PLANE_A, 8, text_y, hex_str); 101 | 102 | // Finished with the frame. 103 | megadrive_finish(); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /mdk/src/util/kosinski.a68: -------------------------------------------------------------------------------- 1 | .section .text 2 | .global kosinski_decomp 3 | 4 | ; Courtesy of https://segaretro.org/Kosinski_compression 5 | 6 | kosinski_decomp: 7 | .set SPID, 4 8 | .set ARG_SRC, SPID 9 | .set ARG_DST, SPID+(4*1) 10 | movea.l ARG_SRC(sp), a0 11 | movea.l ARG_DST(sp), a1 12 | movem.l d2-d6, -(sp) 13 | 14 | kos_decomp: 15 | subq.l #2, sp ; Two bytes on the stack 16 | move.b (a0)+, 1(sp) 17 | move.b (a0)+, (sp) 18 | move.w (sp), d5 ; Copy first desc field 19 | moveq #$F, d4 ; 16 bits in a byte 20 | 21 | kos_decomp_loop: 22 | lsr.w #1, d5 ; Bit shifted out goes into C flag 23 | move.w sr, d6 24 | dbf d4, kos_decomp_chkbit 25 | move.b (a0)+, 1(sp) 26 | move.b (a0)+, (sp) 27 | move.w (sp), d5 ; Get next desc field if needed 28 | moveq #$F, d4 ; Reset bit counter 29 | 30 | kos_decomp_chkbit: 31 | move.w d6, ccr ; was the bit set? 32 | bcc.s kos_decomp_match ; if not, branch (C flag = bit clear 33 | move.b (a0)+, (a1)+ ; otherwise, copy byte as-is 34 | bra.s kos_decomp_loop 35 | 36 | ; ------------------------------------------------------------------------------ 37 | 38 | kos_decomp_match: 39 | moveq #0, d3 40 | lsr.w #1, d5 ; get next bit 41 | move.w sr, d6 42 | dbf d4, kos_decomp_chkbit2 43 | move.b (a0)+, 1(sp) 44 | move.b (a0)+, (sp) 45 | move.w (sp), d5 46 | moveq #$F, d4 47 | 48 | kos_decomp_chkbit2: 49 | move.w d6, ccr ; bit set? 50 | bcs kos_decomp_fullmatch ; if it was, branch 51 | lsr.w #1, d5 ; bit shifted out goes to X flag 52 | 53 | dbf d4, 1f 54 | move.b (a0)+, 1(sp) 55 | move.b (a0)+, (sp) 56 | move.w (sp), d5 57 | moveq #$F, d4 58 | 1: 59 | roxl.w #1, d3 ; get high repeat count bit (shift x flag in) 60 | lsr.w #1, d5 61 | 62 | dbf d4, 2f 63 | move.b (a0)+, 1(sp) 64 | move.b (a0)+, (sp) 65 | move.w (sp), d5 66 | moveq #$F, d4 67 | 2: 68 | roxl.w #1, d3 ; get low repeat count bit 69 | addq.w #1, d3 ; increment repeat count 70 | moveq #-1, d2 71 | move.b (a0)+, d2 ; calculate offset 72 | bra.s kos_decomp_matchloop 73 | 74 | kos_decomp_fullmatch: 75 | move.b (a0)+, d0 ; first byte 76 | move.b (a0)+, d1 ; second byte 77 | moveq #-1, d2 78 | move.b d1, d2 79 | lsl.w #5, d2 80 | move.b d0, d2 ; calculate offset 81 | andi.w #7, d1 ; third byte needed? 82 | beq.s kos_decomp_fullmatch2 ; if so, branch 83 | move.b d1, d3 ; copy repeat count 84 | addq.w #1, d3 ; and increment it 85 | 86 | kos_decomp_matchloop: 87 | move.b (a1, d2.w), d0 88 | move.b d0, (a1)+ ; copy appropriate byte 89 | dbf d3, kos_decomp_matchloop ; and repeat the copy 90 | bra.w kos_decomp_loop 91 | 92 | kos_decomp_fullmatch2: 93 | move.b (a0)+, d1 94 | beq.s kos_decomp_done ; 0 indicates end of stream 95 | cmpi.b #1, d1 96 | beq.w kos_decomp_loop ; 1 indicates new desc needed 97 | move.b d1, d3 ; else, copy repeat count 98 | bra.s kos_decomp_matchloop 99 | kos_decomp_done: 100 | addq.l #2, sp ; restore from two scratch bytes 101 | 102 | movem.l (sp)+, d2-d6 103 | rts 104 | -------------------------------------------------------------------------------- /mdk/src/md/psg.h: -------------------------------------------------------------------------------- 1 | // mdk PSG audio support 2 | // Michael Moffitt 2018-2024 3 | #pragma once 4 | 5 | #ifdef __cplusplus 6 | extern "C" 7 | { 8 | #endif // __cplusplus 9 | 10 | #include "md/mmio.h" 11 | 12 | // TODO: PSG noise control 13 | 14 | // Base note frequencies - tuned to 4th octave 15 | #define PSG_BASE_C 0x1AC 16 | #define PSG_BASE_Db 0x194 17 | #define PSG_BASE_D 0x17D 18 | #define PSG_BASE_Eb 0x168 19 | #define PSG_BASE_E 0x153 20 | #define PSG_BASE_F 0x140 21 | #define PSG_BASE_Gb 0x12E 22 | #define PSG_BASE_G 0x11D 23 | #define PSG_BASE_Ab 0x10D 24 | #define PSG_BASE_A 0x0FE 25 | #define PSG_BASE_Bb 0x0F0 26 | #define PSG_BASE_B 0x0E2 27 | 28 | #define PSG_NOTE_Db 1 29 | #define PSG_NOTE_D 2 30 | #define PSG_NOTE_Eb 3 31 | #define PSG_NOTE_E 4 32 | #define PSG_NOTE_F 5 33 | #define PSG_NOTE_Gb 6 34 | #define PSG_NOTE_G 7 35 | #define PSG_NOTE_Ab 8 36 | #define PSG_NOTE_A 9 37 | #define PSG_NOTE_Bb 10 38 | #define PSG_NOTE_B 11 39 | #define PSG_NOTE_C 12 40 | 41 | // Megadrive PSG functions 42 | static inline void md_psg_vol(uint8_t chan, uint8_t vol); 43 | static inline void md_psg_pitch(uint8_t chan, uint16_t pitch); 44 | static inline void md_psg_tone(uint8_t chan, uint8_t vol, uint16_t pitch); 45 | static inline void md_psg_note(uint8_t chan, uint8_t note, uint8_t octave); 46 | 47 | #define PSG_CHAN(ch) ((chan & 0x03) << 5) 48 | 49 | static inline void md_psg_vol(uint8_t chan, uint8_t vol) 50 | { 51 | PSG_PORT = 0x90 | PSG_CHAN(chan) | (vol & 0x0F); 52 | } 53 | 54 | static inline void md_psg_pitch(uint8_t chan, uint16_t pitch) 55 | { 56 | PSG_PORT = 0x80 | PSG_CHAN(chan) | (pitch & 0x0F); 57 | PSG_PORT = (pitch >> 4) & 0x3F; 58 | } 59 | 60 | #undef PSG_CHAN 61 | 62 | static inline void md_psg_tone(uint8_t chan, uint8_t vol, uint16_t pitch) 63 | { 64 | md_psg_pitch(chan,pitch); 65 | md_psg_vol(chan,vol); 66 | } 67 | 68 | static inline uint32_t note_lookup(uint8_t note) 69 | { 70 | switch (note) 71 | { 72 | case PSG_NOTE_C: 73 | return PSG_BASE_C; 74 | case PSG_NOTE_Db: 75 | return PSG_BASE_Db; 76 | case PSG_NOTE_D: 77 | return PSG_BASE_D; 78 | case PSG_NOTE_Eb: 79 | return PSG_BASE_Eb; 80 | case PSG_NOTE_E: 81 | return PSG_BASE_E; 82 | case PSG_NOTE_F: 83 | return PSG_BASE_F; 84 | case PSG_NOTE_Gb: 85 | return PSG_BASE_Gb; 86 | case PSG_NOTE_G: 87 | return PSG_BASE_G; 88 | case PSG_NOTE_Ab: 89 | return PSG_BASE_Ab; 90 | case PSG_NOTE_A: 91 | return PSG_BASE_A; 92 | case PSG_NOTE_Bb: 93 | return PSG_BASE_Bb; 94 | case PSG_NOTE_B: 95 | return PSG_BASE_B; 96 | } 97 | return PSG_BASE_C; 98 | } 99 | 100 | static inline void md_psg_note(uint8_t chan, uint8_t note, uint8_t octave) 101 | { 102 | uint32_t base = note_lookup(note); 103 | base = base << 1; 104 | base = base >> octave; 105 | md_psg_pitch(chan,base); 106 | } 107 | 108 | #ifdef __cplusplus 109 | } 110 | #endif // __cplusplus 111 | -------------------------------------------------------------------------------- /mdk/util/image/pngto/indexedimage.h: -------------------------------------------------------------------------------- 1 | /* 2 | Indexed image loading and blitting 3 | 4 | Copyright (c) 2019 Damian Yerrick 5 | 6 | This software is provided 'as-is', without any express or implied 7 | warranty. In no event will the authors be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this software must not be misrepresented; you must not 15 | claim that you wrote the original software. If you use this software 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 22 | 3. This notice may not be removed or altered from any source 23 | distribution. 24 | */ 25 | 26 | #ifndef INDEXEDIMAGE_H 27 | #define INDEXEDIMAGE_H 28 | #include 29 | 30 | typedef struct IndexedImage { 31 | unsigned char *pixels; 32 | unsigned char *palette; 33 | unsigned int width, height, palettesize; 34 | } IndexedImage; 35 | 36 | /** 37 | * Creates a blank indexed image. 38 | * im must not already own pixels. 39 | * If there is an error, im is not modified. Otherwise, im takes 40 | * ownership of the image's pixels. 41 | * Ownership of a palette is not affected. 42 | * @return a LodePNG error code (0: success; nonzero: failure) 43 | */ 44 | unsigned IndexedImage_init(IndexedImage *im, unsigned width, unsigned height); 45 | 46 | /** 47 | * Frees the pixels and palette that an indexed image owns. 48 | * Subsequent cleanups are no-ops. 49 | */ 50 | void IndexedImage_cleanup(IndexedImage *im); 51 | 52 | /** 53 | * Sets all pixels in an image to a constant value. 54 | */ 55 | void IndexedImage_clear(IndexedImage *im, unsigned pixelvalue); 56 | 57 | /** 58 | * Pastes as much of src as will fit on dst. 59 | * May be used to crop or build. 60 | * dst cannot be src 61 | * @param x horizontal position on dst corresponding to left side 62 | * of src, which may be negative 63 | * @param y vertical position on dst corresponding to top of src, 64 | * which may be negative 65 | */ 66 | void IndexedImage_paste(const IndexedImage *restrict dst, 67 | const IndexedImage *restrict src, 68 | int x, int y); 69 | 70 | /** 71 | * Performs a shallow copy of a data object. 72 | * The object's structure must permit this. For example, it must 73 | * not be owned, and it usually should not own other resources. 74 | */ 75 | void *memdup(const void *mem, size_t size); 76 | 77 | /** 78 | * Loads a PNG image from a path in the file system. 79 | * If there is an error, im is not modified. Otherwise, im takes 80 | * ownership of the image's pixels and palette. 81 | * @return a LodePNG error code 82 | */ 83 | unsigned IndexedImage_frompng(IndexedImage *im, const char* filename); 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /mdk/src/md/spr.c: -------------------------------------------------------------------------------- 1 | /* mdk sprite support 2 | Michael Moffitt */ 3 | #include "md/spr.h" 4 | #include "md/dma.h" 5 | 6 | #define SPR_STATIC_OFFS 128 7 | 8 | SprSlot g_sprite_table[MD_SPR_MAX]; 9 | SprSlot *g_sprite_next; 10 | uint16_t g_sprite_count; 11 | 12 | static SprMode s_mode; 13 | 14 | void md_spr_init(SprMode mode) 15 | { 16 | s_mode = mode; 17 | // The sprite table has its link values installed ahead of time. 18 | for (uint16_t i = 0; i < MDK_ARRAYSIZE(g_sprite_table); i++) 19 | { 20 | g_sprite_table[i].link = i + 1; 21 | g_sprite_table[i].xpos = 2; // Avoid triggering line mask. 22 | } 23 | 24 | g_sprite_next = &g_sprite_table[0]; 25 | 26 | switch (s_mode) 27 | { 28 | case SPR_MODE_SIMPLE: 29 | md_spr_start(); 30 | md_spr_finish(); 31 | break; 32 | case SPR_MODE_DIRECT: 33 | g_sprite_count = (md_vdp_get_hmode() == VDP_HMODE_H40) ? 80 : 64; 34 | break; 35 | } 36 | } 37 | 38 | void md_spr_start(void) 39 | { 40 | if (s_mode != SPR_MODE_SIMPLE) return; 41 | // Restoration of link field. 42 | g_sprite_table[0].link = 1; 43 | if (g_sprite_count > 0) 44 | { 45 | g_sprite_table[g_sprite_count - 1].link = g_sprite_count; 46 | g_sprite_count = 0; 47 | } 48 | g_sprite_next = &g_sprite_table[0]; 49 | } 50 | 51 | void md_spr_finish(void) 52 | { 53 | uint8_t transfer_count = g_sprite_count; 54 | if (s_mode == SPR_MODE_SIMPLE) 55 | { 56 | if (g_sprite_count == 0) 57 | { 58 | // Sets sprite 0 to be invisible, and point at itself. This is used 59 | // to terminate the list in the absence of any sprite placements. 60 | g_sprite_table[0].link = 0; 61 | g_sprite_table[0].ypos = 0; 62 | g_sprite_table[0].size = 0; 63 | transfer_count = 1; 64 | } 65 | else 66 | { 67 | // The last sprite in the table is terminated with a link back to zero. 68 | g_sprite_table[g_sprite_count - 1].link = 0; 69 | } 70 | } 71 | 72 | // Schedule a transfer for the sprite table. 73 | md_dma_transfer_spr_vram(md_vdp_get_sprite_base(), (void *)g_sprite_table, 74 | sizeof(SprSlot) * transfer_count / 2, 2); 75 | g_sprite_next = &g_sprite_table[0]; 76 | } 77 | 78 | void md_spr_mask_line_full(int16_t y, uint8_t size) 79 | { 80 | if (g_sprite_count >= MDK_ARRAYSIZE(g_sprite_table)) return; 81 | SprSlot *spr = &g_sprite_table[g_sprite_count]; 82 | spr->ypos = y + SPR_STATIC_OFFS; 83 | spr->size = size; 84 | spr->xpos = 0; 85 | spr->attr = 0; 86 | spr->link = 7; 87 | g_sprite_count++; 88 | g_sprite_next++; 89 | } 90 | 91 | void md_spr_mask_line_overlap(int16_t y1, uint8_t size1, 92 | int16_t y2, uint8_t size2) 93 | { 94 | if (g_sprite_count >= MDK_ARRAYSIZE(g_sprite_table) - 1) return; 95 | SprSlot *spr = &g_sprite_table[g_sprite_count]; 96 | spr->ypos = y1 + SPR_STATIC_OFFS; 97 | spr->size = size1; 98 | spr->xpos = 0; 99 | spr->attr = 0; 100 | spr->link = 7; 101 | spr++; 102 | spr->ypos = y2 + SPR_STATIC_OFFS; 103 | spr->size = size2; 104 | spr->xpos = 1; 105 | spr->attr = 0; 106 | spr->link = 7; 107 | g_sprite_count += 2; 108 | g_sprite_next++; 109 | g_sprite_next++; 110 | } 111 | 112 | #undef SPR_STATIC_OFFS 113 | -------------------------------------------------------------------------------- /mdk/src/md/vdp.c: -------------------------------------------------------------------------------- 1 | // mdk VDP control functions 2 | // Michael Moffitt 2018-2024 3 | 4 | #include "md/vdp.h" 5 | #include "md/dma.h" 6 | #include "md/sys.h" 7 | 8 | #include 9 | 10 | volatile uint16_t g_vblank_wait; 11 | static void (*s_vbl_wait_func)(void) = NULL; 12 | 13 | uint8_t g_md_vdp_regs[0x18]; 14 | uint16_t g_md_vdp_debug_regs[0x10]; 15 | 16 | static uint16_t s_plane_base[3]; 17 | static uint16_t s_sprite_base; 18 | static uint16_t s_hscroll_base; 19 | 20 | void md_vdp_init(void) 21 | { 22 | md_vdp_set_hint_line(255); 23 | 24 | // H-int disabled 25 | md_vdp_set_reg(VDP_MODESET1, VDP_MODESET1_DEFAULT); 26 | 27 | // V-int enabled, display disabled, DMA enabled, 224-line mode, Mode 5 28 | md_vdp_set_reg(VDP_MODESET2, VDP_MODESET2_DEFAULT); 29 | 30 | // Plane scroll, TH ints disabled 31 | md_vdp_set_reg(VDP_MODESET3, VDP_MODESET3_DEFAULT); 32 | 33 | // H40 mode by default 34 | md_vdp_set_reg(VDP_MODESET4, VDP_MODESET4_DEFAULT); 35 | 36 | // Just in case... 37 | md_vdp_set_thint_en(false); 38 | md_vdp_set_hint_en(false); 39 | 40 | md_vdp_set_vscroll_mode(VDP_VSCROLL_PLANE); 41 | md_vdp_set_hscroll_mode(VDP_HSCROLL_PLANE); 42 | 43 | // BG color 0 44 | md_vdp_set_bg_color(0); 45 | 46 | // Clear VRAM 47 | md_vdp_vram_clear(); 48 | 49 | // Standard 1-word auto inc 50 | md_vdp_set_autoinc(2); 51 | 52 | // Configure for 512 x 256px planes 53 | md_vdp_set_plane_size(VDP_PLANESIZE_64x32); 54 | 55 | md_vdp_set_window_top(0); 56 | md_vdp_set_window_left(0); 57 | // Set up VRAM base addresses 58 | md_vdp_set_plane_base(VDP_PLANE_A, VRAM_SCRA_BASE_DEFAULT); 59 | md_vdp_set_plane_base(VDP_PLANE_B, VRAM_SCRB_BASE_DEFAULT); 60 | md_vdp_set_plane_base(VDP_PLANE_WINDOW, VRAM_SCRW_BASE_DEFAULT); 61 | md_vdp_set_sprite_base(VRAM_SPR_BASE_DEFAULT); 62 | md_vdp_set_hscroll_base(VRAM_HSCR_BASE_DEFAULT); 63 | } 64 | 65 | void md_vdp_wait_vblank(void) 66 | { 67 | g_vblank_wait = 1; 68 | while (g_vblank_wait) 69 | { 70 | if (s_vbl_wait_func) s_vbl_wait_func(); 71 | } 72 | } 73 | 74 | void md_vdp_register_vblank_wait_callback(void (*function)(void)) 75 | { 76 | s_vbl_wait_func = function; 77 | } 78 | 79 | void md_vdp_set_plane_base(VdpPlane plane, uint16_t value) 80 | { 81 | switch (plane) 82 | { 83 | default: 84 | return; 85 | case VDP_PLANE_A: 86 | md_vdp_set_reg(VDP_SCRABASE, (value >> 13) << 3); 87 | s_plane_base[VDP_PLANE_A] = value; 88 | break; 89 | case VDP_PLANE_B: 90 | md_vdp_set_reg(VDP_SCRBBASE, (value >> 13)); 91 | s_plane_base[VDP_PLANE_B] = value; 92 | break; 93 | case VDP_PLANE_WINDOW: 94 | md_vdp_set_reg(VDP_SCRWBASE, (value >> 11) << 1); 95 | s_plane_base[VDP_PLANE_WINDOW] = value; 96 | break; 97 | } 98 | } 99 | 100 | void md_vdp_set_sprite_base(uint16_t value) 101 | { 102 | s_sprite_base = value; 103 | md_vdp_set_reg(VDP_SPRBASE, (value >> 9)); 104 | } 105 | 106 | void md_vdp_set_hscroll_base(uint16_t value) 107 | { 108 | s_hscroll_base = value; 109 | md_vdp_set_reg(VDP_HSCRBASE, (value >> 10)); 110 | } 111 | 112 | uint16_t md_vdp_get_plane_base(VdpPlane plane) 113 | { 114 | return s_plane_base[plane]; 115 | } 116 | 117 | uint16_t md_vdp_get_sprite_base(void) 118 | { 119 | return s_sprite_base; 120 | } 121 | 122 | uint16_t md_vdp_get_hscroll_base(void) 123 | { 124 | return s_hscroll_base; 125 | } 126 | -------------------------------------------------------------------------------- /mdk/src/md/io_pad_read.a68: -------------------------------------------------------------------------------- 1 | ; ASM routines supporting io.h. 2 | #include "md/top.inc" 3 | 4 | ; uint16_t[2] 5 | .extern g_md_pad[2] 6 | .extern g_md_pad_pos[2] 7 | .extern g_md_pad_neg[2] 8 | .extern g_md_pad_prev[2] 9 | 10 | ; a0 points to pad cache in RAM we are updating. 11 | ; a1 points to the pad data port (byte address). 12 | .global md_io_read_pad 13 | md_io_read_pad: 14 | #ifdef MDK_TARGET_C2 15 | rts 16 | #else 17 | ; First step - x1CBRLDU into d0 18 | move.b #$40, (a1) ; TH hi 19 | nop 20 | nop 21 | nop 22 | move.b (a1), d0 23 | ; Second step - x0SA00DU into d1 24 | move.b #$00, (a1) ; TH low 25 | ori.w #$FFC0, d0 26 | nop 27 | move.b (a1), d1 28 | ; If the pad doesn't positively identify as an MD pad (bits 2 and 3 29 | ; cleared), then don't proceed to read later phases. 30 | ; TODO: Gracefully handle peripherals like Mega Mouse, etc. 31 | btst.b #2, d1 32 | bne store_pad_unplugged 33 | btst.b #3, d1 34 | bne store_pad_unplugged 35 | ; Proceed to use the second phase data. 36 | lsl.b #2, d1 37 | ori.w #$FF3F, d1 38 | and.w d1, d0 39 | ; d0 contains a full 3-button controller's worth now. 40 | ; Third step - write TH high and disregard data. 41 | ; Fifth step - write TH high and disregard data. 42 | move.b #$40, (a1) ; TH hi 43 | nop 44 | nop 45 | nop 46 | ; Sixth step - write TH low, and use lower nybble to check pad type. 47 | move.b #$00, (a1) ; TH low 48 | nop 49 | nop 50 | nop 51 | move.b (a1), d1 52 | ; If the lower four bits are non-zero, it's not a 6-button pad. 53 | ; Seventh and final step - write TH high, read extra buttons 54 | move.b #$40, (a1) ; TH hi 55 | andi.b #$0F, d1 56 | bne.s store_pad 57 | move.b (a1), d1 58 | ; Shift buttons into place in upper byte and mask off other bits 59 | lsl.w #8, d1 60 | ori.w #$F0FF, d1 61 | and.w d1, d0 62 | ; Mark highest bit to indicate a 6-button controller. 63 | bclr #15, d0 64 | bra.s store_pad 65 | 66 | store_pad_unplugged: 67 | ; Mark second-highest bit for "unplugged/maybe sms" status. 68 | bclr #14, d0 69 | ; Fall-through intended. 70 | store_pad: 71 | ; Invert buttons for easier use with the C MdButton enum. 72 | not.w d0 73 | move.w d0, (a0) 74 | move.b #$00, (a1) ; TH back to low 75 | rts 76 | #endif // MDK_TARGET_C2 77 | 78 | .global md_io_init 79 | md_io_init: 80 | moveq #0, d0 81 | move.l d0, g_md_pad 82 | move.l d0, g_md_pad_prev 83 | move.l d0, g_md_pad_pos 84 | move.l d0, g_md_pad_neg 85 | #ifndef MDK_TARGET_C2 86 | move.l #$00400040, (IO_LOC_CTRL1+1).l ; Set TH pin as an output 87 | #endif 88 | rts 89 | 90 | .global md_io_poll 91 | md_io_poll: 92 | #ifdef MDK_TARGET_C2 93 | bra.w md_ioc_poll 94 | #else 95 | ; Move stale pad data into previous frame var. 96 | move.l g_md_pad, g_md_pad_prev 97 | 98 | ; Pause Z80 before we touch the ports. 99 | zbusreq 100 | zbuswt 101 | 102 | lea g_md_pad, a0 103 | lea IO_LOC_DATA1, a1 104 | bsr.w md_io_read_pad 105 | lea g_md_pad + 2, a0 106 | lea IO_LOC_DATA1 + 2, a1 107 | bsr.w md_io_read_pad 108 | 109 | ; Done with the ports, so release the Z80. 110 | zbusrel 111 | ; Fall-through to md_io_generate_edges 112 | #endif // MDK_TARGET_C2 113 | 114 | 115 | .global md_io_generate_edges 116 | md_io_generate_edges: 117 | ; Generate pos and neg edge data. 118 | move.l g_md_pad, d0 119 | move.l g_md_pad_prev, d1 120 | not.l d1 121 | and.l d1, d0 122 | move.l d0, g_md_pad_pos 123 | 124 | move.l g_md_pad_prev, d0 125 | move.l g_md_pad, d1 126 | not.l d1 127 | and.l d1, d0 128 | move.l d0, g_md_pad_neg 129 | rts 130 | -------------------------------------------------------------------------------- /mdk/src/md/cspr_put_fast.a68: -------------------------------------------------------------------------------- 1 | .section .text 2 | 3 | .extern g_sprite_count 4 | .extern g_sprite_table 5 | .extern g_sprite_next 6 | 7 | .include "md/cspr_types.inc" 8 | 9 | .set MD_SPR_MAX, 80 10 | 11 | # 12 | ; void md_cspr_put_st_fast(const CSprParam *s); 13 | # 14 | .global md_cspr_put_st_fast 15 | .set SPID, 4 16 | 17 | ; SP - pushed args 18 | .set ARG_CSPR_STRUCT, SPID 19 | 20 | ; A0 - draw params (CSprParam struct) 21 | .set PRM_CSPR_DATA, 0 22 | .set PRM_VRAM_BASE, 4 23 | .set PRM_FRAME, 6 24 | .set PRM_X, 8 25 | .set PRM_Y, 10 26 | .set PRM_ATTR, 12 27 | .set PRM_USE_DMA, 14 28 | 29 | md_cspr_put_st_fast: 30 | ; a0 := draw params (CSprParam) 31 | movea.l ARG_CSPR_STRUCT(sp), a0 32 | 33 | movem.l a2-a3, -(sp) 34 | movem.l d2-d3/d7, -(sp) 35 | 36 | ; a1 := cspr blob 37 | move.l PRM_CSPR_DATA(a0), a1 38 | 39 | ; a2 := frame ref 40 | lea CSPR_REFS(a1), a2 41 | move.w PRM_FRAME(a0), d0 42 | 43 | 44 | lsl.w #3, d0 ; index by 8, sizeof(CSprRef) 45 | adda.w d0, a2 ; a1 now points to the ref 46 | 47 | ; Sprite count checks 48 | move.w REF_SPR_COUNT(a2), d7 49 | beq.w 0f ; no sprites in this frame 50 | 51 | move.w d7, d0 52 | add.w g_sprite_count, d0 ; d0 := final sprite index 53 | cmpi.w #MD_SPR_MAX, d0 ; compare d0 to max sprites 54 | bls.s sprite_count_ok 55 | ; Too many sprites; limit amount in d7. 56 | move.w #MD_SPR_MAX, d7 ; d7 takes max 57 | sub.w g_sprite_count, d7 ; d7 takes available slots 58 | ble.w 0f 59 | sprite_count_ok: 60 | 61 | ; Queue DMA if needed. 62 | move.w PRM_VRAM_BASE(a0), d1 63 | tst.w PRM_USE_DMA(a0) 64 | beq.s cspr_put_no_dma 65 | 66 | jsr (pc, cspr_dma_setup_sub) 67 | bra.s cspr_put_after_dma 68 | 69 | cspr_put_no_dma: 70 | ; Just offset vram base 71 | lsr.w #5, d1 72 | add.w REF_TILE_SRC_OFFSET(a2), d1 73 | bra.s cspr_put_attributes_set 74 | 75 | cspr_put_after_dma: 76 | ; d1 := base attributes for sprite 77 | lsr.w #5, d1 78 | cspr_put_attributes_set: 79 | or.w PRM_ATTR(a0), d1 80 | 81 | ; a1 := spr (CSprSprite) 82 | adda.l CSPR_SPR_LIST_OFFSET(a1), a1 83 | adda.w REF_SPR_LIST_OFFSET(a2), a1 84 | 85 | ; d2 := sprite X 86 | move.w PRM_X(a0), d2 87 | 88 | ; d3 := sprite Y 89 | move.w PRM_Y(a0), d3 90 | 91 | ; a3 := MD hardware sprite slot 92 | movea.l g_sprite_next, a3 93 | add.w d7, g_sprite_count 94 | subq.w #1, d7 ; for dbf loop 95 | 96 | btst #11, d1 ; H flip? 97 | bne.s cspr_hflip 98 | btst #12, d1 ; V flip? 99 | bne.s cspr_draw_top_vflip 100 | 101 | .macro cspr_draw_body 102 | move.w d3, (a3)+ ; Y 103 | move.b SPR_SIZE(a1), (a3)+ ; leave link field alone 104 | addq #1, a3 105 | move.w d1, d0 ; Base attr in d1 106 | add.w SPR_TILE(a1), d0 107 | move.w d0, (a3)+ ; Attr 108 | move.w d2, (a3)+ ; X 109 | lea $10(a1), a1 110 | .endm 111 | 112 | cspr_draw_top_normal: 113 | add.w SPR_DX(a1), d2 114 | add.w SPR_DY(a1), d3 115 | cspr_draw_body 116 | dbf d7, cspr_draw_top_normal 117 | bra.s cspr_draw_finished 118 | 119 | cspr_draw_top_vflip: 120 | add.w SPR_DX(a1), d2 121 | add.w SPR_FDY(a1), d3 122 | cspr_draw_body 123 | dbf d7, cspr_draw_top_vflip 124 | bra.s cspr_draw_finished 125 | 126 | cspr_hflip: 127 | btst #12, d1 ; V flip? 128 | bne.s cspr_draw_top_hvflip 129 | 130 | cspr_draw_top_hflip: 131 | add.w SPR_FDX(a1), d2 132 | add.w SPR_DY(a1), d3 133 | cspr_draw_body 134 | dbf d7, cspr_draw_top_hflip 135 | bra.b cspr_draw_finished 136 | 137 | cspr_draw_top_hvflip: 138 | add.w SPR_FDX(a1), d2 139 | add.w SPR_FDY(a1), d3 140 | cspr_draw_body 141 | dbf d7, cspr_draw_top_hvflip 142 | ; fall-through to cspr_draw_finished 143 | 144 | cspr_draw_finished: 145 | 0: 146 | move.l a3, g_sprite_next 147 | movem.l (sp)+, d2-d3/d7 148 | movem.l (sp)+, a2-a3 149 | rts 150 | -------------------------------------------------------------------------------- /samples/cd-mode-1/src/kos.a68: -------------------------------------------------------------------------------- 1 | ; --------------------------------------------------------------------------- 2 | ; Kosinski decompression subroutine 3 | ; void Kos_Decomp(uint8_t *src, uint8_t *dst) 4 | ; Inputs: 5 | ; 4(%sp) = compressed data location 6 | ; 8(%sp) = destination 7 | ; --------------------------------------------------------------------------- 8 | 9 | .global Kos_Decomp 10 | Kos_Decomp: 11 | movea.l 4(%sp),a0 12 | movea.l 8(%sp),a1 13 | movem.l d2-d6,-(%sp) 14 | subq.l #2,%sp ; make %space for two bytes on the stack 15 | move.b (a0)+,1(%sp) 16 | move.b (a0)+,(%sp) 17 | move.w (%sp),d5 ; copy first description field 18 | moveq #15,d4 ; 16 bits in a byte 19 | 20 | Kos_Decomp_Loop: 21 | lsr.w #1,d5 ; bit which is shifted out goes into C flag 22 | move sr,d6 23 | dbra d4,Kos_Decomp_ChkBit 24 | move.b (a0)+,1(%sp) 25 | move.b (a0)+,(%sp) 26 | move.w (%sp),d5 ; get next description field if needed 27 | moveq #15,d4 ; reset bit counter 28 | 29 | Kos_Decomp_ChkBit: 30 | move d6,sr ; was the bit set? 31 | bcc.b Kos_Decomp_RLE ; if not, branch (C flag clear means bit was clear) 32 | move.b (a0)+,(a1)+ ; otherwise, copy byte as-is 33 | bra.b Kos_Decomp_Loop 34 | 35 | ; --------------------------------------------------------------------------- 36 | 37 | Kos_Decomp_RLE: 38 | moveq #0,d3 39 | lsr.w #1,d5 ; get next bit 40 | move sr,d6 41 | dbra d4,Kos_Decomp_ChkBit2 42 | move.b (a0)+,1(%sp) 43 | move.b (a0)+,(%sp) 44 | move.w (%sp),d5 45 | moveq #15,d4 46 | 47 | Kos_Decomp_ChkBit2: 48 | move d6,sr ; was the bit set? 49 | bcs.b Kos_Decomp_SeparateRLE ; if it was, branch 50 | lsr.w #1,d5 ; bit which is shifted out goes into X flag 51 | dbra d4,1f 52 | move.b (a0)+,1(%sp) 53 | move.b (a0)+,(%sp) 54 | move.w (%sp),d5 55 | moveq #15,d4 56 | 1: 57 | roxl.w #1,d3 ; get high repeat count bit (shift X flag in) 58 | lsr.w #1,d5 59 | dbra d4,2f 60 | move.b (a0)+,1(%sp) 61 | move.b (a0)+,(%sp) 62 | move.w (%sp),d5 63 | moveq #15,d4 64 | 2: 65 | roxl.w #1,d3 ; get low repeat count bit 66 | addq.w #1,d3 ; increment repeat count 67 | moveq #-1,d2 68 | move.b (a0)+,d2 ; calculate offset 69 | bra.b Kos_Decomp_RLELoop 70 | 71 | ; --------------------------------------------------------------------------- 72 | 73 | Kos_Decomp_SeparateRLE: 74 | move.b (a0)+,d0 ; get first byte 75 | move.b (a0)+,d1 ; get second byte 76 | moveq #-1,d2 77 | move.b d1,d2 78 | lsl.w #5,d2 79 | move.b d0,d2 ; calculate offset 80 | andi.w #7,d1 ; does a third byte need to be read? 81 | beq.b Kos_Decomp_SeparateRLE2 ; if it does, branch 82 | move.b d1,d3 ; copy repeat count 83 | addq.w #1,d3 ; and increment it 84 | 85 | Kos_Decomp_RLELoop: 86 | move.b (a1,d2.w),d0 87 | move.b d0,(a1)+ ; copy appropriate byte 88 | dbra d3,Kos_Decomp_RLELoop ; and repeat the copying 89 | bra Kos_Decomp_Loop 90 | 91 | ; --------------------------------------------------------------------------- 92 | 93 | Kos_Decomp_SeparateRLE2: 94 | move.b (a0)+,d1 95 | beq.b Kos_Decomp_Done ; 0 indicates end of compressed data 96 | cmpi.b #1,d1 97 | beq.w Kos_Decomp_Loop ; 1 indicates a new description needs to be read 98 | move.b d1,d3 ; otherwise, copy repeat count 99 | bra.b Kos_Decomp_RLELoop 100 | 101 | ; --------------------------------------------------------------------------- 102 | 103 | Kos_Decomp_Done: 104 | addq.l #2,%sp ; restore stack pointer to original state 105 | movem.l (%sp)+,d2-d6 106 | rts 107 | 108 | ; End of function Kos_Decomp 109 | -------------------------------------------------------------------------------- /mdk/src/md/mmio.h: -------------------------------------------------------------------------------- 1 | // mdk memory map and MMIO definitions 2 | // 2018-2024 Michael Moffitt 3 | #pragma once 4 | 5 | #define ROM_BASE (0x00000000) 6 | #define WRAM_BASE (0xFFFF0000) 7 | #define WRAM_SIZE (0x10000) 8 | #define VRAM_SIZE (0x10000) 9 | 10 | #define SRAM_BASE (0x200000) 11 | #define SRAM_CTRL (0xA130F1) 12 | 13 | /* I/O (MD Pads) */ 14 | #define IO_LOC_BASE (0xA10000) 15 | #define IO_LOC_VERSION (IO_LOC_BASE + 0x01) 16 | #define IO_LOC_DATA1 (IO_LOC_BASE + 0x03) 17 | #define IO_LOC_DATA2 (IO_LOC_BASE + 0x05) 18 | #define IO_LOC_DATA3 (IO_LOC_BASE + 0x07) 19 | #define IO_LOC_CTRL1 (IO_LOC_BASE + 0x09) 20 | #define IO_LOC_CTRL2 (IO_LOC_BASE + 0x0B) 21 | #define IO_LOC_CTRL3 (IO_LOC_BASE + 0x0D) 22 | #define IO_LOC_TXD1 (IO_LOC_BASE + 0x0F) 23 | #define IO_LOC_RXD1 (IO_LOC_BASE + 0x11) 24 | #define IO_LOC_SERIO1 (IO_LOC_BASE + 0x13) 25 | #define IO_LOC_TXD2 (IO_LOC_BASE + 0x15) 26 | #define IO_LOC_RXD2 (IO_LOC_BASE + 0x17) 27 | #define IO_LOC_SERIO2 (IO_LOC_BASE + 0x19) 28 | #define IO_LOC_TXD3 (IO_LOC_BASE + 0x1B) 29 | #define IO_LOC_RXD3 (IO_LOC_BASE + 0x1D) 30 | #define IO_LOC_SERIO3 (IO_LOC_BASE + 0x1F) 31 | /* I/O (Sys) */ 32 | #define SYS_Z80_PRG_LOC (0xA00000) 33 | #define SYS_Z80_PORT_BUS_LOC (0xA11100) 34 | #define SYS_Z80_PORT_RESET_LOC (0xA11200) 35 | #define TMSS_PORT (0xA14000) 36 | /* VDP */ 37 | #define VDP_LOC_BASE (0xC00000) 38 | /* PSG */ 39 | #define PSG_LOC_BASE (0xC00011) 40 | /* OPN */ 41 | #ifdef MDK_TARGET_C2 42 | #define OPN_BASE 0x840100 43 | #else 44 | #define OPN_BASE 0xA04000 45 | #endif // MDK_TARGET_C2 46 | /* System C/C2 additions */ 47 | /* Color RAM */ 48 | #define CRAM_SYSTEMC_LOC_BASE (0x8C0000) 49 | /* I/O ports (buttons, some peripheral control) */ 50 | #define SYSC_IO_LOC_BASE (0x840000) 51 | #define SYSC_IO_LOC_PORTA (SYSC_IO_LOC_BASE + 0x01) 52 | #define SYSC_IO_LOC_PORTB (SYSC_IO_LOC_BASE + 0x03) 53 | #define SYSC_IO_LOC_PORTC (SYSC_IO_LOC_BASE + 0x05) 54 | #define SYSC_IO_LOC_PORTD (SYSC_IO_LOC_BASE + 0x07) 55 | #define SYSC_IO_LOC_PORTE (SYSC_IO_LOC_BASE + 0x09) 56 | #define SYSC_IO_LOC_PORTF (SYSC_IO_LOC_BASE + 0x0B) 57 | #define SYSC_IO_LOC_PORTG (SYSC_IO_LOC_BASE + 0x0D) 58 | #define SYSC_IO_LOC_PORTH (SYSC_IO_LOC_BASE + 0x0F) 59 | /* I/O protection registers (for reading the string "SEGA") */ 60 | #define SYSC_IO_LOC_PROT0 (SYSC_IO_LOC_BASE + 0x11) 61 | #define SYSC_IO_LOC_PROT1 (SYSC_IO_LOC_BASE + 0x13) 62 | #define SYSC_IO_LOC_PROT2 (SYSC_IO_LOC_BASE + 0x15) 63 | #define SYSC_IO_LOC_PROT3 (SYSC_IO_LOC_BASE + 0x17) 64 | /* I/O control registers */ 65 | #define SYSC_IO_LOC_CTRL0 (SYSC_IO_LOC_BASE + 0x19) 66 | #define SYSC_IO_LOC_CTRL1 (SYSC_IO_LOC_BASE + 0x1B) 67 | #define SYSC_IO_LOC_CTRL2 (SYSC_IO_LOC_BASE + 0x1D) 68 | #define SYSC_IO_LOC_CTRL3 (SYSC_IO_LOC_BASE + 0x1F) 69 | /* Protection register */ 70 | #define SYSC_PROTECTION_LOC_BASE (0x800000) 71 | #define SYSC_PROTECTION_LOC_SECURITY (SYSC_PROTECTION_LOC_BASE + 0x001) 72 | #define SYSC_PROTECTION_LOC_VCTRL (SYSC_PROTECTION_LOC_BASE + 0x201) 73 | 74 | #define SYSC_IO_UPD7759_LOC_BASE (0x880000) 75 | #define SYSC_IO_UPD7759_PHRASE_LOC (SYSC_IO_UPD7759_LOC_BASE + 1) 76 | 77 | #ifndef __ASSEMBLER__ 78 | #define PSG_PORT (*(volatile uint8_t *)(PSG_LOC_BASE)) 79 | #define SYS_PORT_VERSION (*(volatile uint8_t *)(IO_LOC_BASE + 1)) 80 | #define VDPPORT_DATA (*(volatile uint16_t*)(VDP_LOC_BASE)) 81 | #define VDPPORT_DATA32 (*(volatile uint32_t*)(VDP_LOC_BASE)) 82 | #define VDPPORT_CTRL (*(volatile uint16_t*)(VDP_LOC_BASE + 4)) 83 | #define VDPPORT_CTRL32 (*(volatile uint32_t*)(VDP_LOC_BASE + 4)) 84 | #define VDPPORT_HVCOUNT (*(volatile uint16_t*)(VDP_LOC_BASE + 8)) 85 | #define VDPPORT_DBG_SEL (*(volatile uint16_t *)(VDP_LOC_BASE + 0x18)) 86 | #define VDPPORT_DBG_DATA (*(volatile uint16_t *)(VDP_LOC_BASE + 0x1C)) 87 | #endif /* __ASSEMBLER__ */ 88 | -------------------------------------------------------------------------------- /samples/mdsdrv-usage/src/mdsdrv/mdsdrv.h: -------------------------------------------------------------------------------- 1 | //====================================================================== 2 | // MDSDRV API wrapper for MDK 3 | //====================================================================== 4 | // Copyright (c) 2020 Ian Karlsson 5 | // Source modified 2022 by Mike Moffitt 6 | // 7 | // This software is provided 'as-is', without any express or implied 8 | // warranty. In no event will the authors be held liable for any 9 | // damages arising from the use of this software. 10 | // 11 | // Permission is granted to anyone to use this software for any 12 | // purpose, including commercial applications, and to alter it and 13 | // redistribute it freely, subject to the following restrictions: 14 | // 15 | // 1. The origin of this software must not be misrepresented; you must 16 | // not claim that you wrote the original software. If you use this 17 | // software in a product, an acknowledgment in the product 18 | // documentation would be appreciated but is not required. 19 | // 2. Altered source versions must be plainly marked as such, and must 20 | // not be misrepresented as being the original software. 21 | // 3. This notice may not be removed or altered from any source 22 | // distribution. 23 | //====================================================================== 24 | 25 | #ifndef MDSDRV_H 26 | #define MDSDRV_H 27 | 28 | #include "res.h" 29 | 30 | // Music and sound effect priority slots. 31 | typedef enum MdsSlot 32 | { 33 | MDS_SLOT_BGM = 3, 34 | MDS_SLOT_SE1 = 2, 35 | MDS_SLOT_SE2 = 1, 36 | MDS_SLOT_SE3 = 0 37 | } MdsSlot; 38 | 39 | // Commands. 40 | #define MDS_CMD_GET_CMD_CNT 0 41 | #define MDS_CMD_GET_SOUND_CNT 1 42 | #define MDS_CMD_GET_STATUS 2 43 | #define MDS_CMD_GET_VERSION 3 44 | #define MDS_CMD_GET_GTEMPO 4 45 | #define MDS_CMD_SET_GTEMPO 5 46 | #define MDS_CMD_GET_GVOLUME 6 47 | #define MDS_CMD_SET_GVOLUME 7 48 | #define MDS_CMD_WRITE_FM_PORT0 8 49 | #define MDS_CMD_WRITE_FM_PORT1 9 50 | #define MDS_CMD_FADE_BGM 10 51 | #define MDS_CMD_SET_PAUSE 11 52 | #define MDS_CMD_GET_VOLUME 12 53 | #define MDS_CMD_SET_VOLUME 13 54 | #define MDS_CMD_GET_TEMPO 14 55 | #define MDS_CMD_SET_TEMPO 15 56 | 57 | // Initializes the sound driver. 58 | // This initializes the work area, unpacks and starts up the PCM driver. MDSDRV 59 | // should then be updated every vblank using mds_update(). 60 | // seqdata must be word-aligned, while pcmdata must be 32k-aligned. 61 | uint16_t mds_init(const uint8_t *seqdata, const uint8_t *pcmdata); 62 | 63 | // Request playback of a sound. 64 | // Specify 0 to stop the currently playing sound. 65 | void mds_request(MdsSlot slot, uint16_t id); 66 | 67 | // Command request for low-level access. 68 | // Not all commands return a meaningful value, see MDSDRV 69 | // documentation for details. 70 | uint32_t mds_command(uint16_t id, uint16_t param); 71 | 72 | // Command request for low-level access (command number >= 0x09). 73 | // Not all commands return a meaningful value, see MDSDRV 74 | // documentation for details. 75 | uint32_t mds_command2(uint16_t id, uint16_t param1, uint16_t param2); 76 | 77 | // MDSDRV update function. Must be called every vblank. 78 | void mds_update(void); 79 | 80 | // Returns a pointer to a NULL-terminated string containing version information. 81 | const char *mds_get_version_str(void); 82 | 83 | // Sets pause on/off. 84 | void mds_pause(MdsSlot slot, uint8_t paused); 85 | 86 | // Set BGM fade in/out. 87 | // The volume of the BGM track will fade to the target level. The duration of 88 | // the fade is as follows: 89 | // (target - current) / (speed + 1) 90 | // The target range is 0 to 127, with -0.75dB per step. 91 | // 0 is the fastest fade speed, while 7 is the slowest. 92 | void mds_fade(uint8_t target, uint8_t speed, uint8_t stop_when_done); 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /mdk/src/md/vdp_min_init.a68: -------------------------------------------------------------------------------- 1 | #include "md/top.inc" 2 | .global md_vdp_min_init 3 | ; 4 | ; Very basic VDP init and font install for the sake of diagnostics. 5 | ; Does NOT clobber d0. 6 | ; 7 | ; a6 = return 8 | ; 9 | md_vdp_min_init: 10 | lea VDP_LOC_BASE, a4 11 | 12 | ; Basic VDP Init 13 | lea vdp_init_reg_tbl, a0 14 | reg_init_copy_top: 15 | move.w (a0)+, d1 16 | bpl.s reg_init_done 17 | move.w d1, VDP_OFFS_CTRL(a4) 18 | bra.s reg_init_copy_top 19 | 20 | reg_init_done: 21 | ; TODO: 128K VRAM flag 22 | ; Clear VRAM 23 | md_set_vram_addr $0000 24 | moveq #0, d1 25 | move.w #(VRAM_SIZE/4) - 1, d7 26 | vdp_mem_clear_top: 27 | move.l d1, VDP_OFFS_DATA(a4) 28 | dbf d7, vdp_mem_clear_top 29 | 30 | ; Write palette for font 31 | #ifndef MDK_TARGET_C2 32 | md_set_cram_addr $0000 33 | moveq #(128 / (4 * 4)) - 1, d7 34 | lea md_min_palette, a0 35 | pal_copy_top: 36 | .rept 4 37 | move.l (a0)+, VDP_OFFS_DATA(a4) 38 | .endr 39 | dbf d7, pal_copy_top 40 | #else 41 | move.w #VDP_REGST(VDP_MODESET3, $00), VDP_OFFS_CTRL(a4) ; Enable color bus master 42 | lea CRAM_SYSTEMC_LOC_BASE, a1 43 | moveq #(128 / (4 * 4)) - 1, d7 44 | lea md_min_palette, a0 45 | pal_copy_top: 46 | .rept 4 47 | move.l (a0), (a1) 48 | move.l (a0)+, 2*16*16(a1) ; sprites 49 | addq.w #4, a1 50 | .endr 51 | dbf d7, pal_copy_top 52 | move.w #VDP_REGST(VDP_MODESET3, $80), VDP_OFFS_CTRL(a4) ; Disable color bus master 53 | #endif 54 | ; Copy font into VRAM 55 | md_set_vram_addr $0000 56 | lea md_error_font, a0 57 | move.w #((12 * 16 * 32) / (4 * 4)) - 1, d7 58 | vram_copy_top: 59 | .rept 4 60 | move.l (a0)+, VDP_OFFS_DATA(a4) 61 | .endr 62 | dbf d7, vram_copy_top 63 | 64 | ; Return handled with a6 65 | jmp (a6) 66 | 67 | vdp_init_reg_tbl: 68 | dc.w VDP_REGST(VDP_MODESET1, VDP_MODESET1_DEFAULT) 69 | dc.w VDP_REGST(VDP_MODESET2, VDP_MODESET2_DEFAULT) 70 | dc.w VDP_REGST(VDP_MODESET3, VDP_MODESET3_DEFAULT) 71 | dc.w VDP_REGST(VDP_MODESET4, VDP_MODESET4_DEFAULT) 72 | dc.w VDP_REGST(VDP_SCRABASE, (VRAM_SCRA_BASE_DEFAULT >> 10)) 73 | dc.w VDP_REGST(VDP_SCRBBASE, (VRAM_SCRB_BASE_DEFAULT >> 13)) 74 | dc.w VDP_REGST(VDP_SPRBASE, $78) 75 | dc.w VDP_REGST(VDP_SCRWBASE, (VRAM_SCRW_BASE_DEFAULT >> 10)) 76 | dc.w VDP_REGST(VDP_HSCRBASE, $3D) 77 | dc.w VDP_REGST(VDP_PLANESIZE, $01) 78 | dc.w VDP_REGST(VDP_WINHORI, $1F) 79 | dc.w VDP_REGST(VDP_WINVERT, $1F) 80 | dc.w VDP_REGST(VDP_AUTOINC, $02) 81 | dc.w VDP_REGST(VDP_BGCOL, $00) 82 | dc.w VDP_REGST(VDP_HINTC, $FF) 83 | dc.w 0 ; end marker 84 | 85 | ; 86 | ; Graphics 87 | ; 88 | 89 | md_min_palette: 90 | ; 91 | dc.w $0000 92 | dc.w $0EEE 93 | dc.w $0EEC 94 | dc.w $0EEA 95 | dc.w $0EE8 96 | dc.w $0EE6 97 | dc.w $0EE4 98 | dc.w $0EE2 99 | dc.w $0EE0 100 | dc.w $0000 101 | dc.w $0000 102 | dc.w $0000 103 | dc.w $0000 104 | dc.w $0000 105 | dc.w $0222 106 | dc.w $0000 107 | ; 108 | dc.w $0000 109 | dc.w $0EE0 110 | dc.w $0CE2 111 | dc.w $0AE4 112 | dc.w $08E6 113 | dc.w $06E8 114 | dc.w $04EA 115 | dc.w $02EC 116 | dc.w $00EE 117 | dc.w $0000 118 | dc.w $0000 119 | dc.w $0000 120 | dc.w $0000 121 | dc.w $0000 122 | dc.w $0222 123 | dc.w $0000 124 | ; 125 | dc.w $0000 126 | dc.w $00EE 127 | dc.w $00CE 128 | dc.w $00AE 129 | dc.w $008E 130 | dc.w $006E 131 | dc.w $004E 132 | dc.w $002E 133 | dc.w $000E 134 | dc.w $0000 135 | dc.w $0000 136 | dc.w $0000 137 | dc.w $0000 138 | dc.w $0000 139 | dc.w $0222 140 | dc.w $0000 141 | ; 142 | dc.w $0000 143 | dc.w $0E2E 144 | dc.w $0C2E 145 | dc.w $0A2E 146 | dc.w $082E 147 | dc.w $062E 148 | dc.w $042E 149 | dc.w $022E 150 | dc.w $002E 151 | dc.w $0000 152 | dc.w $0000 153 | dc.w $0000 154 | dc.w $0000 155 | dc.w $0000 156 | dc.w $0222 157 | dc.w $0000 158 | 159 | md_error_font: 160 | .incbin "md/font.bin" 161 | -------------------------------------------------------------------------------- /mdk/src/md/spr_put.a68: -------------------------------------------------------------------------------- 1 | .section .text 2 | 3 | ; Sprite placement functions. 4 | 5 | .extern g_sprite_count 6 | .extern g_sprite_table 7 | .extern g_sprite_next 8 | 9 | .set SPR_STATIC_OFFS, 128 10 | .set MD_SPR_MAX, 80 11 | 12 | ; 13 | ; void md_spr_put(int16_t x, int16_t y, uint16_t attr, uint16_t size) 14 | ; 15 | .global md_spr_put 16 | .set SPID, 4 17 | 18 | ; All arguments are word-sized, while aligned to longword displacements. 19 | .set ARG_X, SPID+2 20 | .set ARG_Y, SPID+(4*1)+2 21 | .set ARG_ATTR, SPID+(4*2)+2 22 | .set ARG_SIZE, SPID+(4*3)+2 23 | 24 | md_spr_put: 25 | ; Check if sprite count hasn't been exceeded. 26 | move.w g_sprite_count, d1 27 | cmpi.w #MD_SPR_MAX, d1 28 | bcc.s 0f 29 | ; Check if X coordinates are out of frame. 30 | move.w ARG_X(sp), d0 ; X position argument. 31 | addi.w #SPR_STATIC_OFFS, d0 ; Offset for screen space. 32 | andi.w #$01FF, d0 ; Mask 9 bits valid for VDP. 33 | cmpi.w #SPR_STATIC_OFFS-32, d0 ; Check for sprites too far off 34 | bcs.s 0f ; screen to avoid line mask. 35 | swap d0 36 | ; Set A1 to point at sprite slot and store updated sprite count. 37 | addq.w #1, d1 38 | move.w d1, g_sprite_count 39 | movea.l g_sprite_next, a1 40 | ; Y position and link. 41 | move.w ARG_Y(sp), d1 42 | addi.w #SPR_STATIC_OFFS, d1 43 | move.w d1, (a1)+ 44 | move.b ARG_SIZE+1(sp), (a1) ; no increment to avoid touching link 45 | ; Attribute, X position. 46 | move.w ARG_ATTR(sp), d0 47 | swap d0 ; d0 now has attr in high word and x in low word. 48 | addq #2, a1 49 | move.l d0, (a1)+ 50 | move.l a1, g_sprite_next 51 | 0: 52 | rts 53 | 54 | ; 55 | ; void md_spr_put_st(const SprParam *s) 56 | ; 57 | .global md_spr_put_st 58 | .set SPID, 4 59 | 60 | .set ARG_PARAM, SPID 61 | .set PRM_X, 0 62 | .set PRM_Y, 2 63 | .set PRM_ATTR, 4 64 | .set PRM_SIZE, 6 65 | 66 | md_spr_put_st: 67 | move.w g_sprite_count, d1 68 | cmpi.w #MD_SPR_MAX, d1 69 | bcc.s 0f 70 | movea.l ARG_PARAM(sp), a0 71 | ; Check if X coordinates are out of frame. 72 | move.w PRM_X(a0), d0 ; X position argument. 73 | addi.w #SPR_STATIC_OFFS, d0 ; Offset for screen space. 74 | andi.w #$01FF, d0 ; Mask 9 bits valid for VDP. 75 | cmpi.w #SPR_STATIC_OFFS-32, d0 ; Check for sprites too far off 76 | bcs.s 0f ; screen to avoid line mask. 77 | swap d0 78 | ; Set A1 to point at sprite slot and store updated sprite count. 79 | addq.w #1, d1 80 | move.w d1, g_sprite_count 81 | movea.l g_sprite_next, a1 82 | ; Y position and link. 83 | move.w PRM_Y(a0), d1 84 | addi.w #SPR_STATIC_OFFS, d1 85 | move.w d1, (a1)+ 86 | move.b PRM_SIZE(a0), (a1) ; no increment to avoid touching link 87 | ; Attribute, X position. 88 | move.w PRM_ATTR(a0), d0 89 | swap d0 ; d0 now has attr in high word and x in low word. 90 | addq.l #2, a1 91 | move.l d0, (a1)+ 92 | move.l a1, g_sprite_next 93 | 0: 94 | rts 95 | 96 | ; 97 | ; void md_spr_put_st_fast(const SprParam *s) 98 | ; 99 | .global md_spr_put_st_fast 100 | .global md_spr_put_st_fast_direct 101 | .set SPID, 4 102 | 103 | .set ARG_PARAM, SPID 104 | .set PRM_X, 0 105 | .set PRM_Y, 2 106 | .set PRM_ATTR, 4 107 | .set PRM_SIZE, 6 108 | .set PRM_PRIO, 7 109 | 110 | md_spr_put_st_fast: 111 | movea.l ARG_PARAM(sp), a0 112 | md_spr_put_st_fast_direct: 113 | move.w g_sprite_count, d1 114 | cmpi.w #MD_SPR_MAX, d1 115 | bcc.s 0f 116 | ; Set A1 to point at sprite slot and store updated sprite count. 117 | addq.w #1, d1 118 | move.w d1, g_sprite_count 119 | movea.l g_sprite_next, a1 120 | ; Y position and link. 121 | move.w PRM_Y(a0), (a1)+ 122 | move.b PRM_SIZE(a0), (a1) ; no increment to avoid touching link 123 | ; Attribute, X position. 124 | addq.l #2, a1 125 | move.w PRM_ATTR(a0), (a1)+ 126 | move.w PRM_X(a0), (a1)+ 127 | move.l a1, g_sprite_next 128 | 0: 129 | rts 130 | -------------------------------------------------------------------------------- /samples/system-c2-demo/src/main.c: -------------------------------------------------------------------------------- 1 | // md-toolchain example main.c 2 | // Michael Moffitt 2018 3 | // 4 | // This main shows a simple "hello world" demo. 5 | 6 | // megadrive.h is an umbrella for all headers in src/md. Specific modules like 7 | // md/vdp.h do not need to be individually included. However, utility funcitons 8 | // are not included, as they are not core support functions. 9 | #include "md/megadrive.h" 10 | 11 | #include "util/text.h" 12 | #include "util/plane.h" 13 | #include "md/psg.h" 14 | 15 | #include "md/adpcm.h" 16 | 17 | #include "res.h" 18 | 19 | static const uint16_t tile_vram_addr = 0x0; 20 | 21 | static void draw_interface(void) 22 | { 23 | text_puts(VDP_PLANE_A, 1, 1, "Hello System C2"); 24 | text_puts(VDP_PLANE_A, 1, 3, " 0123456789ABCDEF"); 25 | text_puts(VDP_PLANE_A, 1, 4, "BG 0"); 26 | text_puts(VDP_PLANE_A, 1, 5, "BG 1"); 27 | text_puts(VDP_PLANE_A, 1, 6, "BG 2"); 28 | text_puts(VDP_PLANE_A, 1, 7, "BG 3"); 29 | text_puts(VDP_PLANE_A, 1, 8, "SP 0"); 30 | text_puts(VDP_PLANE_A, 1, 9, "SP 1"); 31 | text_puts(VDP_PLANE_A, 1, 10, "SP 2"); 32 | text_puts(VDP_PLANE_A, 1, 11, "SP 3"); 33 | } 34 | 35 | static void draw_bg_color_bars(void) 36 | { 37 | md_vdp_set_autoinc(2); 38 | static const uint16_t start_tile_x = 8; 39 | static const uint16_t start_tile_y = 4; 40 | static const uint16_t start_tile_no = tile_vram_addr / 32; 41 | 42 | for (uint16_t i = 0; i < 4; i++) 43 | { 44 | const uint16_t pal = i % 4; 45 | const uint16_t prio = (i / 4) % 2; 46 | const uint16_t plane_a_base = md_vdp_get_plane_base(VDP_PLANE_A); 47 | md_vdp_set_addr(plane_a_base + 48 | (2 * start_tile_x) + 49 | (2 * (start_tile_y + i) * md_vdp_get_plane_width())); 50 | for (uint16_t j = 0; j < 16; j++) 51 | { 52 | const uint16_t tile = start_tile_no + j; 53 | md_vdp_write(VDP_ATTR(tile, 0, 0, pal, prio)); 54 | } 55 | } 56 | } 57 | 58 | static void draw_sprite_color_bars(void) 59 | { 60 | static const uint16_t start_tile_x = 8; 61 | static const uint16_t start_tile_y = 8; 62 | static const uint16_t start_tile_no = tile_vram_addr / 32; 63 | 64 | for (uint16_t i = 0; i < 4; i++) 65 | { 66 | const uint16_t pal = i % 4; 67 | const uint16_t prio = (i / 4) % 2; 68 | for (uint16_t j = 0; j < 4; j++) 69 | { 70 | const uint16_t x = (start_tile_x * 8) + (j * 32); 71 | const uint16_t y = (start_tile_y + i) * 8; 72 | const uint16_t tile = start_tile_no + (j * 4); 73 | const uint16_t attr = SPR_ATTR(tile, 0, 0, pal, prio); 74 | const uint16_t size = SPR_SIZE(4, 1); 75 | md_spr_put(x, y, attr, size); 76 | } 77 | } 78 | } 79 | 80 | static void initialize_resources(void) 81 | { 82 | text_init(res_font_gfx_bin, sizeof(res_font_gfx_bin), 0x400, res_font_pal_bin, 0); 83 | md_dma_transfer_vram(tile_vram_addr, res_color_blocks_bin, 84 | sizeof(res_color_blocks_bin) / 2, 2); 85 | 86 | // Generate color bars spanning the four palettes. 87 | for (uint16_t i = 0; i < 16; i++) 88 | { 89 | md_pal_set(i, PALRGB(i, i, i)); 90 | md_pal_set(i+(1*16), PALRGB(i, 0, 0)); 91 | md_pal_set(i+(2*16), PALRGB(i, i, 0)); 92 | md_pal_set(i+(3*16), PALRGB(0, i, 0)); 93 | md_pal_set(i+(16*16), PALRGB(0, i, i)); 94 | md_pal_set(i+(17*16), PALRGB(0, 0, i)); 95 | md_pal_set(i+(18*16), PALRGB(i, 0, i)); 96 | md_pal_set(i+(19*16), PALRGB(i, i/2, 0)); 97 | } 98 | 99 | md_pal_set_bg_bank(0); 100 | } 101 | 102 | void main(void) 103 | { 104 | megadrive_init(); 105 | initialize_resources(); 106 | draw_interface(); 107 | draw_bg_color_bars(); 108 | 109 | // Make a bunch of noise. 110 | md_psg_vol(0, 0); 111 | md_psg_vol(1, 0); 112 | md_psg_vol(2, 0); 113 | md_adpcm_play(0); 114 | 115 | static uint16_t period = 0; 116 | while (1) 117 | { 118 | md_psg_pitch(2, 2 * (0x400 - period)); 119 | md_psg_pitch(1, 0x400 - period); 120 | md_psg_pitch(0, period++); 121 | draw_sprite_color_bars(); 122 | megadrive_finish(); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /mdk/util/png2csp/src/types.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | 3 | #include 4 | #include 5 | 6 | static ConvOrigin conv_origin_from_string(const char *argstr) 7 | { 8 | ConvOrigin ret = CONV_ORIGIN_DEFAULT; 9 | switch (argstr[0]) 10 | { 11 | case 'l': 12 | case 'L': 13 | ret = CONV_ORIGIN_LEFT_TOP; 14 | break; 15 | case 'c': 16 | case 'C': 17 | ret = CONV_ORIGIN_CENTER_TOP; 18 | break; 19 | case 'r': 20 | case 'R': 21 | ret = CONV_ORIGIN_RIGHT_TOP; 22 | break; 23 | default: 24 | printf("Warning: Unhandled X origin argument '%c'.\n", argstr[0]); 25 | return ret; 26 | } 27 | 28 | switch (argstr[1]) 29 | { 30 | case 't': 31 | case 'T': 32 | switch (ret) 33 | { 34 | case CONV_ORIGIN_LEFT_TOP: 35 | ret = CONV_ORIGIN_LEFT_TOP; 36 | break; 37 | case CONV_ORIGIN_CENTER_TOP: 38 | ret = CONV_ORIGIN_CENTER_TOP; 39 | break; 40 | case CONV_ORIGIN_RIGHT_TOP: 41 | ret = CONV_ORIGIN_RIGHT_TOP; 42 | break; 43 | case CONV_ORIGIN_LEFT_CENTER: 44 | ret = CONV_ORIGIN_LEFT_TOP; 45 | break; 46 | case CONV_ORIGIN_CENTER_CENTER: 47 | ret = CONV_ORIGIN_CENTER_TOP; 48 | break; 49 | case CONV_ORIGIN_RIGHT_CENTER: 50 | ret = CONV_ORIGIN_RIGHT_TOP; 51 | break; 52 | case CONV_ORIGIN_LEFT_BOTTOM: 53 | ret = CONV_ORIGIN_LEFT_TOP; 54 | break; 55 | case CONV_ORIGIN_CENTER_BOTTOM: 56 | ret = CONV_ORIGIN_CENTER_TOP; 57 | break; 58 | case CONV_ORIGIN_RIGHT_BOTTOM: 59 | ret = CONV_ORIGIN_RIGHT_TOP; 60 | break; 61 | } 62 | break; 63 | case 'c': 64 | case 'C': 65 | switch (ret) 66 | { 67 | case CONV_ORIGIN_LEFT_TOP: 68 | ret = CONV_ORIGIN_LEFT_CENTER; 69 | break; 70 | case CONV_ORIGIN_CENTER_TOP: 71 | ret = CONV_ORIGIN_CENTER_CENTER; 72 | break; 73 | case CONV_ORIGIN_RIGHT_TOP: 74 | ret = CONV_ORIGIN_RIGHT_CENTER; 75 | break; 76 | case CONV_ORIGIN_LEFT_CENTER: 77 | ret = CONV_ORIGIN_LEFT_CENTER; 78 | break; 79 | case CONV_ORIGIN_CENTER_CENTER: 80 | ret = CONV_ORIGIN_CENTER_CENTER; 81 | break; 82 | case CONV_ORIGIN_RIGHT_CENTER: 83 | ret = CONV_ORIGIN_RIGHT_CENTER; 84 | break; 85 | case CONV_ORIGIN_LEFT_BOTTOM: 86 | ret = CONV_ORIGIN_LEFT_CENTER; 87 | break; 88 | case CONV_ORIGIN_CENTER_BOTTOM: 89 | ret = CONV_ORIGIN_CENTER_CENTER; 90 | break; 91 | case CONV_ORIGIN_RIGHT_BOTTOM: 92 | ret = CONV_ORIGIN_RIGHT_CENTER; 93 | break; 94 | } 95 | break; 96 | case 'b': 97 | case 'B': 98 | switch (ret) 99 | { 100 | case CONV_ORIGIN_LEFT_TOP: 101 | ret = CONV_ORIGIN_LEFT_BOTTOM; 102 | break; 103 | case CONV_ORIGIN_CENTER_TOP: 104 | ret = CONV_ORIGIN_CENTER_BOTTOM; 105 | break; 106 | case CONV_ORIGIN_RIGHT_TOP: 107 | ret = CONV_ORIGIN_RIGHT_BOTTOM; 108 | break; 109 | case CONV_ORIGIN_LEFT_CENTER: 110 | ret = CONV_ORIGIN_LEFT_BOTTOM; 111 | break; 112 | case CONV_ORIGIN_CENTER_CENTER: 113 | ret = CONV_ORIGIN_CENTER_BOTTOM; 114 | break; 115 | case CONV_ORIGIN_RIGHT_CENTER: 116 | ret = CONV_ORIGIN_RIGHT_BOTTOM; 117 | break; 118 | case CONV_ORIGIN_LEFT_BOTTOM: 119 | ret = CONV_ORIGIN_LEFT_BOTTOM; 120 | break; 121 | case CONV_ORIGIN_CENTER_BOTTOM: 122 | ret = CONV_ORIGIN_CENTER_BOTTOM; 123 | break; 124 | case CONV_ORIGIN_RIGHT_BOTTOM: 125 | ret = CONV_ORIGIN_RIGHT_BOTTOM; 126 | break; 127 | } 128 | break; 129 | default: 130 | printf("Warning: Unhandled Y origin argument '%c'.\n", argstr[1]); 131 | return ret; 132 | } 133 | return ret; 134 | } 135 | 136 | ConvOrigin conv_origin_from_args(int argc, char **argv) 137 | { 138 | if (argc < 7) return CONV_ORIGIN_DEFAULT; 139 | const char *argstr = argv[6]; 140 | if (strlen(argstr) < 2) 141 | { 142 | printf("Warning: Invalid origin '%s'; need two characters.\n", argstr); 143 | return CONV_ORIGIN_DEFAULT; 144 | } 145 | 146 | return conv_origin_from_string(argstr); 147 | } 148 | -------------------------------------------------------------------------------- /mdk/src/md/cspr_put.a68: -------------------------------------------------------------------------------- 1 | .section .text 2 | 3 | .extern g_sprite_count 4 | .extern g_sprite_table 5 | .extern g_sprite_next 6 | 7 | .include "md/cspr_types.inc" 8 | 9 | .set MD_SPR_MAX, 80 10 | 11 | # 12 | ; void md_cspr_put_st(const CSprParam *s); 13 | # 14 | .global md_cspr_put_st 15 | .set SPID, 4 16 | 17 | ; SP - pushed args 18 | .set ARG_CSPR_STRUCT, SPID 19 | 20 | ; A0 - draw params (CSprParam struct) 21 | .set PRM_CSPR_DATA, 0 22 | .set PRM_VRAM_BASE, 4 23 | .set PRM_FRAME, 6 24 | .set PRM_X, 8 25 | .set PRM_Y, 10 26 | .set PRM_ATTR, 12 27 | .set PRM_USE_DMA, 14 28 | 29 | md_cspr_put_st: 30 | ; a0 := draw params (CSprParam) 31 | movea.l ARG_CSPR_STRUCT(sp), a0 32 | 33 | movem.l a2-a3, -(sp) 34 | movem.l d2-d3/d7, -(sp) 35 | 36 | ; a3 := MD hardware sprite slot 37 | movea.l g_sprite_next, a3 38 | 39 | ; a1 := cspr blob 40 | move.l PRM_CSPR_DATA(a0), a1 41 | 42 | ; a2 := frame ref 43 | lea CSPR_REFS(a1), a2 44 | move.w PRM_FRAME(a0), d0 45 | cmp.w CSPR_REF_COUNT(a1), d0 46 | bcc.w 0f 47 | lsl.w #3, d0 ; index by 8, sizeof(CSprRef) 48 | adda.w d0, a2 ; a2 now points to the ref 49 | 50 | ; Sprite count checks 51 | move.w REF_SPR_COUNT(a2), d7 52 | beq.w 0f ; no sprites in this frame 53 | 54 | move.w d7, d0 55 | add.w g_sprite_count, d0 ; d0 := final sprite index 56 | cmpi.w #MD_SPR_MAX, d0 ; compare d0 to max sprites 57 | bls.s sprite_count_ok 58 | ; Too many sprites; limit amount in d7. 59 | move.w #MD_SPR_MAX, d7 ; d7 takes max 60 | sub.w g_sprite_count, d7 ; d7 takes available slots 61 | ble.w 0f 62 | sprite_count_ok: 63 | 64 | ; Queue DMA if needed. 65 | move.w PRM_VRAM_BASE(a0), d1 66 | tst.w PRM_USE_DMA(a0) 67 | beq.s cspr_put_no_dma 68 | 69 | jsr (pc, cspr_dma_setup_sub) 70 | bra.s cspr_put_after_dma 71 | 72 | cspr_put_no_dma: 73 | ; Just offset vram base 74 | lsr.w #5, d1 75 | add.w REF_TILE_SRC_OFFSET(a2), d1 76 | bra.s cspr_put_attributes_set 77 | 78 | cspr_put_after_dma: 79 | ; d1 := base attributes for sprite 80 | lsr.w #5, d1 81 | cspr_put_attributes_set: 82 | or.w PRM_ATTR(a0), d1 83 | 84 | ; a1 := spr (CSprSprite) 85 | adda.l CSPR_SPR_LIST_OFFSET(a1), a1 86 | adda.w REF_SPR_LIST_OFFSET(a2), a1 87 | 88 | ; d2 := sprite X 89 | move.w PRM_X(a0), d2 90 | 91 | ; d3 := sprite Y 92 | move.w PRM_Y(a0), d3 93 | 94 | subq.w #1, d7 ; for dbf loop 95 | 96 | btst #11, d1 ; H flip? 97 | bne.w cspr_hflip 98 | btst #12, d1 ; V flip? 99 | bne.s cspr_draw_top_vflip 100 | 101 | .macro cspr_draw_body_safe 102 | ; X pos checks 103 | cmpi.w #128+320, d2 104 | bcc.s 1f ; skip sprite 105 | cmpi.w #128-32, d2 106 | bls.s 1f ; skip sprite 107 | ; Y pos checks 108 | cmpi.w #128+240, d3 109 | bcc.s 1f ; skip sprite 110 | cmpi.w #128-32, d3 111 | bls.s 1f ; skip sprite 112 | 113 | ; With bounds checks done we can now write to the sprite. 114 | move.w d3, (a3)+ ; Y 115 | move.b SPR_SIZE(a1), (a3)+ ; leave link field alone 116 | addq.l #1, a3 117 | move.w d1, d0 ; Base attr in d1 118 | add.w SPR_TILE(a1), d0 119 | move.w d0, (a3)+ ; Attr 120 | move.w d2, (a3)+ ; X 121 | add.w #1, g_sprite_count 122 | 1: 123 | lea $10(a1), a1 124 | .endm 125 | 126 | cspr_draw_top_normal: 127 | add.w SPR_DX(a1), d2 128 | add.w SPR_DY(a1), d3 129 | cspr_draw_body_safe 130 | dbf d7, cspr_draw_top_normal 131 | bra.w cspr_draw_finished 132 | 133 | cspr_draw_top_vflip: 134 | add.w SPR_DX(a1), d2 135 | add.w SPR_FDY(a1), d3 136 | cspr_draw_body_safe 137 | dbf d7, cspr_draw_top_vflip 138 | bra.w cspr_draw_finished 139 | 140 | cspr_hflip: 141 | btst #12, d1 ; V flip? 142 | bne.s cspr_draw_top_hvflip 143 | 144 | cspr_draw_top_hflip: 145 | add.w SPR_FDX(a1), d2 146 | add.w SPR_DY(a1), d3 147 | cspr_draw_body_safe 148 | dbf d7, cspr_draw_top_hflip 149 | bra.s cspr_draw_finished 150 | 151 | cspr_draw_top_hvflip: 152 | add.w SPR_FDX(a1), d2 153 | add.w SPR_FDY(a1), d3 154 | cspr_draw_body_safe 155 | dbf d7, cspr_draw_top_hvflip 156 | ; fall-through to cspr_draw_finished 157 | 158 | cspr_draw_finished: 159 | 0: 160 | move.l a3, g_sprite_next 161 | movem.l (sp)+, d2-d3/d7 162 | movem.l (sp)+, a2-a3 163 | rts 164 | -------------------------------------------------------------------------------- /samples/mdsdrv-usage/src/mdsdrv/mdsdrv.c: -------------------------------------------------------------------------------- 1 | //====================================================================== 2 | // MDSDRV API wrapper for MDK 3 | //====================================================================== 4 | // Copyright (c) 2020 Ian Karlsson 5 | // Source modified 2022 by Mike Moffitt 6 | // 7 | // This software is provided 'as-is', without any express or implied 8 | // warranty. In no event will the authors be held liable for any 9 | // damages arising from the use of this software. 10 | // 11 | // Permission is granted to anyone to use this software for any 12 | // purpose, including commercial applications, and to alter it and 13 | // redistribute it freely, subject to the following restrictions: 14 | // 15 | // 1. The origin of this software must not be misrepresented; you must 16 | // not claim that you wrote the original software. If you use this 17 | // software in a product, an acknowledgment in the product 18 | // documentation would be appreciated but is not required. 19 | // 2. Altered source versions must be plainly marked as such, and must 20 | // not be misrepresented as being the original software. 21 | // 3. This notice may not be removed or altered from any source 22 | // distribution. 23 | //====================================================================== 24 | 25 | #include "md/megadrive.h" 26 | #include "mdsdrv/mdsdrv.h" 27 | 28 | #include 29 | 30 | #include "res.h" 31 | 32 | // Sound driver work area. 33 | 34 | #define MDS_WORK_SIZE 512 35 | static uint16_t s_mds_work[MDS_WORK_SIZE]; 36 | 37 | uint16_t mds_init(const uint8_t *seqdata, const uint8_t *pcmdata) 38 | { 39 | register uint16_t *a0 asm ("a0") = s_mds_work; 40 | register const uint8_t *a1 asm ("a1") = seqdata; 41 | register const uint8_t *a2 asm ("a2") = pcmdata; 42 | register uint16_t d0 asm ("d0") = 0; 43 | asm volatile ( 44 | "jsr res_mdsdrv_bin+0" 45 | : "+a" (a0), "+a" (a1), "=r" (d0) 46 | : "a" (a2) 47 | : "d1", "cc"); 48 | 49 | return d0; 50 | } 51 | 52 | 53 | void mds_request(MdsSlot slot, uint16_t id) 54 | { 55 | register uint16_t *a0 asm ("a0") = s_mds_work; 56 | register uint16_t d0 asm ("d0") = id; 57 | register uint16_t d1 asm ("d1") = slot; 58 | asm volatile ( 59 | "jsr res_mdsdrv_bin+8" 60 | : 61 | : "r" (d0), "r" (d1), "a" (a0) 62 | : "cc" ); 63 | } 64 | 65 | uint32_t mds_command(uint16_t id, uint16_t param) 66 | { 67 | register uint16_t *a0 asm ("a0") = s_mds_work; 68 | register uint32_t d0 asm ("d0") = id; 69 | register uint16_t d1 asm ("d1") = param; 70 | asm volatile ( 71 | "jsr res_mdsdrv_bin+12" 72 | : "+r" (d0), "+r" (d1) 73 | : "a" (a0) 74 | : "a1", "d2", "cc"); 75 | return d0; 76 | } 77 | 78 | 79 | uint32_t mds_command2(uint16_t id, uint16_t param1, uint16_t param2) 80 | { 81 | register uint16_t *a0 asm ("a0") = s_mds_work; 82 | register uint32_t d0 asm ("d0") = id; 83 | register uint16_t d1 asm ("d1") = param1; 84 | register uint16_t d2 asm ("d2") = param2; 85 | asm volatile ( 86 | "jsr res_mdsdrv_bin+12" 87 | : "+r" (d0), "+r" (d1), "+r" (d2) 88 | : "a" (a0) 89 | : "a1", "cc"); 90 | return d0; 91 | } 92 | 93 | void mds_update(void) 94 | { 95 | register uint16_t *a0 asm ("a0") = s_mds_work; 96 | register void *a6 asm ("a6"); // Hack since gcc didn't like clobbering the frame pointer 97 | asm volatile ( 98 | "jsr res_mdsdrv_bin+4" 99 | : "=a" (a6) 100 | : "a" (a0) 101 | : "a1", "a2", "a3", "a4", "a5", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "cc"); 102 | } 103 | 104 | const char *mds_get_version_str(void) 105 | { 106 | register uint16_t *a0 asm ("a0") = s_mds_work; 107 | register uint32_t d0 asm ("d0") = MDS_CMD_GET_VERSION; 108 | asm volatile ( 109 | "jsr res_mdsdrv_bin+12" 110 | : "+r" (d0), "+a" (a0) 111 | : 112 | : "a1", "d1", "d2", "cc"); 113 | return (char*)a0; 114 | } 115 | 116 | void mds_pause(MdsSlot slot, uint8_t paused) 117 | { 118 | mds_command2(MDS_CMD_SET_PAUSE, slot, paused); 119 | } 120 | 121 | 122 | void mds_fade(uint8_t target, uint8_t speed, uint8_t stop_when_done) 123 | { 124 | mds_command(MDS_CMD_FADE_BGM, (speed << 8) | ((stop_when_done & 1) << 7) | (target & 0x7f)); 125 | } 126 | -------------------------------------------------------------------------------- /mdk/src/md/ioc.h: -------------------------------------------------------------------------------- 1 | // mdk System C/C2 I/O support 2 | // Michael Moffitt 2018-2024 3 | #pragma once 4 | 5 | #ifdef MDK_TARGET_C2 6 | 7 | #ifdef __cplusplus 8 | extern "C" 9 | { 10 | #endif // __cplusplus 11 | 12 | #include 13 | #include 14 | 15 | // Input types for C/C2. 16 | 17 | #define SYSC_PL_LEFT 0x80 18 | #define SYSC_PL_RIGHT 0x40 19 | #define SYSC_PL_UP 0x20 20 | #define SYSC_PL_DOWN 0x10 21 | #define SYSC_PL_D 0x08 22 | #define SYSC_PL_C 0x04 23 | #define SYSC_PL_B 0x02 24 | #define SYSC_PL_A 0x01 25 | 26 | #define SYSC_MISC_MB3773P_RESET 0x80 27 | #define SYSC_MISC_UPD7759_BUSY 0x40 28 | #define SYSC_MISC_CN2_PIN8 0x20 29 | #define SYSC_MISC_CN2_PIN7 0x10 30 | #define SYSC_MISC_CN2_PIN5 0x08 31 | #define SYSC_MISC_CN2_PIN4 0x04 32 | #define SYSC_MISC_CN2_PIN3 0x02 33 | #define SYSC_MISC_CN2_PIN2 0x01 34 | 35 | #define SYSC_SYSTEM_SELECT 0x40 36 | #define SYSC_SYSTEM_START2 0x20 37 | #define SYSC_SYSTEM_START1 0x10 38 | #define SYSC_SYSTEM_SERVICE 0x08 39 | #define SYSC_SYSTEM_TEST 0x04 40 | #define SYSC_SYSTEM_COIN1 0x02 41 | #define SYSC_SYSTEM_COIN2 0x01 42 | 43 | #define SYSC_DIPSW_7 0x80 44 | #define SYSC_DIPSW_6 0x40 45 | #define SYSC_DIPSW_5 0x20 46 | #define SYSC_DIPSW_4 0x10 47 | #define SYSC_DIPSW_3 0x08 48 | #define SYSC_DIPSW_2 0x04 49 | #define SYSC_DIPSW_1 0x02 50 | #define SYSC_DIPSW_0 0x01 51 | 52 | #define SYSC_COIN_METER1 (1 << 0) 53 | #define SYSC_COIN_METER2 (1 << 1) 54 | #define SYSC_COIN_LOCKOUT1 (1 << 2) 55 | #define SYSC_COIN_LOCKOUT2 (1 << 3) 56 | 57 | typedef enum MdIoCIoPort 58 | { 59 | SYSC_IO_PORT_A = 0, 60 | SYSC_IO_PORT_B = 1, 61 | SYSC_IO_PORT_C = 2, 62 | SYSC_IO_PORT_D = 3, 63 | SYSC_IO_PORT_E = 4, 64 | SYSC_IO_PORT_F = 5, 65 | SYSC_IO_PORT_G = 6, 66 | SYSC_IO_PORT_H = 7 67 | } MdIoCIoPort; 68 | 69 | typedef enum MdIoCInput 70 | { 71 | SYSC_INPUT_P1, 72 | SYSC_INPUT_P2, 73 | SYSC_INPUT_MISC, 74 | SYSC_INPUT_SYSTEM, 75 | SYSC_INPUT_DIP1, 76 | SYSC_INPUT_DIP2, 77 | SYSC_INPUT_TYPE_COUNT 78 | } MdIoCInput; 79 | 80 | // Input data for the various input ports. Index with MdIoCInput. 81 | // Data is freshly updated at the start of vblank with md_ioc_poll(). 82 | // Test for switches with the bitfields defined above. 83 | extern uint8_t g_md_c2_in[SYSC_INPUT_TYPE_COUNT]; 84 | extern uint8_t g_md_c2_in_prev[SYSC_INPUT_TYPE_COUNT]; 85 | extern uint8_t g_md_c2_in_pos[SYSC_INPUT_TYPE_COUNT]; 86 | extern uint8_t g_md_c2_in_neg[SYSC_INPUT_TYPE_COUNT]; 87 | 88 | // Read register data directly from Port A - Port H. 89 | // Reading from registers intended for output isn't recommended. 90 | uint8_t md_ioc_read_reg_raw(MdIoCIoPort port); 91 | 92 | // Setting outputs. Last register value is cached since readback can't be done. 93 | // Other code may use these (e.g. pal_systemc, adpcm_systemc) 94 | 95 | // Controls pin 3 of JP15. 96 | void md_ioc_set_watchdog_ctrl(bool jp15_pin3); 97 | 98 | // Mutes the audio amplifier. 99 | void md_ioc_set_tda1518bq_mute(bool mute); 100 | 101 | // Controls pin 10 and pin 11 of CN2. 102 | void md_ioc_set_cn2_bits(bool pin10, bool pin11); 103 | 104 | // Sets the coin meter and coil driver outputs. Use SYSC_COIN_* definitions. 105 | void md_ioc_set_coin_outputs(uint8_t state); 106 | 107 | // Set two bits corresponding to CN4 outputs A19 and B19. 108 | void md_ioc_set_cn4_bits(bool a19, bool b19); 109 | 110 | // Set upper address pins for uPD7759 sample ROM A17-A18. 111 | // Upper bits theorized to exist via Charles McDonald's doc, unconfirmed. 112 | // Banks 0-3 valid; possibly up to 0xF. 113 | void md_ioc_set_udp7759_bank(uint16_t bank); 114 | 115 | // Assert (drive to 0) or Deassert (raise to 1) the reset pin for the uPD7759. 116 | void md_ioc_set_upd7759_reset(bool asserted); 117 | 118 | // Returns true if the uPD7759 reports it is busy. 119 | bool md_ioc_get_upd7759_busy(void); 120 | 121 | // Set global palette bank 0-3 through bits A9 and A10 of CRAM. 122 | void md_ioc_set_pal_bank(uint16_t bank); 123 | 124 | // Returns the global palette bank. 125 | uint16_t md_ioc_get_pal_bank(void); 126 | 127 | // Internal Use --------------------------------------------------------------- 128 | 129 | // Called at startup to clear IO register caches. 130 | void md_ioc_init(void); 131 | 132 | // Poll controller inputs. 133 | void md_ioc_poll(void); 134 | 135 | #ifdef __cplusplus 136 | } 137 | #endif // __cplusplus 138 | 139 | #endif // MDK_TARGET_C2 140 | -------------------------------------------------------------------------------- /samples/cspr-demo/src/main.c: -------------------------------------------------------------------------------- 1 | // mdk composite sprite demo 2 | // Michael Moffitt 2023 3 | // 4 | // This file shwos how to draw composite sprites made with png2cspr. 5 | 6 | #include "md/megadrive.h" 7 | 8 | #include "util/text.h" 9 | #include "util/plane.h" 10 | 11 | #include "res.h" 12 | 13 | #include 14 | #include 15 | 16 | void main(void) 17 | { 18 | megadrive_init(); 19 | text_init(res_font_gfx_bin, sizeof(res_font_gfx_bin), 0x400, res_font_pal_bin, 0); 20 | 21 | text_puts(VDP_PLANE_A, 1, 1, "==== MDK COMPOSITE SPRITE SAMPLE! ===="); 22 | 23 | md_cspr_upload_pal(res_count_csp, 1); 24 | md_cspr_upload_pal(res_cirno_csp, 2); 25 | md_cspr_upload_pal(res_sonic_csp, 3); 26 | 27 | const char *kinstructions[] = 28 | { 29 | "BUTTON A: Select Sprite", 30 | "BUTTON B: Toggle V flip", 31 | "BUTTON C: Toggle H flip", 32 | "BUTTON X: Toggle priority", 33 | "BUTTON Y: Change palette", 34 | "BUTTON Z: Test \"fast\" routine", 35 | " D-PAD: Move Sprite", 36 | " START: Pause / Resume", 37 | }; 38 | 39 | for (uint16_t i = 0; i < MDK_ARRAYSIZE(kinstructions); i++) 40 | { 41 | const int16_t print_y = 28 - MDK_ARRAYSIZE(kinstructions) + i - 1; 42 | text_puts(VDP_PLANE_A, 1, print_y, kinstructions[i]); 43 | } 44 | 45 | // The sprites. 46 | CSprParam cspr_param[16]; 47 | // First one will show the count animation. 48 | cspr_param[0].cspr_data = res_count_csp; 49 | cspr_param[0].vram_base = 0x1000; 50 | cspr_param[0].x = 282; 51 | cspr_param[0].y = 52; 52 | cspr_param[0].attr = SPR_ATTR(0, 0, 0, 1, 0); 53 | cspr_param[0].frame = 0; 54 | cspr_param[0].use_dma = true; 55 | // Second is sonic. 56 | cspr_param[1].cspr_data = res_sonic_csp; 57 | cspr_param[1].vram_base = 0x1800; 58 | cspr_param[1].x = 32; 59 | cspr_param[1].y = 128; 60 | cspr_param[1].attr = SPR_ATTR(0, 0, 0, 3, 0); 61 | cspr_param[1].frame = 0; 62 | cspr_param[1].use_dma = true; 63 | // Populate with data for the cirnos. 64 | for (uint16_t i = 2; i < MDK_ARRAYSIZE(cspr_param); i++) 65 | { 66 | cspr_param[i].cspr_data = res_cirno_csp; 67 | cspr_param[i].vram_base = 0x1C00; 68 | cspr_param[i].x = (18 * i); 69 | cspr_param[i].y = 10+(12 * i); 70 | cspr_param[i].attr = SPR_ATTR(0, 0, 0, 2, 0); 71 | cspr_param[i].frame = i % md_cspr_get_frame_count(res_cirno_csp); 72 | cspr_param[i].use_dma = false; 73 | } 74 | 75 | // Cirno isn't using DMA, so tiles should be uploaded prior to drawing. 76 | md_cspr_upload_tiles(res_cirno_csp, 0x1C00); 77 | 78 | uint16_t frame = 0; 79 | uint16_t selected_spr = 0; 80 | bool anim_pause = false; 81 | while (true) 82 | { 83 | frame++; 84 | 85 | // Animate and render 86 | if (!anim_pause) 87 | { 88 | for (uint16_t i = 0; i < MDK_ARRAYSIZE(cspr_param); i++) 89 | { 90 | if (frame % 8 == 0) cspr_param[i].frame++; 91 | const uint16_t max = md_cspr_get_frame_count(cspr_param[i].cspr_data); 92 | if (cspr_param[i].frame >= max) 93 | cspr_param[i].frame = 0; 94 | } 95 | } 96 | 97 | // The A button changes which sprite is under control. 98 | if (g_md_pad_pos[0] & BTN_A) 99 | { 100 | selected_spr++; 101 | if (selected_spr >= MDK_ARRAYSIZE(cspr_param)) selected_spr = 0; 102 | } 103 | 104 | // Start is used to pause and resume. 105 | if (g_md_pad_pos[0] & BTN_START) anim_pause = !anim_pause; 106 | // Movement of selected sprite 107 | if (g_md_pad[0] & BTN_UP) cspr_param[selected_spr].y--; 108 | if (g_md_pad[0] & BTN_DOWN) cspr_param[selected_spr].y++; 109 | if (g_md_pad[0] & BTN_LEFT) cspr_param[selected_spr].x--; 110 | if (g_md_pad[0] & BTN_RIGHT) cspr_param[selected_spr].x++; 111 | // Flipping of sprites with B and C buttons 112 | if (g_md_pad_pos[0] & BTN_C) 113 | cspr_param[selected_spr].attr ^= SPR_ATTR(0, 1, 0, 0, 0); 114 | if (g_md_pad_pos[0] & BTN_B) 115 | cspr_param[selected_spr].attr ^= SPR_ATTR(0, 0, 1, 0, 0); 116 | // Various attr bits with X and Y 117 | if (g_md_pad_pos[0] & BTN_X) 118 | cspr_param[selected_spr].attr ^= SPR_ATTR(0, 0, 0, 0, 1); 119 | if (g_md_pad_pos[0] & BTN_Y) 120 | { 121 | int16_t pal = (cspr_param[selected_spr].attr >> 13) & 0x03; 122 | pal = (pal + 1) & 0x03; 123 | cspr_param[selected_spr].attr &= ~SPR_ATTR(0, 0, 0, 3, 0); 124 | cspr_param[selected_spr].attr |= SPR_ATTR(0, 0, 0, pal, 0); 125 | } 126 | 127 | // Use the MODE button to test the "fast" drawing function.. 128 | for (uint16_t i = 0; i < MDK_ARRAYSIZE(cspr_param); i++) 129 | { 130 | if (g_md_pad[0] & BTN_Z) md_cspr_put_st_fast(&cspr_param[i]); 131 | else md_cspr_put_st(&cspr_param[i]); 132 | } 133 | megadrive_finish(); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /samples/cd-mode-1/src/cd_sub_cpu_program.a68: -------------------------------------------------------------------------------- 1 | ; Written by Chilly Willy. 2 | 3 | .text 4 | 5 | .global Sub_Start 6 | Sub_Start: 7 | 8 | ; Standard MegaCD Sub-CPU Program Header (copied to $6000) 9 | 10 | SPHeader: 11 | .asciz "MAIN-SUBCPU" 12 | .word $0001,$0000 13 | .long $00000000 14 | .long $00000000 15 | .long SPHeaderOffsets-SPHeader 16 | .long $00000000 17 | 18 | SPHeaderOffsets: 19 | .word SPInit-SPHeaderOffsets 20 | .word SPMain-SPHeaderOffsets 21 | .word SPInt2-SPHeaderOffsets 22 | .word SPNull-SPHeaderOffsets 23 | .word $0000 24 | 25 | ; Sub-CPU Program Initialization (VBlank not enabled yet) 26 | 27 | SPInit: 28 | move.b #'I,$800F.w ; sub comm port = INITIALIZING 29 | andi.b #$E2,$8003.w ; Priority Mode = off, 2M mode, Sub-CPU has access 30 | rts 31 | 32 | ; Sub-CPU Program Main Entry Point (VBlank now enabled) 33 | 34 | SPMain: 35 | move.w #$0081,d0 ; CDBSTAT 36 | jsr $5F22.w ; call CDBIOS function 37 | move.w 0(a0),d0 ; BIOS status word 38 | bmi.b 1f ; not ready 39 | lsr.w #8,d0 40 | cmpi.b #$40,d0 41 | beq.b 9f ; open 42 | cmpi.b #$10,d0 43 | beq.b 9f ; no disc 44 | 1: 45 | ; Initialize Drive 46 | lea drive_init_parms(pc),a0 47 | move.w #$0010,d0 ; DRVINIT 48 | jsr $5F22.w ; call CDBIOS function 49 | 50 | move.w #$0089,d0 ; CDCSTOP - stop reading data 51 | jsr $5F22.w ; call CDBIOS function 52 | 9: 53 | move.b #0,$800F.w ; sub comm port = READY 54 | 55 | ; wait for command in main comm port 56 | WaitCmd: 57 | tst.b $800E.w 58 | beq.b WaitCmd 59 | cmpi.b #'D,$800E.w 60 | beq GetDiscInfo 61 | cmpi.b #'T,$800E.w 62 | beq GetTrackInfo 63 | cmpi.b #'P,$800E.w 64 | beq PlayTrack 65 | cmpi.b #'S,$800E.w 66 | beq StopPlaying 67 | cmpi.b #'Z,$800E.w 68 | beq PauseResume 69 | cmpi.b #'C,$800E.w 70 | beq CheckDisc 71 | move.b #'E,$800F.w ; sub comm port = ERROR 72 | WaitAck: 73 | tst.b $800E.w 74 | bne.b WaitAck ; wait for result acknowledged 75 | move.b #0,$800F.w ; sub comm port = READY 76 | bra.b WaitCmd 77 | 78 | GetDiscInfo: 79 | move.w #$0081,d0 ; CDBSTAT 80 | jsr $5F22.w ; call CDBIOS function 81 | move.w 0(a0),$8020.w ; BIOS status word 82 | move.w 16(a0),$8022.w ; First song number, Last song number 83 | move.w 18(a0),$8024.w ; Drive version, Flag 84 | 85 | move.b #'D,$800F.w ; sub comm port = DONE 86 | bra WaitAck 87 | 88 | GetTrackInfo: 89 | move.w $8010.w,d1 ; track number 90 | move.w #$0083,d0 ; CDBTOCREAD 91 | jsr $5F22.w ; call CDBIOS function 92 | move.l d0,$8020.w ; MMSSFFTN 93 | move.b d1,$8024.w ; track type 94 | 95 | move.b #'D,$800F.w ; sub comm port = DONE 96 | bra WaitAck 97 | 98 | PlayTrack: 99 | move.w #$0002,d0 ; MSCSTOP - stop playing 100 | jsr $5F22.w ; call CDBIOS function 101 | 102 | move.w $8010.w,d1 ; track number 103 | move.w #$0011,d0 ; MSCPLAY - play from track on 104 | move.b $8012.w,d2 ; flag 105 | bmi.b 2f 106 | beq.b 1f 107 | move.w #$0013,d0 ; MSCPLAYR - play with repeat 108 | bra.b 2f 109 | 1: 110 | move.w #$0012,d0 ; MSCPLAY1 - play once 111 | 2: 112 | lea track_number(pc),a0 113 | move.w d1,(a0) 114 | jsr $5F22.w ; call CDBIOS function 115 | 116 | move.b #'D,$800F.w ; sub comm port = DONE 117 | bra WaitAck 118 | 119 | StopPlaying: 120 | move.w #$0002,d0 ; MSCSTOP - stop playing 121 | jsr $5F22.w ; call CDBIOS function 122 | 123 | move.b #'D,$800F.w ; sub comm port = DONE 124 | bra WaitAck 125 | 126 | PauseResume: 127 | move.w #$0081,d0 ; CDBSTAT 128 | jsr $5F22.w ; call CDBIOS function 129 | move.b (a0),d0 130 | cmpi.b #1,d0 131 | beq.b 1f ; playing - pause playback 132 | cmpi.b #5,d0 133 | beq.b 2f ; paused - resume playback 134 | 135 | move.b #'E,$800F.w ; sub comm port = ERROR 136 | bra WaitAck 137 | 1: 138 | move.w #$0003,d0 ; MSCPAUSEON - pause playback 139 | jsr $5F22.w ; call CDBIOS function 140 | 141 | move.b #'D,$800F.w ; sub comm port = DONE 142 | bra WaitAck 143 | 2: 144 | move.w #$0004,d0 ; MSCPAUSEOFF - resume playback 145 | jsr $5F22.w ; call CDBIOS function 146 | 147 | move.b #'D,$800F.w ; sub comm port = DONE 148 | bra WaitAck 149 | 150 | CheckDisc: 151 | lea drive_init_parms(pc),a0 152 | move.w #$0010,d0 ; DRVINIT 153 | jsr $5F22.w ; call CDBIOS function 154 | 155 | move.w #$0089,d0 ; CDCSTOP - stop reading data 156 | jsr $5F22.w ; call CDBIOS function 157 | 158 | move.b #'D,$800F.w ; sub comm port = DONE 159 | bra WaitAck 160 | 161 | 162 | ; Sub-CPU Program VBlank (INT02) Service Handler 163 | 164 | SPInt2: 165 | rts 166 | 167 | ; Sub-CPU program Reserved Function 168 | 169 | SPNull: 170 | rts 171 | 172 | 173 | ; Sub-CPU variables 174 | 175 | .align 2 176 | drive_init_parms: 177 | .byte $01, $FF ; first track (1), last track (all) 178 | 179 | track_number: 180 | .word 0 181 | 182 | 183 | 184 | .global Sub_End 185 | Sub_End: 186 | 187 | -------------------------------------------------------------------------------- /mdk/util/image/pngto/indexedimage.c: -------------------------------------------------------------------------------- 1 | /* 2 | Indexed image loading and blitting 3 | 4 | Copyright (c) 2019 Damian Yerrick 5 | 6 | This software is provided 'as-is', without any express or implied 7 | warranty. In no event will the authors be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this software must not be misrepresented; you must not 15 | claim that you wrote the original software. If you use this software 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 22 | 3. This notice may not be removed or altered from any source 23 | distribution. 24 | */ 25 | 26 | #include "indexedimage.h" 27 | #include "lodepng.h" 28 | #include 29 | 30 | unsigned IndexedImage_init(IndexedImage *im, unsigned width, unsigned height) { 31 | unsigned char *pixels = calloc(width, height); 32 | if (!pixels) return 83; // Memory exhausted 33 | im->pixels = pixels; 34 | im->width = width; 35 | im->height = height; 36 | return 0; 37 | } 38 | 39 | void IndexedImage_cleanup(IndexedImage *im) { 40 | free(im->pixels); 41 | im->pixels = 0; 42 | free(im->palette); 43 | im->palette = 0; 44 | } 45 | 46 | void IndexedImage_clear(IndexedImage *im, unsigned pixelvalue) { 47 | memset(im->pixels, pixelvalue, im->width * im->height); 48 | } 49 | 50 | void IndexedImage_paste(const IndexedImage *restrict dst, 51 | const IndexedImage *restrict src, 52 | int x, int y) { 53 | unsigned char *dstrow = dst->pixels; 54 | unsigned char *srcrow = src->pixels; 55 | unsigned int width = dst->width; 56 | unsigned int height = dst->height; 57 | 58 | // Horizontal clipping 59 | { 60 | unsigned int srcw = src->width; 61 | if (x < 0) { 62 | // src is partially to the left of dst, but is it 63 | // entirely off the left side? 64 | if ((unsigned)-(unsigned)x >= srcw) return; 65 | // Clip off the left side of src 66 | srcw += x; 67 | srcrow -= x; 68 | x = 0; 69 | } 70 | // src is at or to the right of the left of dst, but is it 71 | // entirely off the right side? 72 | if ((unsigned)x >= width) return; 73 | width -= x; 74 | dstrow += x; 75 | if (width > srcw) width = srcw; 76 | } 77 | 78 | // Vertical clipping 79 | { 80 | unsigned int srch = src->height; 81 | if (y < 0) { 82 | // src is partially to the left of dst, but is it 83 | // entirely off the left side? 84 | if ((unsigned)-(unsigned)y >= srch) return; 85 | // Clip off the left side of src 86 | srch += y; 87 | srcrow += src->width * (-y); 88 | y = 0; 89 | } 90 | // src is at or to the right of the left of dst, but is it 91 | // entirely off the right side? 92 | if ((unsigned)y >= height) return; 93 | height -= y; 94 | dstrow += dst->width * y; 95 | if (height > srch) height = srch; 96 | } 97 | 98 | for (; height > 0; 99 | --height, srcrow += src->width, dstrow += dst->width) { 100 | memcpy(dstrow, srcrow, width); 101 | } 102 | } 103 | 104 | /* LodePNG integration *********************************************/ 105 | 106 | void *memdup(const void *mem, size_t size) { 107 | void *out = malloc(size); 108 | if(out != NULL) memcpy(out, mem, size); 109 | return out; 110 | } 111 | 112 | unsigned IndexedImage_frompng(IndexedImage *im, const char* filename) { 113 | unsigned error; 114 | unsigned char *png = 0; 115 | size_t pngsize; // file size 116 | unsigned char *image = 0; 117 | unsigned width, height; 118 | LodePNGState state; 119 | unsigned char *palette = 0; 120 | 121 | // Load the file into memory 122 | error = lodepng_load_file(&png, &pngsize, filename); 123 | if (error) { 124 | free(png); 125 | return error; 126 | } 127 | 128 | // Decode the indexed image as is, not converting it 129 | // to 24-bit RGB or 32-bit RGBA 130 | lodepng_state_init(&state); 131 | state.info_raw.colortype = LCT_PALETTE; 132 | state.info_raw.bitdepth = 8; 133 | error = lodepng_decode(&image, &width, &height, &state, png, pngsize); 134 | free(png); 135 | 136 | // At this point: 137 | // Palette is in state.info_png.color.palette, 4 bytes per color 138 | // Palette length in colors is in state.info_png.color.palettesize 139 | // (length in bytes is 4 times this) 140 | // Make a local copy of the palette 141 | if (!error) { 142 | palette = memdup(state.info_png.color.palette, 143 | 4 * state.info_png.color.palettesize); 144 | if (state.info_png.color.palettesize > 0 && !palette) { 145 | error = 83; // Memory exhausted 146 | } 147 | } 148 | 149 | if (!error) { 150 | im->pixels = image; 151 | im->width = width; 152 | im->height = height; 153 | im->palettesize = state.info_png.color.palettesize; 154 | im->palette = palette; 155 | } 156 | 157 | lodepng_state_cleanup(&state); 158 | return error; 159 | } 160 | -------------------------------------------------------------------------------- /samples/move-podge/src/main.c: -------------------------------------------------------------------------------- 1 | // mdk example "move-podge" main.c 2 | // Damian Yerrick 2018, 2019 3 | // Michael Moffitt 2018-2022 4 | // 5 | // This main shows a character's walk cycle. 6 | 7 | #include "md/megadrive.h" 8 | 9 | #include "util/text.h" 10 | #include "util/plane.h" 11 | 12 | #include "res.h" 13 | 14 | /* Color notes 15 | 16 | Per https://gendev.spritesmind.net/forum/viewtopic.php?t=2188 17 | the VDP's DAC is somewhat nonlinear, and the actual usable levels 18 | are 0 52 87 116 144 172 206 255. Less shadow detail. 19 | 20 | In web color format, these are the usable grays: 21 | #000000, #343434, #575757, #747474, #909090, #ACACAC, #CECECE, #FFFFFF 22 | 23 | */ 24 | 25 | /* Player movement *************************************************/ 26 | 27 | #define WALK_SPD 105 28 | #define WALK_ACCEL 4 29 | #define WALK_BRAKE 8 30 | #define LEFT_WALL 40 31 | #define RIGHT_WALL 280 32 | #define PLAYER_Y 168 33 | 34 | #define PLAYER_CEL_ADDR 0x8000 35 | 36 | static unsigned int s_player_x; 37 | static signed short s_player_dx; 38 | static unsigned short s_player_frame; 39 | static unsigned short s_player_facing; 40 | 41 | static SprParam s_spr_param; 42 | 43 | static void load_player(void) 44 | { 45 | s_player_x = 64 << 8; 46 | s_player_dx = s_player_frame = s_player_facing = 0; 47 | md_dma_transfer_vram(PLAYER_CEL_ADDR, (void *)res_PodgeH24_gfx_bin, 32*6*8/2, 2); 48 | md_pal_upload(32, (void *)res_PodgeH24_pal_bin, 16); 49 | 50 | // Set sprite drawing parameters that won't be changed. 51 | s_spr_param.size = SPR_SIZE(2, 3); 52 | s_spr_param.y = PLAYER_Y; 53 | } 54 | 55 | static void move_player(void) 56 | { 57 | unsigned short cur_keys = g_md_pad[0]; 58 | // Acceleration and braking while moving right 59 | if (s_player_dx >= 0) 60 | { 61 | if (cur_keys & BTN_RIGHT) 62 | { 63 | s_player_dx += WALK_ACCEL; 64 | if (s_player_dx > WALK_SPD) s_player_dx = WALK_SPD; 65 | s_player_facing = 0; // 0: R 66 | } 67 | else 68 | { 69 | s_player_dx -= WALK_BRAKE; 70 | if (s_player_dx < 0) s_player_dx = 0; 71 | } 72 | } 73 | 74 | // Acceleration and braking while moving left 75 | if (s_player_dx <= 0) 76 | { 77 | if (cur_keys & BTN_LEFT) 78 | { 79 | s_player_dx -= WALK_ACCEL; 80 | if (s_player_dx < -WALK_SPD) s_player_dx = -WALK_SPD; 81 | s_player_facing = 1; // 1: L 82 | } 83 | else 84 | { 85 | s_player_dx += WALK_BRAKE; 86 | if (s_player_dx > 0) s_player_dx = 0; 87 | } 88 | } 89 | 90 | // In a real game you'd respond to C, B, Up, Down, etc. here 91 | s_player_x += s_player_dx; 92 | 93 | // Test for collision with side walls 94 | if (s_player_x < (LEFT_WALL + 4) << 8) 95 | { 96 | s_player_x = (LEFT_WALL + 4) << 8; 97 | s_player_dx = 0; 98 | } 99 | else if (s_player_x >= (RIGHT_WALL - 4) << 8) 100 | { 101 | s_player_x = (RIGHT_WALL - 5) << 8; 102 | s_player_dx = 0; 103 | } 104 | 105 | // Animate the player 106 | if (s_player_dx == 0) 107 | { 108 | s_player_frame = 0xC0; 109 | } 110 | else 111 | { 112 | unsigned int absspeed = (s_player_dx < 0) ? -s_player_dx : s_player_dx; 113 | s_player_frame += absspeed * 5 / 16; 114 | 115 | // Wrap from end of walk cycle (7) to start of walk cycle (1) 116 | if (s_player_frame >= 0x800) s_player_frame -= 0x700; 117 | } 118 | } 119 | 120 | void draw_player_sprite(void) 121 | { 122 | unsigned short framenum = s_player_frame >> 8; 123 | unsigned short tilenum = framenum * 6 + (PLAYER_CEL_ADDR>>5); 124 | unsigned short player_hotspot_x = (s_player_x >> 8) - 8; 125 | 126 | if (framenum == 7) 127 | { 128 | // This frame needs to be drawn 1 pixel forward 129 | player_hotspot_x += s_player_facing ? -1 : 1; 130 | } 131 | 132 | s_spr_param.x = player_hotspot_x; 133 | s_spr_param.attr = SPR_ATTR(tilenum, s_player_facing, 0, 2, 0); 134 | md_spr_put_st(&s_spr_param); 135 | } 136 | 137 | void draw_bg(void) 138 | { 139 | // Set up text graphics at VRAM address 0x400 palette 0 140 | // This lines it up nicely with the actual ASCII values, which we will 141 | // use to draw letters with sprites. 142 | text_init(res_font_gfx_bin, 2560, 0x400, res_font_pal_bin, 0); 143 | 144 | // Print a simple message in the center of plane A 145 | text_puts(VDP_PLANE_A, 7, 11, "MOVE PODGE WITH THE D-PAD"); 146 | 147 | // Draw the cubes 148 | md_vdp_set_autoinc(2); 149 | for (unsigned short y = 20; y < 24; ++y) 150 | { 151 | unsigned int dest_base = VRAM_SCRA_BASE_DEFAULT + y * 128 + 6; 152 | MD_SYS_BARRIER(); 153 | VDPPORT_CTRL32 = VDP_CTRL_VRAM_WRITE | VDP_CTRL_ADDR(dest_base); 154 | MD_SYS_BARRIER(); 155 | VDPPORT_DATA = 0x62 | (y & 1); 156 | VDPPORT_DATA = 0x64 | (y & 1); 157 | MD_SYS_BARRIER(); 158 | VDPPORT_CTRL32 = VDP_CTRL_VRAM_WRITE | VDP_CTRL_ADDR(dest_base + 64); 159 | MD_SYS_BARRIER(); 160 | VDPPORT_DATA = 0x62 | (y & 1); 161 | VDPPORT_DATA = 0x64 | (y & 1); 162 | } 163 | 164 | // Draw the floor 165 | MD_SYS_BARRIER(); 166 | unsigned int dest_base = VRAM_SCRA_BASE_DEFAULT + 24 * 128; 167 | VDPPORT_CTRL32 = VDP_CTRL_VRAM_WRITE | VDP_CTRL_ADDR(dest_base); 168 | for (unsigned i = 64; i > 0; --i) 169 | { 170 | VDPPORT_DATA = 0x60; 171 | } 172 | for (unsigned i = 64 * 3; i > 0; --i) 173 | { 174 | VDPPORT_DATA = 0x61; 175 | } 176 | } 177 | 178 | void main(void) 179 | { 180 | megadrive_init(); 181 | 182 | s_spr_param = (SprParam){0}; 183 | draw_bg(); 184 | load_player(); 185 | 186 | while (1) 187 | { 188 | move_player(); 189 | draw_player_sprite(); 190 | megadrive_finish(); 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /mdk/src/md/sys.h: -------------------------------------------------------------------------------- 1 | // mdk system functions 2 | // Michael Moffitt 2018-2024 3 | // 4 | // Most things in this file rely on the arbiter / controller in the Mega Drive. 5 | // System C2 does not have this IC, so most functions either work differently or 6 | // are dummy / no-op. 7 | #pragma once 8 | 9 | #ifdef __cplusplus 10 | extern "C" 11 | { 12 | #endif // __cplusplus 13 | 14 | #include 15 | #include 16 | #include "md/mmio.h" 17 | 18 | // ============================================================================= 19 | // Interface 20 | // ============================================================================= 21 | 22 | // If on Mega Drive / Genesis, initializes TMSS if applicable. 23 | void md_sys_init(void); 24 | 25 | // Inserts a memory barrier. 26 | #define MD_SYS_BARRIER() __asm__ __volatile__("": : :"memory") 27 | 28 | // ----------------------------------------------------------------------------- 29 | // Interrupts. 30 | // ----------------------------------------------------------------------------- 31 | 32 | // Enable interrupts. 33 | static inline uint8_t md_sys_ei(void); 34 | // Disable interrupts. Returns the previous interrupt enablement status, so that 35 | // they may be conditionally re-enabled later. 36 | static inline uint8_t md_sys_di(void); 37 | 38 | // ----------------------------------------------------------------------------- 39 | // Z80 control functions 40 | // ----------------------------------------------------------------------------- 41 | 42 | // Uploads a binary blob to the Z80 program memory. Holds the Z80 bus, but does 43 | // not reset the z80. 44 | void md_sys_z80_init(const uint8_t *src, uint16_t size); 45 | 46 | // Requests access to the Z80 bus, halting it. Can block until granted. 47 | static inline void md_sys_z80_bus_req(bool wait); 48 | static inline void md_sys_z80_bus_release(void); 49 | static inline bool md_sys_z80_bus_taken(void); 50 | // Set the Z80 reset line high or low. Low (asserted) holds it and the YM2612 51 | // in a reset state. 52 | static inline void md_sys_z80_reset_deassert(void); 53 | static inline void md_sys_z80_reset_assert(void); 54 | 55 | // ----------------------------------------------------------------------------- 56 | // System information 57 | // ----------------------------------------------------------------------------- 58 | static inline bool md_sys_is_overseas(void); 59 | static inline bool md_sys_is_pal(void); 60 | static inline bool md_sys_is_disk_present(void); 61 | static inline uint8_t md_sys_get_hw_rev(void); 62 | 63 | // ============================================================================= 64 | // Implementations 65 | // ============================================================================= 66 | extern bool g_md_sys_ints_enabled; 67 | 68 | static inline uint8_t md_sys_ei(void) 69 | { 70 | #ifdef MDK_TARGET_C2 71 | __asm__ volatile("\tandi.w #0xFCFF, %sr\n"); 72 | #else 73 | __asm__ volatile("\tandi.w #0xF8FF, %sr\n"); 74 | #endif 75 | const uint8_t ret = g_md_sys_ints_enabled; 76 | g_md_sys_ints_enabled = true; 77 | return ret; 78 | } 79 | 80 | static inline uint8_t md_sys_di(void) 81 | { 82 | #ifdef MDK_TARGET_C2 83 | __asm__ volatile("\tori.w #0x0700, %sr\n"); 84 | #else 85 | __asm__ volatile("\tori.w #0x0700, %sr\n"); 86 | #endif 87 | const uint8_t ret = g_md_sys_ints_enabled; 88 | g_md_sys_ints_enabled = false; 89 | return ret; 90 | } 91 | 92 | // ----------------------------------------------------------------------------- 93 | // Megadrive Implementations 94 | // ----------------------------------------------------------------------------- 95 | 96 | #ifndef MDK_TARGET_C2 97 | 98 | static inline bool md_sys_is_overseas(void) 99 | { 100 | return (SYS_PORT_VERSION & 0x80) ? true : false; 101 | } 102 | 103 | static inline bool md_sys_is_pal(void) 104 | { 105 | return (SYS_PORT_VERSION & 0x40) ? true : false; 106 | } 107 | 108 | static inline bool md_sys_is_disk_present(void) 109 | { 110 | return (SYS_PORT_VERSION & 0x20) ? true : false; 111 | } 112 | 113 | static inline uint8_t md_sys_get_hw_rev(void) 114 | { 115 | return SYS_PORT_VERSION & 0x0F; 116 | } 117 | 118 | static inline bool md_sys_z80_bus_taken(void) 119 | { 120 | volatile uint8_t *z80_bus = (volatile uint8_t *)SYS_Z80_PORT_BUS_LOC; 121 | return !(*z80_bus & 0x01); 122 | } 123 | 124 | static inline void md_sys_z80_bus_req(bool wait) 125 | { 126 | volatile uint8_t *z80_bus = (volatile uint8_t *)SYS_Z80_PORT_BUS_LOC; 127 | 128 | *z80_bus = 0x01; 129 | if (!wait) return; 130 | while (*z80_bus & 0x01) __asm__("nop"); 131 | } 132 | 133 | static inline void md_sys_z80_bus_release(void) 134 | { 135 | volatile uint8_t *z80_bus = (volatile uint8_t *)SYS_Z80_PORT_BUS_LOC; 136 | *z80_bus = 0x00; 137 | } 138 | 139 | static inline void md_sys_z80_reset_deassert(void) 140 | { 141 | volatile uint8_t *z80_reset = (volatile uint8_t *)SYS_Z80_PORT_RESET_LOC; 142 | *z80_reset = 0x01; 143 | } 144 | 145 | static inline void md_sys_z80_reset_assert(void) 146 | { 147 | volatile uint8_t *z80_reset = (volatile uint8_t *)SYS_Z80_PORT_RESET_LOC; 148 | *z80_reset = 0x00; 149 | } 150 | 151 | // ----------------------------------------------------------------------------- 152 | // System C/C2 Implementations 153 | // ----------------------------------------------------------------------------- 154 | 155 | #else 156 | 157 | static inline bool md_sys_is_overseas(void) 158 | { 159 | return false; 160 | } 161 | 162 | static inline bool md_sys_is_pal(void) 163 | { 164 | return false; 165 | } 166 | 167 | static inline bool md_sys_is_disk_present(void) 168 | { 169 | return false; 170 | } 171 | 172 | static inline uint8_t md_sys_get_hw_rev(void) 173 | { 174 | return 0; 175 | } 176 | 177 | static inline bool md_sys_z80_bus_taken(void) 178 | { 179 | return 0; 180 | } 181 | 182 | // There's no Z80. 183 | static inline uint16_t md_sys_z80_get_bus_status(void) 184 | { 185 | return 0; 186 | } 187 | 188 | static inline uint16_t md_sys_z80_get_reset_status(void) 189 | { 190 | return 0; 191 | } 192 | 193 | static inline void md_sys_z80_bus_req(bool wait) 194 | { 195 | (void)wait; 196 | } 197 | 198 | static inline void md_sys_z80_bus_release(void) 199 | { 200 | } 201 | 202 | static inline void md_sys_z80_reset_deassert(void) 203 | { 204 | } 205 | 206 | static inline void md_sys_z80_reset_assert(void) 207 | { 208 | } 209 | 210 | #endif // MDK_TARGET_C2 211 | 212 | #ifdef __cplusplus 213 | } 214 | #endif // __cplusplus 215 | -------------------------------------------------------------------------------- /mdk/util/png2csp/src/records.c: -------------------------------------------------------------------------------- 1 | #include "records.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "constants.h" 9 | #include "claim.h" 10 | #include "endian.h" 11 | 12 | // 13 | // Types 14 | // 15 | 16 | // 17 | // REF 18 | // 19 | 20 | /* 21 | 2 spr quantity used 22 | 2 spr list index * sizeof(spr def) 23 | 2 tile data start offset (within tile data block) 24 | 2 tile count * 16 (words, for DMA, pre-multiplied by VDP tile size) 25 | */ 26 | 27 | typedef struct RecordRef 28 | { 29 | int spr_count; 30 | int spr_index; 31 | int tile_index; // Offset within tile bank. 32 | int tile_count; 33 | } RecordRef; 34 | 35 | 36 | // 37 | // SPR 38 | // 39 | 40 | /* 41 | 2 relative Y offset (signed word) 42 | 2 sprite size attribute field (other bits 0) 43 | 2 tile id 44 | 2 relative X offset (signed word) 45 | 2 flip X offset 46 | 2 flip Y offset 47 | 4 reserved 48 | */ 49 | 50 | typedef struct RecordSpr 51 | { 52 | int dx, dy; // Relative offsets. 53 | int w, h; // In tiles. 54 | int tile; // From first tile of tile bank. 55 | int flip_dx, flip_dy; 56 | } RecordSpr; 57 | 58 | // 59 | // Storage 60 | // 61 | 62 | // "REF" structures, metasprites. 63 | static RecordRef s_ref_dat[RECORD_MAX_REF_COUNT]; 64 | static int s_ref_count = 0; 65 | // "SPR" structures, hardware sprite draw data. 66 | static RecordSpr s_spr_dat[RECORD_MAX_SPR_COUNT]; 67 | static uint32_t s_spr_count = 0; 68 | // Tile data. 69 | static uint8_t s_tile_dat[RECORD_MAX_TILE_COUNT * TBYTES]; 70 | static int s_tile_count = 0; 71 | // Palette data. 72 | static uint16_t s_pal_dat[PALSIZE]; 73 | 74 | // 75 | // Functions for recording output file data 76 | // 77 | 78 | static void write_ref(const RecordRef *ref, FILE *f) 79 | { 80 | fwrite_uint16be(ref->spr_count, f); 81 | fwrite_uint16be(ref->spr_index * 16, f); // * sizeof(spr_def) 82 | fwrite_uint16be(ref->tile_index, f); // Tile offset within tile data. 83 | fwrite_uint16be(ref->tile_count * (TBYTES / 2), f); // DMA size in words. 84 | } 85 | 86 | static void write_spr(const RecordSpr *spr, FILE *f) 87 | { 88 | fwrite_int16be(spr->dy, f); 89 | const uint16_t sizebits = ((spr->h-1) << 8) | ((spr->w-1) << 10); 90 | fwrite_uint16be(sizebits, f); 91 | fwrite_uint16be(spr->tile, f); 92 | fwrite_int16be(spr->dx, f); 93 | fwrite_int16be(spr->flip_dy, f); 94 | fwrite_uint16be(0, f); 95 | fwrite_uint16be(0, f); 96 | fwrite_int16be(spr->flip_dx, f); 97 | } 98 | 99 | // 100 | // Record functions 101 | // 102 | 103 | bool record_init(void) 104 | { 105 | s_tile_count = 0; 106 | s_spr_count = 0; 107 | s_ref_count = 0; 108 | return true; 109 | } 110 | 111 | void record_complete(const char *spr_name, const char *fname) 112 | { 113 | FILE *f = fopen(fname, "wb"); 114 | if (!f) 115 | { 116 | fprintf(stderr, "Couldn't open \"%s\" for writing.\n", fname); 117 | return; 118 | } 119 | 120 | /* 121 | Binary blob format: 122 | ------------------- 123 | $00 16 bank name (null-terminated C string) 124 | $10 32 palette data 125 | $30 2 sprite ref count (available images) 126 | $32 4 spr list offset (from start of file) 127 | $36 4 tile data offset (from start of file) 128 | $3A 2 tile count 129 | $3C 4 reserved 130 | $40 ... ref list 131 | ... ... spr list data (length variable; referened by header) 132 | ... ... tile data (length variable; referenced by header) 133 | */ 134 | 135 | // Write the bank name. 136 | char spr_name_out[16]; 137 | strncpy(spr_name_out, spr_name, sizeof(spr_name_out)-1); 138 | spr_name_out[15] = '\0'; 139 | fwrite(spr_name_out, 1, sizeof(spr_name_out), f); 140 | // Palette data. 141 | for (int i = 0; i < PALSIZE; i++) 142 | { 143 | fwrite_uint16be(s_pal_dat[i], f); 144 | } 145 | // Sprite ref count. 146 | fwrite_uint16be(s_ref_count, f); 147 | // Offsets (written later). 148 | const long spr_list_offset_loc = ftell(f); 149 | fwrite_uint32be(0, f); 150 | const long tile_data_offset_loc = ftell(f); 151 | fwrite_uint32be(0, f); 152 | // Tile count. 153 | fwrite_uint16be(s_tile_count, f); 154 | // Padding. 155 | fwrite_uint16be(0, f); 156 | fwrite_uint16be(0, f); 157 | // The ref list. 158 | for (int i = 0; i < s_ref_count; i++) 159 | { 160 | write_ref(&s_ref_dat[i], f); 161 | } 162 | // The spr list. 163 | const long spr_list_loc = ftell(f); 164 | for (int i = 0; i < s_spr_count; i++) 165 | { 166 | write_spr(&s_spr_dat[i], f); 167 | } 168 | // The tile data. 169 | const long tile_data_loc = ftell(f); 170 | fwrite(s_tile_dat, TBYTES, s_tile_count, f); 171 | 172 | // Compute offsets and record them. 173 | fseek(f, spr_list_offset_loc, SEEK_SET); 174 | fwrite_uint32be((uint32_t)(spr_list_loc), f); 175 | fseek(f, tile_data_offset_loc, SEEK_SET); 176 | fwrite_uint32be((uint32_t)(tile_data_loc), f); 177 | 178 | fclose(f); 179 | } 180 | 181 | void record_palette(const uint16_t *pal_data) 182 | { 183 | memcpy(s_pal_dat, pal_data, sizeof(uint16_t) * PALSIZE); 184 | } 185 | 186 | void record_ref(int spr_count, int spr_index, int tile_index, int tile_count) 187 | { 188 | if (s_ref_count >= RECORD_MAX_REF_COUNT) 189 | { 190 | fprintf(stderr, "Can't record any more REF data!\n"); 191 | return; 192 | } 193 | RecordRef *ref = &s_ref_dat[s_ref_count]; 194 | s_ref_count++; 195 | 196 | ref->spr_count = spr_count; 197 | ref->spr_index = spr_index; 198 | ref->tile_index = tile_index; 199 | ref->tile_count = tile_count; 200 | } 201 | 202 | void record_spr(int dx, int dy, int w, int h, int tile, int flip_dx, int flip_dy) 203 | { 204 | if (s_spr_count >= RECORD_MAX_SPR_COUNT) 205 | { 206 | fprintf(stderr, "Can't record any more SPR data!\n"); 207 | return; 208 | } 209 | RecordSpr *spr = &s_spr_dat[s_spr_count]; 210 | s_spr_count++; 211 | 212 | spr->dx = dx; 213 | spr->dy = dy; 214 | spr->w = w; 215 | spr->h = h; 216 | spr->tile = tile; 217 | spr->flip_dx = flip_dx; 218 | spr->flip_dy = flip_dy; 219 | } 220 | 221 | void record_tiles(const uint8_t *src, int count) 222 | { 223 | if (s_tile_count + count > RECORD_MAX_TILE_COUNT) 224 | { 225 | fprintf(stderr, "Cannot record any more tiles!\n"); 226 | return; 227 | } 228 | memcpy(&s_tile_dat[s_tile_count * TBYTES], src, TBYTES * count); 229 | s_tile_count += count; 230 | } 231 | 232 | int record_get_tile_count(void) 233 | { 234 | return s_tile_count; 235 | } 236 | 237 | int record_get_spr_count(void) 238 | { 239 | return s_spr_count; 240 | } 241 | 242 | int record_get_ref_count(void) 243 | { 244 | return s_ref_count; 245 | } 246 | -------------------------------------------------------------------------------- /mdk/src/md/pal.c: -------------------------------------------------------------------------------- 1 | /* mdk palette support functions for MD 2 | Michael Moffitt 2018-2022 */ 3 | 4 | #include "md/pal.h" 5 | #include "md/dma.h" 6 | #include "md/macro.h" 7 | #include "md/vdp.h" 8 | #include "md/ioc.h" 9 | 10 | #include 11 | 12 | #ifndef MDK_TARGET_C2 13 | 14 | #define MDK_PAL_DIRTY_MASK_FULL 0x000F 15 | uint16_t g_palette[16 * 4]; 16 | static uint16_t s_dirty = MDK_PAL_DIRTY_MASK_FULL; 17 | #else 18 | // Two sets, between BG and Sprites of: 19 | // Four banks of: 20 | // Four palette lines, each with: 21 | // Sixteen colors. 22 | // Four global banks exist, but I do not feel they are particularly useful when 23 | // four banks already exist within each color section. 24 | uint16_t g_palette[16 * 4 * 4 * 2]; 25 | #define MDK_PAL_DIRTY_MASK_FULL 0x1FFFF 26 | static uint32_t s_dirty = MDK_PAL_DIRTY_MASK_FULL; 27 | static uint8_t s_prot_reg_cache; 28 | #endif // MDK_TARGET_C2 29 | 30 | // Interface 31 | // ----------------------------------------------------------------------------- 32 | void md_pal_mark_dirty(uint16_t first_index, uint16_t count) 33 | { 34 | const uint16_t first_line = (first_index / 16) % (MDK_ARRAYSIZE(g_palette) / 16); 35 | const uint16_t last_line = (count / 16) % (MDK_ARRAYSIZE(g_palette) / 16); 36 | if (last_line < first_line) 37 | { 38 | s_dirty |= MDK_PAL_DIRTY_MASK_FULL; 39 | return; 40 | } 41 | 42 | uint32_t dirty_mask = (1 << first_line); 43 | for (uint16_t i = 0; i <= last_line - first_line; i++) 44 | { 45 | s_dirty |= dirty_mask; 46 | dirty_mask = dirty_mask << 1; 47 | } 48 | } 49 | 50 | 51 | // Individual color set functions 52 | // ----------------------------------------------------------------------------- 53 | 54 | void md_pal_set(uint16_t idx, uint16_t val) 55 | { 56 | idx = idx % MDK_ARRAYSIZE(g_palette); 57 | g_palette[idx] = val; 58 | s_dirty |= (1 << (idx >> 4)); 59 | } 60 | 61 | // Color upload functions 62 | // ----------------------------------------------------------------------------- 63 | 64 | // Upload as-is. 65 | void md_pal_upload(uint16_t dest, const void *source, uint16_t count) 66 | { 67 | if (dest + count > MDK_ARRAYSIZE(g_palette)) return; 68 | md_pal_mark_dirty(dest, count); 69 | memcpy(&g_palette[dest], source, count * sizeof(uint16_t)); 70 | } 71 | 72 | #ifndef MDK_TARGET_C2 73 | // ============================================================================= 74 | // Megadrive implementation, using DMA 75 | // ============================================================================= 76 | void md_pal_poll(void) 77 | { 78 | // The s_dirty bitfield is broken down case by case here because 79 | // consecutive palette lines can be uploaded in one DMA transfer. 80 | switch (s_dirty) 81 | { 82 | case 0x0: // .... 83 | break; 84 | 85 | case 0x1: // 0... 86 | md_dma_transfer_cram(0, &g_palette[0], 16, 2); 87 | break; 88 | 89 | case 0x2: // .1.. 90 | md_dma_transfer_cram(32, &g_palette[16], 16, 2); 91 | break; 92 | 93 | case 0x3: // 01.. 94 | md_dma_transfer_cram(0, &g_palette[0], 32, 2); 95 | break; 96 | 97 | case 0x4: // ..2. 98 | md_dma_transfer_cram(64, &g_palette[32], 16, 2); 99 | break; 100 | 101 | case 0x5: // 0.2. 102 | md_dma_transfer_cram(0, &g_palette[0], 16, 2); 103 | md_dma_transfer_cram(64, &g_palette[32], 16, 2); 104 | break; 105 | 106 | case 0x6: // .12. 107 | md_dma_transfer_cram(32, &g_palette[16], 16, 2); 108 | md_dma_transfer_cram(64, &g_palette[32], 16, 2); 109 | break; 110 | 111 | case 0x7: // 012. 112 | md_dma_transfer_cram(0, &g_palette[0], 48, 2); 113 | break; 114 | 115 | case 0x8: // ...3 116 | md_dma_transfer_cram(96, &g_palette[48], 16, 2); 117 | break; 118 | 119 | case 0x9: // 0..3 120 | md_dma_transfer_cram(96, &g_palette[48], 16, 2); 121 | md_dma_transfer_cram(0, &g_palette[0], 16, 2); 122 | break; 123 | 124 | case 0xA: // .1.3 125 | md_dma_transfer_cram(96, &g_palette[48], 16, 2); 126 | md_dma_transfer_cram(32, &g_palette[16], 16, 2); 127 | break; 128 | 129 | case 0xB: // 01.3 130 | md_dma_transfer_cram(96, &g_palette[48], 16, 2); 131 | md_dma_transfer_cram(0, &g_palette[0], 32, 2); 132 | break; 133 | 134 | case 0xC: // ..23 135 | md_dma_transfer_cram(64, &g_palette[48], 32, 2); 136 | break; 137 | 138 | case 0xD: // 0.23 139 | md_dma_transfer_cram(64, &g_palette[48], 32, 2); 140 | md_dma_transfer_cram(0, &g_palette[0], 16, 2); 141 | break; 142 | 143 | case 0xE: // .123 144 | md_dma_transfer_cram(32, &g_palette[48], 48, 2); 145 | break; 146 | 147 | case 0xF: // 0123 148 | md_dma_transfer_cram(0, &g_palette[0], 64, 2); 149 | break; 150 | } 151 | s_dirty = 0; 152 | } 153 | 154 | void md_pal_init(void) 155 | { 156 | memset(g_palette, 0, sizeof(g_palette)); 157 | s_dirty = 0x000F; 158 | } 159 | 160 | #else 161 | 162 | // ============================================================================= 163 | // System C/C2 implementation, as a memory copy into color RAM 164 | // ============================================================================= 165 | 166 | // Select between banks 0 - 3 for sprites and backgrounds. 167 | void md_pal_set_spr_bank(uint16_t bank) 168 | { 169 | s_prot_reg_cache &= ~(0x03 << 2); 170 | s_prot_reg_cache |= (bank & 0x03) << 2; 171 | volatile uint8_t *prot = (volatile uint8_t *)SYSC_PROTECTION_LOC_SECURITY; 172 | *prot = s_prot_reg_cache; 173 | } 174 | 175 | void md_pal_set_bg_bank(uint16_t bank) 176 | { 177 | s_prot_reg_cache &= ~(0x03); 178 | s_prot_reg_cache |= (bank & 0x03); 179 | volatile uint8_t *prot = (volatile uint8_t *)SYSC_PROTECTION_LOC_SECURITY; 180 | *prot = s_prot_reg_cache; 181 | } 182 | 183 | void md_pal_poll(void) 184 | { 185 | if (!s_dirty) return; 186 | md_vdp_set_cbus_cpu_mux(false); 187 | uint32_t test_bit = 0x00000001; 188 | volatile uint32_t *cram32 = (volatile uint32_t *)CRAM_SYSTEMC_LOC_BASE; 189 | volatile uint32_t *src32 = (uint32_t *)g_palette; 190 | 191 | for (uint16_t i = 0; i < MDK_ARRAYSIZE(g_palette) / 16; i++) 192 | { 193 | if (s_dirty & test_bit) 194 | { 195 | uint16_t *cram = (uint16_t *)CRAM_SYSTEMC_LOC_BASE; 196 | memcpy(&cram[i * 16], &g_palette[i * 16], sizeof(uint16_t) * 16); 197 | } 198 | test_bit = test_bit << 1; 199 | cram32 += 8; 200 | src32 += 8; 201 | } 202 | md_vdp_set_cbus_cpu_mux(true); 203 | s_dirty = 0; 204 | } 205 | 206 | void md_pal_init(void) 207 | { 208 | volatile uint8_t *prot = (volatile uint8_t *)SYSC_PROTECTION_LOC_SECURITY; 209 | *prot = 0x00; 210 | MD_SYS_BARRIER(); 211 | md_pal_set_spr_bank(0); 212 | md_pal_set_bg_bank(0); 213 | memset(g_palette, 0, sizeof(g_palette)); 214 | s_dirty = 0x0001FFFF; 215 | } 216 | 217 | #endif // MD_TARGET_C2 218 | -------------------------------------------------------------------------------- /mdk/util/core/bsplit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void split(const char *in_fname, const char *out_even_fname, const char *out_odd_fname, int bytes) 6 | { 7 | int c; 8 | FILE *fin, *fout_odd, *fout_even; 9 | fin = fopen(in_fname, "rb"); 10 | if (!fin) 11 | { 12 | fprintf(stderr, "Couldn't open %s.\n", in_fname); 13 | return; 14 | } 15 | fout_odd = fopen(out_odd_fname, "wb"); 16 | if (!fout_odd) 17 | { 18 | fprintf(stderr, "Couldn't open %s.\n", out_odd_fname); 19 | fclose(fin); 20 | return; 21 | } 22 | fout_even = fopen(out_even_fname, "wb"); 23 | if (!fout_even) 24 | { 25 | fprintf(stderr, "Couldn't open %s.\n", out_even_fname); 26 | fclose(fin); 27 | fclose(fout_odd); 28 | return; 29 | } 30 | 31 | do 32 | { 33 | for (int i = 0; i < bytes; i++) 34 | { 35 | c = fgetc(fin); 36 | if (feof(fin)) 37 | { 38 | goto done; 39 | } 40 | fputc(c, fout_even); 41 | } 42 | for (int i = 0; i < bytes; i++) 43 | { 44 | c = fgetc(fin); 45 | if (feof(fin)) 46 | { 47 | goto done; 48 | } 49 | fputc(c, fout_odd); 50 | } 51 | } 52 | while (c != EOF); 53 | done: 54 | fclose(fin); 55 | fclose(fout_odd); 56 | fclose(fout_even); 57 | } 58 | 59 | void combine(const char *in_even_fname, const char *in_odd_fname, const char *out_fname, int bytes) 60 | { 61 | int c; 62 | FILE *fin_odd, *fin_even, *fout; 63 | fout = fopen(out_fname, "wb"); 64 | if (!fout) 65 | { 66 | fprintf(stderr, "Couldn't open %s.\n", out_fname); 67 | return; 68 | } 69 | fin_even = fopen(in_even_fname, "rb"); 70 | if (!fin_even) 71 | { 72 | fprintf(stderr, "Couldn't open %s.\n", in_even_fname); 73 | fclose(fout); 74 | return; 75 | } 76 | fin_odd = fopen(in_odd_fname, "rb"); 77 | if (!fin_odd) 78 | { 79 | fprintf(stderr, "Couldn't open %s.\n", in_odd_fname); 80 | fclose(fin_even); 81 | fclose(fout); 82 | return; 83 | } 84 | 85 | do 86 | { 87 | for (int i = 0; i < bytes; i++) 88 | { 89 | c = fgetc(fin_even); 90 | if (c != EOF) 91 | { 92 | fputc(c, fout); 93 | } 94 | } 95 | 96 | for (int i = 0; i < bytes; i++) 97 | { 98 | c = fgetc(fin_odd); 99 | if (c != EOF) 100 | { 101 | fputc(c, fout); 102 | } 103 | } 104 | } while (c != EOF); 105 | 106 | fclose(fout); 107 | fclose(fin_even); 108 | fclose(fin_odd); 109 | } 110 | 111 | void exchange(const char *in_fname, const char *out_fname) 112 | { 113 | int c; 114 | uint8_t byte[2]; 115 | FILE *fin, *fout; 116 | fout = fopen(out_fname, "wb"); 117 | if (!fout) 118 | { 119 | fprintf(stderr, "Couldn't open %s.\n", out_fname); 120 | return; 121 | } 122 | fin = fopen(in_fname, "rb"); 123 | if (!fin) 124 | { 125 | fprintf(stderr, "Couldn't open %s.\n", in_fname); 126 | fclose(fout); 127 | return; 128 | } 129 | 130 | do 131 | { 132 | c = fgetc(fin); 133 | if (c == EOF) 134 | { 135 | break; 136 | } 137 | byte[0] = c; 138 | c = fgetc(fin); 139 | if (c == EOF) 140 | { 141 | break; 142 | } 143 | byte[1] = c; 144 | 145 | fputc(byte[1], fout); 146 | fputc(byte[0], fout); 147 | } while (c != EOF); 148 | 149 | fclose(fout); 150 | fclose(fin); 151 | } 152 | 153 | void exchange_nybbles(const char *in_fname, const char *out_fname) 154 | { 155 | int c; 156 | FILE *fin, *fout; 157 | fout = fopen(out_fname, "wb"); 158 | if (!fout) 159 | { 160 | fprintf(stderr, "Couldn't open %s.\n", out_fname); 161 | return; 162 | } 163 | fin = fopen(in_fname, "rb"); 164 | if (!fin) 165 | { 166 | fprintf(stderr, "Couldn't open %s.\n", in_fname); 167 | fclose(fout); 168 | return; 169 | } 170 | 171 | do 172 | { 173 | c = fgetc(fin); 174 | if (c == EOF) 175 | { 176 | break; 177 | } 178 | fputc(((c & 0xF) << 4) | ((c & 0xF0) >> 4), fout); 179 | } while (c != EOF); 180 | 181 | fclose(fout); 182 | fclose(fin); 183 | } 184 | 185 | void exchange_halfnybbles(const char *in_fname, const char *out_fname) 186 | { 187 | int c; 188 | uint8_t byte; 189 | FILE *fin, *fout; 190 | fout = fopen(out_fname, "wb"); 191 | if (!fout) 192 | { 193 | fprintf(stderr, "Couldn't open %s.\n", out_fname); 194 | return; 195 | } 196 | fin = fopen(in_fname, "rb"); 197 | if (!fin) 198 | { 199 | fprintf(stderr, "Couldn't open %s.\n", in_fname); 200 | fclose(fout); 201 | return; 202 | } 203 | 204 | do 205 | { 206 | c = fgetc(fin); 207 | if (c == EOF) 208 | { 209 | break; 210 | } 211 | byte = c; 212 | c = ((byte & 0x03) << 2) | ((byte & 0x0C) >> 2) | (((byte & 0x30) << 2) | ((byte & 0xC0) >> 2)); 213 | 214 | fputc(c, fout); 215 | } while (c != EOF); 216 | 217 | fclose(fout); 218 | fclose(fin); 219 | } 220 | 221 | void usage(const char *name) 222 | { 223 | printf("Usage: %s [op]\n", name); 224 | printf(" split: s in out.even out.odd \n"); 225 | printf(" combine: c in.even in.odd out \n"); 226 | printf(" exchange: x in out\n"); 227 | printf(" ex. 4bit: n in out\n"); 228 | printf(" ex. 2bit: z in out\n"); 229 | } 230 | 231 | int main(int argc, char **argv) 232 | { 233 | if (argc < 2) 234 | { 235 | usage(argv[0]); 236 | return 0; 237 | } 238 | 239 | int bytes = 1; 240 | 241 | if (argc > 5) 242 | { 243 | bytes = atoi(argv[5]); 244 | printf("Using %d bytes interleave cadence\n", bytes); 245 | } 246 | 247 | if (argv[1][0] == 's') 248 | { 249 | if (argc < 5) 250 | { 251 | usage(argv[0]); 252 | return 0; 253 | } 254 | printf("Splitting\n"); 255 | split(argv[2], argv[3], argv[4], bytes); 256 | } 257 | else if (argv[1][0] == 'c') 258 | { 259 | if (argc < 5) 260 | { 261 | usage(argv[0]); 262 | return 0; 263 | } 264 | printf("Combining\n"); 265 | combine(argv[2], argv[3], argv[4], bytes); 266 | } 267 | else if (argv[1][0] == 'x') 268 | { 269 | if (argc < 4) 270 | { 271 | usage(argv[0]); 272 | return 0; 273 | } 274 | printf("Exchanging bytes\n"); 275 | exchange(argv[2], argv[3]); 276 | } 277 | else if (argv[1][0] == 'n') 278 | { 279 | if (argc < 4) 280 | { 281 | usage(argv[0]); 282 | return 0; 283 | } 284 | printf("Exchanging nybbles\n"); 285 | exchange_nybbles(argv[2], argv[3]); 286 | } 287 | else if (argv[1][0] == 'z') 288 | { 289 | if (argc < 4) 290 | { 291 | usage(argv[0]); 292 | return 0; 293 | } 294 | printf("Exchanging half-nybbles\n"); 295 | exchange_halfnybbles(argv[2], argv[3]); 296 | } 297 | 298 | return 0; 299 | } 300 | -------------------------------------------------------------------------------- /mdk/util/core/bin2s.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | bin2s: convert a binary file to an ARM asm module 4 | for gfx/foo3.bin it'll write gfx_foo_bin (an array of char) 5 | and gfx_foo_bin_len (an unsigned int) 6 | for 4bit.chr it'll write _4bit_chr and _4bit_chr_len 7 | 8 | 9 | Copyright 2003, 2019 Damian Yerrick 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining 12 | a copy of this software and associated documentation files (the 13 | "Software"), to deal in the Software without restriction, including 14 | without limitation the rights to use, copy, modify, merge, publish, 15 | distribute, sublicense, and/or sell copies of the Software, and to 16 | permit persons to whom the Software is furnished to do so, subject to 17 | the following conditions: 18 | 19 | The above copyright notice and this permission notice shall be 20 | included in all copies or substantial portions of the Software. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 24 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 25 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 26 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 27 | AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 28 | OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 29 | IN THE SOFTWARE. 30 | 31 | */ 32 | 33 | 34 | 35 | /* 36 | .align 37 | .global SomeLabel_len 38 | .int 1234 39 | .global SomeLabel 40 | .byte blah,blah,blah,blah... 41 | */ 42 | 43 | 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | /* strnident() ************************* 50 | Print the closest valid C identifier to a given word. 51 | */ 52 | void strnident(FILE *fout, const char *src) 53 | { 54 | char got_first = 0; 55 | 56 | while (*src != 0) { 57 | int s = *src++; 58 | 59 | // Add an underscore before an initial digit 60 | if (isdigit(s) && !got_first) { 61 | fputc('_', fout); /* stick a '_' before an initial digit */ 62 | } 63 | 64 | /* convert out-of-range characters */ 65 | if (!isalpha(s) && !isdigit(s)) { 66 | s = '_'; 67 | } 68 | 69 | if (s) { 70 | fputc(s, fout); 71 | got_first = 1; 72 | } 73 | } 74 | } 75 | 76 | const char usageText[] = 77 | "usage: bin2s foo.bin bar.bin baz.bin > foo.s\n" 78 | "Converts binary files to GNU assembly language.\n" 79 | "Each object is named as the file path with non-alphanumeric\n" 80 | "characters converted to underscores. For example, 'res/kitten.chr'\n" 81 | "becomes 'res_kitten_chr'. Then each file is written as two symbols:\n" 82 | " 1. The symbol with '_size' appended (e.g. 'res_kitten_chr_size')\n" 83 | " Points to a uint32_t holding the file's length in bytes\n" 84 | " 2. The symbol itself (e.g. 'res_kitten_chr')\n" 85 | " Points to file's contents\n" 86 | "The sizes are 32-bit aligned, and the contents always directly follow\n" 87 | "the size, so a program can treat them as Pascal strings and traverse\n" 88 | "a set of files linearly.\n" 89 | ; 90 | 91 | const char versionText[] = 92 | "bin2s 19.11\n" 93 | "Copyright 2003, 2019 Damian Yerrick. Comes with NO WARRANTY.\n" 94 | "This is free software under the MIT/Expat license.\n" 95 | ; 96 | 97 | // Metadata procured from an optional external metadata file. 98 | typedef struct FileMeta { 99 | int alignment; 100 | } FileMeta; 101 | 102 | // Initializes metadata from (optional) filename with derivative path. 103 | void initialize_meta(const char *base_fname, FileMeta *meta) { 104 | memset(meta, 0, sizeof(*meta)); 105 | meta->alignment = 2; // Default. 106 | 107 | char meta_fname[256]; 108 | snprintf(meta_fname, sizeof(meta_fname), "%s.cfg", base_fname); 109 | FILE *f = fopen(meta_fname, "r"); 110 | if (!f) return; 111 | 112 | fseek(f, 0, SEEK_END); 113 | const size_t flen = ftell(f); 114 | fseek(f, 0, SEEK_SET); 115 | 116 | char *fbuf = malloc(flen); 117 | if (!fbuf) { 118 | fprintf(stderr, "cfg file of size $%X couldn't be loaded\n"); 119 | fclose(f); 120 | return; 121 | } 122 | 123 | fread(fbuf, 1, flen, f); 124 | fclose(f); 125 | 126 | static const char *delim = " \t\n\r"; 127 | 128 | char *fbuf_orig = fbuf; 129 | 130 | strtok(fbuf, delim); 131 | do { 132 | if (strcmp(fbuf, "align") == 0) { 133 | fbuf = strtok(NULL, delim); 134 | if (!fbuf) break; 135 | meta->alignment = strtoul(fbuf, 0, 0); 136 | } 137 | } 138 | while (fbuf = strtok(NULL, delim)); 139 | 140 | free(fbuf_orig); 141 | } 142 | 143 | int main(int argc, char **argv) { 144 | FILE *fin; 145 | long int filelen; 146 | int linelen; 147 | int arg; 148 | 149 | if (argc < 2) { 150 | fputs("bin2s: not enough arguments; try bin2s --help\n", stderr); 151 | return EXIT_FAILURE; 152 | } 153 | 154 | FileMeta meta; 155 | 156 | if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help") 157 | || !strcmp(argv[1], "-?")) { 158 | fputs(usageText, stdout); 159 | return 0; 160 | } 161 | 162 | for (arg = 1; arg < argc; arg++) { 163 | // Omit optional ".cfg" files 164 | if (strstr(argv[arg], ".cfg")) continue; 165 | initialize_meta(argv[arg], &meta); 166 | fin = fopen(argv[arg], "rb"); 167 | if (!fin) { 168 | fputs("bin2s: could not open ", stderr); 169 | perror(argv[arg]); 170 | return 1; 171 | } 172 | 173 | fseek(fin, 0, SEEK_END); 174 | filelen = ftell(fin); 175 | if (filelen == 0) { 176 | fclose(fin); 177 | fprintf(stderr, "bin2s: warning: skipping empty file %s\n", 178 | argv[arg]); 179 | continue; 180 | } 181 | rewind(fin); 182 | 183 | // The prolog for each included file has two purposes: 184 | // 1. align to 32-bit boundary 185 | // 2. provide length info (as a Pascal-like string) 186 | fprintf(stdout, 187 | "/* Generated by BIN2S - please don't edit directly */\n" 188 | ".section .rodata\n" 189 | ".balign %d\n" 190 | ".global ", meta.alignment); 191 | strnident(stdout, argv[arg]); 192 | fputs("_size\n", stdout); 193 | strnident(stdout, argv[arg]); 194 | printf("_size: .dc.l %lu\n.global ", (unsigned long)filelen); 195 | strnident(stdout, argv[arg]); 196 | fputs("\n", stdout); 197 | strnident(stdout, argv[arg]); 198 | fputs(":\n.byte ", stdout); 199 | 200 | linelen = 0; 201 | while (filelen > 0) { 202 | unsigned char c = fgetc(fin); 203 | 204 | printf("%3u", (unsigned int)c); 205 | filelen--; 206 | 207 | /* don't put a comma after the last item */ 208 | if (filelen > 0) { 209 | /* break after every 16th number */ 210 | if (++linelen >= 16) { 211 | linelen = 0; 212 | fputs("\n.byte ", stdout); 213 | } else { 214 | fputc(',', stdout); 215 | } 216 | } 217 | } 218 | fputc('\n', stdout); 219 | 220 | fclose(fin); 221 | } 222 | return 0; 223 | } 224 | 225 | --------------------------------------------------------------------------------