diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..c58a124 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +export GDK ?= /opt/toolchains/mars/m68k-elf +MMLNK = $(CTRMML_DIR)/mdslink +MMLNK_ARGS = -o ./res/mdsseq.bin ./res/mdsbin.bin -h ./res/mdsseq.h +mdsdrv: +ifndef CTRMML_DIR + $(error "Can not find MDSDRV music linker! Please set the CTRMML_DIR environment variable to its path.") +else + $(MMLNK) $(MMLNK_ARGS) res/mus/*.mds +endif +include $(GDK)/makefile.gen \ No newline at end of file diff --git a/build.fish b/build.fish new file mode 100755 index 0000000..464c8e7 --- /dev/null +++ b/build.fish @@ -0,0 +1,2 @@ +#!/usr/bin/fish +make mdsdrv && make release && blastem ./out/rom.bin diff --git a/inc/bsod.h b/inc/bsod.h new file mode 100755 index 0000000..44fbfbf --- /dev/null +++ b/inc/bsod.h @@ -0,0 +1,10 @@ +#pragma once + +#include "includes.h" + +enum stopCodes {genericErr,featureNotFound,regionInvalid,menuIndexInvalid}; + + +/// @brief Shows a BSOD. +/// @param stopcode The error code. +void killExec(u32 stopcode); \ No newline at end of file diff --git a/inc/includes.h b/inc/includes.h new file mode 100755 index 0000000..3c568d8 --- /dev/null +++ b/inc/includes.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include "palettes.h" +#include "image.h" +#include "tiles.h" +#include "spr.h" +#include "mdsdat.h" +#include "mdsdrv.h" +#include "main.h" +#include "bsod.h" \ No newline at end of file diff --git a/inc/main.h b/inc/main.h new file mode 100755 index 0000000..3c0a289 --- /dev/null +++ b/inc/main.h @@ -0,0 +1,24 @@ +#pragma once + +#include "includes.h" + +enum consoleRegions {ntscJPN = 0x20, ntscUSA = 0xA0, palEUR = 0xE0, palJPN = 0x60}; + +typedef struct +{ + u8 x; + u8 y; + char* label; +}Option; + +extern u8 palFadeTime; +extern u8 vblankRate; +extern const u8* region; +extern u8* currentIndex; + +#define MUSIC_FADE MDS_fade(127,7,TRUE) +#define PAL_FADE_OUT PAL_fadeOutAll(palFadeTime,TRUE); +#define CLEAR_BG1 VDP_clearPlane(BG_A,TRUE); +#define CLEAR_BG2 VDP_clearPlane(BG_B,TRUE); +#define TILE_TO_PIXEL(tile) (tile << 3) +#define SELECTION_SFX MDS_request(MDS_SE1,BGM_SFX_S1SELECT) \ No newline at end of file diff --git a/inc/palettes.h b/inc/palettes.h new file mode 100755 index 0000000..2832709 --- /dev/null +++ b/inc/palettes.h @@ -0,0 +1,8 @@ +#pragma once + +#include "includes.h" + +extern const u16 segaPalette[32]; +extern const u16 titlePalette[32]; +extern const u16 playerPalettes[4][16]; +extern const u16 bsodPalette[16]; \ No newline at end of file diff --git a/res/aseprite/pal/stephanie.pal b/res/aseprite/pal/stephanie.pal new file mode 100755 index 0000000..3c54811 --- /dev/null +++ b/res/aseprite/pal/stephanie.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +238 0 238 +0 0 0 +68 68 136 +102 102 170 +136 136 204 +170 170 238 +238 238 238 +0 204 102 +0 136 68 +0 102 68 +0 68 68 +136 68 34 +170 102 68 +204 136 102 +0 0 0 +0 0 0 diff --git a/res/aseprite/stephanie.aseprite b/res/aseprite/stephanie.aseprite new file mode 100755 index 0000000..e8bc470 Binary files /dev/null and b/res/aseprite/stephanie.aseprite differ diff --git a/res/image.h b/res/image.h new file mode 100755 index 0000000..7553d29 --- /dev/null +++ b/res/image.h @@ -0,0 +1,9 @@ +#ifndef _RES_IMAGE_H_ +#define _RES_IMAGE_H_ + +extern const Image sega_fg; +extern const Image sega_bg; +extern const Image title_logo; +extern const Image title_bg; + +#endif // _RES_IMAGE_H_ diff --git a/res/image.res b/res/image.res new file mode 100755 index 0000000..f1b9acc --- /dev/null +++ b/res/image.res @@ -0,0 +1,4 @@ +IMAGE sega_fg "image/sega.png" BEST ALL +IMAGE sega_bg "image/sega_clr.png" BEST ALL +IMAGE title_logo "image/title.png" BEST ALL +IMAGE title_bg "image/title_bg.png" BEST ALL \ No newline at end of file diff --git a/res/image/sega.png b/res/image/sega.png new file mode 100755 index 0000000..229a674 Binary files /dev/null and b/res/image/sega.png differ diff --git a/res/image/sega_clr.png b/res/image/sega_clr.png new file mode 100755 index 0000000..34f4788 Binary files /dev/null and b/res/image/sega_clr.png differ diff --git a/res/image/title.png b/res/image/title.png new file mode 100755 index 0000000..cef5895 Binary files /dev/null and b/res/image/title.png differ diff --git a/res/image/title_bg.png b/res/image/title_bg.png new file mode 100755 index 0000000..11f44b9 Binary files /dev/null and b/res/image/title_bg.png differ diff --git a/res/mdsbin.bin b/res/mdsbin.bin new file mode 100755 index 0000000..d587e80 Binary files /dev/null and b/res/mdsbin.bin differ diff --git a/res/mdsdat.h b/res/mdsdat.h new file mode 100755 index 0000000..0cc2385 --- /dev/null +++ b/res/mdsdat.h @@ -0,0 +1,8 @@ +#ifndef _RES_MDSDAT_H_ +#define _RES_MDSDAT_H_ + +extern const u8 mdsdrvdat[8202]; +extern const u8 mdsseqdat[2176]; +extern const u8 mdspcmdat[61516]; + +#endif // _RES_MDSDAT_H_ diff --git a/res/mdsdat.res b/res/mdsdat.res new file mode 100755 index 0000000..6436493 --- /dev/null +++ b/res/mdsdat.res @@ -0,0 +1,3 @@ +BIN mdsdrvdat "mdsdrv.bin" 2 2 0 NONE FALSE +BIN mdsseqdat "mdsseq.bin" 2 2 0 NONE FALSE +BIN mdspcmdat "mdsbin.bin" 32768 2 0 NONE FALSE diff --git a/res/mdsdrv.bin b/res/mdsdrv.bin new file mode 100755 index 0000000..abd98a3 Binary files /dev/null and b/res/mdsdrv.bin differ diff --git a/res/mdsseq.bin b/res/mdsseq.bin new file mode 100755 index 0000000..0594436 Binary files /dev/null and b/res/mdsseq.bin differ diff --git a/res/mdsseq.h b/res/mdsseq.h new file mode 100755 index 0000000..aa9ddc5 --- /dev/null +++ b/res/mdsseq.h @@ -0,0 +1,7 @@ +#define BGM_MIN 1 +#define BGM_MUS_CLI2 1 +#define BGM_MUS_S2BLVS 2 +#define BGM_MUS_S3CLEAR 3 +#define BGM_SFX_S1SELECT 4 +#define BGM_SFX_SEGA 5 +#define BGM_MAX 5 diff --git a/res/mus/mus_cli2.mds b/res/mus/mus_cli2.mds new file mode 100755 index 0000000..c3bcf65 Binary files /dev/null and b/res/mus/mus_cli2.mds differ diff --git a/res/mus/mus_s2blvs.mds b/res/mus/mus_s2blvs.mds new file mode 100755 index 0000000..cb6ef64 Binary files /dev/null and b/res/mus/mus_s2blvs.mds differ diff --git a/res/mus/mus_s3clear.mds b/res/mus/mus_s3clear.mds new file mode 100755 index 0000000..0f8f95f Binary files /dev/null and b/res/mus/mus_s3clear.mds differ diff --git a/res/mus/sfx_s1-select.mds b/res/mus/sfx_s1-select.mds new file mode 100755 index 0000000..fe1fdd4 Binary files /dev/null and b/res/mus/sfx_s1-select.mds differ diff --git a/res/mus/sfx_sega.mds b/res/mus/sfx_sega.mds new file mode 100755 index 0000000..847507e Binary files /dev/null and b/res/mus/sfx_sega.mds differ diff --git a/res/spr.h b/res/spr.h new file mode 100755 index 0000000..880a972 --- /dev/null +++ b/res/spr.h @@ -0,0 +1,6 @@ +#ifndef _RES_SPR_H_ +#define _RES_SPR_H_ + +extern const SpriteDefinition cursor; + +#endif // _RES_SPR_H_ diff --git a/res/spr.res b/res/spr.res new file mode 100755 index 0000000..9b2e1d5 --- /dev/null +++ b/res/spr.res @@ -0,0 +1 @@ +SPRITE cursor "spr/cursor.png" 1 1 BEST 0 \ No newline at end of file diff --git a/res/spr/cursor.png b/res/spr/cursor.png new file mode 100755 index 0000000..3289e07 Binary files /dev/null and b/res/spr/cursor.png differ diff --git a/res/tiles.h b/res/tiles.h new file mode 100755 index 0000000..6e5fcc4 --- /dev/null +++ b/res/tiles.h @@ -0,0 +1,7 @@ +#ifndef _RES_TILES_H_ +#define _RES_TILES_H_ + +extern const TileSet main_font; +extern const TileSet bsodFont; + +#endif // _RES_TILES_H_ diff --git a/res/tiles.res b/res/tiles.res new file mode 100755 index 0000000..f0959bd --- /dev/null +++ b/res/tiles.res @@ -0,0 +1,2 @@ +TILESET main_font "tiles/font_menu.png" BEST NONE +TILESET bsodFont "tiles/font_lc.png" BEST NONE \ No newline at end of file diff --git a/res/tiles/font_lc.png b/res/tiles/font_lc.png new file mode 100755 index 0000000..90a8765 Binary files /dev/null and b/res/tiles/font_lc.png differ diff --git a/res/tiles/font_menu.png b/res/tiles/font_menu.png new file mode 100755 index 0000000..7b9fa69 Binary files /dev/null and b/res/tiles/font_menu.png differ diff --git a/src/boot/rom_head.c b/src/boot/rom_head.c new file mode 100755 index 0000000..f43a6d1 --- /dev/null +++ b/src/boot/rom_head.c @@ -0,0 +1,33 @@ +#include "genesis.h" + +__attribute__((externally_visible)) +const ROMHeader rom_header = { +#if (ENABLE_BANK_SWITCH != 0) + "SEGA SSF ", +#elif (MODULE_MEGAWIFI != 0) + "SEGA MEGAWIFI ", +#else + "SEGA MEGA DRIVE ", +#endif + "(C)RD23 2023.AUG", + "PROJECT DAISY MEGADRIVE BUILD 20230820", + "PROJECT DAISY GENESIS BUILD 20230820", + "GM 88688463-00", + 0x000, + "JD ", + 0x00000000, +#if (ENABLE_BANK_SWITCH != 0) + 0x003FFFFF, +#else + 0x000FFFFF, +#endif + 0xE0FF0000, + 0xE0FFFFFF, + "RA", + 0xF820, + 0x00200000, + 0x0020FFFF, + " ", + "193 days remaining; it's a new day ", + "JUE " +}; diff --git a/src/boot/sega.s b/src/boot/sega.s new file mode 100755 index 0000000..6dcde0b --- /dev/null +++ b/src/boot/sega.s @@ -0,0 +1,487 @@ +#include "task_cst.h" + +.section .text.keepboot + +*------------------------------------------------------- +* +* Sega startup code for the GNU Assembler +* Translated from: +* Sega startup code for the Sozobon C compiler +* Written by Paul W. Lee +* Modified by Charles Coty +* Modified by Stephane Dallongeville +* +*------------------------------------------------------- + + .globl rom_header + + .org 0x00000000 + +_Start_Of_Rom: +_Vecteurs_68K: + dc.l __stack /* Stack address */ + dc.l _Entry_Point /* Program start address */ + dc.l _Bus_Error + dc.l _Address_Error + dc.l _Illegal_Instruction + dc.l _Zero_Divide + dc.l _Chk_Instruction + dc.l _Trapv_Instruction + dc.l _Privilege_Violation + dc.l _Trace + dc.l _Line_1010_Emulation + dc.l _Line_1111_Emulation + dc.l _Error_Exception, _Error_Exception, _Error_Exception, _Error_Exception + dc.l _Error_Exception, _Error_Exception, _Error_Exception, _Error_Exception + dc.l _Error_Exception, _Error_Exception, _Error_Exception, _Error_Exception + dc.l _Error_Exception + dc.l _INT + dc.l _EXTINT + dc.l _INT + dc.l hintCaller + dc.l _INT + dc.l _VINT + dc.l _INT + dc.l _trap_0 /* Resume supervisor task */ + dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT + dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT,_INT + dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT,_INT + dc.l _INT,_INT,_INT,_INT,_INT,_INT,_INT,_INT + +rom_header: + .incbin "out/rom_head.bin", 0, 0x100 + +_Entry_Point: +* disable interrupts + move #0x2700,%sr + +* Configure a USER_STACK_LENGTH bytes user stack at bottom, and system stack on top of it + move %sp, %usp + sub #USER_STACK_LENGTH, %sp + +* Halt Z80 (need to be done as soon as possible on reset) + move.l #0xA11100,%a0 /* Z80_HALT_PORT */ + move.w #0x0100,%d0 + move.w %d0,(%a0) /* HALT Z80 */ + move.w %d0,0x0100(%a0) /* END RESET Z80 */ + + tst.l 0xa10008 + bne.s SkipInit + + tst.w 0xa1000c + bne.s SkipInit + +* Check Version Number + move.b -0x10ff(%a0),%d0 + andi.b #0x0f,%d0 + beq.s NoTMSS + +* Sega Security Code (SEGA) + move.l #0x53454741,0x2f00(%a0) + +NoTMSS: + jmp _start_entry + +SkipInit: + jmp _reset_entry + + +*------------------------------------------------ +* +* interrupt functions +* +*------------------------------------------------ + +registersDump: + move.l %d0,registerState+0 + move.l %d1,registerState+4 + move.l %d2,registerState+8 + move.l %d3,registerState+12 + move.l %d4,registerState+16 + move.l %d5,registerState+20 + move.l %d6,registerState+24 + move.l %d7,registerState+28 + move.l %a0,registerState+32 + move.l %a1,registerState+36 + move.l %a2,registerState+40 + move.l %a3,registerState+44 + move.l %a4,registerState+48 + move.l %a5,registerState+52 + move.l %a6,registerState+56 + move.l %a7,registerState+60 + rts + +busAddressErrorDump: + move.w 4(%sp),ext1State + move.l 6(%sp),addrState + move.w 10(%sp),ext2State + move.w 12(%sp),srState + move.l 14(%sp),pcState + jmp registersDump + +exception4WDump: + move.w 4(%sp),srState + move.l 6(%sp),pcState + move.w 10(%sp),ext1State + jmp registersDump + +exceptionDump: + move.w 4(%sp),srState + move.l 6(%sp),pcState + jmp registersDump + + +_Bus_Error: + jsr busAddressErrorDump + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l busErrorCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_Address_Error: + jsr busAddressErrorDump + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l addressErrorCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_Illegal_Instruction: + jsr exception4WDump + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l illegalInstCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_Zero_Divide: + jsr exceptionDump + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l zeroDivideCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_Chk_Instruction: + jsr exception4WDump + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l chkInstCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_Trapv_Instruction: + jsr exception4WDump + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l trapvInstCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_Privilege_Violation: + jsr exceptionDump + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l privilegeViolationCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_Trace: + jsr exceptionDump + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l traceCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_Line_1010_Emulation: +_Line_1111_Emulation: + jsr exceptionDump + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l line1x1xCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_Error_Exception: + jsr exceptionDump + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l errorExceptionCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_INT: + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l intCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_EXTINT: + movem.l %d0-%d1/%a0-%a1,-(%sp) + move.l eintCB, %a0 + jsr (%a0) + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +_VINT: + btst #5, (%sp) /* Skip context switch if not in user task */ + bne.s no_user_task + + tst.w task_lock + bne.s 1f + move.w #0, -(%sp) /* TSK_superPend() will return 0 */ + bra.s unlock /* If lock == 0, supervisor task is not locked */ + +1: + bcs.s no_user_task /* If lock < 0, super is locked with infinite wait */ + subq.w #1, task_lock /* Locked with wait, subtract 1 to the frame count */ + bne.s no_user_task /* And do not unlock if we did not reach 0 */ + move.w #1, -(%sp) /* TSK_superPend() will return 1 */ + +unlock: + /* Save bg task registers (excepting a7, that is stored in usp) */ + move.l %a0, task_regs + lea (task_regs + UTSK_REGS_LEN), %a0 + movem.l %d0-%d7/%a1-%a6, -(%a0) + + move.w (%sp)+, %d0 /* Load return value previously pushed to stack */ + + move.w (%sp)+, task_sr /* Pop user task sr and pc, and save them, */ + move.l (%sp)+, task_pc /* so they can be restored later. */ + movem.l (%sp)+, %d2-%d7/%a2-%a6 /* Restore non clobberable registers */ + +no_user_task: + /* At this point, we always have in the stack the SR and PC of the task */ + /* we want to jump after processing the interrupt, that might be the */ + /* point where we came from (if there is no context switch) or the */ + /* supervisor task (if we unlocked it). */ + + movem.l %d0-%d1/%a0-%a1,-(%sp) + ori.w #0x0001, intTrace /* in V-Int */ + addq.l #1, vtimer /* increment frame counter (more a vint counter) */ + btst #3, VBlankProcess+1 /* PROCESS_XGM_TASK ? (use VBlankProcess+1 as btst is a byte operation) */ + beq.s no_xgm_task + + jsr XGM_doVBlankProcess /* do XGM vblank task */ + +no_xgm_task: + btst #1, VBlankProcess+1 /* PROCESS_BITMAP_TASK ? (use VBlankProcess+1 as btst is a byte operation) */ + beq.s no_bmp_task + + jsr BMP_doVBlankProcess /* do BMP vblank task */ + +no_bmp_task: + move.l vintCB, %a0 /* load user callback */ + jsr (%a0) /* call user callback */ + andi.w #0xFFFE, intTrace /* out V-Int */ + movem.l (%sp)+,%d0-%d1/%a0-%a1 + rte + +*------------------------------------------------ +* +* Copyright (c) 1988 by Sozobon, Limited. Author: Johann Ruegg +* +* Permission is granted to anyone to use this software for any purpose +* on any computer system, and to redistribute it freely, with the +* following restrictions: +* 1) No charge may be made other than reasonable charges for reproduction. +* 2) Modified versions must be clearly marked as such. +* 3) The authors are not responsible for any harmful consequences +* of using this software, even if they result from defects in it. +* +*------------------------------------------------ + +ldiv: + move.l 4(%a7),%d0 + bpl ld1 + neg.l %d0 +ld1: + move.l 8(%a7),%d1 + bpl ld2 + neg.l %d1 + eor.b #0x80,4(%a7) +ld2: + bsr i_ldiv /* d0 = d0/d1 */ + tst.b 4(%a7) + bpl ld3 + neg.l %d0 +ld3: + rts + +lmul: + move.l 4(%a7),%d0 + bpl lm1 + neg.l %d0 +lm1: + move.l 8(%a7),%d1 + bpl lm2 + neg.l %d1 + eor.b #0x80,4(%a7) +lm2: + bsr i_lmul /* d0 = d0*d1 */ + tst.b 4(%a7) + bpl lm3 + neg.l %d0 +lm3: + rts + +lrem: + move.l 4(%a7),%d0 + bpl lr1 + neg.l %d0 +lr1: + move.l 8(%a7),%d1 + bpl lr2 + neg.l %d1 +lr2: + bsr i_ldiv /* d1 = d0%d1 */ + move.l %d1,%d0 + tst.b 4(%a7) + bpl lr3 + neg.l %d0 +lr3: + rts + +ldivu: + move.l 4(%a7),%d0 + move.l 8(%a7),%d1 + bsr i_ldiv + rts + +lmulu: + move.l 4(%a7),%d0 + move.l 8(%a7),%d1 + bsr i_lmul + rts + +lremu: + move.l 4(%a7),%d0 + move.l 8(%a7),%d1 + bsr i_ldiv + move.l %d1,%d0 + rts +* +* A in d0, B in d1, return A*B in d0 +* +i_lmul: + move.l %d3,%a2 /* save d3 */ + move.w %d1,%d2 + mulu %d0,%d2 /* d2 = Al * Bl */ + + move.l %d1,%d3 + swap %d3 + mulu %d0,%d3 /* d3 = Al * Bh */ + + swap %d0 + mulu %d1,%d0 /* d0 = Ah * Bl */ + + add.l %d3,%d0 /* d0 = (Ah*Bl + Al*Bh) */ + swap %d0 + clr.w %d0 /* d0 = (Ah*Bl + Al*Bh) << 16 */ + + add.l %d2,%d0 /* d0 = A*B */ + move.l %a2,%d3 /* restore d3 */ + rts +* +*A in d0, B in d1, return A/B in d0, A%B in d1 +* +i_ldiv: + tst.l %d1 + bne nz1 + +* divide by zero +* divu #0,%d0 /* cause trap */ + move.l #0x80000000,%d0 + move.l %d0,%d1 + rts +nz1: + move.l %d3,%a2 /* save d3 */ + cmp.l %d1,%d0 + bhi norm + beq is1 +* AB and B is not 0 +norm: + cmp.l #1,%d1 + bne not1 +* B==1, so ret A, rem 0 + clr.l %d1 + move.l %a2,%d3 /* restore d3 */ + rts +* check for A short (implies B short also) +not1: + cmp.l #0xffff,%d0 + bhi slow +* A short and B short -- use 'divu' + divu %d1,%d0 /* d0 = REM:ANS */ + swap %d0 /* d0 = ANS:REM */ + clr.l %d1 + move.w %d0,%d1 /* d1 = REM */ + clr.w %d0 + swap %d0 + move.l %a2,%d3 /* restore d3 */ + rts +* check for B short +slow: + cmp.l #0xffff,%d1 + bhi slower +* A long and B short -- use special stuff from gnu + move.l %d0,%d2 + clr.w %d2 + swap %d2 + divu %d1,%d2 /* d2 = REM:ANS of Ahi/B */ + clr.l %d3 + move.w %d2,%d3 /* d3 = Ahi/B */ + swap %d3 + + move.w %d0,%d2 /* d2 = REM << 16 + Alo */ + divu %d1,%d2 /* d2 = REM:ANS of stuff/B */ + + move.l %d2,%d1 + clr.w %d1 + swap %d1 /* d1 = REM */ + + clr.l %d0 + move.w %d2,%d0 + add.l %d3,%d0 /* d0 = ANS */ + move.l %a2,%d3 /* restore d3 */ + rts +* A>B, B > 1 +slower: + move.l #1,%d2 + clr.l %d3 +moreadj: + cmp.l %d0,%d1 + bhs adj + add.l %d2,%d2 + add.l %d1,%d1 + bpl moreadj +* we shifted B until its >A or sign bit set +* we shifted #1 (d2) along with it +adj: + cmp.l %d0,%d1 + bhi ltuns + or.l %d2,%d3 + sub.l %d1,%d0 +ltuns: + lsr.l #1,%d1 + lsr.l #1,%d2 + bne adj +* d3=answer, d0=rem + move.l %d0,%d1 + move.l %d3,%d0 + move.l %a2,%d3 /* restore d3 */ + rts diff --git a/src/bsod.c b/src/bsod.c new file mode 100755 index 0000000..e72c2ae --- /dev/null +++ b/src/bsod.c @@ -0,0 +1,103 @@ +#include "includes.h" + +const char stopStrings[][41] = {"ERROR_USER_GENERIC","ERROR_FUNCTION_UNIMPLEMENTED","ERROR_REGION_INVALID","ERROR_MENU_INDEX_INVALID"}; +u32* stopcode_public; + +static void joyEvent_BSOD(u16 joy, u16 changed, u16 state) +{ + if (changed & state & BUTTON_START) + { + char unfixableErrorStr[41] = "Error is unfixable!"; + VDP_drawText("Fixing errors...",0,22); + MDS_request(MDS_BGM,BGM_SFX_S1SELECT); + switch (*stopcode_public) + { + case 2: + { + VDP_drawText(unfixableErrorStr,0,22); + break; + } + default: + { + SYS_hardReset(); + break; + } + } + } +} + +void killExec(u32 stopcode) +{ + u8 i = 0; + for (i; i < 4; i++) + { + PAL_setPalette(i,palette_black,DMA); + } + VDP_loadFont(&bsodFont,DMA); + CLEAR_BG1; + CLEAR_BG2; + VDP_setWindowVPos(FALSE,0); + SPR_end(); + PAL_interruptFade(); + VDP_setTextPalette(PAL0); + char bsodStrings[14][41] = {"A problem has been detected and the","current process has ended to prevent","damage to your console.","If this is the first time you've seen","this screen, reset your console. If you","see this screen again, press the","START button. This will make an attempt","to fix any problems you are having.","If problems continue, contact the","developer for a potential fix, if any","can be provided, or erase all save data","and start over. A backup is recommended.","Technical Information:","***** STOP: 0x"}; + u32 errPtr; + u32 errVal; + for (i = 0; i < 3; i++) + { + VDP_drawText(bsodStrings[i],0,i); + } + VDP_drawText(stopStrings[stopcode],0,4); + for (i = 3; i < 8; i++) + { + VDP_drawText(bsodStrings[i],0,i+3); + } + for (i = 8; i < 12; i++) + { + VDP_drawText(bsodStrings[i],0,i+4); + } + for (i = 12; i < 14; i++) + { + VDP_drawText(bsodStrings[i],0,i+5); + } + switch (stopcode) + { + case 2: + { + errPtr = region; + errVal = *region; + break; + } + case 3: + { + errPtr = currentIndex; + errVal = *currentIndex; + break; + } + default: + { + errPtr = NULL; + errVal = NULL; + break; + } + } + char scStr[9] = "00000000"; + intToHex(stopcode,scStr,8); + VDP_drawText(scStr,14,18); + intToHex(errPtr,scStr,8); + VDP_drawText(scStr,14,19); + intToHex(errVal,scStr,8); + VDP_drawText(scStr,14,20); + VDP_drawText("** ADDRESS: 0x",0,19); + VDP_drawText("** CONTENT: 0x",0,20); + PAL_setPalette(PAL0,bsodPalette,DMA); + MUSIC_FADE; + stopcode_public = MEM_alloc(sizeof(u32)); + memcpy(stopcode_public,&stopcode,sizeof(u32)); + JOY_setEventHandler(joyEvent_BSOD); + while (1) + { + MDS_update(); + SYS_doVBlankProcess(); + } +} \ No newline at end of file diff --git a/src/main.c b/src/main.c new file mode 100755 index 0000000..2d6625b --- /dev/null +++ b/src/main.c @@ -0,0 +1,248 @@ +#include "includes.h" + +u8 palFadeTime; +u8 vblankRate; +bool isNTSC; +const u8* region = (u8 *)0x00A10001; +const u8 titleX = 7; +const u8 titleY = 23; +const u8 numOptsTitle = 4; +const Option menuTitle[] = +{ + {titleX,titleY,"Start Game"}, + {titleX,titleY+1,"Continue Game"}, + {titleX+15,titleY,"Statistics"}, + {titleX+15,titleY+1,"Preferences"} +}; +Sprite* menuCursor; +u8* currentIndex; +u8* musIndex; + +static void fadePalette(const u16* palette) +{ + u16 finalPalette[64]; + memcpy(finalPalette,palette,48*sizeof(u16)); + memcpy(&finalPalette[48],playerPalettes[1],16*sizeof(u16)); + PAL_fadeInAll(finalPalette,palFadeTime,TRUE); +} + +static void drawMenu(const Option* option, u8 length, u8 paletteLine, u8 plane) +{ + VDP_setTextPalette(paletteLine); + VDP_setTextPlane(plane); + for (u8 i = 0; i < length; i++) + { + Option o = option[i]; + VDP_drawText(o.label,o.x,o.y); + } + u16 basetile = TILE_ATTR(paletteLine,FALSE,FALSE,FALSE); + currentIndex = MEM_alloc(sizeof(u8)); + menuCursor = SPR_addSprite(&cursor,TILE_TO_PIXEL(option[*currentIndex].x) - 8,TILE_TO_PIXEL(option[*currentIndex].y),basetile); +} + +static void curMove(const Option* option, u8 length, bool direction) +{ + SELECTION_SFX; + if (!direction) + { + if (*currentIndex == 0) + { + *currentIndex = length - 1; + } + else + { + *currentIndex -= 1; + } + } + else + { + if (*currentIndex == length - 1) + { + *currentIndex = 0; + } + else + { + *currentIndex += 1; + } + } + SPR_setPosition(menuCursor,TILE_TO_PIXEL(option[*currentIndex].x) - 8,TILE_TO_PIXEL(option[*currentIndex].y)); +} + +static void selectOption_Title() +{ + SELECTION_SFX; + u8 timer = palFadeTime; + PAL_FADE_OUT; + MUSIC_FADE; + while(1) + { + timer--; + SYS_doVBlankProcess(); + MDS_update(); + if (timer == 0) + { + SPR_releaseSprite(menuCursor); + SPR_update(); + break; + } + } + switch (*currentIndex) + { + default: + { + killExec(menuIndexInvalid); + break; + } + } +} + +static void joyEvent_TitleMenu(u16 joy, u16 changed, u16 state) +{ + if (joy != JOY_1) + { + return; + } + if (changed & state & BUTTON_UP) + { + curMove(menuTitle,numOptsTitle,FALSE); + } + else if (changed & state & BUTTON_DOWN) + { + curMove(menuTitle,numOptsTitle,TRUE); + } + if (changed & state & BUTTON_START) + { + selectOption_Title(); + } +} + +static void joyEvent_Title(u16 joy, u16 changed, u16 state) +{ + if (joy != JOY_1) + { + return; + } + if (changed & state & BUTTON_START) + { + VDP_clearTextLine(14); + MDS_request(MDS_SE1,BGM_SFX_S1SELECT); + drawMenu(menuTitle,4,PAL3,BG_A); + JOY_setEventHandler(joyEvent_TitleMenu); + } +} + +static void title() +{ + s16 indLogo = TILE_USER_INDEX; + s16 indBG = indLogo + title_logo.tileset->numTile; + u16 basetileLogo = TILE_ATTR_FULL(PAL0,FALSE,FALSE,FALSE,indLogo); + u16 basetileBG = TILE_ATTR_FULL(PAL1,FALSE,FALSE,FALSE,indBG); + fix16 scroll = FIX16(0); + fix16 scrollSpeed; + u8 y; + if (isNTSC) + { + y = 27; + scrollSpeed = FIX16(1.0/3.0); + } + else + { + y = 29; + scrollSpeed = FIX16(0.4); + } + CLEAR_BG1; + CLEAR_BG2; + VDP_drawImageEx(BG_A,&title_logo,basetileLogo,0,0,FALSE,TRUE); + VDP_drawImageEx(BG_B,&title_bg,basetileBG,0,0,FALSE,TRUE); + VDP_setTextPalette(PAL3); + VDP_drawText("}TheWindowsPro98 2023",9,y); + VDP_drawText("Version ppa1.2",0,6); + VDP_drawText("Press START button",11,14); + fadePalette(titlePalette); + JOY_setEventHandler(joyEvent_Title); + MDS_request(MDS_BGM,BGM_MUS_CLI2); + while (1) + { + scroll -= scrollSpeed; + SYS_doVBlankProcess(); + SPR_update(); + MDS_update(); + VDP_setHorizontalScroll(BG_B,fix16ToInt(scroll)); + } +} + +static void segaScreen() +{ + s16 indFG = TILE_USER_INDEX; + s16 indBG = indFG + sega_fg.tileset->numTile; + u16 basetileFG = TILE_ATTR_FULL(PAL0,FALSE,FALSE,FALSE,indFG); + u16 basetileBG = TILE_ATTR_FULL(PAL1,FALSE,FALSE,FALSE,indBG); + u8 timer = (1.65 * vblankRate) + (palFadeTime << 1); + u8 y; + if (isNTSC) + { + y = 12; + } + else + { + y = 13; + } + VDP_drawImageEx(BG_A,&sega_fg,basetileFG,0,!isNTSC,FALSE,TRUE); + VDP_drawImageEx(BG_B,&sega_bg,basetileBG,13,y,FALSE,TRUE); + fadePalette(segaPalette); + while (1) + { + timer--; + SYS_doVBlankProcess(); + MDS_update(); + if (timer == 129) + { + MDS_request(MDS_BGM,BGM_SFX_SEGA); + } + else if (timer == 30) + { + PAL_FADE_OUT; + } + else if (timer == 0) + { + PAL_interruptFade(); + title(); + } + } +} + +int main(bool resetType) +{ + if (*region == ntscJPN || *region == ntscUSA) + { + palFadeTime = 30; + vblankRate = 60; + isNTSC = TRUE; + } + else if (*region == palEUR) + { + VDP_setScreenHeight240(); + palFadeTime = 25; + vblankRate = 50; + isNTSC = FALSE; + } + else + { + killExec(regionInvalid); + } + for (u8 i = 0; i < 4; i++) + { + PAL_setPalette(i,palette_black,DMA); + } + Z80_unloadDriver(); + VDP_loadFont(&main_font,DMA); + MDS_init(mdsseqdat,mdspcmdat); + SPR_init(); + segaScreen(); + while(1) + { + SYS_doVBlankProcess(); + MDS_update(); + } + return (0); +} diff --git a/src/mdsdrv.c b/src/mdsdrv.c new file mode 100755 index 0000000..8979264 --- /dev/null +++ b/src/mdsdrv.c @@ -0,0 +1,174 @@ +//====================================================================== +// MDSDRV API wrapper for SGDK +//====================================================================== +// Copyright (c) 2020 Ian Karlsson +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any +// damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any +// purpose, including commercial applications, and to alter it and +// redistribute it freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must +// not claim that you wrote the original software. If you use this +// software in a product, an acknowledgment in the product +// documentation would be appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must +// not be misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source +// distribution. +//====================================================================== + +#include +#include "mdsdrv.h" + +//! Sound driver work area. +u16 MDS_work [MDS_WORK_SIZE]; + +//! Initialize the sound driver +/*! + * This initializes the work area, unpacks and starts up the PCM + * driver. MDSDRV should then be updated every vblank using + * MDS_update(). + * + * \param seqdata Pointer to sequence data file. Must be word aligned. + * \param pcmdata Pointer to PCM data file. Must be 32K aligned. + */ +u16 MDS_init(const u8* seqdata, const u8* pcmdata) +{ + register u16* a0 asm ("a0") = MDS_work; + register const u8* a1 asm ("a1") = seqdata; + register const u8* a2 asm ("a2") = pcmdata; + register u16 d0 asm ("d0") = 0; + asm volatile ( + "jsr mdsdrvdat+0" + : "+a" (a0), "+a" (a1), "=r" (d0) + : "a" (a2) + : "d1", "cc"); + return d0; +} + +//! Sound request +/*! + * \param slot Request priority (range 0-3). I suggest using the + * predefined values MDS_BGM, MDS_SE1, etc. + * \param id Request sound number. Set 0 to stop the currently + * playing sound. + */ +void MDS_request(u16 slot, u16 id) +{ + register u16* a0 asm ("a0") = MDS_work; + register u16 d0 asm ("d0") = id; + register u16 d1 asm ("d1") = slot; + asm volatile ( + "jsr mdsdrvdat+8" + : + : "r" (d0), "r" (d1), "a" (a0) + : "cc" ); +} + +//! Command request for low-level access. +/*! + * Not all commands return a meaningful value, see MDSDRV + * documentation for details. + * + * \param id Command number + * \param param Command parameters + */ +u32 MDS_command(u16 id, u16 param) +{ + register u16* a0 asm ("a0") = MDS_work; + register u32 d0 asm ("d0") = id; + register u16 d1 asm ("d1") = param; + asm volatile ( + "jsr mdsdrvdat+12" + : "+r" (d0), "+r" (d1) + : "a" (a0) + : "a1", "d2", "cc"); + return d0; +} + + +//! Command request for low-level access (command number >= 0x09) +/*! + * Not all commands return a meaningful value, see MDSDRV + * documentation for details. + * + * \param id Command number + * \param param1 Command parameters + * \param param2 Command parameters + */ +u32 MDS_command2(u16 id, u16 param1, u16 param2) +{ + register u16* a0 asm ("a0") = MDS_work; + register u32 d0 asm ("d0") = id; + register u16 d1 asm ("d1") = param1; + register u16 d2 asm ("d2") = param2; + asm volatile ( + "jsr mdsdrvdat+12" + : "+r" (d0), "+r" (d1), "+r" (d2) + : "a" (a0) + : "a1", "cc"); + return d0; +} + +//! MDSDRV update function +/*! + * This must be called every vblank. + */ +void MDS_update() +{ + register u16* a0 asm ("a0") = MDS_work; + register void* a6 asm ("a6"); /* Hack since gcc didn't like clobbering the frame pointer */ + asm volatile ( + "jsr mdsdrvdat+4" + : "=a" (a6) + : "a" (a0) + : "a1", "a2", "a3", "a4", "a5", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "cc"); +} + +//! Get sound driver version +/*! + * Returns a pointer to a zero-terminated string containing the + * sound driver version and date. + */ +char* MDS_get_version_str() +{ + register u16* a0 asm ("a0") = MDS_work; + register u32 d0 asm ("d0") = MDS_CMD_GET_VERSION; + asm volatile ( + "jsr mdsdrvdat+12" + : "+r" (d0), "+a" (a0) + : + : "a1", "d1", "d2", "cc"); + return (char*)a0; +} + +//! Set pause on/off +/*! + * This pauses all tracks playing with the specified priority. + * + * \param slot Request priority (range 0-3) + * \param state Non-zero to pause, zero to unpause + */ +void MDS_pause(u16 slot, bool state) +{ + MDS_command2(MDS_CMD_SET_PAUSE, slot, state); +} + +//! Set BGM fade in/out +/*! + * This fades the volume of the BGM track (MDS_BGM) to the target + * level. The exact time it takes to fade out is as follows: + * (target-current) / (1+speed) + * + * \param target Target volume. Range 0 to 127, -0.75 dB per step. + * \param speed Fading speed, 0 is fastest, 7 is slowest. + * \param stop_when_done BGM track + */ +void MDS_fade(u8 target, u8 speed, bool stop_when_done) +{ + MDS_command(MDS_CMD_FADE_BGM, (speed << 8) | ((stop_when_done & 1) << 7) | (target & 0x7f)); +} diff --git a/src/mdsdrv.h b/src/mdsdrv.h new file mode 100755 index 0000000..870cb1d --- /dev/null +++ b/src/mdsdrv.h @@ -0,0 +1,74 @@ +//====================================================================== +// MDSDRV API wrapper for SGDK +//====================================================================== +// Copyright (c) 2020 Ian Karlsson +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any +// damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any +// purpose, including commercial applications, and to alter it and +// redistribute it freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must +// not claim that you wrote the original software. If you use this +// software in a product, an acknowledgment in the product +// documentation would be appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must +// not be misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source +// distribution. +//====================================================================== + +#ifndef MDSDRV_H +#define MDSDRV_H + +#include "mdsdat.h" +#include "mdsseq.h" + +/* Work area size */ +#define MDS_WORK_SIZE 512 + +/* Sound effect / music priority slots */ +#define MDS_BGM 3 +#define MDS_SE1 2 +#define MDS_SE2 1 +#define MDS_SE3 0 + +/* Command numbers */ +#define MDS_CMD_GET_CMD_CNT 0 +#define MDS_CMD_GET_SOUND_CNT 1 +#define MDS_CMD_GET_STATUS 2 +#define MDS_CMD_GET_VERSION 3 +#define MDS_CMD_GET_GTEMPO 4 +#define MDS_CMD_SET_GTEMPO 5 +#define MDS_CMD_GET_GVOLUME 6 +#define MDS_CMD_SET_GVOLUME 7 +#define MDS_CMD_WRITE_FM_PORT0 8 +#define MDS_CMD_WRITE_FM_PORT1 9 +#define MDS_CMD_FADE_BGM 10 +#define MDS_CMD_SET_PAUSE 11 +#define MDS_CMD_GET_VOLUME 12 +#define MDS_CMD_SET_VOLUME 13 +#define MDS_CMD_GET_TEMPO 14 +#define MDS_CMD_SET_TEMPO 15 + +/* Work area, resides in ram */ +extern u16 MDS_work [MDS_WORK_SIZE]; + +/* API functions - see mdsdrv.c */ +u16 MDS_init(const u8* seqdata, const u8* pcmdata); +void MDS_request(u16 slot, u16 id); +u32 MDS_command(u16 id, u16 param); +u32 MDS_command2(u16 id, u16 param1, u16 param2); + +/* Manually update sound driver */ +void MDS_update(); + +/* Wrapper functions */ +char* MDS_get_version_str(); +void MDS_pause(u16 slot, bool state); +void MDS_fade(u8 target, u8 speed, bool stop_when_done); + +#endif diff --git a/src/palettes.c b/src/palettes.c new file mode 100755 index 0000000..82a3cb3 --- /dev/null +++ b/src/palettes.c @@ -0,0 +1,26 @@ +#include "includes.h" + +const u16 segaPalette[32] = +{ + 0x0000, 0x0EEE, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0606, 0x0808, 0x0C0C, 0x0E0E, 0x0E6E, 0x0E8E, 0x0EAE, 0x0E8E, 0x0E6E, 0x0E0E, 0x0C0C, 0x0808, 0x0000, 0x0000, 0x0000 +}; + +const u16 titlePalette[32] = +{ + 0x0000, 0x0E0E, 0x0EEE, 0x0EAA, 0x00EE, 0x008E, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0422, 0x00EE, 0x0844, 0x0EAA, 0x0EEE, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 +}; + +const u16 playerPalettes[4][16] = +{ + 0x0E0E, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0E0E, 0x0000, 0x0444, 0x0666, 0x0888, 0x0AAA, 0x0EEE, 0x06C0, 0x0480, 0x0260, 0x0040, 0x0248, 0x046A, 0x068C, 0x0000, 0x0000, + 0x0E0E, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0E0E, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +const u16 bsodPalette[16] = +{ + 0x0800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0EEE, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 +}; \ No newline at end of file