diff options
37 files changed, 3800 insertions, 0 deletions
@@ -0,0 +1,52 @@ + _ _ _ _____ _____ _ _ _ _ _ _____ +| | | | | | | _ | | _ | | \ | | | | | \ | | | ___| +| | | | | | | |_| | | |_| | | \| | | | | \| | | | __ +| | | | | | | _ | | _| | | | | | | | || | +| |_| |_| | | | | | | |\ \ | |\ | | | | |\ | | |_| | +|____.____| |_| |_| |_| \_\ |_| \_| |_| |_| \_| |___._| + +This is a VERY unstable and unfinished version of Echo. It isn't anywhere +ready for use in production code, and it's up here only so other people can +help with development. This also means this git will be a mess until it's +ready for use. + +tl;dr DON'T USE ECHO YET :| + +----------------------------------------------------------------------------- + +Current version: 0.8 + + And yes, I know the source code is a mess, I need to clean it up severely. + Also I know some stuff could be done better (e.g. buffering PCM instead of + reading each byte from ROM every time), but leaving that for after the + first official release, don't fix what isn't broken -_-' + +----------------------------------------------------------------------------- + +How to build: + + * Make sure the root of this source code tree is the current directory + * Assemble src-z80/build.z80 to bin/prog-z80.bin + * Assemble src-68k/build.68k to bin/prog-68k.bin + + Sorry for not providing a build script but my current setup is a mess and + needs to be changed x_x; Currently using z80-asm to build the Z80 code and + asm68k to build the 68000 code, I need to find good replacements for both + (z80-asm for being crappy, asm68k for not being free). + + Also no source for the data blobs yet, for similar reasons, although I + have more control over those tools at least so I may come up with good + replacements (or even just rebuild the current tools, pretty sure one of + them (mdtiler) already has a replacement) :/ + +----------------------------------------------------------------------------- + +Available builds: + + built/echo-timer-version-a.bin + Timer test. Both timers get reloaded each time they're fired. + built/echo-timer-version-b.bin + Timer test. Only timer B is reloaded when it fires, timer A is left to + loop on its own. + built/echo-timer-version-c.bin + Timer test. Both timers are left to loop on their own. diff --git a/built/echo-timer-version-a.bin b/built/echo-timer-version-a.bin Binary files differnew file mode 100644 index 0000000..cf637f5 --- /dev/null +++ b/built/echo-timer-version-a.bin diff --git a/built/echo-timer-version-b.bin b/built/echo-timer-version-b.bin Binary files differnew file mode 100644 index 0000000..2c2df59 --- /dev/null +++ b/built/echo-timer-version-b.bin diff --git a/built/echo-timer-version-c.bin b/built/echo-timer-version-c.bin Binary files differnew file mode 100644 index 0000000..0459b30 --- /dev/null +++ b/built/echo-timer-version-c.bin diff --git a/data/bg.bin b/data/bg.bin Binary files differnew file mode 100644 index 0000000..0ce7ca6 --- /dev/null +++ b/data/bg.bin diff --git a/data/fm/bass.eif b/data/fm/bass.eif Binary files differnew file mode 100644 index 0000000..3daf3e1 --- /dev/null +++ b/data/fm/bass.eif diff --git a/data/fm/dguitar.eif b/data/fm/dguitar.eif Binary files differnew file mode 100644 index 0000000..dbf2b29 --- /dev/null +++ b/data/fm/dguitar.eif diff --git a/data/fm/string.eif b/data/fm/string.eif Binary files differnew file mode 100644 index 0000000..1bb4b06 --- /dev/null +++ b/data/fm/string.eif diff --git a/data/font.bin b/data/font.bin Binary files differnew file mode 100644 index 0000000..f7de517 --- /dev/null +++ b/data/font.bin diff --git a/data/music/hol.esf b/data/music/hol.esf Binary files differnew file mode 100644 index 0000000..2a4b894 --- /dev/null +++ b/data/music/hol.esf diff --git a/data/music/minion.esf b/data/music/minion.esf Binary files differnew file mode 100644 index 0000000..2b89ee6 --- /dev/null +++ b/data/music/minion.esf diff --git a/data/pcm/kick.ewf b/data/pcm/kick.ewf new file mode 100644 index 0000000..6902184 --- /dev/null +++ b/data/pcm/kick.ewf @@ -0,0 +1,4 @@ +���~���~����}�|��~�v}yztyxjzto�|d`��S�qE�G)QEFBaO�[Q�4wuh�}yf������������ƪ���տ�����������˺���ŝ�������{u|{rS\UCRB,6+#!. # +!%&+5/55=JGVYfpw�������������������������������������������������������«�������|���k^muc^`ILKFB(06$$#')" #
"*+,,-39:59<BE>EJNTSTTX[__aegiktqpwy}}���������������������������������������������������������������������������������������ż������������������xvvqqkjd^_ZUTRPNJHHEAB;6431-('(#$!
+ +
!$'+.147:>ADFJNQTXZ_eklr}z�����������������������������������������������������������������������û���������������|xusokgd`_\YXUSQPLGGEDCA@?>;<=;:;:79:9:9<:;><>==@>@BAABDEFHIJLLMNOPPQRSTUWXZ[[]]```caceefhjjkmoqpqqsttuwz|{~~}������������������������������������������������������������������������������������������������������~zyyurommlgdd`^^\[ZWVUQPOMKHIIFEFDCDAABAA?>>@A??@?<>>=?@BAAEEDFIKINNORRVXZ]bbeimppwyz|����������������������������������������������������������������¿�������������������������~zwtrpmjhd_^\WVTQOMIJIGCECA@100,/-.+)-+)++,)*-*,+*//.-11/12759:9=:<>>>BDDGGKMMQTTYZ]baeihkloqtwy|�������������������������������������������������������������������������������������������������������||yzuuusrqqpnopmlkmllmlnnpononqrrrrtsuwxyxzzz||}}}~~}��~��~��~}�}}�|{{z{zyzywwvvwvutrssrqrsrrqrrtroqrrqsssutuvvxwwyz{}}�����������������������������������������������������~~{}{{zwxvussrppqnmnllklliihihihggghggeggffggggghghhijjiikjkjkkllmonopqortrsuvwxzzz||~�������������������������������������������������������������|{zwutrponmkiihfeedbbaa`^``_````bbbdbdffhikllnprrstvxz{|}�������������������������������������������������������������������������������������������������������������������������������������������������������~}||{zzyxwvvvtttrrqpppnnnmlllkkjiiiiiihhiihhiiiijjjjkkkllmnopppqrrstuuvwwxyyz{||}~������������������������������������������������������������������������������������~~}||{zzzxxxwwvvvvuuuutuuutuuuuvvvwwwwxyyzz{{|}}~~��������������������������������������������������������~~}}}}}}}}|||||||||}}|}}}}}}}~~~~~~~~������������������������������������������~~~~~~~~}}}}}}|||{|{{{{zzzzzyyyyyyyxyxxxxxxxxyyyyzzz{{{{||}}~~����������������������������������������������������������������~~~}}}}||||{{{{{{{{{{{z{{{{{{{{{{{{{{{{{{{{{{{||||||||||||||||||||||||||||||||||||}}}}}}}}}}~~~~~~�
\ No newline at end of file diff --git a/data/pcm/snare.ewf b/data/pcm/snare.ewf new file mode 100644 index 0000000..886db5c --- /dev/null +++ b/data/pcm/snare.ewf @@ -0,0 +1 @@ +~�|�yo�\hx��n���z���id�vee*M1>kN\Ze}wZy����������Ǿ������������������|l\ZL=C1?'% *0-1ENOWR]q�������ȿ���������ٿ�ſ�DZ�������������v��rp||^snufZqPaUN_S_V^MTUKX][OeX_i_^i]X`R^WJTXHQa[SZ\_YcuPggJZ}oiaQ�wj��������Ĝ���Ը���̿��ֳ�α���Ĵ���������������x���}�������q���y��d�{Z�vlghkhaMg]HTPQD6CB+6C,)&,0-7%,)2819)G95N?RHMXWjfzw����������˽����������������������������������������~��y~�v}pmttodvoYcew`WgiU_mQfW\gT^\KXSZXP]VN=]PRYQ[S^bZfcZydjqanontxq}z�����������������������������������������������������������|��������v��p{unehxmb_bZ[WZZSRXSKU;JY<GHEM>DUBG^PPSQa[a{rfxq~�������������ѻ���ǰ��˽�ٶ�Ǿ��������������������������y����yy{yw|jltlel]EYFQD(K;,42>6%1/0#6=.9:A1@R6:NGaUAe\[wxp���������������������������������������������������������������������x�q�zt{ryh��h�|vu��vlp�hu��qhyvp�c|�q|w{lymr�hooprww}l�zp�{~~y�y������������vv~z�����|~��u|��k�tw���~���~������y���t��vvsmcpmnhfldk[^dQgaZ[L_RQaW\a\cwr_{zl��z�����������������������������������}v�wuphkeoefck_UaYaS__QbRZa_ad{mqvw�p���������������������������������z����������������������������������������{���{�������������y��t}ovygdo{rfhnidnebfZio\f_^fdmeelkltolutnrwo{znqrv�qv}q��u���vz��~}����������������������������������|��z��w�|z�{p}�hhimngjajlisacejzq{ws|������������������������������y��y�j}{q�kyvnvp}rjjtooyowhty�}}x������~�����������~���������{�{s�y|ttuw{nrvwqjmpejpfjhjohnpnuqvxyy|x����������������������������������}�z{�wwy}|x{�v�~x}�zv�{z���������|}��}uvpuspuhkojqoedmnkjojhnnqirrmwnw{y{�|�����������������������������������v}�xzytxvz{s|vw|z�v��|����������������������z�}vvymovnkjoqggigjfnsmhmxmswwyw�����������������������������y�{��|y~|y}uywxztuwzw~w�~x���������������������������|�{{~{vywqruosmeqiejmd`mdkkdjjsqouqt}{||}���������������������������������y~�~}|~w|~|~xvzzw}|uz~||{}z���������������~���}|~{|�x{w}z|{uxy|w{ys~xv�vt}}~�~|��~�������������������}�~}}|}{|wyxyyzyv{w}|z�}~����������������������������������~}�~wwxwyuuvutuuswxvyvw{xz{z{�x���������������������������~}zw|{vwuvruwssqsqssoqoptrrstvvwvyz|{~��������������������������������������~����}|�~��}��|��}��������������~�~~~~{}|{y|{yzxzz{wxywwwxwzxyzvzzz}y{||}}}}~��������������������������������������������������������������������~|�}}|z|y{yyyvyxwxvwwvwyvxzxwx||zz}�}�����������������������������~���}}�~}���~~�~�~}���������������������������������~{�}{||}{}}}{{}~{{~~}|}���~����������������������������~~}}|~~~{~~~}~~~�����������������������������~�~~|}|}}|~}}}~}}}}}~}��������������������������}�~}}~~}}}}}}}}~}~~~������������������������������~~}~}}~}~~~~~~~~��������������������������������~�~~~~~~}~}~~}~||~~~|~~}~~~~��������������������������������~~~~|~~~}~}}|~~}}~~~���������������������������~~~}�~~~~�������������������������~~�~~�~~��������������������������������������~~~~~��~��������������������������~����������������~����������������������������������~�~���������������������������������������������~�����~����������������������������~�������������������������������~~�����
\ No newline at end of file diff --git a/src-68k/build.68k b/src-68k/build.68k new file mode 100644 index 0000000..dc118b1 --- /dev/null +++ b/src-68k/build.68k @@ -0,0 +1,21 @@ +Echo_ProgFile equs "bin/prog-z80.bin" + + include "src-68k/core/header.68k" + include "src-68k/core/entry.68k" + include "src-68k/core/menu.68k" + include "src-68k/core/songlist.68k" + + include "src-68k/video/text.68k" + include "src-68k/video/bg.68k" + include "src-68k/video/vsync.68k" + + include "src-68k/input/joypad.68k" + + include "src-68k/sound/echo.68k" + include "src-68k/sound/list.68k" + include "src-68k/sound/bgms.68k" + include "src-68k/sound/sfxs.68k" + + cnop 0, $8000 + + include "src-68k/core/vars.68k" diff --git a/src-68k/core/entry.68k b/src-68k/core/entry.68k new file mode 100644 index 0000000..b8a6f27 --- /dev/null +++ b/src-68k/core/entry.68k @@ -0,0 +1,170 @@ +;**************************************************************************** +; EntryPoint +; Where the program starts +;**************************************************************************** + +EntryPoint: + move.w #$2700, sr ; Disable interrupts + + move.b ($A10001), d0 ; Disable TMSS if needed + and.b #$0F, d0 + beq.s @NoTMSS + move.l #"SEGA", ($A14000) +@NoTMSS: + + bsr InitJoypad ; Init joypad + + lea ($C00004), a0 ; Init VDP + move.w #$8004, (a0) ; No IRQ4, no HV latch + move.w #$8114, (a0) ; Disable display + move.w #$8230, (a0) ; Scroll A: $C000 + move.w #$8407, (a0) ; Scroll B: $E000 + move.w #$8578, (a0) ; Sprites: $F000 + move.w #$8700, (a0) ; Background: pal 0, color 0 + move.w #$8B00, (a0) ; No IRQ2, full scroll + move.w #$8C81, (a0) ; H40, no S/H, no interlace + move.w #$8D3E, (a0) ; HScroll: $F800 + move.w #$8F02, (a0) ; Autoincrement: 2 bytes + move.w #$9001, (a0) ; Scroll size: 64x32 + move.w #$9100, (a0) ; Hide window plane + move.w #$9200, (a0) ; " " " + + moveq #0, d0 ; Clear VRAM + move.l #$40000000, (a0) + lea ($C00000), a1 + move.w #$800-1, d1 +@ClearVRAM: + move.l d0, (a1) + move.l d0, (a1) + move.l d0, (a1) + move.l d0, (a1) + move.l d0, (a1) + move.l d0, (a1) + move.l d0, (a1) + move.l d0, (a1) + dbf d1, @ClearVRAM + + move.l #$C0000000, (a0) ; Clear CRAM + moveq #8-1, d1 +@ClearCRAM: + move.l d0, (a1) + move.l d0, (a1) + move.l d0, (a1) + move.l d0, (a1) + dbf d1, @ClearCRAM + + move.l #$40000010, (a0) ; Clear VSRAM + move.l d0, (a1) + + lea (PointerList), a0 ; Initialize Echo + bsr Echo_Init + + bsr LoadFont ; Load font + + lea ($C00004), a0 ; Load palette + lea ($C00000), a1 + move.l #$C0020000, (a0) + move.l #$066600CE, (a1) + move.l #$000E000E, (a1) + move.l #$C0220000, (a0) + move.l #$06660EEE, (a1) + move.l #$000E000E, (a1) + move.l #$C0420000, (a0) + move.l #$04440E88, (a1) + move.l #$C0620000, (a0) + move.l #$04440888, (a1) + + move.l #$C00A0000, (a0) + move.l #$02220444, (a1) + move.l #$02240024, (a1) + move.l #$02440020, (a1) + move.w #$0040, (a1) + + move.l #$58000000, (a0) ; Load graphics + lea (@Gfx_Arrows), a2 + moveq #12-1, d7 +@LoadGfx: + move.l (a2)+, (a1) + move.l (a2)+, (a1) + move.l (a2)+, (a1) + move.l (a2)+, (a1) + move.l (a2)+, (a1) + move.l (a2)+, (a1) + move.l (a2)+, (a1) + move.l (a2)+, (a1) + dbf d7, @LoadGfx + + move.l #$44880003, (a0) ; Write left arrow + move.l #$00C000C1, (a1) + move.l #$45080003, (a0) + move.l #$00C200C3, (a1) + + move.l #$44C40003, (a0) ; Write right arrow + move.l #$28C128C0, (a1) + move.l #$45440003, (a0) + move.l #$28C328C2, (a1) + + moveq #2, d0 ; Write title + moveq #1, d1 + move.w #$8000, d2 + lea (@Str_Title1), a0 + bsr WriteString + addq.w #2, d1 + move.w #$E000, d2 + lea (@Str_Title2), a0 + bsr WriteString + + moveq #20-(24/2), d0 ; Write instructions + moveq #23, d1 + lea (@Str_Instr1), a0 + bsr WriteString + moveq #20-(18/2), d0 + addq.w #2, d1 + lea (@Str_Instr2), a0 + bsr WriteString + + bsr DrawBG ; Draw background + + move.w #$8154, ($C00004) ; Enable display + bra MainMenu ; Go into the main menu + +;**************************************************************************** + +@Gfx_Arrows: + dc.l $00000000, $00000000, $00000000, $00000000 + dc.l $00000034, $00004343, $00343434, $43434343 + dc.l $00000034, $00004343, $00343434, $43434343 + dc.l $34343434, $43434343, $34343434, $43434343 + dc.l $34343434, $00434343, $00003434, $00000043 + dc.l $00000000, $00000000, $00000000, $00000000 + dc.l $34343434, $43434343, $34343434, $43434343 + dc.l $34343434, $00434343, $00003434, $00000043 + +@Gfx_BG: + dc.l $55555555, $55555555, $55555555, $55555555 + dc.l $55555555, $55555555, $55555555, $55555555 + dc.l $00000000, $00000000, $00000000, $00000000 + dc.l $00000000, $00000000, $00000000, $00000000 + dc.l $66666666, $66666666, $66666666, $66666666 + dc.l $66666666, $66666666, $66666666, $66666666 + + dc.l $78787878, $87878787, $78787878, $87878787 + dc.l $78787878, $87878787, $78787878, $87878787 + dc.l $79797979, $97979797, $79797979, $97979797 + dc.l $79797979, $97979797, $79797979, $97979797 + + dc.l $AAAAAAAA, $AAAAAAAA, $AAAAAAAA, $AAAAAAAA + dc.l $AAAAAAAA, $AAAAAAAA, $AAAAAAAA, $AAAAAAAA + dc.l $BABABABA, $ABABABAB, $BABABABA, $ABABABAB + dc.l $BABABABA, $ABABABAB, $BABABABA, $ABABABAB + + dc.l $56565656, $65656565, $56565656, $65656565 + dc.l $56565656, $65656565, $56565656, $65656565 + + ; 123456789012345678901234567890123456 + +@Str_Title1: dc.b "Echo sound engine", 0 +@Str_Title2: dc.b "Version 0.8 by Sik", 0 +@Str_Instr1: dc.b "Use D-pad to select song", 0 +@Str_Instr2: dc.b "A/C: play, B: stop", 0 + even diff --git a/src-68k/core/header.68k b/src-68k/core/header.68k new file mode 100644 index 0000000..fde18ee --- /dev/null +++ b/src-68k/core/header.68k @@ -0,0 +1,37 @@ +;**************************************************************************** +; 68000 vectors +;**************************************************************************** + + dc.l $1000000 + dc.l EntryPoint + dcb.l 62, ErrorInt + +;**************************************************************************** +; Mega Drive header +;**************************************************************************** + + dc.b "SEGA MEGA DRIVE " + dc.b "(C) SIK 2010.NOV" + dc.b "ECHO TESTER PROGRAM" + dcb.b $150-*, $20 + dc.b "ECHO TESTER PROGRAM" + dcb.b $180-*, $20 + dc.b "XX XXXXXXXX-00" + dc.w $0000 + dc.b "J" + dcb.b $1A0-*, $20 + dc.l $000000, $3FFFFF + dc.l $FF0000, $FFFFFF + dcb.b 12, $20 + dcb.b 12, $20 + dcb.b 40, $20 + dc.b "JUE" + dcb.b $200-*, $20 + +;**************************************************************************** +; ErrorInt +; Generic error handler routine (hangs up) +;**************************************************************************** + +ErrorInt: + bra.s * diff --git a/src-68k/core/menu.68k b/src-68k/core/menu.68k new file mode 100644 index 0000000..d5f7914 --- /dev/null +++ b/src-68k/core/menu.68k @@ -0,0 +1,141 @@ +;**************************************************************************** +; MainMenu +; Menu where you get to select the song and such +; The main screen, bah... +;**************************************************************************** + +MainMenu: + move.w #0, (RAM_CurrSong) ; Selected song + bsr UpdateMenu ; Show description of first song + + move.l #0, (RAM_LArrowAnim) ; Reset arrows anim + move.w #$8407, (RAM_BGAnim) ; Reset background anim + +@MainLoop: + move.b (RAM_JoyPress), d0 ; Get joypad input + + btst.l #2, d0 ; Previous song? + beq.s @NoLeft + + move.w (RAM_CurrSong), d7 ; Select previous song + bne.s @NotTooLeft + move.w #NumSongs, d7 +@NotTooLeft: + subq.w #1, d7 + move.w d7, (RAM_CurrSong) + + bsr UpdateMenu ; Update current song + move.w #28, (RAM_LArrowAnim) ; Animate left arrow + lea (SFX_Beep), a0 ; Beep! + bsr Echo_PlaySFX +@NoLeft: + + btst.l #3, d0 ; Next song? + beq.s @NoRight + + move.w (RAM_CurrSong), d7 ; Select next song + addq.w #1, d7 + cmp.w #NumSongs, d7 + blt.s @NotTooRight + moveq #0, d7 +@NotTooRight: + move.w d7, (RAM_CurrSong) + bsr UpdateMenu ; Update current song + + move.w #28, (RAM_RArrowAnim) ; Animate right arrow + lea (SFX_Beep), a0 ; Beep! + bsr Echo_PlaySFX +@NoRight: + + btst.l #5, d0 ; Play song? + bne.s @DoPlay + btst.l #6, d0 + beq.s @NoPlay +@DoPlay: + lea (SongList), a1 ; Get song address + move.w (RAM_CurrSong), d7 + lsl.w #4, d7 + lea (a1,d7.w), a1 + move.l (a1), a0 + bsr Echo_PlayBGM ; Play song +@NoPlay: + + btst.l #4, d0 ; Stop song? + beq.s @NoStop + bsr Echo_StopBGM +@NoStop: + + btst.l #7, d0 ; Debug key + beq.s @NoDebug + lea (BGM_Test), a0 ; Play test BGM + ;bsr Echo_PlayBGM + lea (SFX_Test), a0 ; Play test SFX + bsr Echo_PlaySFX +@NoDebug: + + lea (@ArrowPal), a0 + + move.w (RAM_LArrowAnim), d0 ; Animate left arrow + move.l (a0,d0.w), d1 + move.l #$C0060000, ($C00004) + move.l d1, ($C00000) + subq.w #2, d0 + bge.s @NoLArrowOver + moveq #0, d0 +@NoLArrowOver: + move.w d0, (RAM_LArrowAnim) + + move.w (RAM_RArrowAnim), d0 ; Animate right arrow + move.l (a0,d0.w), d1 + move.l #$C0260000, ($C00004) + move.l d1, ($C00000) + subq.w #2, d0 + bge.s @NoRArrowOver + moveq #0, d0 +@NoRArrowOver: + move.w d0, (RAM_RArrowAnim) + + move.w (RAM_BGAnim), d0 ; Animate background + bchg.l #1, d0 + move.w d0, (RAM_BGAnim) + move.w d0, ($C00004) + + bsr VSync ; Next frame + bsr ReadJoypad + bra @MainLoop + +;**************************************************************************** + +@ArrowPal: + dc.w $00E, $00E, $02E, $02E, $04E, $04E, $06E, $06E + dc.w $28E, $28E, $4AE, $4AE, $6CE, $6CE, $8EE, $8EE + +;**************************************************************************** +; UpdateMenu +; Shows the current option on screen +;**************************************************************************** + +UpdateMenu: + bsr ClearLines ; Clear lines + + lea (SongList), a1 ; Get address of song data + move.w (RAM_CurrSong), d0 + lsl.w #4, d0 + lea (a1,d0.w), a1 + addq.l #4, a1 + + moveq #7, d0 ; Write song title + moveq #9, d1 + move.w #$A000, d2 + move.l (a1)+, a0 + bsr WriteString + + addq.w #2, d1 ; Write song description + move.w #$C000, d2 + move.l (a1)+, a0 + bsr WriteString + addq.w #2, d1 + move.l (a1)+, a0 + bsr WriteString + + rts ; End of subroutine diff --git a/src-68k/core/songlist.68k b/src-68k/core/songlist.68k new file mode 100644 index 0000000..69562af --- /dev/null +++ b/src-68k/core/songlist.68k @@ -0,0 +1,28 @@ +;**************************************************************************** +; SongList +; List of songs that appear on the menu +;**************************************************************************** + +SongList: + dc.l BGM_Minion, @Str_Minion_1, @Str_Minion_2, @Str_Minion_3 + dc.l BGM_HoL, @Str_HoL_1, @Str_Null, @Str_Null + + ; 012345678901234567890123456 + +@Str_Null: dc.b 0 +@Str_Untitled: dc.b "[untitled]", 0 + +@Str_Minion_1: dc.b "Evil's Minion", 0 +@Str_Minion_2: dc.b "My distortion guitar is", 0 +@Str_Minion_3: dc.b "better than Tiido's B)", 0 + +@Str_HoL_1: dc.b "Hall of Loneliness", 0 + + even + +;**************************************************************************** +; NumSongs +; Number of songs in song list +;**************************************************************************** + +NumSongs equ 2 diff --git a/src-68k/core/vars.68k b/src-68k/core/vars.68k new file mode 100644 index 0000000..8097c73 --- /dev/null +++ b/src-68k/core/vars.68k @@ -0,0 +1,12 @@ +;**************************************************************************** +; Where program variables are defined +;**************************************************************************** + + rsset $FF0000 + +RAM_JoyHold rs.b 1 ; Current "held" joypad status +RAM_JoyPress rs.b 1 ; Current "pressed" joypad status +RAM_LArrowAnim rs.w 1 ; Animation for left arrow +RAM_RArrowAnim rs.w 1 ; Animation for right arrow +RAM_BGAnim rs.w 1 ; Animation for background +RAM_CurrSong rs.w 1 ; Selected song diff --git a/src-68k/input/joypad.68k b/src-68k/input/joypad.68k new file mode 100644 index 0000000..f33d000 --- /dev/null +++ b/src-68k/input/joypad.68k @@ -0,0 +1,43 @@ +;**************************************************************************** +; InitJoypad +; Initializes the joypad +;**************************************************************************** + +InitJoypad: + move.b #$40, ($A10009) ; Initialize I/O ports + move.b #$40, ($A10003) + move.w #0, (RAM_JoyHold) ; Initialize status + rts ; End of subroutine + +;**************************************************************************** +; ReadJoypad +; Reads the joypad status +;**************************************************************************** + +ReadJoypad: + lea ($A10003), a6 ; I/O data port + + move.b #$40, (a6) ; Read D-pad, B and C + nop + nop + move.b (a6), d7 + move.b #$00, (a6) ; Read A and Start + nop + nop + move.b (a6), d6 + + and.b #$3F, d7 ; Process input + and.b #$30, d6 + add.b d6, d6 + add.b d6, d6 + or.b d6, d7 + not.b d7 + + lea (RAM_JoyHold), a6 ; Store new joypad status + move.b (a6), d6 + move.b d7, (a6)+ + not.b d6 + and.b d6, d7 + move.b d7, (a6) + + rts ; End of subroutine diff --git a/src-68k/sound/bgms.68k b/src-68k/sound/bgms.68k new file mode 100644 index 0000000..7264048 --- /dev/null +++ b/src-68k/sound/bgms.68k @@ -0,0 +1,23 @@ +;**************************************************************************** +; BGM_Test +; Generic BGM to test events +;**************************************************************************** + +BGM_Test: + dc.b $FF + +;**************************************************************************** +; BGM_Minion +; "Evil's Minion" +;**************************************************************************** + +BGM_Minion: + incbin "data/music/minion.esf" + +;**************************************************************************** +; BGM_HoL +; "Hall of Loneliness" +;**************************************************************************** + +BGM_HoL: + incbin "data/music/hol.esf" diff --git a/src-68k/sound/echo.68k b/src-68k/sound/echo.68k new file mode 100644 index 0000000..73eb82f --- /dev/null +++ b/src-68k/sound/echo.68k @@ -0,0 +1,245 @@ +;**************************************************************************** +; Echo_Z80Request +; Requests the Z80 bus +;**************************************************************************** + +Echo_Z80Request macro + move.w #$100, ($A11100) ; Request Z80 bus +@Echo_WaitZ80\@: + btst.b #0, ($A11100) ; Did we get it yet? + bne.s @Echo_WaitZ80\@ ; Keep waiting + endm ; End of macro + +;**************************************************************************** +; Echo_Z80Release +; Releases the Z80 bus +;**************************************************************************** + +Echo_Z80Release macro + move.w #$000, ($A11100) ; Release Z80 bus + endm ; End of macro + +;**************************************************************************** +; Echo_Z80Reset +; Resets the Z80 and YM2612 +;**************************************************************************** + +Echo_Z80Reset macro + move.w #$000, ($A11200) ; Assert reset line + rept $10 ; Wait until hardware resets + nop ; ... + endr ; ... + move.w #$100, ($A11200) ; Release reset line + endm ; End of macro + +;**************************************************************************** +; Echo_Init +; Initializes Echo +; +; input a0.l ... Address of pointer list +;**************************************************************************** + +Echo_Init: + movem.l d0/a0-a1, -(sp) ; Save registers + + Echo_Z80Reset ; May not work without this... + Echo_Z80Request ; We need the Z80 bus + + move.b #$01, ($A01FFF) ; Command: load pointer list + + move.l a0, d0 ; Easier to manipulate here + move.b d0, ($A01FFD) ; Store low address byte + lsr.l #7, d0 ; Get high address byte + lsr.b #1, d0 ; We skip one bit + bset.l #7, d0 ; Point into bank window + move.b d0, ($A01FFE) ; Store high address byte + lsr.w #8, d0 ; Get bank byte + move.w d0, d1 ; Parse 32X bit separately + lsr.w #1, d1 ; Put 32X bit in place + and.b #$7F, d0 ; Filter out unused bit from addresses + and.b #$80, d1 ; Filter out all but 32X bit + or.b d1, d0 ; Put everything together + move.b d0, ($A01FFC) ; Store bank byte + + lea @Z80Program(pc), a0 ; Where Z80 program starts + lea ($A00000), a1 ; Where Z80 RAM starts + move.w #@Z80ProgSize-1, d0 ; Size of Z80 program (DBF adjusted) +@LoadLoop: ; Go through all the program + move.b (a0)+, (a1)+ ; Copy byte into Z80 RAM + dbf d0, @LoadLoop ; Go for next byte + + Echo_Z80Reset ; Now reset for real + Echo_Z80Release ; Let the Z80 go! + + movem.l (sp)+, d0/a0-a1 ; Restore registers + rts ; End of subroutine + +;**************************************************************************** +; Echo Z80 program +; It should be located wherever Echo_ProgFile was defined +;**************************************************************************** + +@Z80Program: incbin "\Echo_ProgFile" +@Z80ProgSize equ *-@Z80Program + even + +;**************************************************************************** +; Echo_SendCommand +; Sends an Echo command (no address parameter) +; +; input d0.b ... Echo command +;**************************************************************************** + +Echo_SendCommand: + move.w d1, -(sp) ; Save register + + Echo_Z80Request ; We need the Z80 bus + +@Try: + tst.b ($A01FFF) ; Check if Echo is ready + beq.s @Ready ; Too busy? + Echo_Z80Release ; Let Echo continue + move.w #$FF, d1 ; Give it some time + dbf d1, * ; ... + Echo_Z80Request ; Get Z80 bus back + bra.s @Try ; Try again + +@Ready: + move.b d0, ($A01FFF) ; Write command ID + Echo_Z80Release ; We're done with the Z80 bus + + move.w (sp)+, d1 ; Restore register + rts ; End of subroutine + +;**************************************************************************** +; Echo_SendCommandEx +; Sends an Echo command (with address parameter) +; +; input d0.b ... Echo command +; input a0.l ... Address parameter +;**************************************************************************** + +Echo_SendCommandEx: + movem.l d0-d1, -(sp) ; Save register + + Echo_Z80Request ; We need the Z80 bus + +@Try: + tst.b ($A01FFF) ; Check if Echo is ready + beq.s @Ready ; Too busy? + Echo_Z80Release ; Let Echo continue + move.w #$FF, d1 ; Give it some time + dbf d1, * ; ... + Echo_Z80Request ; Get Z80 bus back + bra.s @Try ; Try again + +@Ready: + move.b d0, ($A01FFF) ; Write command ID + + move.l a0, d0 ; Easier to manipulate here + move.b d0, ($A01FFD) ; Store low address byte + lsr.l #7, d0 ; Get high address byte + lsr.b #1, d0 ; We skip one bit + bset.l #7, d0 ; Point into bank window + move.b d0, ($A01FFE) ; Store high address byte + + lsr.w #8, d0 ; Get bank byte + move.w d0, d1 ; Parse 32X bit separately + lsr.w #1, d1 ; Put 32X bit in place + and.b #$7F, d0 ; Filter out unused bit from addresses + and.b #$80, d1 ; Filter out all but 32X bit + or.b d1, d0 ; Put everything together + move.b d0, ($A01FFC) ; Store bank byte + + Echo_Z80Release ; We're done with the Z80 bus + + movem.l (sp)+, d0-d1 ; Restore register + rts ; End of subroutine + +;**************************************************************************** +; Echo_PlaySFX +; Plays a SFX +; +; input a0.l ... Pointer to SFX data +;**************************************************************************** + +Echo_PlaySFX: + move.w d0, -(sp) ; Save register + move.b #$02, d0 ; Command $02 = play SFX + bsr Echo_SendCommandEx ; Send command to Echo + move.w (sp)+, d0 ; Restore register + rts ; End of subroutine + +;**************************************************************************** +; Echo_StopSFX +; Stops SFX playback +;**************************************************************************** + +Echo_StopSFX: + move.w d0, -(sp) ; Save register + move.b #$03, d0 ; Command $03 = stop SFX + bsr Echo_SendCommand ; Send command to Echo + move.w (sp)+, d0 ; Restore register + rts ; End of subroutine + +;**************************************************************************** +; Echo_PlayBGM +; Plays a BGM +; +; input a0.l ... Pointer to BGM data +;**************************************************************************** + +Echo_PlayBGM: + move.w d0, -(sp) ; Save register + move.b #$04, d0 ; Command $04 = play BGM + bsr Echo_SendCommandEx ; Send command to Echo + move.w (sp)+, d0 ; Restore register + rts ; End of subroutine + +;**************************************************************************** +; Echo_StopBGM +; Stops BGM playback +;**************************************************************************** + +Echo_StopBGM: + move.w d0, -(sp) ; Save register + move.b #$05, d0 ; Command $05 = stop BGM + bsr Echo_SendCommand ; Send command to Echo + move.w (sp)+, d0 ; Restore register + rts ; End of subroutine + +;**************************************************************************** +; Echo_GetStatus +; Gets the current status of Echo +; +; output d0.b ... Echo status +; Bit #0: SFX is playing +; Bit #1: BGM is playing +;**************************************************************************** + +Echo_GetStatus: + Echo_Z80Request ; We need the Z80 bus + move.b ($A01FF0), d0 ; Just get the status + Echo_Z80Release ; Let the Z80 go! + rts ; End of subroutine + +;**************************************************************************** +; Echo_ListEntry +; Defines an entry in a pointer list +;**************************************************************************** + +Echo_ListEntry macro addr + dc.b $80|((addr)>>8&$7F) ; High byte of address + dc.b (addr)&$FF ; Low byte of address + dc.b ((addr)>>15&$7F)|((addr)>>16&$80) ; Bank number + endm + +;**************************************************************************** +; Echo_ListEnd +; Ends a pointer list +;**************************************************************************** + +Echo_ListEnd macro + dc.b $00 ; End of list mark + even ; Just in case... + endm diff --git a/src-68k/sound/list.68k b/src-68k/sound/list.68k new file mode 100644 index 0000000..f871b0a --- /dev/null +++ b/src-68k/sound/list.68k @@ -0,0 +1,85 @@ +;**************************************************************************** +; PointerList +; Pointer list used by Echo +;**************************************************************************** + +PointerList: + Echo_ListEntry Instr_PSGFlat ; $00 [PSG] Flat PSG instrument + Echo_ListEntry Instr_DGuitar ; $01 [FM] Distortion guitar + Echo_ListEntry Instr_Snare ; $02 [PCM] Snare drum + Echo_ListEntry Instr_Kick ; $03 [PCM] Bass drum (kick) + Echo_ListEntry Instr_Strings ; $04 [FM] String ensemble + Echo_ListEntry Instr_Bass ; $05 [FM] Standard bass + Echo_ListEntry Instr_SoftPSG ; $06 [PSG] Soft PSG envelope + Echo_ListEntry Instr_PianoPSG ; $07 [PSG] Piano PSG instrument + Echo_ListEnd + +;**************************************************************************** +; Instrument $00 [PSG] +; Flat PSG instrument (no envelope) +;**************************************************************************** + +Instr_PSGFlat: + dc.b $FE,$00,$FF + +;**************************************************************************** +; Instrument $01 [FM] +; Distortion guitar +;**************************************************************************** + +Instr_DGuitar: + incbin "data/fm/dguitar.eif" + +;**************************************************************************** +; Instrument $02 [PCM] +; Snare drum +;**************************************************************************** + +Instr_Snare: + incbin "data/pcm/snare.ewf" + +;**************************************************************************** +; Instrument $03 [PCM] +; Bass drum +;**************************************************************************** + +Instr_Kick: + incbin "data/pcm/kick.ewf" + +;**************************************************************************** +; Instrument $04 [FM] +; String ensemble +;**************************************************************************** + +Instr_Strings: + incbin "data/fm/string.eif" + +;**************************************************************************** +; Instrument $05 [FM] +; Standard bass +;**************************************************************************** + +Instr_Bass: + incbin "data/fm/bass.eif" + +;**************************************************************************** +; Instrument $06 [PSG] +; "Soft" PSG envelope +;**************************************************************************** + +Instr_SoftPSG: + dc.b $00,$01,$01,$02,$02,$02,$03,$03,$03,$03,$FE,$04,$FF + +;**************************************************************************** +; Instrument $07 [PSG] +; Piano-like PSG instrument +;**************************************************************************** + +Instr_PianoPSG: + dc.b $00,$01,$02,$03,$04,$04,$05,$05 + dc.b $06,$06,$07,$07,$08,$08,$08,$08 + dc.b $09,$09,$09,$09,$0A,$0A,$0A,$0A + dc.b $0B,$0B,$0B,$0B,$0C,$0C,$0C,$0C + dc.b $0C,$0C,$0C,$0C,$0D,$0D,$0D,$0D + dc.b $0D,$0D,$0D,$0D,$0E,$0E,$0E,$0E + dc.b $0E,$0E,$0E,$0E,$FE,$0F,$FF diff --git a/src-68k/sound/sfxs.68k b/src-68k/sound/sfxs.68k new file mode 100644 index 0000000..ac969dc --- /dev/null +++ b/src-68k/sound/sfxs.68k @@ -0,0 +1,26 @@ +;**************************************************************************** +; SFX_Test +; Generic SFX to test events +;**************************************************************************** + +SFX_Test: + dc.b $E8,$E9,$EA + dc.b $28,$00,$48,$00 + dc.b $29,$08,$49,$00 + dc.b $2A,$08,$4A,$00 + + dc.b $08,12,$09,24,$0A,36, $FE,$10, $18,$19,$1A, $FE,$10 + dc.b $08,12,$09,24,$0A,36, $FE,$10, $18,$19,$1A, $FE,$10 + dc.b $08,14,$09,26,$0A,38, $FE,$10, $18,$19,$1A, $FE,$10 + + dc.b $FF + +;**************************************************************************** +; SFX_Beep +; Beep SFX +;**************************************************************************** + +SFX_Beep: + dc.b $EA,$1A,$4A,$00,$2A,$00 + dc.b $0A,2*36,$FE,4 + dc.b $FF diff --git a/src-68k/video/bg.68k b/src-68k/video/bg.68k new file mode 100644 index 0000000..c64e287 --- /dev/null +++ b/src-68k/video/bg.68k @@ -0,0 +1,43 @@ +;**************************************************************************** +; DrawBG +; Draws the background +;**************************************************************************** + +DrawBG: + lea ($C00004), a2 + lea ($C00000), a1 + + lea (@BGData), a0 + move.l #$60000003, d0 + moveq #28-1, d6 +@YLoop: + move.l d0, (a2) + moveq #40-1, d7 +@XLoop: + move.w (a0)+, (a1) + dbf d7, @XLoop + add.l #$00800000, d0 + dbf d6, @YLoop + + lea (@BGData), a0 + move.l #$60000002, d0 + moveq #28-1, d6 +@YLoop2: + move.l d0, (a2) + moveq #40-1, d7 +@XLoop2: + move.w (a0)+, d1 + bset.l #11, d1 + move.w d1, (a1) + dbf d7, @XLoop2 + add.l #$00800000, d0 + dbf d6, @YLoop2 + + rts ; End of subroutine + +;**************************************************************************** +; Background data +;**************************************************************************** + +@BGData: + incbin "data/bg.bin" diff --git a/src-68k/video/text.68k b/src-68k/video/text.68k new file mode 100644 index 0000000..832605e --- /dev/null +++ b/src-68k/video/text.68k @@ -0,0 +1,108 @@ +;**************************************************************************** +; LoadFont +; Loads the font in VRAM +;**************************************************************************** + +LoadFont: + move.l #$40000000, ($C00004) ; Where font will be stored + + lea (@Font), a0 ; Font data + lea ($C00000), a1 ; VDP data port + move.w #96*16-1, d7 ; Go through all lines +@Loop: + + move.b (a0)+, d0 ; Fetch next line + moveq #0, d1 ; Initial color + moveq #8-1, d6 ; Go through all pixels +@ILoop: + + add.b d0, d0 ; Get pixel color + bcc.s @Transparent + moveq #3, d1 +@Transparent: + subq.b #1, d1 + bgt.s @NoUnderflow + moveq #0, d1 +@NoUnderflow: + + lsl.l #4, d2 ; Make room for pixel + or.b d1, d2 ; Store pixel + dbf d6, @ILoop ; Next pixel + + move.l d2, (a1) ; Store line in VRAM + dbf d7, @Loop ; Next line + + rts ; End of subroutine + +@Font: + incbin "data/font.bin" + +;**************************************************************************** +; WriteString +; Writes a string on screen +; +; input d0.w ... X coordinate +; input d1.w ... Y coordinate +; input d2.w ... FX and such +; input a0.l ... String +;**************************************************************************** + +WriteString: + movem.l d0-d2, -(sp) ; Save registers + + lsl.w #6, d1 ; Calculate address + add.w d1, d0 + add.w d0, d0 + + and.l #$FFFF, d0 ; Tell VDP the address + or.l #$00034000, d0 + swap d0 + move.l d0, ($C00004) + + lea ($C00000), a5 ; VDP data port + moveq #2-1, d7 ; Go through both lines +@Loop: + + move.l a0, a6 +@ILoop: + move.b (a6)+, d1 ; Get next character + beq.s @End ; End of string? + + sub.b #$20, d1 ; Write tile in VRAM + and.w #$7F, d1 + add.w d1, d1 + add.w d2, d1 + move.w d1, (a5) + + bra.s @ILoop ; Next character + +@End: + add.l #$80<<16, d0 + move.l d0, ($C00004) + addq.w #1, d2 ; Next line + dbf d7, @Loop + + movem.l (sp)+, d0-d2 ; Restore registers + rts ; End of subroutine + +;**************************************************************************** +; ClearLines +; Clears the description lines +;**************************************************************************** + +ClearLines: + move.l #$448E0003, d0 ; Initial position to clear + lea ($C00004), a0 ; VDP control port + lea ($C00000), a1 ; VDP data port + + moveq #7-1, d1 ; Clear all lines + moveq #0, d2 +@Loop: + move.l d0, (a0) + rept 26/2 + move.l d2, (a1) + endr + add.l #$80<<16, d0 + dbf d1, @Loop + + rts ; End of subroutine diff --git a/src-68k/video/vsync.68k b/src-68k/video/vsync.68k new file mode 100644 index 0000000..ed5ac17 --- /dev/null +++ b/src-68k/video/vsync.68k @@ -0,0 +1,19 @@ +;**************************************************************************** +; VSync +; Waits until the next frame +;**************************************************************************** + +VSync: + lea ($C00004), a6 + +@Loop1: ; Wait until current VBlank is over + move.w (a6), d7 + btst.l #3, d7 + bne.s @Loop1 + +@Loop2: ; Wait until next VBlank starts + move.w (a6), d7 + btst.l #3, d7 + beq.s @Loop2 + + rts ; End of subroutine diff --git a/src-z80/build.z80 b/src-z80/build.z80 new file mode 100644 index 0000000..88932ac --- /dev/null +++ b/src-z80/build.z80 @@ -0,0 +1,10 @@ + include "src-z80/core/main.z80" + include "src-z80/player/pcm.z80" + include "src-z80/core/bgm.z80" + include "src-z80/core/sfx.z80" + include "src-z80/player/fm.z80" + include "src-z80/player/psg.z80" + include "src-z80/player/misc.z80" + + include "src-z80/player/freq.z80" + include "src-z80/core/vars.z80" diff --git a/src-z80/core/bgm.z80 b/src-z80/core/bgm.z80 new file mode 100644 index 0000000..f69235d --- /dev/null +++ b/src-z80/core/bgm.z80 @@ -0,0 +1,290 @@ +;**************************************************************************** +; PlayBGM [command $04] +; Plays a BGM +;**************************************************************************** + +PlayBGM: + PollPCM + call ClearBGM ; Clear BGM resources + PollPCM + + ld a, (RAM_Status) ; Show BGM playback in Echo's status + or $02 + ld (RAM_Status), a + + PollPCM + + ld hl, RAM_ComBank ; Get command parameters + ld c, (hl) + inc l + ld e, (hl) + inc l + ld d, (hl) + + PollPCM + + xor a ; Command parsed + ld (RAM_Command), a + + ld hl, RAM_BGMData ; Set BGM as playing + ld (hl), $01 + inc l ; No delays! + ld (hl), $01 + inc l ; Store BGM start bank + ld (hl), c + inc l ; Store BGM start address (low) + ld (hl), e + inc l ; Store BGM start address (high) + ld (hl), d + + PollPCM + + ld hl, ProcessBGM ; Tell Echo to process BGM + ld (DoTick_BGM+1), hl + + PollPCM + jp IdleLoop ; End of subroutine + +;**************************************************************************** +; ProcessBGM +; Processes a tick for a BGM +;**************************************************************************** + +ProcessBGM: + PollPCM + + ld hl, RAM_BGMData+1 ; BGM data address + + ld a, (hl) ; Delaying? + dec a + jp z, .nodelay + ld (hl), a + + jp DoTick_BGMSkip ; End of subroutine + +.nodelay: + PollPCM + + inc l + ld c, (hl) ; Get current address + inc l + ld e, (hl) + inc l + ld d, (hl) + ex de, hl + +ProcessBGMRun: + PollPCM ; Fetch next event + call GetParam + PollPCM + + ld a, b ; Parse byte + + cp $08 + jp c, NoteOnFMBGM ; Events $00-$07: note on FM + cp $0B + jp c, NoteOnPSGBGM ; Events $08-$0A: note on PSG (square) + jp z, NoteOnNoiseBGM ; Event $0B: note on PSG (noise) + cp $0C + jp z, PlayPCMBGM ; Event $0C: play PCM + + PollPCM + ld a, b + + cp $18 + jp c, NoteOffFMBGM ; Events $10-$17: note off FM + cp $1C + jp c, NoteOffPSGBGM ; Events $18-$1B: note off PSG + jp z, StopPCMBGM ; Event $1C: note off PCM + + PollPCM + ld a, b + + cp $FE + jp z, SetDelayBGM ; Event $FE: set delay + cp $FF + jp z, StopBGMEvent ; Event $FF: stop BGM + cp $FC + jp z, LoopBGM ; Event $FC: loop BGM + cp $FD + jp z, SetLoopBGM ; Event $FD: set loop point + + PollPCM + ld a, b + + cp $28 + jp c, SetFMVolBGM ; Events $28-$2B: set FM volume + cp $2C + jp c, SetPSGVolBGM ; Events $28-$2B: set PSG volume + + PollPCM + ld a, b + + cp $38 + jp c, SetNoteFMBGM ; Events $30-$37: set FM note + cp $3B + jp c, SetNotePSGBGM ; Events $38-$3A: set PSG note (square) + jp z, SetNoteNoiseBGM ; Event $3B: set PSG note (noise) + + PollPCM + ld a, b + + cp $48 + jp c, LoadFMBGM ; Events $40-$47: load FM instrument + cp $4C + jp c, LoadPSGBGM ; Events $48-$4B: load PSG instrument + + PollPCM + ld a, b + + cp $F8 ; Events $F0-$F7: set FM parameters + jp c, SetFMParamBGM + + PollPCM ; FFFFFFFFF bad event >:( + jp StopBGMEvent ; End of subroutine + +ProcessBGMSkip2: ; This is where we land after a locked event + PollPCM ; that had two bytes for the parameter + inc l ; Skip first byte + jp nz, .nobankskip2 + inc h + jp nz, .nobankskip2 + ld h, $80 + inc c +.nobankskip2: + +ProcessBGMSkip1: ; This is where we land after a locked event + PollPCM ; that had one byte for the parameter + inc l ; Skip byte + jp nz, .nobankskip1 + inc h + jp nz, .nobankskip1 + ld h, $80 + inc c +.nobankskip1: + +ProcessBGMSkip: ; This is where we land after a locked event + PollPCM ; without parameters + + jp ProcessBGMRun ; Keep processing + +;**************************************************************************** +; StopBGM* [command $05, event $FF] +; Stops BGM playback +;**************************************************************************** + +StopBGMEvent: + call StopBGM ; We're just a wrapper + jp DoTick_BGMSkip ; End of subroutine + +StopBGMCmd: + xor a ; Command parsed + ld (RAM_Command), a + call StopBGM ; We're just a wrapper + jp IdleLoop ; End of subroutine + +StopBGM: + ld a, (RAM_Status) ; Hide BGM playback in Echo's status + and $FD + ld (RAM_Status), a + + PollPCM + + call ClearBGM ; Clear BGM resources + PollPCM + + xor a ; Stop playback + ld (RAM_BGMPlaying), a + ld hl, DoTick_BGMSkip + ld (DoTick_BGM+1), hl + + ret ; End of subroutine + +;**************************************************************************** +; ClearBGM +; Clears BGM resources +;**************************************************************************** + +ClearBGM: + ld a, (RAM_Locked+6) ; Stop PCM playback if needed + or a + call z, StopPCM + + ld b, 4 ; Mute all non-locked PSG channels + ld de, RAM_PSGData+48 + ld hl, RAM_Locked+11 +.mutepsg: + PollPCM + ld a, (hl) + or a + jr nz, .nopsgmute + xor a + ld (de), a +.nopsgmute: + PollPCM + ld a, e + sub 16 + ld e, a + dec l + djnz .mutepsg + + ld b, 8 ; Mute all non-locked FM channels +.mutefm: + PollPCM + ld a, (hl) + or a + jr nz, .nofmmute + PollPCM + dec b + ld (ix+0), $28 + ld (ix+1), b + inc b +.nofmmute: + dec l + djnz .mutefm + + ret ; End of subroutine + +;**************************************************************************** +; LoopBGM [event $FC] +; Makes a BGM loop +;**************************************************************************** + +LoopBGM: + PollPCM + + ex de, hl ; Get looping address + inc l + ld c, (hl) + inc l + ld e, (hl) + inc l + ld d, (hl) + dec l + dec l + dec l + ex de, hl + + jp ProcessBGMRun ; End of subroutine + +;**************************************************************************** +; SetLoopBGM [event $FD] +; Sets the BGM loop point +;**************************************************************************** + +SetLoopBGM: + PollPCM + + ex de, hl + inc l ; Store loop point address + ld (hl), c + inc l + ld (hl), e + inc l + ld (hl), d + dec l + dec l + dec l + ex de, hl + + jp ProcessBGMRun ; End of subroutine diff --git a/src-z80/core/main.z80 b/src-z80/core/main.z80 new file mode 100644 index 0000000..4018a1b --- /dev/null +++ b/src-z80/core/main.z80 @@ -0,0 +1,330 @@ +;**************************************************************************** +; EntryPoint +; Where the program starts +;**************************************************************************** + +EntryPoint: + ld sp, RAM_Stack ; Init stack + + xor a ; Reset Echo status + ld (RAM_Status), a + + ld hl, $7F11 ; Mute PSG + ld (hl), $9F + ld (hl), $BF + ld (hl), $DF + ld (hl), $FF + xor a + ld (RAM_PSGData), a + ld (RAM_PSGData+16), a + ld (RAM_PSGData+32), a + ld (RAM_PSGData+48), a + + ld hl, $6000 ; Set default bank + ld (hl), l + ld (hl), l + ld (hl), l + ld (hl), l + ld (hl), l + ld (hl), l + ld (hl), l + ld (hl), l + ld (hl), l + + ;ex af, af' ; Set 9th bank bit to 0 by default + ;xor a ; (0 = $000000-$7FFFFF range aka MD mode) + ;ex af, af' ; (1 = $800000-$FFFFFF range aka 32X mode) + + ld ix, $4000 ; YM2612 I/O ports base address + ld iyh, $40 + + exx ; Init PCM playback status + ld bc, $0000 ; B = playing, C = bank + ld de, $0000 ; DE = address + ld hl, $6000 ; HL = always $6000 + exx + + ld (ix+0), $2B ; Disable DAC by default + ld (ix+1), $00 + + ld e, $7F ; Mute all FM channels + ld a, $40 + ld b, 4 +.mutefm: + ld (ix+0), a + ld (ix+1), e + ld (ix+2), a + ld (ix+3), e + inc a + ld (ix+0), a + ld (ix+1), e + ld (ix+2), a + ld (ix+3), e + inc a + ld (ix+0), a + ld (ix+1), e + ld (ix+2), a + ld (ix+3), e + inc a + inc a + djnz .mutefm + + ld (ix+0), $B4 ; Ensure all channels can be heard from both + ld (ix+1), $C0 ; speakers (by default they're mute!) + ld (ix+0), $B5 + ld (ix+1), $C0 + ld (ix+0), $B6 + ld (ix+1), $C0 + ld (ix+2), $B4 + ld (ix+3), $C0 + ld (ix+2), $B5 + ld (ix+3), $C0 + ld (ix+2), $B6 + ld (ix+3), $C0 + + ld (ix+0), $24 ; Init timers + ld (ix+1), $FE + ld (ix+0), $25 + ld (ix+1), $03 + ld (ix+0), $26 + ld (ix+0), $C9 + ld (ix+1), $FF + ld (ix+0), $27 + ld (ix+1), $3F + + jp IdleLoop ; Go into idle loop + +;**************************************************************************** +; PollPCM +; Used to update PCM while not idle +;**************************************************************************** + +PollPCM: macro + ld a, ($4000) + bit 0, a + call nz, UpdatePCM + endm + +;**************************************************************************** +; RunCommand +; Checks which command to run +;**************************************************************************** + +RunCommand: + dec a ; Command $01: load list + jp z, LoadList + dec a ; Command $02: play SFX + jp z, PlaySFX + dec a ; Command $03: stop SFX + jp z, StopSFXCmd + dec a ; Command $04: play BGM + jp z, PlayBGM + dec a ; Command $05: stop BGM + jp z, StopBGMCmd + + PollPCM + + xor a ; Bad command, ignore >:( + ld (RAM_Command), a + + PollPCM + +;**************************************************************************** +; IdleLoop +; Loop that runs when not processing SFX or BGM +;**************************************************************************** + +IdleLoop: + ld a, (RAM_Command) ; Look for commands + or a + jr nz, RunCommand + + PollPCM ; Poll PCM + + ld a, ($4000) ; Tick? + bit 1, a + jr nz, DoTick + bit 0, a ; Poll PCM again + call nz, UpdatePCM ; Not using macro for optimization purposes + + jp IdleLoop ; Keep idling + +;**************************************************************************** +; DoTick +; Called whenever a new tick triggers +;**************************************************************************** + +DoTick: + ld a, (ix+0) + bit 0, a + call nz, UpdatePCM + + ld (ix+0), $26 ; Reset timer +.timerset: + ld (ix+1), $C8 + ld (ix+0), $27 + ld (ix+1), $2F + + ld a, (.timerset+3) ; $C8 is too fast, $C9 is too slow + xor $01 ; So, we alternate between them to compensate + ld (.timerset+3), a + + PollPCM + +DoTick_SFX: ; Process SFXs + jp DoTick_SFXSkip +DoTick_SFXSkip: + + PollPCM + +DoTick_BGM: ; Process BGMs + jp DoTick_BGMSkip +DoTick_BGMSkip: + + PollPCM + + jp UpdatePSG ; Update PSG envelopes +DoTick_PSGSkip: + + PollPCM + + jp IdleLoop ; End of subroutine + +;**************************************************************************** +; BankSwitch +; Switches into a new bank (won't update player status!) +; +; input A .... New bank to switch into +; input HL ... Must be $6000 +; breaks ..... AF +;**************************************************************************** + +BankSwitch: macro + ld (hl), a + rrca + ld (hl), a + rrca + ld (hl), a + rrca + ld (hl), a + rrca + ld (hl), a + rrca + ld (hl), a + rrca + ld (hl), a + ld (hl), l + rrca + ld (hl), a + endm + +;**************************************************************************** +; LoadList [command $01] +; Loads the pointer list +;**************************************************************************** + +LoadList: + ld hl, RAM_ComBank ; Get command parameters + ld c, (hl) + inc l + ld e, (hl) + inc l + ld d, (hl) + ex de, hl + + xor a ; Command parsed + ld (RAM_Command), a + + ld a, c ; Do initial bank switch + ld de, $6000 + ex de, hl + BankSwitch + ex de, hl + + ld de, RAM_PointerList ; Where the pointer list starts + +.loop: + ld a, (hl) ; Get high address byte + or a ; Is it the end of the list? + jp z, .end ; If so, stop parsing list + ld (de), a ; Nope, store it in list + + inc d ; Get address for next byte + inc l + jr nz, .noswitch1 + inc h + jr nz, .noswitch1 + inc c + ld a, c + ld h, $60 + BankSwitch + ld h, $80 +.noswitch1: + + ld a, (hl) ; Get low address byte + ld (de), a ; Store it in pointer list + + inc d ; Get address for next byte + inc l + jr nz, .noswitch2 + inc h + jr nz, .noswitch2 + inc c + ld a, c + ld h, $60 + BankSwitch + ld h, $80 +.noswitch2: + + ld a, (hl) ; Get bank byte + ld (de), a ; Store it in pointer list + + dec d ; Get address for next byte + dec d + inc e + inc l + jr nz, .noswitch3 + inc h + jr nz, .noswitch3 + inc c + ld a, c + ld h, $60 + BankSwitch + ld h, $80 +.noswitch3: + + jp .loop ; Go for next byte + +.end: + ld a, c ; Store current bank + ld (RAM_LastBank), a + jp IdleLoop ; End of subroutine + +;**************************************************************************** +; GetParam +; Subroutine for getting the parameter byte +;**************************************************************************** + +GetParam: + ld a, (RAM_LastBank) ; Bank switch? + cp c + jp z, .noswitchp + ld a, c + ld (RAM_LastBank), a + exx + BankSwitch + exx +.noswitchp: + ld b, (hl) ; Get volume + + PollPCM + + inc l ; Get next address + jp nz, .nonewbankp + inc h + jp nz, .nonewbankp + ld h, $80 + inc c +.nonewbankp: + + ret ; End of subroutine diff --git a/src-z80/core/sfx.z80 b/src-z80/core/sfx.z80 new file mode 100644 index 0000000..b7e26aa --- /dev/null +++ b/src-z80/core/sfx.z80 @@ -0,0 +1,316 @@ +;**************************************************************************** +; PlaySFX [command $02] +; Plays a SFX +;**************************************************************************** + +PlaySFX: + PollPCM + call ClearSFX ; Clear SFX resources + PollPCM + + ld a, (RAM_Status) ; Show SFX playback in Echo's status + or $01 + ld (RAM_Status), a + + PollPCM + + ld hl, RAM_ComBank ; Get command parameters + ld c, (hl) + inc l + ld e, (hl) + inc l + ld d, (hl) + + PollPCM + + xor a ; Command parsed + ld (RAM_Command), a + + ld hl, RAM_SFXData ; Set SFX as playing + ld (hl), $01 + inc l ; No delays! + ld (hl), $01 + inc l ; Store SFX start bank + ld (hl), c + inc l ; Store SFX start address (low) + ld (hl), e + inc l ; Store SFX start address (high) + ld (hl), d + + PollPCM + + ld hl, ProcessSFX ; Tell Echo to process SFX + ld (DoTick_SFX+1), hl + + PollPCM + jp IdleLoop ; End of subroutine + +;**************************************************************************** +; ProcessSFX +; Processes a tick for a SFX +;**************************************************************************** + +ProcessSFX: + PollPCM + + ld hl, RAM_SFXData+1 ; SFX data address + + ld a, (hl) ; Delaying? + dec a + jp z, .nodelay + ld (hl), a + + jp DoTick_SFXSkip ; End of subroutine + +.nodelay: + PollPCM + + inc l ; Get current address + ld c, (hl) + inc l + ld e, (hl) + inc l + ld d, (hl) + ex de, hl + +ProcessSFXRun: + PollPCM ; Fetch next event + call GetParam + PollPCM + + ld a, b ; Parse byte + + cp $08 + jp c, NoteOnFMSFX ; Events $00-$07: note on FM + cp $0B + jp c, NoteOnPSGSFX ; Events $08-$0A: note on PSG (square) + jp z, NoteOnNoiseSFX ; Event $0B: note on PSG (noise) + cp $0C + jp z, PlayPCMSFX ; Event $0C: note on PCM + + PollPCM + ld a, b + + cp $18 + jp c, NoteOffFMSFX ; Events $10-$17: note off FM + cp $1C + jp c, NoteOffPSGSFX ; Events $18-$1B: note off PSG + jp z, StopPCMSFX ; Event $1C: note off PCM + + PollPCM + ld a, b + + cp $FE + jp z, SetDelaySFX ; Event $FE: set delay + cp $FF + jp z, StopSFXEvent ; Event $FF: stop SFX + + PollPCM + ld a, b + + cp $28 + jp c, SetFMVolSFX ; Events $28-$2B: set FM volume + cp $2C + jp c, SetPSGVolSFX ; Events $28-$2B: set PSG volume + + PollPCM + ld a, b + + cp $38 + jp c, SetNoteFMSFX ; Events $30-$37: set FM note + cp $3B + jp c, SetNotePSGSFX ; Events $38-$3A: set PSG note (square) + jp z, SetNoteNoiseSFX ; Event $3B: set PSG note (noise) + + PollPCM + ld a, b + + cp $48 + jp c, LoadFMSFX ; Events $40-$47: load FM instrument + cp $4C + jp c, LoadPSGSFX ; Events $48-$4B: load PSG instrument + + PollPCM + ld a, b + + cp $E8 + jp c, LockChannelFM ; Events $E0-$E7: lock FM channel + cp $EC + jp c, LockChannelPSG ; Events $E8-$EB: lock PSG channel + jp z, LockChannelPCM ; Event $EC: lock PCM channel + + PollPCM + ld a, b + + cp $F8 ; Events $F0-$F7: set FM parameters + jp c, SetFMParamSFX + +;**************************************************************************** +; StopSFX* [command $03, event $FF] +; Stops SFX playback +;**************************************************************************** + +StopSFXEvent: + call StopSFX ; We're just a wrapper + jp DoTick_SFXSkip ; End of subroutine + +StopSFXCmd: + xor a ; Command parsed + ld (RAM_Command), a + call StopSFX ; We're just a wrapper + jp IdleLoop ; End of subroutine + +StopSFX: + PollPCM + + ld a, (RAM_Status) ; Hide SFX playback in Echo's status + and $FE + ld (RAM_Status), a + + PollPCM + + xor a ; Stop playback + ld (RAM_SFXPlaying), a + ld hl, DoTick_SFXSkip + ld (DoTick_SFX+1), hl + + PollPCM + call ClearSFX ; Clear SFX resources + + PollPCM + ret ; End of subroutine + +;**************************************************************************** +; ClearSFX +; Clears SFX resources +;**************************************************************************** + +ClearSFX: + ld a, (RAM_Locked+6) ; Stop PCM playback if needed + or a + call nz, StopPCM + +;---------------------------------------------------------------------------- + + ld b, 4 ; Look for locked PSG channels + ld de, RAM_Locked+11 +.unlockpsg: + + PollPCM + + ld a, (de) ; Check if this channel needs unlocking + or a + jr z, .psgfree + xor a + ld (de), a + + PollPCM + + ld a, b ; Restore BGM volume + rrca + rrca + rrca + rrca + dec a + ld h, RAM_PSGData>>8 + ld l, a + ld c, (hl) + sub 15 + ld l, a + ld (hl), c + + PollPCM + push de + + ld a, l ; Restore BGM envelope + add 8 + ld l, a + add 12-8 + ld e, a + ld d, h + + PollPCM + + ld a, (de) + ld (hl), a + inc l + inc e + ld a, (de) + ld (hl), a + inc l + inc e + ld a, (de) + ld (hl), a + + pop de + PollPCM + +.psgfree: + dec e ; Go for next PSG channel to unlock + djnz .unlockpsg + +;---------------------------------------------------------------------------- + + ld b, 8 ; Look for locked FM channels +.unlockfm: + + PollPCM + + ld a, (de) ; Check if this channel needs unlocking + or a + jp z, .fmfree + xor a + ld (de), a + + dec b ; Mute FM channel + ld (ix+0), $28 + ld (ix+1), b + + PollPCM + + ld a, b + and $04 ; Determine which port to write + rrca + ld iyl, a + + ld a, b ; Kill ADSR + call KillFM + + PollPCM + + ld hl, RAM_BGMFMVol ; Restore BGM FM volume + ld a, b + add l + ld l, a + ld c, (hl) + ld a, l + add 8 + ld l, a + ld (hl), c + + PollPCM + + ld a, l ; Restore BGM FM instrument + sub 8*2 + ld l, a + push bc + push de + push hl + ld a, b + ld b, (hl) + call LoadFMDirect + pop hl + pop de + pop bc + + PollPCM + inc b +.fmfree: + PollPCM + dec e ; Go for next FM channel to unlock + dec b + jp nz, .unlockfm + +;---------------------------------------------------------------------------- + + ret ; End of subroutine diff --git a/src-z80/core/vars.z80 b/src-z80/core/vars.z80 new file mode 100644 index 0000000..ef9a676 --- /dev/null +++ b/src-z80/core/vars.z80 @@ -0,0 +1,70 @@ +;**************************************************************************** +; Player variables +;**************************************************************************** + + ds $100-($&$FF), $FF +RAM_PSGData: ds 4*16 ; PSG envelope data + ; ds 1 ... Channel volume + ; ds 1 ... Global volume + ; ds 3 ... Current address + ; ds 3 ... Looping address + ; ds 3 ... Start address + ; ds 1 ... Padding + ; ds 3 ... BGM instrument address + ; ds 1 ... BGM channel volume + +RAM_BGMFMInstr: ds 8 ; FM instruments used by BGM +RAM_BGMFMVol: ds 8 ; FM volumes used by BGM +RAM_FMVolume: ds 8 ; Volume of each FM channel +RAM_FMData: ds 8*5 ; FM info (for volume handling) + ; ds 8*1 ... Register $B0 + ; ds 8*1 ... Register $40 + ; ds 8*1 ... Register $44 + ; ds 8*1 ... Register $48 + ; ds 8*1 ... Register $4C + +RAM_LastBank: ds 1 ; Last accessed bank + +RAM_BGMData: ; Where BGM data starts +RAM_BGMPlaying: ds 1 ; Set if a BGM is playing +RAM_BGMDelay: ds 1 ; How many ticks to wait +RAM_BGMBank: ds 1 ; Current BGM bank +RAM_BGMAddress: ds 2 ; Current BGM address +RAM_BGMLoopPoint: ds 3 ; BGM loop point + +RAM_SFXData: ; Where SFX data starts +RAM_SFXPlaying: ds 1 ; Set if a SFX is playing +RAM_SFXDelay: ds 1 ; How many ticks to wait +RAM_SFXBank: ds 1 ; Current SFX bank +RAM_SFXAddress: ds 2 ; Current SFX address + +RAM_Locked: ds 12 ; Locked channels + +RAM_Scratch: ds 32 ; Scratch bytes, may be useful when + ; buffering to speed up to avoid bank + ; switching conflicts + +;**************************************************************************** +; Pointer list starts being stored from here +; $300 (768) bytes are needed to store the pointer list +; +; Format for a pointer list entry is as follows: +; RAM_PointerList[$000+n] = address high +; RAM_PointerList[$100+n] = address low +; RAM_PointerList[$200+n] = bank +;**************************************************************************** + + ds $100-($&$FF), $FF + +RAM_PointerList: equ $1C00 + +;**************************************************************************** +; 68000 communication variables +;**************************************************************************** + +RAM_Stack: equ $1FF0 ; Where stack starts + +RAM_Status: equ $1FF0 ; Current playback status +RAM_Command: equ $1FFF ; Command type +RAM_ComAddr: equ $1FFD ; Command address parameter +RAM_ComBank: equ $1FFC ; Command bank parameter diff --git a/src-z80/player/fm.z80 b/src-z80/player/fm.z80 new file mode 100644 index 0000000..40cf5da --- /dev/null +++ b/src-z80/player/fm.z80 @@ -0,0 +1,784 @@ +;**************************************************************************** +; NoteOnFM +; Does a "note on" for a FM channel +;**************************************************************************** + +NoteOnFMSFX: + call NoteOnFM ; We're just a wrapper + jp ProcessSFXRun ; End of subroutine + +NoteOnFMBGM: + ld b, a + PollPCM + + push hl + ld a, b + and $07 ; Check if channel is free + ld hl, RAM_Locked + add l + ld l, a + ld a, (hl) + pop hl + or a + jp nz, ProcessBGMSkip1 ; Don't play if locked + + ld a, b + call NoteOnFM ; We're just a wrapper + jp ProcessBGMRun ; End of subroutine + +NoteOnFM: + and $07 ; Get channel ID + ld (ix+0), $28 ; Note off + ld (ix+1), a + + ld b, a + ex af, af' + PollPCM + + ld a, b ; Determine which port to write + and $04 + rrca + ld iyl, a + + PollPCM + call GetParam ; Get note + PollPCM + + ex af, af' + push de + push hl + + ld d, a + and $03 ; Index of first frequency register + add $A4 + ld e, a + + PollPCM + + ld h, FMFreqTable>>8 ; Get address of note + ld a, b + and $1F + add FMFreqTable&$FF + ld l, a + + PollPCM + + ld a, b ; Set new frequency + and $E0 + rrca + rrca + ld b, a + ld a, (hl) + or b + ld (iy+0), e + ld (iy+1), a + + PollPCM + + ld a, e + sub 4 + ld e, a + dec l + ld a, (hl) + ld (iy+0), e + ld (iy+1), a + + PollPCM + + ld a, d ; Note on! + or $F0 + ld (ix+0), $28 + ld (ix+1), a + + pop hl + pop de + ret ; End of subroutine + +;**************************************************************************** +; NoteOffFM +; Does a "note off" for a FM channel +;**************************************************************************** + +NoteOffFMSFX: + call NoteOffFM ; We're just a wrapper + jp ProcessSFXRun ; End of subroutine + +NoteOffFMBGM: + ld b, a + PollPCM + ld a, b + + push hl + and $07 ; Check if channel is free + ld hl, RAM_Locked + add l + ld l, a + ld a, (hl) + pop hl + or a + jp nz, ProcessBGMSkip ; Don't stop if locked + + ld a, b + call NoteOffFM ; We're just a wrapper + jp ProcessBGMRun ; End of subroutine + +NoteOffFM: + and $07 ; Get channel ID + ld (ix+0), $28 ; Note off + ld (ix+1), a + ret ; End of subroutine + +;**************************************************************************** +; SetFMNote* +; Sets the note of a FM channel without "note on" +;**************************************************************************** + +SetNoteFMSFX: + call SetNoteFM ; We're just a wrapper + jp ProcessSFXRun ; End of subroutine + +SetNoteFMBGM: + ld b, a + PollPCM + ld a, b + + push hl + and $07 ; Check if channel is free + ld hl, RAM_Locked + add l + ld l, a + ld a, (hl) + pop hl + or a + jp nz, ProcessBGMSkip2 ; Don't change if locked + + ld a, b + call SetNoteFM ; We're just a wrapper + jp ProcessBGMRun ; End of subroutine + +SetNoteFM: + push af + ld b, a + PollPCM + + ld a, b ; Determine which port to write + and $04 + rrca + ld iyl, a + + PollPCM + call GetParam ; Get high byte + PollPCM + + pop af + push de + + and $07 + ld d, a + and $03 ; Index of first frequency register + add $A4 + ld e, a + + PollPCM + + ld (iy+0), e ; Load high byte + ld (iy+1), b + + PollPCM ; Load low byte + call GetParam + PollPCM + + ld a, e + sub 4 + ld e, a + + ld (iy+0), e + ld (iy+1), b + + PollPCM + pop de + ret ; End of subroutine + +;**************************************************************************** +; LoadFM* +; Loads a FM instrument +;**************************************************************************** + +LoadFMSFX: + call LoadFMEvent ; We're just a wrapper + jp ProcessSFXRun ; End of subroutine + +LoadFMBGM: + and $07 ; Get channel ID + ld b, a + PollPCM + + push de + push bc + ld a, b + ld de, RAM_BGMFMInstr ; Store instrument ID + add e + ld e, a + PollPCM + call GetParam + PollPCM + ex de, hl + ld (hl), b + ex de, hl + ld e, c + pop bc + ld c, e + pop de + + PollPCM + + push hl ; Check if channel is free + ld hl, RAM_Locked + ld a, b + add l + ld l, a + ld a, (hl) + pop hl + or a + jp nz, ProcessBGMSkip ; Don't load if locked + + PollPCM + + ld a, b + and $07 + push hl + ld hl, RAM_BGMFMInstr + add l + ld l, a + ld b, (hl) + pop hl + + push af + PollPCM + pop af + + call LoadFMDirect ; We're just a wrapper + jp ProcessBGMRun ; End of subroutine + +LoadFMEvent: + and $07 ; Get channel ID + + ex af, af' + PollPCM + call GetParam ; Get instrument ID + PollPCM + ex af, af' +LoadFMDirect: + + push af + and $04 ; Determine which port to write + rrca + ld iyl, a + PollPCM + pop af + + push bc + push de + push hl + + ld h, RAM_PointerList>>8 ; Get instrument address + ld l, b + ld d, (hl) + inc h + ld e, (hl) + inc h + ld c, (hl) + ex de, hl + + push af + PollPCM + + ld b, 28/4 ; Load FM instrument into our scratch buffer, + ld de, RAM_Scratch ; so we don't conflict with PCM bank +.getinstr: ; switch (which would slow down A LOT) + ld a, b + ex af, af' + call GetParam + ex de, hl + ld (hl), b + ex de, hl + inc e + call GetParam + ex de, hl + ld (hl), b + ex de, hl + inc e + call GetParam + ex de, hl + ld (hl), b + ex de, hl + inc e + call GetParam + ex de, hl + ld (hl), b + ex de, hl + inc e + ex af, af' + ld b, a + djnz .getinstr + call GetParam + ex de, hl + ld (hl), b + ex de, hl + + PollPCM + + pop af ; Kill ADSR + ld b, a + call KillFM + ld a, b + + ld de, RAM_FMData ; Get address of FM data + and $07 + add e + ld e, a + + push af + and $03 ; Register index for operator + add $B0 + + ld hl, RAM_Scratch ; Get address of buffer with the instrument + ; data + + ex af, af' + PollPCM ; Write operator + ex af, af' + ld (iy+0), a + ld b, (hl) + inc l + ld (iy+1), b + + ex af, af' ; Store $B0 register in FM data buffer + ld a, b ; Also store it in the FM channel information + ld (de), a ; table since we need it when changing the + ld a, e ; FM channel volume + add 8 + ld e, a + PollPCM + ex af, af' + + sub $B0-$30 + + ex af, af' ; Write registers $30-$3C + PollPCM + ex af, af' + + ld (iy+0), a + ld b, (hl) + ld (iy+1), b + add 4 + inc l + ld (iy+0), a + ld b, (hl) + ld (iy+1), b + add 4 + inc l + ld (iy+0), a + ld b, (hl) + ld (iy+1), b + add 4 + inc l + ld (iy+0), a + ld b, (hl) + ld (iy+1), b + add 4 + inc l + + ld b, 4 ; Write registers $40-$4C +.reg40: ; Also store the registers in the FM channel + ld c, a ; information field since we'll need them to + push af ; set the FM channel volume + PollPCM + ld (iy+0), c + ld a, (hl) + ld (iy+1), a + ld (de), a + ld a, e + add 8 + ld e, a + pop af + add 4 + inc l + djnz .reg40 + + ld b, 20/4 ; Write registers $50-$5C +.reg50: + ld c, b + ex af, af' + PollPCM + ex af, af' + + ld (iy+0), a + ld b, (hl) + ld (iy+1), b + add 4 + inc l + ld (iy+0), a + ld b, (hl) + ld (iy+1), b + add 4 + inc l + ld (iy+0), a + ld b, (hl) + ld (iy+1), b + add 4 + inc l + ld (iy+0), a + ld b, (hl) + ld (iy+1), b + add 4 + + inc l + ld b, c + djnz .reg50 + + PollPCM + + ex de, hl + ld a, l + sub 8*6 + ld l, a + ld b, (hl) + add 8 + ld l, a + + PollPCM + + pop af + pop hl + pop de + pop bc + ret + + ;jp SetFMVolLoad ; End of subroutine + +;**************************************************************************** +; SetFMVol* +; Sets the volume of a FM channel +;**************************************************************************** + +SetFMVolSFX: + call SetFMVolEvent ; We're just a wrapper + jp ProcessSFXRun ; End of subroutine + +SetFMVolBGM: + and $07 + ld b, a + + PollPCM + + push de + push bc + ld a, b + ld de, RAM_BGMFMVol ; Store BGM volume + add e + ld e, a + PollPCM + call GetParam + PollPCM + ex de, hl + ld (hl), b + ex de, hl + ld e, c + pop bc + ld c, e + pop de + + PollPCM + + push hl + ld a, b ; Check if channel is free + ld hl, RAM_Locked + add l + ld l, a + ld a, (hl) + pop hl + or a + jp nz, ProcessBGMSkip ; Don't change if locked + + PollPCM + + ld a, b + push hl + ld hl, RAM_BGMFMVol + add l + ld l, a + ld b, (hl) + pop hl + + push af + PollPCM + pop af + + call SetFMVolBGMEvent ; We're just a wrapper + jp ProcessBGMRun ; End of subroutine + +SetFMVolEvent: + push af + PollPCM + call GetParam ; Get new volume + PollPCM + pop af + +SetFMVolBGMEvent: + push bc + push de + push hl + + ld hl, RAM_FMVolume ; Store new volume + and $07 + add l + ld l, a + ld (hl), b + + ex af, af' + PollPCM + ld a, l ; Get address of FM data + add 8 + ld l, a + ex af, af' + +SetFMVolLoad: + push af + ;ld iyh, $40 ; Determine which port to write + and $04 + rrca + ld iyl, a + PollPCM + pop af + + and $03 ; Index of first volume register + add $40 + ld c, a + + PollPCM + + ld a, (hl) ; Get algorithm + and $07 + ld e, a + + PollPCM + + ld a, l + add 8 + ld l, a + ld a, e ; Process operator #1 + cp $07 + jr c, .noop1 + ld a, (hl) + add b + cp $7F + jr c, .notooloud1 + ld a, $7F +.notooloud1: + ld (iy+0), c + ld (iy+1), a +.noop1: + ld a, c + add 4 + ld c, a + + PollPCM + + ld a, l + add 8 + ld l, a + ld a, e ; Process operator #2 + cp $05 + jr c, .noop2 + ld a, (hl) + add b + cp $7F + jr c, .notooloud2 + ld a, $7F +.notooloud2: + ld (iy+0), c + ld (iy+1), a +.noop2: + ld a, c + add 4 + ld c, a + + PollPCM + + ld a, l + add 8 + ld l, a + ld a, e ; Process operator #3 + cp $04 + jr c, .noop3 + ld a, (hl) + add b + cp $7F + jr c, .notooloud3 + ld a, $7F +.notooloud3: + ld (iy+0), c + ld (iy+1), a +.noop3: + ld a, c + add 4 + ld c, a + + PollPCM + + ld a, l + add 8 + ld l, a + ld a, (hl) ; Process operator #4 + add b + cp $7F + jr c, .notooloud4 + ld a, $7F +.notooloud4: + ld (iy+0), c + ld (iy+1), a + + PollPCM + + pop hl + pop de + pop bc + ret ; End of subroutine + +;**************************************************************************** +; SetFMParam* +; Sets the different parameters of a FM channel +;**************************************************************************** + +SetFMParamSFX: + call SetFMParam ; We're just a wrapper + jp ProcessSFXRun ; End of subroutine + +SetFMParamBGM: + ld b, a + PollPCM + ld a, b + + push hl + and $07 ; Check if channel is free + ld hl, RAM_Locked + add l + ld l, a + ld a, (hl) + pop hl + or a + jp nz, ProcessBGMSkip1 ; Don't modify if locked + + ld a, b + call SetFMParam ; We're just a wrapper + jp ProcessBGMRun ; End of subroutine + +SetFMParam: + ld b, a + PollPCM + + ;ld iyh, $40 ; Determine which port to write + ld a, b + and $04 + rrca + ld iyl, a + + ld a, b + ex af, af' + PollPCM + call GetParam ; Get parameters + PollPCM + ex af, af' + + and $03 ; Get channel ID + add $B4 + ld (iy+0), a ; Set new parameters + ld (iy+1), b + + ret ; End of subroutine + +;**************************************************************************** +; LockChannelFM [events $E0-$E7] +; Locks a FM channel +;**************************************************************************** + +LockChannelFM: + and $07 + ld b, a + PollPCM + + push hl + ld h, RAM_Locked>>8 ; Get address of channel to lock + ld a, b + add RAM_Locked&$FF + ld l, a + ld (hl), $01 ; Lock channel + pop hl + + PollPCM + + ld a, b ; Determine which port to write + and $04 + rrca + ld iyl, a + + ld a, b ; Reset stereo + and $03 + add a + add a + add $B4 + ld (iy+0), a + ld (iy+1), $C0 + + jp ProcessSFXRun ; End of subroutine + +;**************************************************************************** +; KillFM +; Kills a FM channel +;**************************************************************************** + +KillFM: + push af + push de + + and $03 ; Load dummy FM instrument + add $40 + ld c, 6 + ld hl, DummyFMInstr +.loaddummy: + ex af, af' + PollPCM + ex af, af' + ld e, (hl) + + ld (iy+0), a + ld (iy+1), e + add 4 + ld (iy+0), a + ld (iy+1), e + add 4 + ld (iy+0), a + ld (iy+1), e + add 4 + ld (iy+0), a + ld (iy+1), e + add 4 + + inc l + dec c + jp nz, .loaddummy + + pop de + PollPCM + pop af + + ld c, a ; Reset ADSR + or $F0 + ld (ix+0), $28 + ld (ix+1), a + ld (ix+0), $28 + ld (ix+1), c + + PollPCM + ret ; End of subroutine diff --git a/src-z80/player/freq.z80 b/src-z80/player/freq.z80 new file mode 100644 index 0000000..a469c0b --- /dev/null +++ b/src-z80/player/freq.z80 @@ -0,0 +1,105 @@ +;**************************************************************************** +; PSGFreqTable +; Frequency table for all PSG notes +;**************************************************************************** + + ds $100-($&$FF), $FF +PSGFreqTable: + db $83, $35 ; C-4 - 851 + db $83, $32 ; C#4 - 803 + db $86, $2F ; D-4 - 758 + db $8B, $2C ; D#4 - 715 + db $83, $2A ; E-4 - 675 + db $8D, $27 ; F-4 - 637 + db $89, $25 ; F#4 - 601 + db $88, $23 ; G-4 - 568 + db $88, $21 ; G#4 - 536 + db $8A, $1F ; A-4 - 506 + db $8D, $1D ; A#4 - 477 + db $82, $1C ; B-4 - 450 + db $89, $1A ; C-5 - 425 + db $81, $19 ; C#5 - 401 + db $8B, $17 ; D-5 - 379 + db $85, $16 ; D#5 - 357 + db $81, $15 ; E-5 - 337 + db $8E, $13 ; F-5 - 318 + db $8C, $12 ; F#5 - 300 + db $8C, $11 ; G-5 - 284 + db $8C, $10 ; G#5 - 268 + db $8D, $0F ; A-5 - 253 + db $8E, $0E ; A#5 - 238 + db $81, $0E ; B-5 - 225 + db $84, $0D ; C-6 - 212 + db $88, $0C ; C#6 - 200 + db $8D, $0B ; D-6 - 189 + db $82, $0B ; D#6 - 178 + db $88, $0A ; E-6 - 168 + db $8F, $09 ; F-6 - 159 + db $86, $09 ; F#6 - 150 + db $8E, $08 ; G-6 - 142 + db $86, $08 ; G#6 - 134 + db $8E, $07 ; A-6 - 126 + db $87, $07 ; A#6 - 119 + db $80, $07 ; B-6 - 112 + db $8A, $06 ; C-7 - 106 + db $84, $06 ; C#7 - 100 + db $8E, $05 ; D-7 - 94 + db $89, $05 ; D#7 - 89 + db $84, $05 ; E-7 - 84 + db $8F, $04 ; F-7 - 79 + db $8B, $04 ; F#7 - 75 + db $87, $04 ; G-7 - 71 + db $83, $04 ; G#7 - 67 + db $8F, $03 ; A-7 - 63 + db $8B, $03 ; A#7 - 59 + db $88, $03 ; B-7 - 56 + db $85, $03 ; C-8 - 53 + db $82, $03 ; C#8 - 50 + db $8F, $02 ; D-8 - 47 + db $8C, $02 ; D#8 - 44 + db $8A, $02 ; E-8 - 42 + db $87, $02 ; F-8 - 39 + db $85, $02 ; F#8 - 37 + db $83, $02 ; G-8 - 35 + db $81, $02 ; G#8 - 33 + db $8F, $01 ; A-8 - 31 + db $8D, $01 ; A#8 - 29 + db $8C, $01 ; B-8 - 28 + db $8A, $01 ; C-9 - 26 + db $89, $01 ; C#9 - 25 + db $87, $01 ; D-9 - 23 + db $86, $01 ; D#9 - 22 + db $85, $01 ; E-9 - 21 + db $83, $01 ; F-9 - 19 + db $82, $01 ; F#9 - 18 + db $81, $01 ; G-9 - 17 + db $80, $01 ; G#9 - 16 + db $8F, $00 ; A-9 - 15 + db $8E, $00 ; A#9 - 14 + db $8E, $00 ; B-9 - 14 + +;**************************************************************************** +; FMFreqTable +; Frequency table for all FM notes +;**************************************************************************** + +FMFreqTable: + dw 644, 681, 722, 765 + dw 810, 858, 910, 964 + dw 1021, 1081, 1146, 1214 + +;**************************************************************************** +; DummyFMInstr +; Dummy FM instrument to mute FM channels... +; +; To-do: put this in its own file? Although I'd like for this table to stay +; in this area in memory +;**************************************************************************** + +DummyFMInstr: + db $7F ; $40..$4C + db $1F ; $50..$5C + db $1F ; $60..$6C + db $1F ; $70..$7C + db $0F ; $80..$8C + db $00 ; $90..$9C diff --git a/src-z80/player/misc.z80 b/src-z80/player/misc.z80 new file mode 100644 index 0000000..6373ab0 --- /dev/null +++ b/src-z80/player/misc.z80 @@ -0,0 +1,31 @@ +;**************************************************************************** +; SetDelay* [event $FE] +; Adds a delay in playback +;**************************************************************************** + +SetDelaySFX: + call SetDelay ; We're just a wrapper + jp DoTick_SFXSkip ; End of subroutine + +SetDelayBGM: + call SetDelay ; We're just a wrapper + jp DoTick_BGMSkip ; End of subroutine + +SetDelay: + PollPCM + call GetParam ; Get delay + PollPCM + + ex de, hl + ld (hl), d ; Store new address + dec l + ld (hl), e + dec l + ld (hl), c + + PollPCM + + dec l ; Store new delay + ld (hl), b + + ret ; End of subroutine diff --git a/src-z80/player/pcm.z80 b/src-z80/player/pcm.z80 new file mode 100644 index 0000000..44bc236 --- /dev/null +++ b/src-z80/player/pcm.z80 @@ -0,0 +1,139 @@ +;**************************************************************************** +; PlayPCM* +; Plays a PCM sample +;**************************************************************************** + +PlayPCMSFX: + call PlayPCM ; We're just a wrapper + jp ProcessSFXRun ; End of subroutine + +PlayPCMBGM: + PollPCM + + ld a, (RAM_Locked+6) ; Check if channel is free + or a + jp nz, ProcessBGMSkip1 ; Don't play sample if locked + + call PlayPCM ; We're just a wrapper + jp ProcessBGMRun ; End of subroutine + +PlayPCM: + call GetParam ; Get sample ID + + ld a, b + exx ; We'll modify PCM data now + ld b, $01 ; Play PCM! + + ld h, RAM_PointerList>>8 ; Get offset in pointer list + ld l, a + + ld d, (hl) ; Get PCM address + inc h + ld e, (hl) + inc h + ld c, (hl) + + ld hl, $6000 ; Restore $6000 back to HL + exx ; Back to standard variables + + ld (ix+0), $2B ; Turn on DAC + ld (ix+1), $80 + ld (ix+0), $2A + ld (ix+1), $80 + + ret ; End of subroutine + +;**************************************************************************** +; UpdatePCM +; Updates PCM output upon a timer event +;**************************************************************************** + +UpdatePCM: + ;ld (ix+0), $24 ; Reset timer + ;ld (ix+1), $FE + ;ld (ix+0), $25 + ;ld (ix+1), $03 + ld (ix+0), $27 + ld (ix+1), $1F + + exx ; Switch to PCM registers + + ld a, b ; Do any playback? + or a + jr z, .nopcm + + ld a, (RAM_LastBank) ; Bank switch? + cp c + jp z, .noswitchu + ld a, c + ld (RAM_LastBank), a + BankSwitch +.noswitchu: + + ld a, (de) ; Get sample + inc a ; Is it end of waveform? + jr z, .stop ; If so, stop + ld (ix+0), $2A ; Nope, send sample to YM2612 + ld (ix+1), a + + inc e ; Get address for next sample + jr nz, .nopcm + inc d + jr nz, .nopcm + ld d, $80 + inc c + +.nopcm: + exx ; Go back to normal registers + ret ; End of subroutine + +.stop: + ld b, $00 ; Stop playback + ld (ix+0), $2A ; Turn off DAC + ld (ix+1), $80 + ld (ix+0), $2B + ld (ix+1), $00 + exx ; Go back to normal registers + ret ; End of subroutine + +;**************************************************************************** +; StopPCM* +; Stops a PCM sample +;**************************************************************************** + +StopPCMSFX: + call StopPCM ; We're just a wrapper + jp ProcessSFXRun ; End of subroutine + +StopPCMBGM: + PollPCM + + ld a, (RAM_Locked+6) ; Check if channel is free + or a + jp nz, ProcessBGMRun ; Don't stop sample if locked + + call StopPCM ; We're just a wrapper + jp ProcessBGMRun ; End of subroutine + +StopPCM: + exx ; Stop PCM playback + ld b, $00 + exx + + ld (ix+0), $2B ; Disable DAC + ld (ix+1), $00 + + ret ; End of subroutine + + +;**************************************************************************** +; LockChannelPCM [event $EC] +; Locks the PCM channel +;**************************************************************************** + +LockChannelPCM: + ld a, $01 ; Lock PCM channel + ld (RAM_Locked+6), a + + call StopPCM ; Stop PCM playback + jp ProcessSFXRun ; End of subroutine diff --git a/src-z80/player/psg.z80 b/src-z80/player/psg.z80 new file mode 100644 index 0000000..46df82a --- /dev/null +++ b/src-z80/player/psg.z80 @@ -0,0 +1,667 @@ +;**************************************************************************** +; UpdatePSG +; Updates PSG output +;**************************************************************************** + +UpdatePSG: + ld hl, RAM_PSGData+48 ; PSG envelope data of *last* channel + ld b, 3 ; Go through all channels +.loop: + push bc + + ld a, (hl) ; Get channel volume + bit 7, a + jr nz, .noskip + ld b, $0F + inc l + jp .skip +.noskip: + and $7F + ld b, a + + inc l ; Add global volume + ld a, (hl) + add b + ld b, a + + PollPCM + push bc + + inc l ; Get current address of envelope + ld c, (hl) + inc l + ld e, (hl) + inc l + ld d, (hl) + ex de, hl + +.readenv: + PollPCM + call GetParam ; Get next byte + PollPCM + + ld a, b + cp $FE ; Set loop point? + jp z, .envsetloop + cp $FF ; Loop envelope? + jp z, .envloop + + ld iyl, b ; Keep byte safe somewhere... + PollPCM + + ex de, hl ; Store new address + ld (hl), d + dec l + ld (hl), e + dec l + ld (hl), c + dec l + + pop bc + PollPCM + + db $FD,$7D ; ld a, iyl ; Mix envelope with volume + add b + ld b, a + + cp $10 ; Check for overflow + jr c, .notmute + ld b, $0F +.notmute: + +.skip: + PollPCM + + ld a, b ; Set PSG channel volume + rlca + rlca + rlca + pop bc + or b + rrca + rrca + rrca + or $90 + ld ($7F11), a + + ld a, l ; Go for next channel + sub 16+1 + ld l, a + PollPCM + dec b + jp p, .loop + + jp DoTick_PSGSkip ; End of subroutine + +.envsetloop: + PollPCM + inc e ; Where we store the loop point + + ex de, hl ; Store loop point + ld (hl), c + inc l + ld (hl), e + inc l + ld (hl), d + ex de, hl + + dec e ; Back to where we were... + dec e + dec e + jp .readenv ; Go for next byte + +.envloop: + PollPCM + inc e ; Where we store the loop point + + ex de, hl ; Retrieve loop point + ld c, (hl) + inc l + ld e, (hl) + inc l + ld d, (hl) + ex de, hl + + dec e ; Back to where we were... + dec e + dec e + jp .readenv ; Go for next byte + +;**************************************************************************** +; NoteOnPSG* +; Does a "note on" for a PSG channel +;**************************************************************************** + +NoteOnPSGSFX: + call NoteOnPSG ; We're just a wrapper + jp ProcessSFXRun ; End of subroutine + +NoteOnPSGBGM: + ld b, a + PollPCM + ld a, b + + push hl + and $03 ; Check if channel is free + ld hl, RAM_Locked+8 + add l + ld l, a + ld a, (hl) + pop hl + or a + jp nz, ProcessBGMSkip1 ; Don't play if locked + + ld a, b + call NoteOnPSG ; We're just a wrapper + jp ProcessBGMRun ; End of subroutine + +NoteOnPSG: + and $03 + ld b, a + ;push af + ex af, af' + PollPCM + + push hl ; Set channel volume + ld h, RAM_PSGData>>8 + ld a, b + rrca + rrca + rrca + rrca + ld l, a + ld a, (hl) + or $80 + ld (hl), a + + PollPCM + push de + + inc l ; Now we'll reset the envelope address... + inc l + ld d, h + ld a, l + add 6 + ld e, a + + PollPCM + + ld a, (de) ; Reset envelope address + ld (hl), a + inc l + inc e + ld a, (de) + ld (hl), a + inc l + inc e + ld a, (de) + ld (hl), a + + pop de + pop hl + + PollPCM + call GetParam ; Get note + PollPCM + + ;pop af + ex af, af' + push hl + push de + ld h, PSGFreqTable>>8 ; Get address of frequency data + ld l, b + ld de, $7F11 + + rrca ; Set new frequency + rrca + rrca + ld b, (hl) + or b + ld (de), a + inc l + ld a, (hl) + ld (de), a + pop de + pop hl + + ret ; End of subroutine + +;**************************************************************************** +; NoteOnNoise* +; Does a "note on" for the noise PSG channel +;**************************************************************************** + +NoteOnNoiseSFX: + call NoteOnNoise ; We're just a wrapper + jp ProcessSFXRun ; End of subroutine + +NoteOnNoiseBGM: + ld a, (RAM_Locked+11) ; Check if channel is free + or a + jp nz, ProcessBGMSkip1 ; Don't play if locked + + call NoteOnNoise ; We're just a wrapper + jp ProcessBGMRun ; End of subroutine + +NoteOnNoise: + PollPCM + push hl + + ld hl, RAM_PSGData+48 ; Set channel volume + ld a, (hl) + or $80 + ld (hl), a + + PollPCM + push de + + inc l ; Now we'll reset the envelope address... + inc l + ld d, h + ld a, l + add 6 + ld e, a + + PollPCM + + ld a, (de) ; Reset envelope address + ld (hl), a + inc l + inc e + ld a, (de) + ld (hl), a + inc l + inc e + ld a, (de) + ld (hl), a + + pop de + pop hl + +SetNoteNoise: + PollPCM + call GetParam ; Get noise type + PollPCM + + ld a, $E0 ; Set new noise type + or b + ld ($7F11), a + + ret ; End of subroutine + +;**************************************************************************** +; NoteOffPSG* +; Does a "note off" for a PSG channel +;**************************************************************************** + +NoteOffPSGSFX: + call NoteOffPSG ; We're just a wrapper + jp ProcessSFXRun ; End of subroutine + +NoteOffPSGBGM: + ld b, a + PollPCM + ld a, b + + push hl + and $03 ; Check if channel is free + ld hl, RAM_Locked+8 + add l + ld l, a + ld a, (hl) + pop hl + or a + jp nz, ProcessBGMRun ; Don't stop if locked + + ld a, b + call NoteOffPSG ; We're just a wrapper + jp ProcessBGMRun ; End of subroutine + +NoteOffPSG: + and $03 + ld b, a + PollPCM + + push hl ; Mark channel as not playing + ld h, RAM_PSGData>>8 + ld a, b + rrca + rrca + rrca + rrca + ld l, a + ld a, (hl) + and $7F + ld (hl), a + pop hl + + ret ; End of subroutine + +;**************************************************************************** +; SetPSGVol* +; Sets the volume of a PSG channel +;**************************************************************************** + +SetPSGVolSFX: + and $03 ; Get channel ID + + ex af, af' + PollPCM + call GetParam ; Get volume + PollPCM + ex af, af' + + push hl + ld h, RAM_PSGData>>8 ; Set new volume + rrca + rrca + rrca + rrca + ld l, a + ld a, (hl) + and $80 + or b + ld (hl), a + pop hl + + jp ProcessSFXRun ; End of subroutine + +SetPSGVolBGM: + and $03 ; Get channel ID + + ex af, af' + PollPCM + call GetParam ; Get volume + PollPCM + ex af, af' + + push de + push hl + + push af + ld de, RAM_Locked+8 ; Check if channel is locked + add e ; Keep results for later + ld e, a + ld a, (de) + ld e, a + + PollPCM + pop af + + ld h, RAM_PSGData>>8 ; Store new volume + rrca + rrca + rrca + rrca + add 15 + ld l, a + ld (hl), b + + PollPCM + + ld a, e ; Is channel locked? + or a + jr nz, .nosetvol + + ld a, l ; Set new volume + sub 15 + ld l, a + ld a, (hl) + and $80 + or b + ld (hl), a + + PollPCM + +.nosetvol: + pop hl + pop de + + jp ProcessBGMRun ; End of subroutine + +;**************************************************************************** +; LoadPSG* +; Loads a PSG instrument +;**************************************************************************** + +LoadPSGSFX: + and $03 ; Get channel number + + ex af, af' + PollPCM + call GetParam ; Get instrument ID + PollPCM + ex af, af' + + push de + push hl + + ld d, RAM_PointerList>>8 ; Get position in pointer list + ld e, b + + ld h, RAM_PSGData>>8 ; Where to store address + rrca + rrca + rrca + rrca + add 8+2+(RAM_PSGData&$FF) + ld l, a + + PollPCM + + ld a, (de) ; Store PSG envelope start address + ld (hl), a + inc d + dec l + ld a, (de) + ld (hl), a + inc d + dec l + ld a, (de) + ld (hl), a + + PollPCM + + ld a, l ; Reset volume + sub 8 + ld l, a + ld (hl), $00 + + pop hl + pop de + jp ProcessSFXRun ; End of subroutine + +;---------------------------------------------------------------------------- + +LoadPSGBGM: + and $03 ; Get channel number + + ex af, af' + PollPCM + call GetParam ; Get instrument ID + PollPCM + ex af, af' + + push de + push hl + + ld d, RAM_PointerList>>8 ; Get position in pointer list + ld e, b + + ld hl, RAM_Locked+8 ; Get if channel is locked + push af + add l + ld l, a + pop af + ld b, (hl) + + ld h, RAM_PSGData>>8 ; Where to store BGM instrument data + rrca + rrca + rrca + rrca + add 15 + ld l, a + + PollPCM + + ld (hl), a ; Reset volume for BGM + dec l + + ld a, (de) ; Store PSG envelope address for BGM + ld (hl), a + inc d + dec l + ld a, (de) + ld (hl), a + inc d + dec l + ld a, (de) + ld (hl), a + + PollPCM + + ld a, b ; Don't set PSG envelope if locked + or a + jp z, .noloadlocked + pop hl + pop de + jp ProcessBGMRun +.noloadlocked: + PollPCM + + ld d, h ; Set PSG envelope + ld a, l + sub 12-8 + ld e, a + + ld a, (hl) + ld (de), a + inc l + inc e + ld a, (hl) + ld (de), a + inc l + inc e + ld a, (hl) + ld (de), a + + PollPCM + + ld a, l ; Reset volume + sub 8 + ld l, a + ld (hl), $00 + + pop hl + pop de + jp ProcessBGMRun ; End of subroutine + +;**************************************************************************** +; SetNotePSG* +; Sets the note of a PSG channel without "note on" +;**************************************************************************** + +SetNotePSGSFX: + call SetNotePSG ; We're just a wrapper + jp ProcessSFXRun ; End of subroutine + +SetNotePSGBGM: + ld b, a + PollPCM + ld a, b + + push hl + and $0F ; Check if channel is free + ;ld hl, RAM_Locked + ;add l + ld h, RAM_Locked>>8 + add RAM_Locked&$FF + ld l, a + ld a, (hl) + pop hl + or a + jp nz, ProcessBGMSkip2 ; Don't play if locked + + ld a, b + call SetNotePSG ; We're just a wrapper + jp ProcessBGMRun ; End of subroutine + +SetNotePSG: + and $03 ; Get channel number + + ex af, af' + PollPCM + call GetParam ; Get first byte + PollPCM + ex af, af' + + push de ; PSG port address + ld de, $7F11 + + rrca ; Set first frequency byte + rrca + rrca + or b + or $80 + ld (de), a + + PollPCM + call GetParam ; Get second byte + PollPCM + + ex de, hl + ld (hl), b ; Set second frequency byte + ex de, hl + pop de + + ret ; End of subroutine + +;**************************************************************************** +; SetNoteNoise* +; Sets the note of the noise PSG channel without "note on" +;**************************************************************************** + +SetNoteNoiseSFX: + call SetNoteNoise ; We're just a wrapper + jp ProcessSFXRun ; End of subroutine + +SetNoteNoiseBGM: + ld a, (RAM_Locked+11) ; Check if channel is free + or a + jp nz, ProcessBGMSkip1 ; Don't play if locked + + call SetNoteNoise ; We're just a wrapper + jp ProcessBGMRun ; End of subroutine + +;**************************************************************************** +; LockChannelPSG [events $E8-$EB] +; Locks a PSG channel +;**************************************************************************** + +LockChannelPSG: + ld b, a + PollPCM + push hl + + ld h, RAM_Locked>>8 ; Get address of channel to lock + ld a, b + and $0F + add RAM_Locked&$FF + ld l, a + + ld (hl), $01 ; Lock channel + + PollPCM + + ld a, b ; Stop channel + rrca + rrca + rrca + rrca + ld l, a + ld h, RAM_PSGData>>8 + ld (hl), $00 + + pop hl + jp ProcessSFXRun ; End of subroutine |
