From 17d12a31cc4f5cd613388fd2e872081603d0fd38 Mon Sep 17 00:00:00 2001 From: "U-ESAAD\\daniel lakey" Date: Fri, 12 Nov 2021 13:48:15 +0100 Subject: [PATCH 01/23] Updated to ptplayer 6.1 Add VBR move workaround --- plplayer.obj | Bin 0 -> 17350 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 plplayer.obj diff --git a/plplayer.obj b/plplayer.obj new file mode 100644 index 0000000000000000000000000000000000000000..862b793e58444c7411832422246267998f8980f7 GIT binary patch literal 17350 zcmeHv4Rlo1x%RuyoJ=MmB$FY;fH96C#1O}re}M#`$d*IKpK_WHHG)Kbg!(w1A1QVU4EDWZ#d+lpSR{0R~H3E?OA**|CIoWQNC z>+4$IT6f)JXXe>^&hx&{e&2WRv*(agVl0)Y>Zbc9z-^SB3M9nn~%f51KH?(6T4hy3o2 z?(V3^-4h;&MHNn*Q-WT9G&W$I+7piTyCb2FJN?lah8~vmYx{tEH2a06P>2@7Ha->8 z9qo_DdcyuPcdKvFoLDsO_qbb@E@_+-=m-t?VL%!**_Go++HHjbx;}i>^?`sp&>uC_ zabcYuT`LR=l7@GE1BXfeVt-%%NFv4>j5m6d z@IkUM&Z7u0PhMYqpr^~-wY(!7_J;<_VCcL&rS0P8P1ZRDR#(sxf6U$4gF-65vdWDb ziuzGkEBy&`GL6DRe>imK1+5F|F=g)L^_4oC4guA&w^I_Y=ThdB9(Yr0H z*Rqy@rjM!M(QTepq&v9h89~PvM#nyCd1>r}>rT;o!*o@~!qFK84IZ}3w~toMXkwcx z>)o^^O!;3PTtl;bZ1bE!H__8GTG{%>QTmhnE51>7O@q^Sie4R}C}j`QJrgP0mx)&v zH6CIcUHf!$?MuPSPOk5#QZoe{?s~fT`s>>5{4?vp8D6$w%^A8Y+pE*wrfhEtw~`GD zgXFdIcMYtPi6lHfgZ6zKKMxh{#2(>_OG46rIke(?D464=rv=AL&u|slH|vyrGc4Z> zi#Me5vz)?%Gm8pCY*XPnwjO==gBfC*>FJWTmbhaU>gm-#QlPTwyjUSDE+#jizZikIzhrv(Zd_t`c^?un6W%F9}pmcwq?Hg;mM z7D<2qg~s6$YTU2a&p*VGqNt~V^FQq)8NI3F&0PuJZZ znYBrq>DArH8~=QprVNsuZA1NRZwRq1G>bHPbuaUc7O#b-Hyb>p;|xZvc-QC|yxVBs zEZ&okca4_gU8fCrcQC^H8Y-JLh)KPA)*x1l*Jlmt#9Za|%sb0VIe4|>wGOY5h7eWb zwFa-9@SFz)Z{hVeUcW7Aga77+61EFt$i=G@ub1!|jqp18K3*H~+J@JjIfIl%PqxIb zKQeZ#Y;DtzDV6lOgJX=+-Pd>6uRlT=E%Bz0=bl+OI(=l$pcge5A!iAt_Y&KSiui4S z=*5_$xPEMG7+2FNURsFP6L`Ik99+3-Z1vdS*xh5rA-3)d2W=ZGJ#359!*)1ph?=~O z2le_xYv0;Df4@$`uLCVQw~h9q3IF zy-%2W)6i=dz3-cP)6tvCd)bw;w|!Z0dSEGZ=|SfP-EXnooPxd-(U-8?Y)7A6^d;;z zrv@`Or25m9Rjp`YaeAZ9f zrj=$+ZlIJogPRc{clx6D=AOZRif9o%MISbfPJ2CK+l*Qn^6olK2W0h31HTGpyiZt_*qF5khXM|@4b?0YGzC4M*gMo*_W zA6$%;0yALydJo&?ub*-)+m6cF8H|15Fg-ticL!y^pVy1Z6w;h?=~7 zUhX=?wisSChsnzslb2zWmk;6PvY^B8!Plag(}nTD?i)e7i!k0dnprS?2@7g5n&M+S zcZb-Ptaa=+K}WTRZJl*3UJY!^2FxS>%vm9}m2KgD;JqG6%xtb(tn{u3A)apsba>jO zdFUSYHedM>(bof8WXw-J>U>a*p)4_iqJ;jb3H46r>{I@v_Lg`vYCc$0UP{#gH}LF0 z9&p*_Fckzdf$}%6K2;siCtph`E%8dU=lJMJcQ2K5UazzD0L@;yc8RxiZJn3&_6(di zTjB_t-b5KSUUI=oExxq_=hXGmOmfs*OVz&7kw4tms{DJo|6qQ{b2IC+#jXV|D$+e+X9Mo~8-eXt@R&!<~YjY`SL*DW_vbUTj zP1|dmX}iU?(zaXA(erf=6;rW(KYypZ~2`{_gx$lxJ_62W7+C7Bnqz+Z$;Avi(o(9LyRU z@}gRIRMye0K3t)PyhY`7#`l?%%JTyDA`dHH?490#czMaQl5+FjX)MJ4BfWZPxSSB( zJh$%^?01$Ppt>lz@@jmPTbmbb-F@Hg`^ud>@~<@xHMTDzq5`U?TWA$MNgHSv4YN$< zVa;qgdw_k9ZDD)aIW1qCtu515YL9C#YeU*$TZXOF)?^FV?zg>A>kR0N8)H<{NG@1) zLQw`@C8*CgUFA-y^O0jo<4vf9TD@sr^8$=$_bY>$t-)pGb*&=m)GPiwfp`Am|1DCV zqJ}sFt8%--mW_7Hf z!z3cHUc~|5)JCR7&n@+BTDEi>c4NJiR}GCd4U~iU6hPNdu$t!#yMiYj$PUdF)qRb? zjRH56!Xt9%B)zVI?7q)7nvO8~1J2g6gRY zI+}e!{l>L7`na0vQp%}xZ}0`QD4|`L@ZsWqTjIQKP$hJ@rM#|PMDlFlLjSSRvjMGG zTdyq@Yuk2b9hC&>Z1oC#Uq-u*d62bd-kx46*f>Z{-RWFcUBpY=1F^Bc_&=;M+k<*b zyj;iJ&w0%I@*v&btwnE#+8pE^7OZFN!Wp(@UDy%WmYL4cgN~YcErKdZcmY%ceb zSMH^{d3>&5Zp^FH_bnW%bLPPQ+|C{Qn`U_p9;N3LlvLI)XuSn11CEXLHRX!?clpNn05)Co5!?YyrE) zP-0@ZsTTjuq-$s%-AWJ8L!!<5YFQ4|e&Fz@JANjAU*NaeDFb0$nAZ}=`&_<*O2Jw= ze(uX!3=}p~FmFjIEePiI^3`O)tX?V-WB(0S5nP9(#8FYX?R2i%XuF7K89g5CfVKzt z6)(5$TAmxx)|(dxz>;}xe_nXc!28aCeKF7S8)_{4npw=x#EdTcT`67LTZB1ut-F*m z@H?)QT)XbwwFYyeWbxXcyyn;3UR)JQ7GRa66nm;8eZoZNq}-|QE4*t&qW_=Y*o6+0 z)v*g5#`oajh7|4|nh*4#Uz`H&xpU{vS`YM~pMTHUHk+>NDaivZ8}54v1Dt*K?3puX zPA3nv&YtBKIG?jqQtbBBKUX68g#fC3#i zbjQw~fp@S!J9_j}c^p0Z87x4-nX{aqVt1rX06oi@Ee~fFH<0GArwIP((UFs!{^{@$ zc?^Hb4V)YqJuUgEpmVlsl0015umA(8ihl}p&OZE!JPsd$1sFKR`I>Hbq-RXba=Iqv znY|J2E2M~4p|I(T6JzK{8_Z~uXVhYk-PJ$CXG_n+cOpO}?BX)@STFXqQo zSeQI1J8NRPBL(_TkDU1I(<7f81pA{u^W!5}IQYqtPd_^`a$4{wz&+?wFV4S&ANdzg zg#iedp!mn(9`uj?{NaD{>~VG%c8W{DEMu3jiF6Kkr#_%}Xb0B%4Y*(R zJpChmgC3-Nu)n$0eh5viTT+L>>BJn^H~O+ zr6cq{y-nNc-{`0G1Nt_0rw`+La5vonJBw`j+OYO(?Ky3gcB58>n_4H>yX;l=B72yv zVqNTdR?kY=RF+Ps=@Z&ZZ_#gP6a9q#FFi+3(AVkj=`LK6BDMv#i?jpUX6>6=Ol#Gy z(wyuldxvde-(_E8an{ZjvDvJI?_AK_|5%xva#A;X(%V9blr+xGv^d|n@ zW+T0XyMy1N$LOo{W%?4usLxhyvup2aKheIX1+;nEG)-q8vz_c^_B6YX^|71Td{%{P zunYH~kI_f;d-^TCM*m9d=)3eZJxX7pducTdz)qQsw70c&+5=jrHdnh8Yw3q9o3o)aCV4`~nWq|Nj%^w0Di-1~Th*5Xdyowz+5vVHAuQ$hH>VN}p} zwZYG=pu?!3-L&oe3bNh(S60w_7p@@N9euUsUTbT-=>0W zxBb->^u`4%h+pFWe;#rV@)s0bXzOF{Oa9B3#fbTo1jerC{KID9p^-RUf$p=aX2qvfXg@W$Iv|`s7@z0U3W}U-*Z7gKzcDrHeg2HtL1}LxAC5 z7EV&VN?Nu{X~*w_T)xlGvltKpEdIslg?nS{!n>g3H$qIW<@ei1+JU&&YQ&!b_C zpnk;i_*?i1^&)c0`^EYBmzWG#1Q>iZe9q_YW#pxJPQRq!JOgq7|1Doco{wG93ohk_ zD1ZM;0pt0+?33=#qhIR&TmzrOzwA5%JOjxAobTVH{!+L5vO<#qz67cf$OP~u$E@EJ z`+|Z?IsLN2X$b==1PK9r;V|>n@Dsrk^ulRH$^c);p};Ia@c9pOkv_p!@|F9-!fDfr zrkf3j5r_!z3grGTz(?u}l=^8!MbnFg0lt`u`!J%2c?pUEhy+;ti_#IJm+q$*Uv3yM zMqmgq0$}jP!~tK(=fM;1i!U!R88Aj51sDTB`5NPwd>MR<9_}wM;eU)~+)9E0<;@@Rj@- zrB`^829hI?DdYlV08ILmd@djSE0qBxfg4aGP!Zq*Kmd^dWBiJ*hJOY4;*X>F4KaR4 z3%!0p#^}KEK*zK*UgvK&C*HpvWNnLx3z_grBGHs&ekX zx<(ohSrk=ZgwPNmG9UvWrVrw8;nykyB8w`CDu;3oBnBYnkCDHG|Jth!10o4Lgz^Ss zLI9tF$pEVS^Y9D(wO7wf7!Y;Hb0CU9+~(sc5PY5i9zb6I=JA6+D``N*P~PDb1_WOT zNCeOnKhB?RG9V^`F#<7*lmMdw4F4kk)ir`&XEBhdqND)61RDbo`gsD4@z0uFSAU)X zJ`cq_PDH@q8v-o+ITtn{?v%;_j1w@<|D0<+*MM~Z=kezz@2{hbcoM}t*5fm9T z5@;U(oNMNKu|xUa<9}!bumRS+7#l8pueIWH9?=&5xs2#n{BuZt&#*bziZ)xW+CL=v zHE$orpD5y!|9$mu%JGRCE!%iILbRQ?qw;)hNPgOIdE0^SZTAzs!9RD%e$rE%@J3~) z@a9x_1h`P)lfdN)p8&3tIDIMbLW$EJ241T0t-x&(r($bGy$bVN5Gy6N?*YDB;*>4G z4@!(bY$SSAV)3`s-&XzTrXNWx{+4}{#I_I6zY_z;;rR~U{zKt*-~$R{mrkc7zT!`a zn?vDefiF@Rnpv^J_+`UtBrbgyc%H)F1YV*ra>P0n#;-wkyTmiz0bZl<6Tsh47_nl{ zDU4sx>_-xp>;m4T@T0(QD2)8DcNNB62Zq1VuizVf;0Xma8!C)M(Qs zE=G>EYK6Z7+^BF9@M48=cT4M#c>3$W5rzLA_#TDl0Y9uT?g(nnN?e58Y3mfe7x)(n zdx3XIJPrTMsQq5yUjpMVwD~dZF5r_2W3ji{B`!P-JXztN0~aZLCvcU*Gl3f-u_$ryd1XwKtXP+@|_Ffp;rB75F`gr=V7KHK(S0 z75KR7Zy-t`iStH*Cn${Arl^|C>jl0{^%nwrB%TBfDYF#z0xyu*wFUS(g&zV|ahS9L zSmk%p^T4WBC-HOcQg#M`*DB2YKA|vTmGXkZ1;8o}lk$LHQT;C9UrFrR2mJ2}4*{#S z#kB)i#lf`|Sk8s~W(c_>uj~s{dBtM-`q2tmdh6Hn5tf&Z)pZmFHw(EcVS3yATum zPKmRJfz|qxjWx}#>O6ZZ@KMFR3HYqUdB|yMhQyiA1LrG@@ups>&R+>!r}_^7t6XQU z0d7_Oh;!=Ssq^On_o)5_z-qji&A@8Bnb!b6B+to1tWv)v@x*h$|D?FMZl=B@an?p) zRr4;afvK;{{%l;oQr}j%7I?41#lXV~BR&pQ=gvO?t2%c=zavkc3VgHdPeGlesk~1>oYPd^Q<{PA zmFL(I+qAE$^JBn|D~!38_8p0@ECT+4#I`>GuUB{v@K%L?39QC0#`9kCAKcPDHsRw* zoNju~FnyA#-)+JbCfsPkUo_$6CVYY+=ogR7 z89t)oQNp&ao(}OPe~JRaK=`RfSbmg;iCBRaJ#mRfSbmg;iCBRTb{4nMGDuRaID3Sw&ejStVH&Sp`}3 zSmlgULzwGJ<6%6+dtHSISDJ8@30IqNjS1J9@YNPCs_Tu``e=an0j&*-{7HbLVs6ZcU!EZlb;*Fx%@NI5yKFh#Kv1Kc0{^ zH@f2ZQ>IuOR=5D3((VqM8u18kNJpHW$+WWvsXya~rXESe&}D zr!(3SOPm%A^$ZLcoIpG@J@FNq9nlDv*BR>T zkNN|vj5tH&K*aCw=6+PHJEJ1l4vJV{Kzv|iCAW%)xD%=e#KYeGfdGt((Vy3m z7>W2=J_AmXgdQHTL?1w4u(e@IA` zNC-C?^#}2XY8ZHDsK0B4pI=3BzP}9xhklVJJhm>9Bm&OMBh)XxL3R*{I2jU%`AgAZ(FXO!AHLp`z8d|MRi z>ExA$&6DhsxUGSIzo@5ki72ip3Mm>LP+KL8trP#J6v@^=EZ!+vezB!PG!t!%+-Rs> oo9Ym){?1;1S4;$5@Dr!-n8`1RFK8&q Date: Fri, 12 Nov 2021 13:50:22 +0100 Subject: [PATCH 02/23] Updated readme --- ptplayer.asm | 950 +++++++++++++++++++++++++++++------------------- ptplayer.readme | 279 ++++++++++++-- 2 files changed, 830 insertions(+), 399 deletions(-) diff --git a/ptplayer.asm b/ptplayer.asm index 9f78d8c..382bfe4 100644 --- a/ptplayer.asm +++ b/ptplayer.asm @@ -1,9 +1,12 @@ ;************************************************** ;* ----- Protracker V2.3B Playroutine ----- * ;************************************************** - -; Version 5.0 -; Written by Frank Wille in 2013, 2016, 2017. +; +; Version 6.1 +; Written by Frank Wille in 2013, 2016, 2017, 2018, 2019, 2020, 2021. +; +; I, the copyright holder of this work, hereby release it into the +; public domain. This applies worldwide. ; ; The default version (single section, local base register) should ; work with most assemblers. Tested are: Devpac, vasm, PhxAss, @@ -13,87 +16,146 @@ ; this case all functions expect a4 to be initialised with the small ; data base register. Interrupt functions restore a4 from _LinkerDB. ; Small data may work with vasm and PhxAss only. - +; ; Exported functions and variables: ; (CUSTOM is the custom-chip register set base address $dff000.) ; -; _mt_install_cia(a6=CUSTOM, a0=AutoVecBase, d0=PALflag.b) +; _mt_install_cia(a6=CUSTOM, a0=VectorBase, d0=PALflag.b) ; Install a CIA-B interrupt for calling _mt_music or mt_sfxonly. ; The music module is replayed via _mt_music when _mt_Enable is non-zero. ; Otherwise the interrupt handler calls mt_sfxonly to play sound -; effects only. +; effects only. VectorBase is 0 for 68000, otherwise set it to the CPU's +; VBR register. A non-zero PALflag selects PAL-clock for the CIA timers +; (NTSC otherwise). ; ; _mt_remove_cia(a6=CUSTOM) -; Remove the CIA-B music interrupt and restore the old vector. +; Remove the CIA-B music interrupt, restore the previous handler and +; reset the CIA timer registers to their original values. ; ; _mt_init(a6=CUSTOM, a0=TrackerModule, a1=Samples|NULL, d0=InitialSongPos.b) ; Initialize a new module. -; Reset speed to 6, tempo to 125 and start at the given position. +; Reset speed to 6, tempo to 125 and start at the given song position. ; Master volume is at 64 (maximum). ; When a1 is NULL the samples are assumed to be stored after the patterns. ; ; _mt_end(a6=CUSTOM) -; Stop playing current module. +; Stop playing current module and sound effects. ; ; _mt_soundfx(a6=CUSTOM, a0=SamplePointer, -; d0=SampleLength.w, d1=SamplePeriod.w, d2=SampleVolume.w) +; d0=SampleLength.w, d1=SamplePeriod.w, d2=SampleVolume.w) ; Request playing of an external sound effect on the most unused channel. -; This function is for compatibility with the old API only! -; You should call _mt_playfx instead. +; This function is for compatibility with the old API only. +; You should call _mt_playfx instead. MINIMAL=0 only. ; -; _mt_playfx(a6=CUSTOM, a0=SfxStructurePointer) +; channelStatus(d0) = _mt_playfx(a6=CUSTOM, a0=SfxStructurePointer) ; Request playing of a prioritized external sound effect, either on a ; fixed channel or on the most unused one. ; Structure layout of SfxStructure: -; APTR sfx_ptr (pointer to sample start in Chip RAM, even address) -; WORD sfx_len (sample length in words) -; WORD sfx_per (hardware replay period for sample) -; WORD sfx_vol (volume 0..64, is unaffected by the song's master volume) -; BYTE sfx_cha (0..3 selected replay channel, -1 selects best channel) -; BYTE sfx_pri (unsigned priority, must be non-zero) +; void *sfx_ptr (pointer to sample start in Chip RAM, even address) +; WORD sfx_len (sample length in words) +; WORD sfx_per (hardware replay period for sample) +; WORD sfx_vol (volume 0..64, is unaffected by the song's master volume) +; BYTE sfx_cha (0..3 selected replay channel, -1 selects best channel) +; BYTE sfx_pri (priority, must be in the range 1..127) ; When multiple samples are assigned to the same channel the lower ; priority sample will be replaced. When priorities are the same, then ; the older sample is replaced. ; The chosen channel is blocked for music until the effect has ; completely been replayed. +; Returns a pointer to a channel-status structure when the sample +; is scheduled for playing, and NULL when the request was ignored. +; MINIMAL=0 only. +; +; _mt_loopfx(a6=CUSTOM, a0=SfxStructurePointer) +; Request playing of a looped sound effect on a fixed channel, which +; will be blocked for music until the effect is stopped (_mt_stopfx). +; It uses the same sfx-structure as _mt_playfx, but the priority is +; ignored. A looped sound effect has always highest priority and will +; replace a previous effect on the same channel. No automatic channel +; selection possible! +; Also make sure the sample starts with a zero-word, which is used +; for idling when the effect is stopped. This word is included in the +; total length calculation, but excluded when actually playing the loop. +; MINIMAL=0 only. +; +; _mt_stopfx(a6=CUSTOM, d0=Channel.b) +; Immediately stop a currently playing sound effect on a channel (0..3) +; and make it available for music, or other effects, again. This is the +; only way to stop a looped sound effect (_mt_loopfx), besides stopping +; replay completely (_mt_end). MINIMAL=0 only. ; ; _mt_musicmask(a6=CUSTOM, d0=ChannelMask.b) -; Set bits in the mask define which specific channels are reserved +; Bits set in the mask define which specific channels are reserved ; for music only. Set bit 0 for channel 0, ..., bit 3 for channel 3. ; When calling _mt_soundfx or _mt_playfx with automatic channel selection ; (sfx_cha=-1) then these masked channels will never be picked. -; The mask defaults to 0. +; The mask defaults to 0. MINIMAL=0 only. ; ; _mt_mastervol(a6=CUSTOM, d0=MasterVolume.w) ; Set a master volume from 0 to 64 for all music channels. ; Note that the master volume does not affect the volume of external -; sound effects (which is desired). +; sound effects (which is desired). MINIMAL=0 only. +; +; _mt_samplevol(d0=SampleNumber.w, d1=Volume.b) +; Redefine a sample's volume. May also be done while the song is playing. +; Warning: Does not check arguments for valid range! You must have done +; _mt_init before calling this function! +; The new volume is persistent. Even when the song is restarted. +; MINIMAL=0 only. ; ; _mt_music(a6=CUSTOM) -; The replayer routine. Is called automatically after mt_install_cia(). +; The replayer routine. Is called automatically after _mt_install_cia. ; -; Variables: +; Byte Variables: ; ; _mt_Enable ; Set this byte to non-zero to play music, zero to pause playing. ; Note that you can still play sound effects, while music is stopped. +; It is set to 0 by _mt_install_cia. ; ; _mt_E8Trigger ; This byte reflects the value of the last E8 command. -; It is reset to 0 after mt_init(). +; It is reset to 0 after _mt_init. ; ; _mt_MusicChannels ; This byte defines the number of channels which should be dedicated ; for playing music. So sound effects will never use more ; than 4 - _mt_MusicChannels channels at once. Defaults to 0. +; MINIMAL=0 only. ; +; Optionally you can build a minimal version, which includes just +; the player. No sound effects insert, no master volume, no sample +; volume, etc. Define the symbol MINIMAL=1 for it. + ifnd MINIMAL +MINIMAL equ 0 + endc + +; You may disable sawtooth and rectangle vibratos/tremolos here, which +; will be replaced by sine-waves. They are rarely used and disabling +; them will free a lot of memory for the tables. + ifnd ENABLE_SAWRECT +ENABLE_SAWRECT equ 0 + endc + +; Set this if you can guarantee that the word at $0 is cleared and if +; you want to use if for idle-looping of samples. + ifnd NULL_IS_CLEARED +NULL_IS_CLEARED equ 0 + endc + +; Delay in CIA-ticks, which guarantees that at least one Audio-DMA +; took place, even with the lowest periods. +; 496 should be the correct value. But there are some A1200 which +; need at least 550. +DMADELAY equ 576 ; was 496 + include "blitz.i" include "custom.i" include "cia.i" - audiolib equ 116 - banklib equ 76 +audiolib equ 116 +banklib equ 76 libheader 195,0,0,blitz_finit,runerrs @@ -196,6 +258,9 @@ sfx_sizeof rs.b 0 n_note rs.w 1 n_cmd rs.b 1 n_cmdlo rs.b 1 +n_index rs.b 1 +n_sfxpri rs.b 1 +n_reserved1 rs.b 2 n_start rs.l 1 n_loopstart rs.l 1 n_length rs.w 1 @@ -212,9 +277,8 @@ n_funk rs.w 1 n_wavestart rs.l 1 n_reallength rs.w 1 n_intbit rs.w 1 -n_audreg rs.w 1 -n_sfxlen rs.w 1 n_sfxptr rs.l 1 +n_sfxlen rs.w 1 n_sfxper rs.w 1 n_sfxvol rs.w 1 n_looped rs.b 1 @@ -232,9 +296,13 @@ n_sampleoffset rs.b 1 n_loopcount rs.b 1 n_funkoffset rs.b 1 n_retrigcount rs.b 1 -n_sfxpri rs.b 1 + ifeq MINIMAL n_freecnt rs.b 1 n_musiconly rs.b 1 +n_reserved2 rs.b 1 + else +n_reserved2 rs.b 3 + endc n_sizeof rs.b 0 @@ -244,20 +312,31 @@ n_sizeof rs.b 0 code endc - +;----- Get VBR 68010+ --- + mc68010 +getvbr: + movec vbr,a0 + rte + mc68000 +;------------------------ ;--------------------------------------------------------------------------- xdef _mt_install_cia _mt_install_cia: ; Install a CIA-B interrupt for calling _mt_music. ; a6 = CUSTOM -; a0 = AutoVecBase +; a0 = VectorBase ; d0 = PALflag.b (0 is NTSC) ;----- Save registers for Blitz 2 --- - movem.l a3-a6,-(sp) - lea CUSTOM,a6 - move.l #0,a0 + movem.l a3-a6,-(sp) + sub.l a0,a0 + move.l 4.w,a6 + btst #0,297(a6) ; check for 68010 + beq .novbr + lea getvbr(pc),a5 + jsr -30(a6) ; Supervisor() +.novbr: lea CUSTOM,a6 ;------------------------------------- ifnd SDATA @@ -267,14 +346,13 @@ _mt_install_cia: clr.b mt_Enable(a4) - lea mt_Lev6Int(pc),a1 - lea $78(a0),a0 ; Level 6 interrupt vector - move.l a0,(a1) - + ; remember level 6 vector and interrupt enable + lea $78(a0),a0 + move.l a0,mt_Lev6Int(a4) move.w #$2000,d1 and.w INTENAR(a6),d1 or.w #$8000,d1 - move.w d1,mt_Lev6Ena(a4) ; remember level 6 interrupt enable + move.w d1,mt_Lev6Ena(a4) ; disable level 6 EXTER interrupts, set player interrupt vector move.w #$2000,INTENA(a6) @@ -282,11 +360,16 @@ _mt_install_cia: lea mt_TimerAInt(pc),a1 move.l a1,(a0) + ; reset TimerB toggle + lea TB_toggle(pc),a0 + clr.b (a0) + ; disable CIA-B interrupts, stop and save all timers lea CIAB,a0 + moveq #0,d1 move.b #$7f,CIAICR(a0) - move.b #$10,CIACRA(a0) - move.b #$10,CIACRB(a0) + move.b d1,CIACRA(a0) + move.b d1,CIACRB(a0) lea mt_oldtimers(a4),a1 move.b CIATALO(a0),(a1)+ move.b CIATAHI(a0),(a1)+ @@ -308,11 +391,13 @@ _mt_install_cia: move.b d0,CIATAHI(a0) move.b #$11,CIACRA(a0) ; load timer, start continuous - ; load TimerB with 496 ticks for setting DMA and repeat - move.b #496&255,CIATBLO(a0) - move.b #496>>8,CIATBHI(a0) + ; load TimerB with DMADELAY ticks for setting DMA and repeat + move.b #DMADELAY&255,CIATBLO(a0) + move.b #DMADELAY>>8,CIATBHI(a0) - ; TimerA and TimerB interrupt enable + ; Ack. pending interrupts, TimerA and TimerB interrupt enable + tst.b CIAICR(a0) + move.w #$2000,INTREQ(a6) move.b #$83,CIAICR(a0) ; enable level 6 interrupts @@ -320,9 +405,6 @@ _mt_install_cia: bra mt_reset -mt_Lev6Int: - dc.l 0 - ;--------------------------------------------------------------------------- xdef _mt_remove_cia @@ -330,9 +412,6 @@ _mt_remove_cia: ; Remove CIA-B music interrupt and restore the old vector. ; a6 = CUSTOM - movem.l a3-a6,-(sp) ; Save registers for Blitz 2 - lea CUSTOM,a6 - ifnd SDATA move.l a4,-(sp) lea mt_data(pc),a4 @@ -340,8 +419,11 @@ _mt_remove_cia: ; disable level 6 and CIA-B interrupts lea CIAB,a0 + move.w #$2000,d0 move.b #$7f,CIAICR(a0) - move.w #$2000,INTENA(a6) + move.w d0,INTENA(a6) + tst.b CIAICR(a0) + move.w d0,INTREQ(a6) ; restore old timer values lea mt_oldtimers(a4),a1 @@ -353,7 +435,7 @@ _mt_remove_cia: move.b #$10,CIACRB(a0) ; restore original level 6 interrupt vector - move.l mt_Lev6Int(pc),a1 + move.l mt_Lev6Int(a4),a1 move.l mt_oldLev6(a4),(a1) ; reenable CIA-B ALRM interrupt, which was set by AmigaOS @@ -365,8 +447,6 @@ _mt_remove_cia: ifnd SDATA move.l (sp)+,a4 endc - - movem.l (sp)+,a3-a6 ; Restore registers for Blitz rts @@ -375,6 +455,12 @@ mt_TimerAInt: ; TimerA interrupt calls _mt_music at a selectable tempo (Fxx command), ; which defaults to 50 times per second. + ; check for TB interrupt and clear CIAB interrupt flags + btst #1,CIAB+CIAICR + bne mt_TimerBInt + + ; Now it should be a TA interrupt. + ; Other level 6 interrupt sources have to be handled elsewhere. movem.l d0-d7/a0-a6,-(sp) lea CUSTOM,a6 ifd SDATA @@ -386,106 +472,85 @@ mt_TimerAInt: ; clear EXTER interrupt flag move.w #$2000,INTREQ(a6) - ; check and clear CIAB interrupt flags - btst #0,CIAB+CIAICR - beq .1 - - ; it was a TA interrupt, do music when enabled + ; do music when enabled tst.b mt_Enable(a4) + ifeq MINIMAL + beq .2 + else beq .1 + endc bsr _mt_music ; music with sfx inserted - movem.l (sp)+,d0-d7/a0-a6 +.1: movem.l (sp)+,d0-d7/a0-a6 nop rte -.1: bsr mt_sfxonly ; no music, only sfx + ifeq MINIMAL +.2: bsr mt_sfxonly ; no music, only sfx movem.l (sp)+,d0-d7/a0-a6 nop rte + endc ;--------------------------------------------------------------------------- -mt_TimerBdmaon: -; One-shot TimerB interrupt to enable audio DMA after 496 ticks. +mt_TimerBInt: +; Handle one-shot TimerB interrupt. +; TB_toggle-technique suggested by Ross/EAB. move.l a0,-(sp) - - ; check and clear CIAB interrupt flags - btst #1,CIAB+CIAICR - beq .1 - - ; it was a TB interrupt, restart timer to set repeat, enable DMA - move.b #$19,CIAB+CIACRB - move.l mt_Lev6Int(pc),a0 - move.l #mt_TimerBsetrep,(a0) - - ; clear EXTER interrupt flag + lea TB_toggle(pc),a0 + not.b (a0) lea CUSTOM+INTREQ,a0 - move.w #$2000,(a0) + beq mt_TimerBsetrep - ; enable audio DMA + ; restart timer for repeat, enable audio DMA after DMADELAY ticks + move.w #$2000,(a0) ; clear EXTER interrupt flag + move.b #$19,CIAB+CIACRB move.w mt_dmaon(pc),DMACON-INTREQ(a0) move.l (sp)+,a0 nop rte - ; clear EXTER interrupt flag -.1: lea CUSTOM+INTREQ,a0 - move.w #$2000,(a0) - move.w #$2000,(a0) - - move.l (sp)+,a0 - nop - rte - mt_dmaon: dc.w $8000 +TB_toggle: + dc.b 0 + even ;--------------------------------------------------------------------------- mt_TimerBsetrep: -; One-shot TimerB interrupt to set repeat samples after another 496 ticks. - - movem.l d0/a4/a6,-(sp) +; Oneshot TimerB interrupt to set repeat samples after another DMADELAY ticks. +; a0 = INTREQ - lea CUSTOM,a6 - moveq #0,d0 - - ; check and clear CIAB interrupt flags - btst #1,CIAB+CIAICR - beq .1 + ; clear EXTER and possible audio interrupt flags + move.l a4,-(sp) + move.l d0,a4 + moveq #$2000>>7,d0 ; EXTER-flag + or.b mt_dmaon+1(pc),d0 + lsl.w #7,d0 + move.w d0,(a0) + move.l a4,d0 - ; it was a TB interrupt, set repeat sample pointers and lengths + ; set repeat sample pointers and lengths ifd SDATA lea _LinkerDB,a4 else lea mt_data(pc),a4 endc - move.l mt_chan1+n_loopstart(a4),AUD0LC(a6) - move.w mt_chan1+n_replen(a4),AUD0LEN(a6) - move.l mt_chan2+n_loopstart(a4),AUD1LC(a6) - move.w mt_chan2+n_replen(a4),AUD1LEN(a6) - move.l mt_chan3+n_loopstart(a4),AUD2LC(a6) - move.w mt_chan3+n_replen(a4),AUD2LEN(a6) - move.l mt_chan4+n_loopstart(a4),AUD3LC(a6) - move.w mt_chan4+n_replen(a4),AUD3LEN(a6) - - ; restore TimerA music interrupt vector - move.l mt_Lev6Int(pc),a4 - move.l #mt_TimerAInt,(a4) - - ; audio interrupt flags to clear, for new samples - move.b mt_dmaon+1(pc),d0 - lsl.w #7,d0 + move.l mt_chan1+n_loopstart(a4),AUD0LC-INTREQ(a0) + move.w mt_chan1+n_replen(a4),AUD0LEN-INTREQ(a0) + move.l mt_chan2+n_loopstart(a4),AUD1LC-INTREQ(a0) + move.w mt_chan2+n_replen(a4),AUD1LEN-INTREQ(a0) + move.l mt_chan3+n_loopstart(a4),AUD2LC-INTREQ(a0) + move.w mt_chan3+n_replen(a4),AUD2LEN-INTREQ(a0) + move.l mt_chan4+n_loopstart(a4),AUD3LC-INTREQ(a0) + move.w mt_chan4+n_replen(a4),AUD3LEN-INTREQ(a0) - ; clear EXTER and possible audio interrupt flags -.1: or.w #$2000,d0 - move.w d0,INTREQ(a6) - move.w d0,INTREQ(a6) - - movem.l (sp)+,d0/a4/a6 + move.l (sp)+,a4 + move.l (sp)+,a0 nop rte @@ -494,28 +559,13 @@ mt_TimerBsetrep: xdef _mt_init _mt_init: ; Initialize new module. -; Reset speed to 6, tempo to 125 and start at first position. +; Reset speed to 6, tempo to 125 and start at given song position. ; Master volume is at 64 (maximum). ; a6 = CUSTOM ; a0 = module pointer ; a1 = sample pointer (NULL means samples are stored within the module) ; d0 = initial song position -; --- Init for Blitz ---- -_mt_init_bank: - move.l #0,a1 ; Set sample pointer to NULL - move.b d1,d0 ; Set starting position - move.l (a0),a0 ; Set module address - bra _mt_init_blitz_done -_mt_init_stub: - move.l d0,a0 ; Set module address - move.l d1,a1 ; Set sample pointer - move.b d2,d0 ; Set starting position -_mt_init_blitz_done: - movem.l a3-a6,-(sp) - lea CUSTOM,a6 -; ----------------------- - ifnd SDATA move.l a4,-(sp) lea mt_data(pc),a4 @@ -547,21 +597,23 @@ _mt_init_blitz_done: ; now we can calculate the base address of the sample data moveq #10,d0 asl.l d0,d2 - add.l #1084,d2 lea (a0,d2.l),a1 - move.l a1,d2 ; use as pointer for empty samples + add.w #1084,a1 - ; save start address of each sample + ; save start address of each sample and do some fixes for broken mods .4: lea mt_SampleStarts(a4),a2 + moveq #1,d2 moveq #31-1,d0 .5: move.l a1,(a2)+ moveq #0,d1 move.w 42(a0),d1 - beq .6 - clr.w (a1) ; make sure sample starts with two 0-bytes + cmp.w d2,d1 ; length 0 and 1 define unused samples + bls .6 add.l d1,d1 add.l d1,a1 -.6: lea 30(a0),a0 + bra .7 +.6: clr.w 42(a0) ; length 1 means zero -> no sample +.7: lea 30(a0),a0 dbf d0,.5 movem.l (sp)+,d2/a2 @@ -584,11 +636,19 @@ mt_reset: ; disable the filter or.b #2,CIAA+CIAPRA + ifeq MINIMAL ; set master volume to 64 lea MasterVolTab64(pc),a0 move.l a0,mt_MasterVolTab(a4) + endc + + ; set channel index + clr.b mt_chan1+n_index(a4) + move.b #1,mt_chan2+n_index(a4) + move.b #2,mt_chan3+n_index(a4) + move.b #3,mt_chan4+n_index(a4) - ; initialise channel DMA, interrupt bits and audio register base + ; initialise channel DMA and interrupt bits move.w #$0001,mt_chan1+n_dmabit(a4) move.w #$0002,mt_chan2+n_dmabit(a4) move.w #$0004,mt_chan3+n_dmabit(a4) @@ -597,10 +657,6 @@ mt_reset: move.w #$0100,mt_chan2+n_intbit(a4) move.w #$0200,mt_chan3+n_intbit(a4) move.w #$0400,mt_chan4+n_intbit(a4) - move.w #AUD0LC,mt_chan1+n_audreg(a4) - move.w #AUD1LC,mt_chan2+n_audreg(a4) - move.w #AUD2LC,mt_chan3+n_audreg(a4) - move.w #AUD3LC,mt_chan4+n_audreg(a4) ; make sure n_period doesn't start as 0 move.w #320,d0 @@ -615,8 +671,11 @@ mt_reset: clr.w mt_chan3+n_sfxlen(a4) clr.w mt_chan4+n_sfxlen(a4) - clr.b mt_SilCntValid(a4) clr.b mt_E8Trigger(a4) + ifeq MINIMAL + clr.b mt_SongEnd(a4) + clr.b mt_SilCntValid(a4) + endc ifnd SDATA move.l (sp)+,a4 @@ -631,9 +690,17 @@ _mt_end: ifd SDATA clr.b mt_Enable(a4) + clr.w mt_chan1+n_volume(a4) + clr.w mt_chan2+n_volume(a4) + clr.w mt_chan3+n_volume(a4) + clr.w mt_chan4+n_volume(a4) else - lea mt_data+mt_Enable(pc),a0 - clr.b (a0) + lea mt_data(pc),a0 + clr.b mt_Enable(a0) + clr.w mt_chan1+n_volume(a0) + clr.w mt_chan2+n_volume(a0) + clr.w mt_chan3+n_volume(a0) + clr.w mt_chan4+n_volume(a0) endc moveq #0,d0 @@ -642,55 +709,13 @@ _mt_end: move.w d0,AUD2VOL(a6) move.w d0,AUD3VOL(a6) move.w #$000f,DMACON(a6) - - movem.l (sp)+,a3-a6 ; Restore registers before returning to Blitz rts -;------------ Blitz2 stubs ----------- -_mt_enable_stub: - ifd SDATA - trap #0 - move.b d0,mt_Enable(a4) - else - lea mt_data+mt_Enable(pc),a0 - move.b d0,(a0) - endc - rts - -_mt_end_stub: - movem.l a3-a6,-(sp) - lea CUSTOM,a6 - bra _mt_end - -_mt_MusicChannels_stub: - ifd SDATA - move.b d0,mt_MusicChannels(a4) - else - lea mt_data+mt_MusicChannels(pc),a0 - move.b d0,(a0) - endc - rts - -_mt_E8Trigger_stub: - ifd SDATA - move.b mt_MusicChannels(a4),d0 - else - lea mt_data+mt_MusicChannels(pc),a0 - move.b (a0),d0 - endc - rts -;------------------------------------- -_mt_soundfx_soundobject: - movem.l a3-a6,-(sp) - lea CUSTOM,a6 - move.w d1,d2 ; volume - move.w 4(a0),d1 ; period - move.w 6(a0),d0 ; length - move.l (a0),a0 ; sample data - bra _mt_soundfx + ifeq MINIMAL +;--------------------------------------------------------------------------- xdef _mt_soundfx -_mt_soundfx_stub: +_mt_soundfx: ; Request playing of an external sound effect on the most unused channel. ; This function is for compatibility with the old API only! ; You should call _mt_playfx instead! @@ -700,25 +725,13 @@ _mt_soundfx_stub: ; d1.w = sample period ; d2.w = sample volume -;--- Blitz 2 call stub ----- - movem.l a3-a6,-(sp) - lea CUSTOM,a6 - move.l d0,a0 - move.w d1,d0 - move.w d2,d1 - move.w d3,d2 -;--------------------------- - -_mt_soundfx: lea -sfx_sizeof(sp),sp - move.l a0,a1 + move.l a0,sfx_ptr(sp) + movem.w d0-d2,sfx_len(sp) + move.w #$ff01,sfx_cha(sp) ; any channel, priority=1 move.l sp,a0 - move.l a1,sfx_ptr(a0) - movem.w d0-d2,sfx_len(a0) - move.w #$ff01,sfx_cha(a0) ; any channel, priority=1 bsr _mt_playfx lea sfx_sizeof(sp),sp - movem.l (sp)+,a3-a6 ; Restore registers for Blitz rts @@ -733,6 +746,7 @@ _mt_playfx: ; a6 = CUSTOM ; a0 = sfx-structure pointer with the following layout: ; 0: ptr, 4: len.w, 6: period.w, 8: vol.w, 10: channel.b, 11: priority.b +; -> d0 = pointer to channel status or NULL when sample was ignored ifd SDATA movem.l d2-d7/a0-a3/a5,-(sp) @@ -741,6 +755,7 @@ _mt_playfx: lea mt_data(pc),a4 endc + move.w #$4000,INTENA(a6) moveq #0,d0 move.b sfx_cha(a0),d0 bpl channelsfx ; use fixed channel for effect @@ -754,18 +769,16 @@ _mt_playfx: moveq #8,d2 move.l #$fffff000,d3 ; mask to ignore effects - ; remember which channels are not available for sound effects - move.b mt_chan1+n_musiconly(a4),d4 - move.b mt_chan2+n_musiconly(a4),d5 - move.b mt_chan3+n_musiconly(a4),d6 - move.b mt_chan4+n_musiconly(a4),d7 - ; reset freecnts for all channels moveq #0,d0 move.b d0,mt_chan1+n_freecnt(a4) move.b d0,mt_chan2+n_freecnt(a4) move.b d0,mt_chan3+n_freecnt(a4) move.b d0,mt_chan4+n_freecnt(a4) + moveq #-1,d4 + moveq #-1,d5 + moveq #-1,d6 + moveq #-1,d7 ; get pattern pointer move.l mt_mod(a4),a3 ; a3 mod pointer @@ -780,39 +793,35 @@ _mt_playfx: lea 1024(a1),a2 ; a2 end of pattern add.w d1,a1 ; a1 current pattern pos -.2: moveq #4,d0 +.2: moveq #0,d0 move.l (a1)+,d1 - tst.b d4 - bne .3 - addq.b #1,mt_chan1+n_freecnt(a4) and.l d3,d1 - sne d4 -.3: add.b d4,d0 + sne d1 + and.b d1,d4 + sub.b d4,mt_chan1+n_freecnt(a4) + add.b d4,d0 move.l (a1)+,d1 - tst.b d5 - bne .4 - addq.b #1,mt_chan2+n_freecnt(a4) and.l d3,d1 - sne d5 -.4: add.b d5,d0 + sne d1 + and.b d1,d5 + sub.b d5,mt_chan2+n_freecnt(a4) + add.b d5,d0 move.l (a1)+,d1 - tst.b d6 - bne .5 - addq.b #1,mt_chan3+n_freecnt(a4) and.l d3,d1 - sne d6 -.5: add.b d6,d0 + sne d1 + and.b d1,d6 + sub.b d6,mt_chan3+n_freecnt(a4) + add.b d6,d0 move.l (a1)+,d1 - tst.b d7 - bne .6 - addq.b #1,mt_chan4+n_freecnt(a4) and.l d3,d1 - sne d7 -.6: add.b d7,d0 + sne d1 + and.b d1,d7 + sub.b d7,mt_chan4+n_freecnt(a4) + add.b d7,d0 ; break the loop when no channel has any more free pattern steps beq .7 @@ -836,8 +845,6 @@ _mt_playfx: .7: st mt_SilCntValid(a4) freecnt_valid: - move.w #$4000,INTENA(a6) - sub.l a2,a2 move.b sfx_pri(a0),d2 @@ -847,43 +854,51 @@ freecnt_valid: moveq #3,d0 sub.b mt_MusicChannels(a4),d0 move.b mt_chan1+n_sfxpri(a4),d4 + or.b mt_chan1+n_musiconly(a4),d4 sne d1 add.b d1,d0 move.b mt_chan2+n_sfxpri(a4),d5 + or.b mt_chan2+n_musiconly(a4),d5 sne d1 add.b d1,d0 move.b mt_chan3+n_sfxpri(a4),d6 + or.b mt_chan3+n_musiconly(a4),d6 sne d1 add.b d1,d0 move.b mt_chan4+n_sfxpri(a4),d7 + or.b mt_chan4+n_musiconly(a4),d7 sne d1 add.b d1,d0 - bmi .overwrite + bmi .overwrite ; all channels reserved/playing effects ; We will prefer a music channel which had an audio interrupt, ; because that means the last instrument sample has been played ; completely, and the channel is now in an idle loop. ; Also exclude channels which have set a repeat loop. ; Try not to break them! - moveq #0,d1 + moveq #0,d3 tst.b mt_chan1+n_looped(a4) bne .1 - or.w #$0080,d1 + or.w #$0080,d3 .1: tst.b mt_chan2+n_looped(a4) bne .2 - or.w #$0100,d1 + or.w #$0100,d3 .2: tst.b mt_chan3+n_looped(a4) bne .3 - or.w #$0200,d1 + or.w #$0200,d3 .3: tst.b mt_chan4+n_looped(a4) bne .4 - or.w #$0400,d1 + or.w #$0400,d3 +.4: move.w INTREQR(a6),d1 + and.w d3,d1 + bne .6 -.4: and.w INTREQR(a6),d1 + ; All channels are busy. Then break the non-looped ones first... + move.w d3,d1 bne .6 - ; All channels are busy, then it doesn't matter which one we break... -.5: move.w #$0780,d1 + ; ..except there are none. Then it doesn't matter. :| + move.w #$0780,d1 ; first look for the best unused channel .6: moveq #0,d3 @@ -973,9 +988,6 @@ found_sfx_ch: lea -n_freecnt(a2),a2 bra set_sfx -channel_offsets: - dc.w 0*n_sizeof,1*n_sizeof,2*n_sizeof,3*n_sizeof - channelsfx: ; a0 = sfx structure ; d0 = fixed channel for new sound effect @@ -984,10 +996,11 @@ channelsfx: add.w channel_offsets(pc,d0.w),a2 ; priority high enough to replace a present effect on this channel? - move.w #$4000,INTENA(a6) move.b sfx_pri(a0),d2 cmp.b n_sfxpri(a2),d2 - blo exit_playfx + bhs set_sfx + sub.l a2,a2 + bra exit_playfx set_sfx: ; activate the sound effect on this channel @@ -1002,6 +1015,7 @@ set_sfx: exit_playfx: move.w #$c000,INTENA(a6) + move.l a2,d0 ; ptr to selected channel or NULL ifd SDATA movem.l (sp)+,d2-d7/a0-a3/a5 @@ -1010,6 +1024,86 @@ exit_playfx: endc rts +channel_offsets: + dc.w 0*n_sizeof,1*n_sizeof,2*n_sizeof,3*n_sizeof + + +;--------------------------------------------------------------------------- + xdef _mt_loopfx +_mt_loopfx: +; Request playing of a looped sound effect on a fixed channel, which +; will be blocked for music until the effect is stopped (_mt_stopfx). +; It uses the same sfx-structure as _mt_playfx, but the priority is +; ignored. A looped sound effect has always highest priority and will +; replace a previous effect on the same channel. No automatic channel +; selection possible! +; Also make sure the sample starts with a zero-word, which is used +; for idling when the effect is stopped. This word is included in the +; total length calculation, but excluded when actually playing the loop. +; a6 = CUSTOM +; a0 = sfx-structure pointer with the following layout: +; 0: ptr, 4: len.w, 6: period.w, 8: vol.w, 10: channel.b + + ifnd SDATA + lea mt_data+mt_chan1(pc),a1 + else + lea mt_chan1(a4),a1 + endc + + moveq #3,d0 + and.b sfx_cha(a0),d0 + add.w d0,d0 + add.w channel_offsets(pc,d0.w),a1 + + move.w #$4000,INTENA(a6) + move.l (a0)+,n_sfxptr(a1) ; sfx_ptr + move.w (a0)+,n_sfxlen(a1) ; sfx_len + move.w (a0)+,n_sfxper(a1) ; sfx_per + move.w (a0),n_sfxvol(a1) ; sfx_vol + st n_sfxpri(a1) ; sfx_pri -1 enables looped mode + move.w #$c000,INTENA(a6) + + rts + + +;--------------------------------------------------------------------------- + xdef _mt_stopfx +_mt_stopfx: +; Immediately stop a currently playing sound effect on a channel. +; a6 = CUSTOM +; d0.b = channel (0..3) + + ifnd SDATA + lea mt_data+mt_chan1(pc),a0 + else + lea mt_chan1(a4),a0 + endc + + and.w #3,d0 + add.w d0,d0 + add.w channel_offsets(pc,d0.w),a0 + + move.w #$4000,INTENA(a6) + tst.b n_sfxpri(a0) + beq .1 ; no sfx playing anyway + moveq #1,d0 + move.b d0,n_sfxpri(a0) + move.w d0,n_sfxlen(a0) ; idle loop + move.w #108,n_sfxper(a0) ; enter idle as quickly as possible + clr.w n_sfxvol(a0) ; and cut volume + ifne NULL_IS_CLEARED + clr.b n_looped(a0) + clr.l n_sfxptr(a0) ; use $0 for idle-looping + else + tst.b n_looped(a0) + beq .1 + clr.b n_looped(a0) + subq.l #2,n_sfxptr(a0) ; idle loop at sample-start - 2 + endc +.1: move.w #$c000,INTENA(a6) + + rts + ;--------------------------------------------------------------------------- xdef _mt_musicmask @@ -1019,9 +1113,6 @@ _mt_musicmask: ; a6 = CUSTOM ; d0.b = channel-mask (bit 0 for channel 0, ..., bit 3 for channel 3) - move.l a6,-(sp) ; Save A6 for Blitz - lea CUSTOM,a6 ; Prepare A6 for player - ifnd SDATA move.l a4,-(sp) lea mt_data(pc),a4 @@ -1043,9 +1134,6 @@ _mt_musicmask: ifnd SDATA move.l (sp)+,a4 endc - - move.l (sp)+,a6 ; Restore A6 for Blitz - rts @@ -1058,8 +1146,10 @@ _mt_mastervol: ; a6 = CUSTOM ; d0.w = master volume - move.l a6,-(sp) ; Save A6 for Blitz - lea CUSTOM,a6 ; Prepare A6 for player + ifnd SDATA + move.l a4,-(sp) + lea mt_data(pc),a4 + endc ; stingray, since each volume table has a size of 65 bytes ; we simply multiply (optimised of course) by 65 to get the @@ -1070,17 +1160,55 @@ _mt_mastervol: add.w d0,a0 move.w #$4000,INTENA(a6) - ifd SDATA + + ; adapt all channel volumes immediately move.l a0,mt_MasterVolTab(a4) - else - lea mt_data+mt_MasterVolTab(pc),a1 - move.l a0,(a1) - endc + move.w mt_chan1+n_volume(a4),d0 + move.b (a0,d0.w),d0 + move.w d0,AUD0VOL(a6) + move.w mt_chan2+n_volume(a4),d0 + move.b (a0,d0.w),d0 + move.w d0,AUD1VOL(a6) + move.w mt_chan3+n_volume(a4),d0 + move.b (a0,d0.w),d0 + move.w d0,AUD2VOL(a6) + move.w mt_chan4+n_volume(a4),d0 + move.b (a0,d0.w),d0 + move.w d0,AUD3VOL(a6) + move.w #$c000,INTENA(a6) - move.l (sp)+,a6 ; Restore A6 for Blitz + ifnd SDATA + move.l (sp)+,a4 + endc + rts + + +;--------------------------------------------------------------------------- + xdef _mt_samplevol +_mt_samplevol: +; Redefine a sample's volume. May also be done while the song is playing. +; Warning: Does not check arguments for valid range! You must have done +; _mt_init before calling this function! +; The new volume is persistent. Even when the song is restarted. +; d0.w = sample number (0-31) +; d1.b = volume (0-64) + + ifd SDATA + move.l mt_mod(a4),a0 + else + move.l mt_data+mt_mod(pc),a0 + endc + swap d1 + move.w d0,d1 + add.w d1,d1 + lsl.w #5,d0 + sub.w d1,d0 ; table index: sample number * 30 + swap d1 + move.b d1,12+3(a0,d0.w) ; set sample's volume rts + endc ; !MINIMAL ;--------------------------------------------------------------------------- @@ -1140,10 +1268,6 @@ no_new_note: ; set one-shot TimerB interrupt for enabling DMA, when needed move.b mt_dmaon+1(pc),d0 beq same_pattern - - move.l mt_Lev6Int(pc),a0 - lea mt_TimerBdmaon(pc),a1 - move.l a1,(a0) move.b #$19,CIAB+CIACRB ; load/start timer B, one-shot bra same_pattern @@ -1179,15 +1303,13 @@ settb_step: ; set one-shot TimerB interrupt for enabling DMA, when needed move.b mt_dmaon+1(pc),d0 beq pattern_step - - move.l mt_Lev6Int(pc),a0 - lea mt_TimerBdmaon(pc),a1 - move.l a1,(a0) move.b #$19,CIAB+CIACRB ; load/start timer B, one-shot pattern_step: ; next pattern line, handle delay and break + ifeq MINIMAL clr.b mt_SilCntValid(a4) ; recalculate silence counters + endc moveq #16,d2 ; offset to next pattern line move.b mt_PattDelTime2(a4),d1 @@ -1205,7 +1327,7 @@ pattern_step: .3: add.w mt_PatternPos(a4),d2 ; d2 PatternPos ; check for break - bclr #0,mt_PBreakFlag(a4) + bclr d7,mt_PBreakFlag(a4) beq .4 move.w mt_PBreakPos(a4),d2 move.w d7,mt_PBreakPos(a4) @@ -1227,7 +1349,13 @@ song_step: move.l mt_mod(a4),a0 cmp.b 950(a0),d0 ; end of song reached? blo .1 - moveq #0,d0 + moveq #0,d0 ; restart the song from the beginning + ifeq MINIMAL + addq.b #1,mt_SongEnd(a4) + bne .2 + clr.b mt_Enable(a4) ; stop the song when mt_SongEnd was -1 +.2: and.b #$7f,mt_SongEnd(a4) + endc .1: move.b d0,mt_SongPos(a4) same_pattern: @@ -1237,6 +1365,7 @@ same_pattern: rts + ifeq MINIMAL ;--------------------------------------------------------------------------- mt_sfxonly: ; Called from interrupt. @@ -1263,10 +1392,6 @@ mt_sfxonly: move.b mt_dmaon+1(pc),d0 beq .1 - - move.l mt_Lev6Int(pc),a0 - lea mt_TimerBdmaon(pc),a1 - move.l a1,(a0) move.b #$19,CIAB+CIACRB ; load/start timer B, one-shot .1: rts @@ -1282,6 +1407,8 @@ chan_sfx_only: move.w n_sfxlen(a2),d0 bne start_sfx + tst.b n_looped(a2) + bne .1 move.w n_intbit(a2),d0 and.w INTREQR(a6),d0 @@ -1307,23 +1434,40 @@ start_sfx: move.w d1,DMACON(a6) move.l n_sfxptr(a2),a0 + tst.b n_sfxpri(a2) + bpl .1 + + ; looped sound effect + st n_looped(a2) + addq.l #2,a0 ; skip first word, used for idling + subq.w #1,d0 move.l a0,AUDLC(a5) move.w d0,AUDLEN(a5) - move.w n_sfxper(a2),d0 - move.w d0,AUDPER(a5) - move.w n_sfxvol(a2),AUDVOL(a5) + bra .2 + + ; normal sound effect +.1: move.b d7,n_looped(a2) + move.l a0,AUDLC(a5) + move.w d0,AUDLEN(a5) + moveq #1,d0 ; idles after playing once + ifne NULL_IS_CLEARED + sub.l a0,a0 + endc ; save repeat and period for TimerB interrupt - move.l a0,n_loopstart(a2) - move.w #1,n_replen(a2) +.2: move.l a0,n_loopstart(a2) + move.w d0,n_replen(a2) + move.w n_sfxper(a2),d0 + move.w d0,AUDPER(a5) move.w d0,n_period(a2) + move.w n_sfxvol(a2),AUDVOL(a5) - move.b d7,n_looped(a2) - move.w d7,n_sfxlen(a2) + move.w d7,n_sfxlen(a2) ; don't call start_sfx again lea mt_dmaon(pc),a0 - or.w d1,(a0) + or.w d1,(a0) ; DMA-channel to enable on TimerB rts + endc ; !MINIMAL ;--------------------------------------------------------------------------- @@ -1331,6 +1475,7 @@ mt_checkfx: ; a2 = channel data ; a5 = audio registers + ifeq MINIMAL tst.b n_sfxpri(a2) beq .3 @@ -1348,7 +1493,9 @@ mt_checkfx: and.w #$00ff,d4 bra blocked_e_cmds -.2: move.w n_intbit(a2),d0 +.2: tst.b n_looped(a2) + bne .1 + move.w n_intbit(a2),d0 and.w INTREQR(a6),d0 beq .1 move.w n_dmabit(a2),d0 @@ -1357,6 +1504,7 @@ mt_checkfx: ; sound effect sample has played, so unblock this channel again move.b d7,n_sfxpri(a2) + endc ; !MINIMAL ; do channel effects between notes .3: move.w n_funk(a2),d0 @@ -1410,6 +1558,7 @@ mt_playvoice: move.l (a1)+,d6 ; d6 current note/cmd words + ifeq MINIMAL ; channel blocked by external sound effect? tst.b n_sfxpri(a2) beq .2 @@ -1420,7 +1569,9 @@ mt_playvoice: bra moreblockedfx ; do only some limited commands, while sound effect is in progress -.1: move.w n_intbit(a2),d0 +.1: tst.b n_looped(a2) + bne moreblockedfx + move.w n_intbit(a2),d0 and.w INTREQR(a6),d0 beq moreblockedfx move.w n_dmabit(a2),d0 @@ -1429,6 +1580,7 @@ mt_playvoice: ; sound effect sample has played, so unblock this channel again move.b d7,n_sfxpri(a2) + endc ; !MINIMAL .2: tst.l (a2) ; n_note/cmd: any note or cmd set? bne .3 @@ -1466,8 +1618,12 @@ mt_playvoice: move.w (a0)+,d0 ; length bne .4 + ifne NULL_IS_CLEARED + moveq #0,d2 ; use $0 for empty samples + else ; use the first two bytes from the first sample for empty samples move.l mt_SampleStarts(a4),d2 + endc addq.w #1,d0 .4: move.l d2,n_start(a2) @@ -1477,28 +1633,28 @@ mt_playvoice: moveq #0,d3 move.b (a0)+,d3 add.w d3,d3 - add.w d3,d3 move.l a0,d1 lea mt_PerFineTune(pc),a0 - move.l (a0,d3.w),n_pertab(a2) + add.w (a0,d3.w),a0 + move.l a0,n_pertab(a2) move.l d1,a0 - cmp.w #32,d3 + cmp.w #2*8,d3 shs n_minusft(a2) moveq #0,d1 move.b (a0)+,d1 ; volume move.w d1,n_volume(a2) move.w (a0)+,d3 ; repeat offset - sne n_looped(a2) - beq no_loop + beq no_offset ; set repeat + add.l d3,d2 + add.l d3,d2 move.w (a0),d0 move.w d0,n_replen(a2) + exg d0,d3 ; n_replen to d3 add.w d3,d0 - add.l d3,d2 - add.l d3,d2 - bra set_loop + bra set_len_start mult30tab: dc.w 0*30,1*30,2*30,3*30,4*30,5*30,6*30,7*30 @@ -1506,17 +1662,36 @@ mult30tab: dc.w 16*30,17*30,18*30,19*30,20*30,21*30,22*30,23*30 dc.w 24*30,25*30,26*30,27*30,28*30,29*30,30*30,31*30 -no_loop: - move.w (a0),n_replen(a2) -set_loop: +no_offset: + move.w (a0),d3 + ifne NULL_IS_CLEARED + cmp.w #1,d3 + beq .1 + bhi set_replen + else + bne set_replen + endc + ; repeat length zero means idle-looping + addq.w #1,d3 +.1: moveq #0,d2 ; expect two zero bytes at $0 +set_replen: + move.w d3,n_replen(a2) +set_len_start: move.w d0,n_length(a2) move.l d2,n_loopstart(a2) move.l d2,n_wavestart(a2) + ifeq MINIMAL move.l mt_MasterVolTab(a4),a0 move.b (a0,d1.w),d1 + endc move.w d1,AUDVOL(a5) + ; remember if sample is looped + ; @@@ FIXME: also need to check if n_loopstart equals n_start + subq.w #1,d3 + sne n_looped(a2) + set_regs: ; d4 = cmd argument | masked E-cmd ; d5 = cmd*2 @@ -1544,41 +1719,11 @@ prefx_tab: dc.w set_period-prefx_tab,set_period-prefx_tab,set_period-prefx_tab dc.w set_period-prefx_tab,set_period-prefx_tab,set_period-prefx_tab -set_toneporta: - move.l n_pertab(a2),a0 ; tuned period table - - ; find first period which is less or equal the note in d6 - moveq #36-1,d0 - moveq #-2,d1 -.1: addq.w #2,d1 - cmp.w (a0)+,d6 - dbhs d0,.1 - - tst.b n_minusft(a2) ; negative fine tune? - beq .2 - tst.w d1 - beq .2 - subq.l #2,a0 ; then take previous period - subq.w #2,d1 - -.2: move.w d1,n_noteoff(a2) ; note offset in period table - move.w n_period(a2),d2 - move.w -(a0),d1 - cmp.w d1,d2 - bne .3 - moveq #0,d1 -.3: move.w d1,n_wantedperiod(a2) - - move.w n_funk(a2),d0 - beq .4 - bsr mt_updatefunk -.4: move.w d2,AUDPER(a5) - rts - -set_sampleoffset: +mt_sampleoffset: ; cmd 9 x y (xy = offset in 256 bytes) ; d4 = xy + moveq #0,d0 move.b d4,d0 bne .1 @@ -1592,19 +1737,24 @@ set_sampleoffset: sub.w d0,n_length(a2) add.w d0,d0 add.l d0,n_start(a2) - bra set_period + rts .3: move.w #1,n_length(a2) + rts + + +set_sampleoffset: + bsr mt_sampleoffset bra set_period set_finetune: lea mt_PerFineTune(pc),a0 moveq #$0f,d0 - and.b n_cmdlo(a2),d0 + and.b d4,d0 add.w d0,d0 - add.w d0,d0 - move.l (a0,d0.w),n_pertab(a2) - cmp.w #32,d0 + add.w (a0,d0.w),a0 + move.l a0,n_pertab(a2) + cmp.w #2*8,d0 shs n_minusft(a2) set_period: @@ -1665,7 +1815,7 @@ morefx_tab: dc.w mt_pernop-morefx_tab,mt_pernop-morefx_tab,mt_pernop-morefx_tab dc.w mt_pernop-morefx_tab,mt_pernop-morefx_tab,mt_pernop-morefx_tab dc.w mt_pernop-morefx_tab,mt_pernop-morefx_tab,mt_pernop-morefx_tab - dc.w mt_pernop-morefx_tab ; $9 + dc.w mt_sampleoffset-morefx_tab ; $9 dc.w mt_pernop-morefx_tab dc.w mt_posjump-morefx_tab ; $B dc.w mt_volchange-morefx_tab @@ -1674,6 +1824,40 @@ morefx_tab: dc.w mt_setspeed-morefx_tab +set_toneporta: + move.l n_pertab(a2),a0 ; tuned period table + + ; find first period which is less or equal the note in d6 + moveq #36-1,d0 + moveq #-2,d1 +.1: addq.w #2,d1 + cmp.w (a0)+,d6 + dbhs d0,.1 + + tst.b n_minusft(a2) ; negative fine tune? + beq .2 + tst.w d1 + beq .2 + subq.l #2,a0 ; then take previous period + subq.w #2,d1 + +.2: move.w d1,n_noteoff(a2) ; note offset in period table + move.w n_period(a2),d2 + move.w -(a0),d1 + cmp.w d1,d2 + bne .3 + moveq #0,d1 +.3: move.w d1,n_wantedperiod(a2) + + move.w n_funk(a2),d0 + beq .4 + bsr mt_updatefunk + +.4: move.w d2,AUDPER(a5) + rts + + + ifeq MINIMAL moreblockedfx: ; d6 = note.w | cmd.w @@ -1696,6 +1880,7 @@ blmorefx_tab: dc.w mt_patternbrk-blmorefx_tab ; $D dc.w blocked_e_cmds-blmorefx_tab dc.w mt_setspeed-blmorefx_tab ; $F + endc ; !MINIMAL mt_arpeggio: @@ -1860,6 +2045,7 @@ mt_vibrato_nc: and.b n_vibratopos(a2),d0 add.w d0,d2 + ifne ENABLE_SAWRECT ; select vibrato waveform moveq #3,d1 and.b n_vibratoctrl(a2),d1 @@ -1874,6 +2060,7 @@ mt_vibrato_nc: ; ctrl 1 selects a sawtooth vibrato .5: lea mt_VibratoSawTable(pc),a0 bra .9 + endc ; ENABLE_SAWRECT ; ctrl 0 selects a sine vibrato .6: lea mt_VibratoSineTable(pc),a0 @@ -1933,6 +2120,7 @@ mt_tremolo: and.b n_tremolopos(a2),d0 add.w d0,d2 + ifne ENABLE_SAWRECT ; select tremolo waveform moveq #3,d1 and.b n_tremoloctrl(a2),d1 @@ -1947,21 +2135,24 @@ mt_tremolo: ; ctrl 1 selects a sawtooth tremolo .5: lea mt_VibratoSawTable(pc),a0 bra .9 + endc ; ENABLE_SAWRECT ; ctrl 0 selects a sine tremolo .6: lea mt_VibratoSineTable(pc),a0 ; add tremolo-offset to volume -.9: move.b (a0,d2.w),d0 - add.w n_volume(a2),d0 +.9: move.w n_volume(a2),d0 + add.b (a0,d2.w),d0 bpl .10 moveq #0,d0 .10: cmp.w #64,d0 bls .11 moveq #64,d0 .11: move.w n_period(a2),AUDPER(a5) + ifeq MINIMAL move.l mt_MasterVolTab(a4),a0 move.b (a0,d0.w),d0 + endc move.w d0,AUDVOL(a5) ; increase tremolopos by speed @@ -1996,8 +2187,10 @@ vol_slide_down: set_vol: move.w d0,n_volume(a2) move.w n_period(a2),AUDPER(a5) + ifeq MINIMAL move.l mt_MasterVolTab(a4),a0 move.b (a0,d0.w),d0 + endc move.w d0,AUDVOL(a5) rts @@ -2024,8 +2217,10 @@ mt_volchange: bls .1 moveq #64,d4 .1: move.w d4,n_volume(a2) + ifeq MINIMAL move.l mt_MasterVolTab(a4),a0 move.b (a0,d4.w),d4 + endc move.w d4,AUDVOL(a5) rts @@ -2152,8 +2347,7 @@ mt_glissctrl: ; cmd E 3 x (x gliss) ; d0 = x - and.b #$04,n_gliss(a2) - or.b d0,n_gliss(a2) + move.b d0,n_gliss(a2) rts @@ -2171,9 +2365,9 @@ mt_finetune: lea mt_PerFineTune(pc),a0 add.w d0,d0 - add.w d0,d0 - move.l (a0,d0.w),n_pertab(a2) - cmp.w #32,d0 + add.w (a0,d0.w),a0 + move.l a0,n_pertab(a2) + cmp.w #2*8,d0 shs n_minusft(a2) rts @@ -2202,8 +2396,7 @@ mt_jumploop: rts ; remember start of loop position -.3: move.w mt_PatternPos(a4),d0 - move.w d0,n_pattpos(a2) +.3: move.w mt_PatternPos(a4),n_pattpos(a2) .4: rts @@ -2228,22 +2421,22 @@ mt_retrignote: ; d0 = x tst.b d0 - beq .2 + beq .1 ; set new retrigger count when Counter=0 -.1: tst.b mt_Counter(a4) - bne .3 + tst.b mt_Counter(a4) + bne .2 move.b d0,n_retrigcount(a2) ; avoid double retrigger, when Counter=0 and a note was set move.w #$0fff,d2 and.w (a2),d2 beq do_retrigger -.2: rts +.1: rts ; check if retrigger count is reached -.3: subq.b #1,n_retrigcount(a2) - bne .2 +.2: subq.b #1,n_retrigcount(a2) + bne .1 move.b d0,n_retrigcount(a2) ; reset do_retrigger: @@ -2300,8 +2493,10 @@ mt_notedelay: cmp.b mt_Counter(a4),d0 bne .1 tst.w (a2) ; trigger note when given - bne do_retrigger + bne .2 .1: rts +.2: move.w n_period(a2),AUDPER(a5) + bra do_retrigger mt_patterndelay: @@ -2420,6 +2615,7 @@ mt_VibratoSineTable: dc.b 0,-2,-5,-8,-11,-14,-16,-18,-21,-23,-24,-26,-27,-28,-29,-29 dc.b -29,-29,-29,-28,-27,-26,-24,-23,-21,-18,-16,-14,-11,-8,-5,-2 + ifne ENABLE_SAWRECT mt_VibratoSawTable: dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 @@ -2551,8 +2747,19 @@ mt_VibratoRectTable: dc.b 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 dc.b -29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29 dc.b -29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29 + endc ; ENABLE_SAWRECT +mt_PerFineTune: + dc.w mt_Tuning0-mt_PerFineTune,mt_Tuning1-mt_PerFineTune + dc.w mt_Tuning2-mt_PerFineTune,mt_Tuning3-mt_PerFineTune + dc.w mt_Tuning4-mt_PerFineTune,mt_Tuning5-mt_PerFineTune + dc.w mt_Tuning6-mt_PerFineTune,mt_Tuning7-mt_PerFineTune + dc.w mt_TuningM8-mt_PerFineTune,mt_TuningM7-mt_PerFineTune + dc.w mt_TuningM6-mt_PerFineTune,mt_TuningM5-mt_PerFineTune + dc.w mt_TuningM4-mt_PerFineTune,mt_TuningM3-mt_PerFineTune + dc.w mt_TuningM2-mt_PerFineTune,mt_TuningM1-mt_PerFineTune + mt_PeriodTable: mt_Tuning0: ; Tuning 0, Normal c-1 - b3 dc.w 856,808,762,720,678,640,604,570,538,508,480,453 @@ -2619,12 +2826,7 @@ mt_TuningM1: dc.w 431,407,384,363,342,323,305,288,272,256,242,228 dc.w 216,203,192,181,171,161,152,144,136,128,121,114 -mt_PerFineTune: - dc.l mt_Tuning0,mt_Tuning1,mt_Tuning2,mt_Tuning3 - dc.l mt_Tuning4,mt_Tuning5,mt_Tuning6,mt_Tuning7 - dc.l mt_TuningM8,mt_TuningM7,mt_TuningM6,mt_TuningM5 - dc.l mt_TuningM4,mt_TuningM3,mt_TuningM2,mt_TuningM1 - + ifeq MINIMAL MasterVolTab0: dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 @@ -3016,7 +3218,7 @@ MasterVolTab64: dc.b 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63 dc.b 64 even - + endc ; !MINIMAL ifd SDATA @@ -3043,7 +3245,7 @@ mt_timerval: ds.l 1 mt_oldtimers: ds.b 4 -mt_MasterVolTab: +mt_Lev6Int: ds.l 1 mt_Lev6Ena: ds.w 1 @@ -3065,8 +3267,13 @@ mt_PattDelTime: ds.b 1 mt_PattDelTime2: ds.b 1 + + ifeq MINIMAL mt_SilCntValid: ds.b 1 +mt_MasterVolTab: + ds.l 1 + endc xdef _mt_Enable _mt_Enable: @@ -3078,11 +3285,17 @@ _mt_E8Trigger: mt_E8Trigger: ds.b 1 + ifeq MINIMAL xdef _mt_MusicChannels _mt_MusicChannels: mt_MusicChannels: ds.b 1 + xdef _mt_SongEnd +_mt_SongEnd: +mt_SongEnd: + ds.b 1 + endc ; !MINIMAL else ; !SDATA : single section with local base register @@ -3096,7 +3309,7 @@ mt_mod rs.l 1 mt_oldLev6 rs.l 1 mt_timerval rs.l 1 mt_oldtimers rs.b 4 -mt_MasterVolTab rs.l 1 +mt_Lev6Int rs.l 1 mt_Lev6Ena rs.w 1 mt_PatternPos rs.w 1 mt_PBreakPos rs.w 1 @@ -3107,10 +3320,14 @@ mt_Counter rs.b 1 mt_SongPos rs.b 1 mt_PattDelTime rs.b 1 mt_PattDelTime2 rs.b 1 + ifeq MINIMAL mt_SilCntValid rs.b 1 +mt_MasterVolTab rs.l 1 + endc mt_Enable rs.b 1 ; exported as _mt_Enable mt_E8Trigger rs.b 1 ; exported as _mt_E8Trigger mt_MusicChannels rs.b 1 ; exported as _mt_MusicChannels +mt_SongEnd rs.b 1 ; exported as _mt_SongEnd mt_data: ds.b mt_Enable @@ -3120,9 +3337,14 @@ _mt_Enable: xdef _mt_E8Trigger _mt_E8Trigger: ds.b 1 + ifeq MINIMAL xdef _mt_MusicChannels _mt_MusicChannels: ds.b 1 + xdef _mt_SongEnd +_mt_SongEnd: + ds.b 1 + endc ; !MINIMAL endc ; SDATA/!SDATA diff --git a/ptplayer.readme b/ptplayer.readme index a5cc07b..be2e2bf 100644 --- a/ptplayer.readme +++ b/ptplayer.readme @@ -1,10 +1,10 @@ -Short: ProTracker 2.3B replayer with support for external sound fx -Author: Frank Wille -Uploader: frank@phoenix.owl.de -Type: mus/play -Version: 5.0 -Requires: Assembler -Achitecture: m68k-amigaos +Short: ProTracker player w/ support for ext. sfx +Author: Frank Wille +Uploader: frank@phoenix.owl.de +Type: mus/play +Version: 6.1 +Requires: Assembler +Architecture: m68k-amigaos While developing the games Sqrxz and Solid Gold I needed a Protracker player which can insert sound effects from the game into the current song. @@ -46,80 +46,167 @@ may be used for sound effects at once. The master volume is always applied to the music, but does not affect external sound effects at all. +NOTE: When playing external sound effects, always make sure the first +word is cleared! It is used for idling when the effect stopped. This +is the same technique as used by the music samples in Amiga trackers. -Selecting the data model: + +How to use: + + +1. Selecting the data model ptplayer.asm can be configured to support two data models: -1. By default ptplayer.asm assembles into a single code section. The - interface functions set up a local base register to access data. +a) By default ptplayer.asm assembles into a single code section. The + player routines set up a local base register to access data. This requires a working RS directive, which is provided by many assemblers, like e.g. vasm, PhxAss, Devpac, Basm, AsmOne, SNMA. -2. By defining the SDATA symbol ptplayer.asm assembles into a code - section and a small-data section (called __MERGED). All interface - functions require that the base register A4 is set up with the +b) By defining the SDATA symbol ptplayer.asm assembles into a code + section and a small-data section (called __MERGED). All player + routines expect that the base register A4 is set up with the small-data base pointer (provided by the linker through _LinkerDB). It uses the NEAR directive, which might work with vasm and PhxAss only. +2. Selecting optional features + +By default ptplayer builds with all features included. + +a) By defining the symbol MINIMAL=1 (defaults to 0) you get just a + standard Protracker player, without the ability to insert external + sound effects or to control the master or sample volumes. + This approximately halves the code size. + +b) By defining the symbol ENABLE_SAWRECT=0 (defaults to 1) you may + disable sawtooth and rectangle vibratos/tremolos. When selected, + they are replaced by sine-waves. These wave forms are extremely + rare and disabling them saves another 2K for the tables. + +c) By defining the symbol NULL_IS_CLEARED=1 (defaults to 0) you indicate + that the memory locations $0 and $1 in your system are zero, so they + will be used for idle-looping of audio channels. Might be useful when + dynamically loading new samples. + +You may change these symbols either directly in the source or override +them via the assembler's command line. + + +3. Common usage + +a) Install a Level-6 interrupt handler for CIA-B timer interrupts + by calling mt_install_cia(). Do this once, during program init. + +b) For every new MOD file to play initialize it with mt_init(). + Most common case is to pass a pointer to the MOD in A0 and set A1 + and D0 to zero, which will play the song from the beginning. + Everything is initialized for replay now, but nothing is played. + +c) Set the byte-variable mt_Enable to non-zero to start the replay. + Clearing that variable again pauses the song. + +d) Stop playing and set all volumes to zero by calling mt_end(). + +e) Finally, in the cleanup routine of your program, remove the Level-6 + interrupt handler again and reset all CIA registers by calling + mt_remove_cia(). + + Exported functions: -(CUSTOM is the custom-chip register set base address $dff000.) -_mt_install_cia(a6=CUSTOM, a0=AutoVecBase, d0=PALflag.b) +Note that the leading underscore disappears when a symbol is referenced +from C. CUSTOM is the custom-chip register set base address $dff000. + +_mt_install_cia(a6=CUSTOM, a0=VectorBase, d0=PALflag.b) Install a CIA-B interrupt for calling _mt_music or mt_sfxonly. The music module is replayed via _mt_music when _mt_Enable is non-zero. Otherwise the interrupt handler calls mt_sfxonly to play sound - effects only. + effects only. VectorBase is 0 for 68000, otherwise set it to the CPU's + VBR register. A non-zero PALflag selects PAL-clock for the CIA timers + (NTSC otherwise). _mt_remove_cia(a6=CUSTOM) - Remove the CIA-B music interrupt and restore the old vector. + Remove the CIA-B music interrupt, restore the previous handler and + reset the CIA timer registers to their original values. _mt_init(a6=CUSTOM, a0=TrackerModule, a1=Samples|NULL, d0=InitialSongPos.b) Initialize a new module. - Reset speed to 6, tempo to 125 and start at the given position. + Reset speed to 6, tempo to 125 and start at the given song position. Master volume is at 64 (maximum). - When a1 is NULL the samples are assumed to be stored after the patterns. + When a1 is NULL the samples are assumed to be stored after the patterns, + which is the usual case. _mt_end(a6=CUSTOM) - Stop playing current module. + Stop playing current module and sound effects. _mt_soundfx(a6=CUSTOM, a0=SamplePointer, - d0=SampleLength.w, d1=SamplePeriod.w, d2=SampleVolume.w) + d0=SampleLength.w, d1=SamplePeriod.w, d2=SampleVolume.w) Request playing of an external sound effect on the most unused channel. - This function is for compatibility with the old API only! - You should call _mt_playfx instead. + This function is for compatibility with the old API only. + You should call _mt_playfx instead. MINIMAL=0 only. -_mt_playfx(a6=CUSTOM, a0=SfxStructurePointer) +channelStatus = _mt_playfx(a6=CUSTOM, a0=SfxStructurePointer) Request playing of a prioritized external sound effect, either on a fixed channel or on the most unused one. Structure layout of SfxStructure: - APTR sfx_ptr (pointer to sample start in Chip RAM, even address) - WORD sfx_len (sample length in words) - WORD sfx_per (hardware replay period for sample) - WORD sfx_vol (volume 0..64, is unaffected by the song's master volume) - BYTE sfx_cha (0..3 selected replay channel, -1 selects best channel) - BYTE sfx_pri (unsigned priority, must be non-zero) + void *sfx_ptr (pointer to sample start in Chip RAM, even address) + WORD sfx_len (sample length in words) + WORD sfx_per (hardware replay period for sample) + WORD sfx_vol (volume 0..64, is unaffected by the song's master volume) + BYTE sfx_cha (0..3 selected replay channel, -1 selects best channel) + BYTE sfx_pri (priority, must be in the range 1..127) When multiple samples are assigned to the same channel the lower priority sample will be replaced. When priorities are the same, then the older sample is replaced. The chosen channel is blocked for music until the effect has completely been replayed. + RETURN VALUES: A pointer to a channel-status structure (see ptplayer.h) + when the sample is scheduled for playing, and NULL when the request was + ignored. + NOTE: Always make sure the first word of your sound effect sample + are zero! + MINIMAL=0 only. + +_mt_loopfx(a6=CUSTOM, a0=SfxStructurePointer) + Request playing of a looped sound effect on a fixed channel, which + will be blocked for music until the effect is stopped (_mt_stopfx). + It uses the same SfxStructure as _mt_playfx, but the priority is + ignored. A looped sound effect has always highest priority and will + replace a previous effect on the same channel. No automatic channel + selection possible! + Also make sure the sample starts with a zero-word, which is used + for idling when the effect is stopped. This word is included in the + total length calculation, but excluded when actually playing the loop. + MINIMAL=0 only. + +_mt_stopfx(a6=CUSTOM, d0=Channel.b) + Immediately stop a currently playing sound effect on a channel (0..3) + and make it available for music, or other effects, again. This is the + only way to stop a looped sound effect (_mt_loopfx), besides stopping + replay completely (_mt_end). MINIMAL=0 only. _mt_musicmask(a6=CUSTOM, d0=ChannelMask.b) - Set bits in the mask define which specific channels are reserved + Bits set in the mask define which specific channels are reserved for music only. Set bit 0 for channel 0, ..., bit 3 for channel 3. When calling _mt_soundfx or _mt_playfx with automatic channel selection (sfx_cha=-1) then these masked channels will never be picked. - The mask defaults to 0. + The mask defaults to 0. MINIMAL=0 only _mt_mastervol(a6=CUSTOM, d0=MasterVolume.w) Set a master volume from 0 to 64 for all music channels. Note that the master volume does not affect the volume of external - sound effects (which is desired). + sound effects (which is desired). MINIMAL=0 only. + +_mt_samplevol(d0=SampleNumber.w, d1=Volume.b) + Redefine a sample's volume. May also be done while the song is playing. + Warning: Does not check arguments for valid range! You must have done + _mt_init before calling this function! + The new volume is persistent. Even when the song is restarted. + MINIMAL=0 only. _mt_music(a6=CUSTOM) - The replayer routine. Is called automatically after mt_install_cia(). + The replayer routine. Is called automatically after _mt_install_cia. Exported byte-sized variables: @@ -127,15 +214,73 @@ Exported byte-sized variables: _mt_Enable Set this byte to non-zero to play music, zero to pause playing. Note that you can still play sound effects, while music is stopped. + It is set to 0 by _mt_install_cia. _mt_E8Trigger This byte reflects the value of the last E8 command. - It is reset to 0 after mt_init(). + It is reset to 0 after _mt_init. _mt_MusicChannels This byte defines the number of channels which should be dedicated for playing music. So sound effects will never use more than 4 - _mt_MusicChannels channels at once. Defaults to 0. + MINIMAL=0 only. + + +There is also a header file for C compilers, called ptplayer.h. +It depends on the SDI_compiler.h header file, which implements +portable macros for defining compiler-specific register arguments. +Get it from Aminet: http://aminet.net/dev/c/SDI_headers.lha + + +License: + +Written by Frank Wille in 2013, 2016, 2017, 2018, 2019, 2020, 2021. +I, the copyright holder of this work, hereby release it into the +public domain. This applies worldwide. + +If still in doubt, please read the included file "LICENSE". + + +FAQ: + +- The player doesn't work. I am hearing no music. + A: This can have multiple reasons. Most likely is that you didn't + call _mt_install_cia at all or with a wrong Vector Base pointer. + The Vector Base is 0 for 68000 systems. Otherwise you *must* read + it from the CPU's VBR register, which is only available in supervisor + mode. Also don't forget to set _mt_enable to true to start playing. + This player is intended for games/demos which take over the hardware + and disable the OS. Running with the OS alive may work, as the OS + doesn't use the CIA-B timers, but it is not recommended. + +- I want to run the player in VERTB interrupt. + A: No. That's currently not possible. Although the player routine + _mt_music could theoretically run in a 50Hz VERTB, it requires + CIA-B Timer-B interrupts to enable Audio DMA and set the loop + pointers. + +- I am hearing a high-pitched noise in my music. + A: Amiga players use the first two bytes of a sample for idle-looping. + Make sure they are zeroed. + Alternatively, assemble ptplayer with NULL_IS_CLEARED=1 and make sure + the bytes at $0 and $1 are zero. + +- I am hearing a high-pitched noise when playing an empty sample. + My first initialized sample is looped or unused, so how can that be? + A: Check the first two bytes of your first initialized sample. + These bytes are used by ptplayer for all empty samples. + Alternatively, assemble ptplayer with NULL_IS_CLEARED=1 and make sure + the bytes at $0 and $1 are zero. + +- I am hearing a high-pitched noise after playing a sound effect. + A: The same technique is used for playing sound effects as for + playing instrumental samples. The sound effect idles in the first + two bytes after being played. So make sure they are zero. + +- But I don't want to clear the first two bytes in sound effects. + A: Then make sure that the bytes at address $0 and $1 are zero, and + assemble the player with NULL_IS_CLEARED=1. History: @@ -179,3 +324,67 @@ History: underscore now, to make them directly accessible to C programs. So in assembler you have to call _mt_init now, while in C you can call mt_init(). + +5.1: +- Included C header file, ptplayer.h, provided by BSzili. +- Fixed bug where other level 6 interrupts could trigger sample replay. +- Eliminated relocations in the fine-tune table, by replacing pointers + with word-offsets (asman). +- More optimizations in the Timer B interrupt handlers and made it + PC-relative (asman). +- Include a public domain license. + +5.2: +- Make it work with broken mods, which have a sample repeat length of zero. +- Never break looped samples with sound effects, except we have looped + samples on all four channels at once! +- New variable _mt_SongEnd to automatically stop the song when having + played the last position. Don't use it! Doesn't work perfectly yet. + +5.3: +- No longer clear the first word of each sample for idle looping. + Either we have a good Amiga tracker MOD with repeat-length one, which + already cleared that word, or we have a PC tracker MOD with a zero + repeat length. In the latter case the idle loop will now point to + address $0. Make sure that the word at this address is cleared! +- Treat samples with a length of one word the same as with zero length + as a workaround for broken PC trackers. +- Changed APTR to void* in the C headers, for better Kickstart 1.x + OS header file compatibility. +- Fixed detection of negative fine-tuning (broken due to optimizations + in V5.1). + +6.0: +- _mt_musicmask works as documented now! Sound effects will never play + on the masked channels. Previously it was rather a hint not to use them. +- Fixed sign-bug in tremolo/vibrato command 7xx (Antiriad/EAB). +- New function _mt_samplevol may be used to redefine a sample's volume. +- _mt_playfx now returns a pointer to the selected channel status structure + when the sample was scheduled for playing and returns NULL when ignored. +- Wait 576 ticks for audio DMA instead of 496, which fixes issues with + low notes on a few A1200 configurations. (No, this doesn't harm the + player's performance, as it is a timer interrupt.) +- Defining the symbol MINIMAL lets you assemble a minimal version of + the player, without the ability to insert sound effects and without + master-volume or changing samples volumes. +- Improved interrupt handling, following a suggestion of Ross/EAB. +- Minor optimizations. + +6.1: +- Fixed note delay command (EDx), which still played the previous note + in some situations (Antiriad/EAB). +- Fixed sample-offset command (9) with empty note-field (h0ffman/EAB). +- mt_mastervol() must change the volumes of all channels immediately + and shouldn't wait for the next sample being played (suggested by + h0ffman/EAB). +- Symbol ENABLE_SAWRECT may be used to disable sawtooth and rectangle + waveforms for vibrato and tremolo, which saves memory for their tables + (suggested by Antiriad). +- Symbol NULL_IS_CLEARED may be used to indicate that the memory locations + $0 and $1 in your system are zero, so they will be used for idle-looping + of audio channels (suggested by h0ffman). +- Removed cia.i and custom.i include files and included the required + symbols directly into the source. +- New function mt_loopfx() for playing looped sound effects (suggested by + mcgeezer/EAB). +- New function mt_stopfx() for immediately stopping a sound effect. From f34acf69a19e6d9f1e0205cb4d42855b51c3355b Mon Sep 17 00:00:00 2001 From: Daniel Lakey Date: Sat, 13 Nov 2021 11:55:57 +0100 Subject: [PATCH 03/23] fixed typo in name --- plplayer.obj => ptplayer.obj | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename plplayer.obj => ptplayer.obj (100%) diff --git a/plplayer.obj b/ptplayer.obj similarity index 100% rename from plplayer.obj rename to ptplayer.obj From 02ef8d17057b4c9288606c0087d8fb000ec5aaf4 Mon Sep 17 00:00:00 2001 From: Daniel Lakey Date: Sat, 13 Nov 2021 12:27:52 +0100 Subject: [PATCH 04/23] Create README.md --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..d9db44e --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# blitz_ptplayer +Port of Frank Wille's Protracker player as a Blitz Basic library + +compiled with vasm 1.8l, with the following options: +vasm -phxass -Faout ptplayer.asm + +Built wit the following configuration flags: +MINIMAL equ 0 +ENABLE_SAWRECT equ 0 +NULL_IS_CLEARED equ 0 From 3f6758a1369689fef6074cb9200533948f6cf8bd Mon Sep 17 00:00:00 2001 From: Daniel Lakey Date: Sat, 13 Nov 2021 12:28:18 +0100 Subject: [PATCH 05/23] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index d9db44e..26d5be4 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,9 @@ compiled with vasm 1.8l, with the following options: vasm -phxass -Faout ptplayer.asm Built wit the following configuration flags: + MINIMAL equ 0 + ENABLE_SAWRECT equ 0 + NULL_IS_CLEARED equ 0 From 1da152a70c7c3232e6e28cc66a53db67f26a83eb Mon Sep 17 00:00:00 2001 From: Daniel Lakey Date: Sat, 13 Nov 2021 12:36:45 +0100 Subject: [PATCH 06/23] Added build 'MINIMAL', no SFX supported. Read the asm for details! --- ptplayer.obj_minimal | Bin 0 -> 9620 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 ptplayer.obj_minimal diff --git a/ptplayer.obj_minimal b/ptplayer.obj_minimal new file mode 100644 index 0000000000000000000000000000000000000000..f8fb9caae4ed6b49d499164ec178b244472abd75 GIT binary patch literal 9620 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 b1+BV>eicTO;g0^UN$hQqxaqNry3zDsh^E4O literal 0 HcmV?d00001 From 50895f2d833eaeef1762d176d70ac8668e10a2ad Mon Sep 17 00:00:00 2001 From: Daniel Lakey Date: Tue, 16 Nov 2021 00:17:36 +0100 Subject: [PATCH 07/23] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 26d5be4..60afca3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ # blitz_ptplayer Port of Frank Wille's Protracker player as a Blitz Basic library +Blitz Basic function/statement entry points added by idrougge + +VBR fix by phx + compiled with vasm 1.8l, with the following options: vasm -phxass -Faout ptplayer.asm From fbcefe0d0de871b5bc10cacaf9c2d76473edfd6b Mon Sep 17 00:00:00 2001 From: Daniel Lakey Date: Tue, 16 Nov 2021 00:23:10 +0100 Subject: [PATCH 08/23] changed library number to 48 to avoid LotanArgs library at 195 --- ptplayer.asm | 3 ++- ptplayer.obj | Bin 17350 -> 17350 bytes ptplayer.obj_minimal | Bin 9620 -> 9620 bytes 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ptplayer.asm b/ptplayer.asm index 382bfe4..602ad3f 100644 --- a/ptplayer.asm +++ b/ptplayer.asm @@ -156,8 +156,9 @@ DMADELAY equ 576 ; was 496 audiolib equ 116 banklib equ 76 +ptplayerlib equ 48 - libheader 195,0,0,blitz_finit,runerrs + libheader ptplayerlib,0,0,blitz_finit,runerrs astatement args word,byte diff --git a/ptplayer.obj b/ptplayer.obj index 862b793e58444c7411832422246267998f8980f7..7d08cda7afa449a696570ebf422321acdef7b5cd 100644 GIT binary patch delta 18 ZcmX@s&Umbyae^wN!DK@biH&w0&Hz8p1}y*p delta 18 ZcmX@s&Umbyae^x2;mL*~5*zJ0oB>4o2EYIS diff --git a/ptplayer.obj_minimal b/ptplayer.obj_minimal index f8fb9caae4ed6b49d499164ec178b244472abd75..0ca565d5f31ce09c85317da7d66bd1ac48469e5e 100644 GIT binary patch delta 16 XcmbQ@J;i&1Dx<+ Date: Tue, 16 Nov 2021 14:42:21 +0100 Subject: [PATCH 09/23] added missing blitz stubs --- ptplayer.asm | 95 ++++++++++++++++++++++++++++++++++++++++--- ptplayer.obj | Bin 17350 -> 16704 bytes ptplayer.obj_minimal | Bin 9620 -> 0 bytes 3 files changed, 90 insertions(+), 5 deletions(-) delete mode 100644 ptplayer.obj_minimal diff --git a/ptplayer.asm b/ptplayer.asm index 602ad3f..c43cde8 100644 --- a/ptplayer.asm +++ b/ptplayer.asm @@ -158,7 +158,7 @@ audiolib equ 116 banklib equ 76 ptplayerlib equ 48 - libheader ptplayerlib,0,0,blitz_finit,runerrs + libheader ptplayerlib,0,0,blitz_finit,0 astatement args word,byte @@ -199,13 +199,13 @@ ptplayerlib equ 48 subs _mt_soundfx_stub,0,0 args word,word libs audiolib,$1080 - subs _mt_soundfx_soundobject,_soundobjectcheck,0 + subs _mt_soundfx_soundobject,0,0 name "MTSoundFX","Sound#, volume (0..64)| sample_addr.l, length.w, period.w, volume.w" astatement args word libs - subs _mt_mastervol,_volumecheck,0 + subs _mt_mastervol,0,0 name "MTMasterVolume","Master volume (0..64) for all music channels." astatement @@ -412,6 +412,8 @@ _mt_install_cia: _mt_remove_cia: ; Remove CIA-B music interrupt and restore the old vector. ; a6 = CUSTOM + movem.l a3-a6,-(sp) ; Save registers for Blitz 2 + lea CUSTOM,a6 ifnd SDATA move.l a4,-(sp) @@ -448,6 +450,8 @@ _mt_remove_cia: ifnd SDATA move.l (sp)+,a4 endc + + movem.l (sp)+,a3-a6 ; Restore registers for Blitz rts @@ -567,6 +571,21 @@ _mt_init: ; a1 = sample pointer (NULL means samples are stored within the module) ; d0 = initial song position +; --- Init for Blitz ---- +_mt_init_bank: + move.l #0,a1 ; Set sample pointer to NULL + move.b d1,d0 ; Set starting position + move.l (a0),a0 ; Set module address + bra _mt_init_blitz_done +_mt_init_stub: + move.l d0,a0 ; Set module address + move.l d1,a1 ; Set sample pointer + move.b d2,d0 ; Set starting position +_mt_init_blitz_done: + movem.l a3-a6,-(sp) + lea CUSTOM,a6 +; ----------------------- + ifnd SDATA move.l a4,-(sp) lea mt_data(pc),a4 @@ -710,13 +729,57 @@ _mt_end: move.w d0,AUD2VOL(a6) move.w d0,AUD3VOL(a6) move.w #$000f,DMACON(a6) + + movem.l (sp)+,a3-a6 ; Restore registers before returning to Blitz + rts + +;------------ Blitz2 stubs ----------- +_mt_enable_stub: + ifd SDATA + trap #0 + move.b d0,mt_Enable(a4) + else + lea mt_data+mt_Enable(pc),a0 + move.b d0,(a0) + endc rts +_mt_end_stub: + movem.l a3-a6,-(sp) + lea CUSTOM,a6 + bra _mt_end + +_mt_MusicChannels_stub: + ifd SDATA + move.b d0,mt_MusicChannels(a4) + else + lea mt_data+mt_MusicChannels(pc),a0 + move.b d0,(a0) + endc + rts + +_mt_E8Trigger_stub: + ifd SDATA + move.b mt_MusicChannels(a4),d0 + else + lea mt_data+mt_MusicChannels(pc),a0 + move.b (a0),d0 + endc + rts +;------------------------------------- +_mt_soundfx_soundobject: + movem.l a3-a6,-(sp) + lea CUSTOM,a6 + move.w d1,d2 ; volume + move.w 4(a0),d1 ; period + move.w 6(a0),d0 ; length + move.l (a0),a0 ; sample data + bra _mt_soundfx ifeq MINIMAL ;--------------------------------------------------------------------------- xdef _mt_soundfx -_mt_soundfx: +_mt_soundfx_stub: ; Request playing of an external sound effect on the most unused channel. ; This function is for compatibility with the old API only! ; You should call _mt_playfx instead! @@ -726,6 +789,16 @@ _mt_soundfx: ; d1.w = sample period ; d2.w = sample volume +;--- Blitz 2 call stub ----- + movem.l a3-a6,-(sp) + lea CUSTOM,a6 + move.l d0,a0 + move.w d1,d0 + move.w d2,d1 + move.w d3,d2 +;--------------------------- + +_mt_soundfx: lea -sfx_sizeof(sp),sp move.l a0,sfx_ptr(sp) movem.w d0-d2,sfx_len(sp) @@ -733,6 +806,7 @@ _mt_soundfx: move.l sp,a0 bsr _mt_playfx lea sfx_sizeof(sp),sp + movem.l (sp)+,a3-a6 ; Restore registers for Blitz rts @@ -1114,6 +1188,9 @@ _mt_musicmask: ; a6 = CUSTOM ; d0.b = channel-mask (bit 0 for channel 0, ..., bit 3 for channel 3) + move.l a6,-(sp) ; Save A6 for Blitz + lea CUSTOM,a6 ; Prepare A6 for player + ifnd SDATA move.l a4,-(sp) lea mt_data(pc),a4 @@ -1135,6 +1212,9 @@ _mt_musicmask: ifnd SDATA move.l (sp)+,a4 endc + + move.l (sp)+,a6 ; Restore A6 for Blitz + rts @@ -1147,6 +1227,9 @@ _mt_mastervol: ; a6 = CUSTOM ; d0.w = master volume + move.l a6,-(sp) ; Save A6 for Blitz + lea CUSTOM,a6 ; Prepare A6 for player + ifnd SDATA move.l a4,-(sp) lea mt_data(pc),a4 @@ -1182,6 +1265,8 @@ _mt_mastervol: ifnd SDATA move.l (sp)+,a4 endc + + move.l (sp)+,a6 ; Restore A6 for Blitz rts @@ -3349,4 +3434,4 @@ _mt_SongEnd: endc ; SDATA/!SDATA - end + end \ No newline at end of file diff --git a/ptplayer.obj b/ptplayer.obj index 7d08cda7afa449a696570ebf422321acdef7b5cd..2b0f3ae5dfd81bc0ab1f5bc8fb6877029e587a32 100644 GIT binary patch delta 5092 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 delta 5745 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 Date: Tue, 16 Nov 2021 14:58:34 +0100 Subject: [PATCH 10/23] fixed horrifying crashes --- README.md | 2 +- ptplayer.obj | Bin 16704 -> 12624 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 60afca3..cd3a3bc 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Blitz Basic function/statement entry points added by idrougge VBR fix by phx compiled with vasm 1.8l, with the following options: -vasm -phxass -Faout ptplayer.asm +vasmm68k_mot.exe -devpac -Fhunkexe -kick1hunks -nosym ptplayer.asm -o ptplayer.obj Built wit the following configuration flags: diff --git a/ptplayer.obj b/ptplayer.obj index 2b0f3ae5dfd81bc0ab1f5bc8fb6877029e587a32..3dea45aade0afabba15759b91162b0f3de489452 100644 GIT binary patch delta 3290 zcmZu!4Qx}_75?sfFOD&8@-QY#3iZ4+HI8u;<0Vmu1H9)1;<(h!0(1yzRl;amWfZw- zf>31P&`y-C)f9Lws;X+5s%e|5sdAnA92>DS}RTKz0m0doT`m6O@ zwbI%x){8f+XN764RVu9yMe}#Y3%e9W!LUA7pgENvu>8tnB4?dYek4v?UveI>dexm` zzx6wHd*j8#=uwe)BK8S9$EJe^q1hV%QU3_wxG$H#lz1)w@%>-p!c{anyL0PomS3wc zzv}&&nG+9MyR^nT2-Vw;)eqd?2iUD~tz*S`scZ2}V5@aW>+c3yVxQ!175Uw{`nioM zFYr=4C}wG^q93-}Z5Pt{#N&x4`ceLy1-j90=HTC|n-arI-0887mNh8#`>e^b)^u%3 zdZ`O`^4+b_|;Pn16mR_hM4g#8vuMA^Xg(pJFcMn)QUZn z)pb{`pu|o|SxNHzQb`)iD6*AAA1xKZK}k6!u8u3>-h#wl*Uju7@Hxm$61YY;t0pw& z`dcRBWd%uDNwUOb+*Xj-N|O7Rn2eWGu`JY5cbpMdvqttCyiv?o+z$!MmlP`ul&v?= zv_6G>XklHoQr1S9!+OZEMyWHbl%vH~#|yFYNo&%PNZ->JGsBoQ{}mfIW9Ev(s7#C; zf|<*$)}QVTigP+gCMc!}JzCQx&M{r*sc!VX&+sb=w;#$T(izg*NQX&Jk$!=+k93yw zCDM1&i5Higag@$j_btE6t+##sJat}Qc>^{0(*>3gu2`=vuTK~C$4h&qeran#zqGZW z-$xzyN;emDL|M4Qx{GM(!P}JBT_FCqb~$Uqs4>O)xqaev<*VY)@^n*BoY~OD z5f!H=QZ@~Ady|!LelN`&B*B2Z#{#iY`K%H|TD&LQ07G(i0QonIfxG^U{xpG=f`isY z@CYt~hj$S?7=Y_LrTGQ$iIF(7PfkN9fF@e>KS+kWN**#AruM@_p^NmIsnP3qN=U1^ zP?i`8le5vp3GX06QpF%)_gqHXj-%U+b!-6<0~({Nb9b9uTi!c->8~cA>bY$(AuUpQ zZ9nqITiYx1(`*AFEs9L40yH!>okG43HmRO~^8Z`}l%FjE`sz{g&q_dC`>4ytW}IBm z#dV9+k*u5wK*<`RbOg4X0$chUTvQ?1|NLdB{{=a_9}dlLAbedBv>Oo zlWIy(9sRg9-v{NzA|&PbA|&NV5z>F&AAm9{A;pi=342jj-I3ACnSd3nxcmFF;RyDa z?9y4IE`%HA7dk@Ka%WvognEs8qOdm`2p&LH_4^&QygrVz)>y?|tAHJIPt83Q(kXSl z)sgLBbbGAVDmqlUZC$HqOwXT6RrC<;-cY2c;3tOVS2&8Ju<9-HiZh$9Y;_JjfGyN! zTV-I&=dsKnbhd+vV|-?fcVB@$G18oZ0DC`Eb)Gxdn7L&chE-#0xC-18Lv<$QfWG50 zA{3pnepxB&xLt;1qk$84#vhw$N*2$i!b7vlOe7cMsj#Vg7N~j_Gth$1Ft{4`#PVj_|NAN}H`|oG$~EOV#!~jM z2pS&2P5V!P8vDFc29*_9`9bFggYZnI5MyAS)|>izMu%oX8n2Q2xN$Ot$0^Z<9w!`k z#FR4L^>Y&T!me5FQg`uX&Ucl%Yso(O0&z5V!pmr?)K|;(gLKI&9i1@QSKQAT6Xt&4 z&L+{^g<7K0({p4!#;Pl_nxk+tTAe&vy%@#`hB2Qj#LQca%W&nHg1J>uv`gpQ0kqFM zC4g0(*eE&G0c`wtJh_=yZquA$=G0C-O%R9xMVEa4y{1K2I#UZ}GceH3yg*?R4 znrT(3Vql38qh!RmZNw}YF-u0G(ui`A{#Ipah;4{JnKCIDjFgr%ir3G(PrRdt3QfsQ z0MW#VwaK--hK((&W!ejS^`x}r7#C85wcE8`mCt8g-)*kqj?H_JpZ+wq!4PM9u86aV z5jfl)f4F^X&u$bxlkM=z+}3o8=|}KI*!s}5e&?@m&hXthgo7BzX-wlC;TAs8Cbo$I zafsKaGpx;b&zj+g&JxoVc+tvl8w_m4Lp*b$u$H`5(S+8I?aZaq>RWktaR*Cn5kEZo_p z_tMN=9XKpIcd;h5#0b+_)rVWIMUD$H2jn^V5|>sSthK60muw%BvyCi!yq7bN7t_K5 zc&-FV=kvVW(JO!NzJ1#n2fLlGc!qZ?{B8hUbjWX|+^c1RUVp*uTD^_`Ir#oYMKKjv m!)GVQE{-h!9r!sO<#>hTGeFTehBzow8#sQ-F;hINUi&W+Flt-? delta 7288 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 From 253af4bcdeda68a9e7fd8939c3cd6df7116db2b3 Mon Sep 17 00:00:00 2001 From: Daniel Lakey Date: Tue, 16 Nov 2021 15:19:15 +0100 Subject: [PATCH 11/23] Added MTLoopFX and MTStopFX to expose _mt_loopfx and _mt_stopfx --- ptplayer.asm | 28 ++++++++++++++++++++++++++-- ptplayer.obj | Bin 12624 -> 12792 bytes 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/ptplayer.asm b/ptplayer.asm index c43cde8..4e51393 100644 --- a/ptplayer.asm +++ b/ptplayer.asm @@ -225,6 +225,18 @@ ptplayerlib equ 48 libs subs _mt_E8Trigger_stub,0,0 name "MTE8Trigger","Value of the last E8 command." + + astatement + args long + libs + subs _mt_loopfx_stub,0,0 + name "MTLoopFx","SfxStructurePointer" + + astatement + args byte + libs + subs _mt_stopfx_stub,0,0 + name "MTStopFx","channel to stop FX loop" blitz_finit: nullsub _blitz_mt_lib_finit,0,0 ; Call deinit routine on exit @@ -1102,7 +1114,12 @@ exit_playfx: channel_offsets: dc.w 0*n_sizeof,1*n_sizeof,2*n_sizeof,3*n_sizeof - +_mt_loopfx_stub: +;--- Blitz 2 call stub ----- + movem.l a3-a6,-(sp) + lea CUSTOM,a6 + move.l d0,a0 ; sfx-structure pointer +;--------------------------- ;--------------------------------------------------------------------------- xdef _mt_loopfx _mt_loopfx: @@ -1137,10 +1154,16 @@ _mt_loopfx: move.w (a0),n_sfxvol(a1) ; sfx_vol st n_sfxpri(a1) ; sfx_pri -1 enables looped mode move.w #$c000,INTENA(a6) + + movem.l (sp)+,a3-a6 ; Restore registers before returning to Blitz rts - +_mt_stopfx_stub: +;--- Blitz 2 call stub ----- + movem.l a3-a6,-(sp) + lea CUSTOM,a6 +;--------------------------- ;--------------------------------------------------------------------------- xdef _mt_stopfx _mt_stopfx: @@ -1177,6 +1200,7 @@ _mt_stopfx: endc .1: move.w #$c000,INTENA(a6) + movem.l (sp)+,a3-a6 ; Restore registers before returning to Blitz rts diff --git a/ptplayer.obj b/ptplayer.obj index 3dea45aade0afabba15759b91162b0f3de489452..a890ee826ca4a1881ee5814f18087f41051c1123 100644 GIT binary patch delta 584 zcmcbR^dotKIA$iIBsc`XDw&v=7)k^hz-%DF{%x`bQw|gNjLFlOVwkuS zCVydyWqdX{h&hz;-Q*R_=JgQs8JS@k7`RS=nE(Gn1bjn$^79MaDj0&(DuPRjN|Q@U zi&6vfGxJJPi-0Pix)F}#=0epSTmsgeoROH9mztwclCMw<6j5-CP{;wA%fJ9}G{i0- z$r3U79kVr~%498;{fsG-U$GR*f3mGnU|^WzrNekBoq>Tbk%7&@=YIWvze2y_LL0_a;|xDaiTUuV6qn z%<-4Xv;#E^%oiLk{AO4%c?FjR3rNZ22V9$&e*T?2g}arpe6kczIwSw&CZ2_ixsz3R zO&QH6NAX%Peg89g3U4CgtjXVa{TaO`yYWqA+_Cuv-)~07J)5rxm@+epY~~WNWn}c& k>>;{Gn#->cm=xXsgGXR8kD;~TIbb;P0I?S&*qA>7019fP=>Px# delta 414 zcmXw#T_{6w7{=fC|KE0IAIBkIQL`i;MX_2)(~N`|DN!h+khFa6LJK#pNRAg*6gOIO z<&-2LxpJW;Nm46`$c5x$&Mw#{@5S>xJ^gOpC)^+GTTV}?0N}3xrV80=Vf;jP=A~&L zAaB`fZBLFdMUum692)t^E>itDp5zfnp3Q!VeMGp=j~1G=21 z(Cm+U%st@&VTV+01CCIwt_vmF(rsc1RS7dJ|C3Gxd-jqv=boy60Za2Ygg&4beh#YD z!s%EW^>IqpccDx1>Zh4hqvl?W^0NXk1fACrcq5I`D#ftel`}u^pfebOBXSwbX~wwE z)GV@@=5dV2bAa3<1Br2==bRVBJGt{CS3k4!asij)=+!L From 16373327922610541764aa1da92d444f16ca7105 Mon Sep 17 00:00:00 2001 From: Daniel Lakey Date: Tue, 18 Jul 2023 10:52:39 +0200 Subject: [PATCH 12/23] Changes to be committed: deleted: cia.i deleted: custom.i modified: ptplayer.asm modified: ptplayer.obj --- cia.i | 25 ----- custom.i | 258 ------------------------------------------- ptplayer.asm | 304 +++++++++++++++++++++++++++++++++++++++++---------- ptplayer.obj | Bin 12792 -> 13016 bytes 4 files changed, 245 insertions(+), 342 deletions(-) delete mode 100644 cia.i delete mode 100644 custom.i diff --git a/cia.i b/cia.i deleted file mode 100644 index 2fa9295..0000000 --- a/cia.i +++ /dev/null @@ -1,25 +0,0 @@ -* -* CIA hardware registers -* -* Written by Frank Wille in 2013. -* - -CIAA equ $bfe001 -CIAB equ $bfd000 - -; registers -CIAPRA equ $000 -CIAPRB equ $100 -CIADDRA equ $200 -CIADDRB equ $300 -CIATALO equ $400 -CIATAHI equ $500 -CIATBLO equ $600 -CIATBHI equ $700 -CIATODLO equ $800 -CIATODMID equ $900 -CIATODHI equ $a00 -CIASDR equ $c00 -CIAICR equ $d00 -CIACRA equ $e00 -CIACRB equ $f00 diff --git a/custom.i b/custom.i deleted file mode 100644 index 0cec4ee..0000000 --- a/custom.i +++ /dev/null @@ -1,258 +0,0 @@ -* -* Custom chips hardware registers -* -* Written by Frank Wille in 2013, 2016. -* - -CUSTOM equ $dff000 - -BLTDDAT equ $000 -DMACONR equ $002 -VPOSR equ $004 -VHPOSR equ $006 -DSKDATR equ $008 -JOY0DAT equ $00a -JOY1DAT equ $00c -CLXDAT equ $00e -ADKCONR equ $010 -POT0DAT equ $012 -POT1DAT equ $014 -POTGOR equ $016 -SERDATR equ $018 -DSKBYTR equ $01a -INTENAR equ $01c -INTREQR equ $01e -DSKPT equ $020 -DSKPTH equ $020 -DSKPTL equ $022 -DSKLEN equ $024 -DSKDAT equ $026 -REFPTR equ $028 -VPOSW equ $02a -VHPOSW equ $02c -COPCON equ $02e -SERDAT equ $030 -SERPER equ $032 -POTGO equ $034 -JOYTEST equ $036 -STREQU equ $038 -STRVBL equ $03a -STRHOR equ $03c -STRLONG equ $03e -BLTCON0 equ $040 -BLTCON1 equ $042 -BLTAFWM equ $044 -BLTALWM equ $046 -BLTCPT equ $048 -BLTCPTH equ $048 -BLTCPTL equ $04a -BLTBPT equ $04c -BLTBPTH equ $04c -BLTBPTL equ $04e -BLTAPT equ $050 -BLTAPTH equ $050 -BLTAPTL equ $052 -BLTDPT equ $054 -BLTDPTH equ $054 -BLTDPTL equ $056 -BLTSIZE equ $058 -BLTCMOD equ $060 -BLTBMOD equ $062 -BLTAMOD equ $064 -BLTDMOD equ $066 -BLTCDAT equ $070 -BLTBDAT equ $072 -BLTADAT equ $074 -DSKSYNC equ $07e -COP1LC equ $080 -COP1LCH equ $080 -COP1LCL equ $082 -COP2LC equ $084 -COP2LCH equ $084 -COP2LCL equ $086 -COPJMP1 equ $088 -COPJMP2 equ $08a -COPINS equ $08c -DIWSTRT equ $08e -DIWSTOP equ $090 -DDFSTRT equ $092 -DDFSTOP equ $094 -DMACON equ $096 -CLXCON equ $098 -INTENA equ $09a -INTREQ equ $09c -ADKCON equ $09e -AUD0LC equ $0a0 -AUD0LCH equ $0a0 -AUD0LCL equ $0a2 -AUD0LEN equ $0a4 -AUD0PER equ $0a6 -AUD0VOL equ $0a8 -AUD0DAT equ $0aa -AUD1LC equ $0b0 -AUD1LCH equ $0b0 -AUD1LCL equ $0b2 -AUD1LEN equ $0b4 -AUD1PER equ $0b6 -AUD1VOL equ $0b8 -AUD1DAT equ $0ba -AUD2LC equ $0c0 -AUD2LCH equ $0c0 -AUD2LCL equ $0c2 -AUD2LEN equ $0c4 -AUD2PER equ $0c6 -AUD2VOL equ $0c8 -AUD2DAT equ $0ca -AUD3LC equ $0d0 -AUD3LCH equ $0d0 -AUD3LCL equ $0d2 -AUD3LEN equ $0d4 -AUD3PER equ $0d6 -AUD3VOL equ $0d8 -AUD3DAT equ $0da -BPL1PT equ $0e0 -BPL1PTH equ $0e0 -BPL1PTL equ $0e2 -BPL2PT equ $0e4 -BPL2PTH equ $0e4 -BPL2PTL equ $0e6 -BPL3PT equ $0e8 -BPL3PTH equ $0e8 -BPL3PTL equ $0ea -BPL4PT equ $0ec -BPL4PTH equ $0ec -BPL4PTL equ $0ee -BPL5PT equ $0f0 -BPL5PTH equ $0f0 -BPL5PTL equ $0f2 -BPL6PT equ $0f4 -BPL6PTH equ $0f4 -BPL6PTL equ $0f6 -BPLCON0 equ $100 -BPLCON1 equ $102 -BPLCON2 equ $104 -BPLCON3 equ $106 -BPL1MOD equ $108 -BPL2MOD equ $10a -BPLCON4 equ $10c -BPL1DAT equ $110 -BPL2DAT equ $112 -BPL3DAT equ $114 -BPL4DAT equ $116 -BPL5DAT equ $118 -BPL6DAT equ $11a -SPR0PT equ $120 -SPR0PTH equ $120 -SPR0PTL equ $122 -SPR1PT equ $124 -SPR1PTH equ $124 -SPR1PTL equ $126 -SPR2PT equ $128 -SPR2PTH equ $128 -SPR2PTL equ $12a -SPR3PT equ $12c -SPR3PTH equ $12c -SPR3PTL equ $12e -SPR4PT equ $130 -SPR4PTH equ $130 -SPR4PTL equ $132 -SPR5PT equ $134 -SPR5PTH equ $134 -SPR5PTL equ $136 -SPR6PT equ $138 -SPR6PTH equ $138 -SPR6PTL equ $13a -SPR7PT equ $13c -SPR7PTH equ $13c -SPR7PTL equ $13e -SPR0POS equ $140 -SPR0CTL equ $142 -SPR0DATA equ $144 -SPR0DATB equ $146 -SPR1POS equ $148 -SPR1CTL equ $14a -SPR1DATA equ $14c -SPR1DATB equ $14e -SPR2POS equ $150 -SPR2CTL equ $152 -SPR2DATA equ $154 -SPR2DATB equ $156 -SPR3POS equ $158 -SPR3CTL equ $15a -SPR3DATA equ $15c -SPR3DATB equ $15e -SPR4POS equ $160 -SPR4CTL equ $162 -SPR4DATA equ $164 -SPR4DATB equ $166 -SPR5POS equ $168 -SPR5CTL equ $16a -SPR5DATA equ $16c -SPR5DATB equ $16e -SPR6POS equ $170 -SPR6CTL equ $172 -SPR6DATA equ $174 -SPR6DATB equ $176 -SPR7POS equ $178 -SPR7CTL equ $17a -SPR7DATA equ $17c -SPR7DATB equ $17e -COLOR00 equ $180 -COLOR01 equ $182 -COLOR02 equ $184 -COLOR03 equ $186 -COLOR04 equ $188 -COLOR05 equ $18a -COLOR06 equ $18c -COLOR07 equ $18e -COLOR08 equ $190 -COLOR09 equ $192 -COLOR10 equ $194 -COLOR11 equ $196 -COLOR12 equ $198 -COLOR13 equ $19a -COLOR14 equ $19c -COLOR15 equ $19e -COLOR16 equ $1a0 -COLOR17 equ $1a2 -COLOR18 equ $1a4 -COLOR19 equ $1a6 -COLOR20 equ $1a8 -COLOR21 equ $1aa -COLOR22 equ $1ac -COLOR23 equ $1ae -COLOR24 equ $1b0 -COLOR25 equ $1b2 -COLOR26 equ $1b4 -COLOR27 equ $1b6 -COLOR28 equ $1b8 -COLOR29 equ $1ba -COLOR30 equ $1bc -COLOR31 equ $1be -HTOTAL equ $1c0 -HSSTOP equ $1c2 -HBSTRT equ $1c4 -HBSTOP equ $1c6 -VTOTAL equ $1c8 -VSSTOP equ $1ca -VBSTRT equ $1cc -VBSTOP equ $1ce -SPRHSTRT equ $1d0 -SPRHSTOP equ $1d2 -BPLHSTRT equ $1d4 -BPLHSTOP equ $1d6 -HHPOSW equ $1d8 -HHPOSR equ $1da -BEAMCON0 equ $1dc -HSSTRT equ $1de -VSSTRT equ $1e0 -HCENTER equ $1e2 -DIWHIGH equ $1e4 -BPLHMOD equ $1e6 -SPRHPT equ $1e8 -SPRHPTH equ $1e8 -SPRHPTL equ $1ea -BPLHPT equ $1ec -BPLHPTH equ $1ec -BPLHPTL equ $1ee -FMODE equ $1fc diff --git a/ptplayer.asm b/ptplayer.asm index 4e51393..ab6af56 100644 --- a/ptplayer.asm +++ b/ptplayer.asm @@ -2,8 +2,9 @@ ;* ----- Protracker V2.3B Playroutine ----- * ;************************************************** ; -; Version 6.1 -; Written by Frank Wille in 2013, 2016, 2017, 2018, 2019, 2020, 2021. +; Version 6.3 +; Written by Frank Wille in 2013, 2016-2023. +; Converted to BlitzBasic library by E-Penguin based on work by idrouge ; ; I, the copyright holder of this work, hereby release it into the ; public domain. This applies worldwide. @@ -13,7 +14,7 @@ ; Barfly-Asm, SNMA, AsmOne, AsmPro. ; ; The small data model can be enabled by defining the symbol SDATA. In -; this case all functions expect a4 to be initialised with the small +; this case all functions expect a4 to be initialized with the small ; data base register. Interrupt functions restore a4 from _LinkerDB. ; Small data may work with vasm and PhxAss only. ; @@ -21,8 +22,8 @@ ; (CUSTOM is the custom-chip register set base address $dff000.) ; ; _mt_install_cia(a6=CUSTOM, a0=VectorBase, d0=PALflag.b) -; Install a CIA-B interrupt for calling _mt_music or mt_sfxonly. -; The music module is replayed via _mt_music when _mt_Enable is non-zero. +; Install a CIA-B interrupt for calling mt_music or mt_sfxonly. +; The music module is replayed via mt_music when _mt_Enable is non-zero. ; Otherwise the interrupt handler calls mt_sfxonly to play sound ; effects only. VectorBase is 0 for 68000, otherwise set it to the CPU's ; VBR register. A non-zero PALflag selects PAL-clock for the CIA timers @@ -45,7 +46,7 @@ ; d0=SampleLength.w, d1=SamplePeriod.w, d2=SampleVolume.w) ; Request playing of an external sound effect on the most unused channel. ; This function is for compatibility with the old API only. -; You should call _mt_playfx instead. MINIMAL=0 only. +; You may prefer _mt_playfx instead. MINIMAL=0 only. ; ; channelStatus(d0) = _mt_playfx(a6=CUSTOM, a0=SfxStructurePointer) ; Request playing of a prioritized external sound effect, either on a @@ -81,11 +82,11 @@ ; _mt_stopfx(a6=CUSTOM, d0=Channel.b) ; Immediately stop a currently playing sound effect on a channel (0..3) ; and make it available for music, or other effects, again. This is the -; only way to stop a looped sound effect (_mt_loopfx), besides stopping +; only way to stop a looped sound effect (_mt_loopfx) besides stopping ; replay completely (_mt_end). MINIMAL=0 only. ; ; _mt_musicmask(a6=CUSTOM, d0=ChannelMask.b) -; Bits set in the mask define which specific channels are reserved +; Bits set in the ChannelMask define which specific channels are reserved ; for music only. Set bit 0 for channel 0, ..., bit 3 for channel 3. ; When calling _mt_soundfx or _mt_playfx with automatic channel selection ; (sfx_cha=-1) then these masked channels will never be picked. @@ -103,15 +104,25 @@ ; The new volume is persistent. Even when the song is restarted. ; MINIMAL=0 only. ; +; _mt_channelmask(a6=CUSTOM, d0=ChannelMask.b) +; Bits cleared in the ChannelMask define which specific channels are muted +; for music replay. Clear bit 0 for channel 0, ..., bit 3 for channel 3. +; Additionally a cleared bit prevents any access to the sample pointers +; of this channel. +; The mask defaults to all channels unmuted (bits set) and is reset to +; this state on _mt_init and _mt_end. MINIMAL=0 only. +; ; _mt_music(a6=CUSTOM) -; The replayer routine. Is called automatically after _mt_install_cia. +; The replayer routine. Can be called from your own VBlank interrupt +; handler when VBLANK_MUSIC is set. Is otherwise called automatically +; by Timer-A interrupts after _mt_install_cia. ; ; Byte Variables: ; ; _mt_Enable ; Set this byte to non-zero to play music, zero to pause playing. ; Note that you can still play sound effects, while music is stopped. -; It is set to 0 by _mt_install_cia. +; It is set to 0 by _mt_install_cia. VBLANK_MUSIC=0 only. ; ; _mt_E8Trigger ; This byte reflects the value of the last E8 command. @@ -124,9 +135,9 @@ ; MINIMAL=0 only. ; -; Optionally you can build a minimal version, which includes just -; the player. No sound effects insert, no master volume, no sample -; volume, etc. Define the symbol MINIMAL=1 for it. +; Optionally you can build a minimal version, which includes only +; the basic player. No sound effects insertion, no master volume, no +; sample volume, etc. Define the symbol MINIMAL=1 for it. ifnd MINIMAL MINIMAL equ 0 endc @@ -139,11 +150,20 @@ ENABLE_SAWRECT equ 0 endc ; Set this if you can guarantee that the word at $0 is cleared and if -; you want to use if for idle-looping of samples. +; you want to use it for idle-looping of samples. ifnd NULL_IS_CLEARED NULL_IS_CLEARED equ 0 endc +; This disables initialization of CIA Timer-A interrupts for music +; replay, which means you cannot set the tempo with the F-command +; anymore and you have to call _mt_music yourself out of your own +; VBlank interrupt handler. Also sound effects will no longer work, +; when no music is playing (_mt_Enable=0). + ifnd VBLANK_MUSIC +VBLANK_MUSIC equ 0 + endc + ; Delay in CIA-ticks, which guarantees that at least one Audio-DMA ; took place, even with the lowest periods. ; 496 should be the correct value. But there are some A1200 which @@ -151,8 +171,7 @@ NULL_IS_CLEARED equ 0 DMADELAY equ 576 ; was 496 include "blitz.i" - include "custom.i" - include "cia.i" + audiolib equ 116 banklib equ 76 @@ -248,12 +267,44 @@ _blitz_mt_lib_finit: bra _mt_remove_cia ;-------------------- +; Custom chip registers +CUSTOM equ $dff000 +INTREQR equ $01e +INTENAR equ $01c +DMACON equ $096 +INTENA equ $09a +INTREQ equ $09c +AUD0LC equ $0a0 +AUD0LEN equ $0a4 +AUD0VOL equ $0a8 +AUD1LC equ $0b0 +AUD1LEN equ $0b4 +AUD1VOL equ $0b8 +AUD2LC equ $0c0 +AUD2LEN equ $0c4 +AUD2VOL equ $0c8 +AUD3LC equ $0d0 +AUD3LEN equ $0d4 +AUD3VOL equ $0d8 + ; Audio channel registers AUDLC equ 0 AUDLEN equ 4 AUDPER equ 6 AUDVOL equ 8 +; CIA registers +CIAA equ $bfe001 +CIAB equ $bfd000 +CIAPRA equ $000 +CIATALO equ $400 +CIATAHI equ $500 +CIATBLO equ $600 +CIATBHI equ $700 +CIAICR equ $d00 +CIACRA equ $e00 +CIACRB equ $f00 + ; Sound effects structure, passed into _mt_playfx rsreset @@ -312,7 +363,7 @@ n_retrigcount rs.b 1 ifeq MINIMAL n_freecnt rs.b 1 n_musiconly rs.b 1 -n_reserved2 rs.b 1 +n_enable rs.b 1 else n_reserved2 rs.b 3 endc @@ -325,6 +376,8 @@ n_sizeof rs.b 0 code endc +; VBR Hack by phx +; https://eab.abime.net/showpost.php?p=1516508&postcount=7 ;----- Get VBR 68010+ --- mc68010 getvbr: @@ -336,12 +389,13 @@ getvbr: ;--------------------------------------------------------------------------- xdef _mt_install_cia _mt_install_cia: -; Install a CIA-B interrupt for calling _mt_music. +; Install a CIA-B interrupt for calling mt_music. ; a6 = CUSTOM ; a0 = VectorBase ; d0 = PALflag.b (0 is NTSC) ;----- Save registers for Blitz 2 --- +; Part of the VBR Hack by phx movem.l a3-a6,-(sp) sub.l a0,a0 move.l 4.w,a6 @@ -389,6 +443,7 @@ _mt_install_cia: move.b CIATBLO(a0),(a1)+ move.b CIATBHI(a0),(a1) + ifeq VBLANK_MUSIC ; determine if 02 clock for timers is based on PAL or NTSC tst.b d0 bne .1 @@ -403,6 +458,7 @@ _mt_install_cia: lsr.w #8,d0 move.b d0,CIATAHI(a0) move.b #$11,CIACRA(a0) ; load timer, start continuous + endc ; !VBLANK_MUSIC ; load TimerB with DMADELAY ticks for setting DMA and repeat move.b #DMADELAY&255,CIATBLO(a0) @@ -411,7 +467,11 @@ _mt_install_cia: ; Ack. pending interrupts, TimerA and TimerB interrupt enable tst.b CIAICR(a0) move.w #$2000,INTREQ(a6) + ifne VBLANK_MUSIC + move.b #$82,CIAICR(a0) ; TimerB only for VBLANK_MUSIC + else move.b #$83,CIAICR(a0) + endc ; enable level 6 interrupts move.w #$a000,INTENA(a6) @@ -469,13 +529,19 @@ _mt_remove_cia: ;--------------------------------------------------------------------------- mt_TimerAInt: -; TimerA interrupt calls _mt_music at a selectable tempo (Fxx command), +; TimerA interrupt calls mt_music at a selectable tempo (Fxx command), ; which defaults to 50 times per second. ; check for TB interrupt and clear CIAB interrupt flags btst #1,CIAB+CIAICR bne mt_TimerBInt + ifne VBLANK_MUSIC + move.w #$2000,CUSTOM+INTREQ ; ack interrupt and leave + nop + rte + + else ; Now it should be a TA interrupt. ; Other level 6 interrupt sources have to be handled elsewhere. movem.l d0-d7/a0-a6,-(sp) @@ -497,7 +563,7 @@ mt_TimerAInt: beq .1 endc - bsr _mt_music ; music with sfx inserted + bsr mt_music ; music with sfx inserted .1: movem.l (sp)+,d0-d7/a0-a6 nop rte @@ -508,6 +574,7 @@ mt_TimerAInt: nop rte endc + endc ; !VBLANK_MUSIC ;--------------------------------------------------------------------------- @@ -557,6 +624,8 @@ mt_TimerBsetrep: else lea mt_data(pc),a4 endc + + ifne MINIMAL move.l mt_chan1+n_loopstart(a4),AUD0LC-INTREQ(a0) move.w mt_chan1+n_replen(a4),AUD0LEN-INTREQ(a0) move.l mt_chan2+n_loopstart(a4),AUD1LC-INTREQ(a0) @@ -566,6 +635,26 @@ mt_TimerBsetrep: move.l mt_chan4+n_loopstart(a4),AUD3LC-INTREQ(a0) move.w mt_chan4+n_replen(a4),AUD3LEN-INTREQ(a0) + else ; !MINIMAL + tst.b mt_chan1+n_enable(a4) + beq .1 + move.l mt_chan1+n_loopstart(a4),AUD0LC-INTREQ(a0) + move.w mt_chan1+n_replen(a4),AUD0LEN-INTREQ(a0) +.1: tst.b mt_chan2+n_enable(a4) + beq .2 + move.l mt_chan2+n_loopstart(a4),AUD1LC-INTREQ(a0) + move.w mt_chan2+n_replen(a4),AUD1LEN-INTREQ(a0) +.2: tst.b mt_chan3+n_enable(a4) + beq .3 + move.l mt_chan3+n_loopstart(a4),AUD2LC-INTREQ(a0) + move.w mt_chan3+n_replen(a4),AUD2LEN-INTREQ(a0) +.3: tst.b mt_chan4+n_enable(a4) + beq .4 + move.l mt_chan4+n_loopstart(a4),AUD3LC-INTREQ(a0) + move.w mt_chan4+n_replen(a4),AUD3LEN-INTREQ(a0) +.4: + endc + move.l (sp)+,a4 move.l (sp)+,a0 nop @@ -650,20 +739,27 @@ _mt_init_blitz_done: movem.l (sp)+,d2/a2 + ifeq VBLANK_MUSIC ; reset CIA timer A to default (125) move.l mt_timerval(a4),d0 divu #125,d0 move.b d0,CIAB+CIATALO lsr.w #8,d0 move.b d0,CIAB+CIATAHI + endc mt_reset: -; a4 must be initialised with base register +; a4 must be initialized with base register ; reset speed and counters move.b #6,mt_Speed(a4) clr.b mt_Counter(a4) clr.w mt_PatternPos(a4) + clr.b mt_PattDelTime(a4) + clr.b mt_PattDelTime2(a4) + clr.w mt_PBreakPos(a4) + clr.b mt_PBreakFlag(a4) + clr.b mt_PosJumpFlag(a4) ; disable the filter or.b #2,CIAA+CIAPRA @@ -680,7 +776,7 @@ mt_reset: move.b #2,mt_chan3+n_index(a4) move.b #3,mt_chan4+n_index(a4) - ; initialise channel DMA and interrupt bits + ; initialize channel DMA and interrupt bits move.w #$0001,mt_chan1+n_dmabit(a4) move.w #$0002,mt_chan2+n_dmabit(a4) move.w #$0004,mt_chan3+n_dmabit(a4) @@ -690,19 +786,6 @@ mt_reset: move.w #$0200,mt_chan3+n_intbit(a4) move.w #$0400,mt_chan4+n_intbit(a4) - ; make sure n_period doesn't start as 0 - move.w #320,d0 - move.w d0,mt_chan1+n_period(a4) - move.w d0,mt_chan2+n_period(a4) - move.w d0,mt_chan3+n_period(a4) - move.w d0,mt_chan4+n_period(a4) - - ; disable sound effects - clr.w mt_chan1+n_sfxlen(a4) - clr.w mt_chan2+n_sfxlen(a4) - clr.w mt_chan3+n_sfxlen(a4) - clr.w mt_chan4+n_sfxlen(a4) - clr.b mt_E8Trigger(a4) ifeq MINIMAL clr.b mt_SongEnd(a4) @@ -720,27 +803,36 @@ _mt_end: ; Stop playing current module. ; a6 = CUSTOM + move.w #$4000,INTENA(a6) ifd SDATA clr.b mt_Enable(a4) - clr.w mt_chan1+n_volume(a4) - clr.w mt_chan2+n_volume(a4) - clr.w mt_chan3+n_volume(a4) - clr.w mt_chan4+n_volume(a4) + lea mt_chan1(a4),a0 + bsr resetch + lea mt_chan2(a4),a0 + bsr resetch + lea mt_chan3(a4),a0 + bsr resetch + lea mt_chan4(a4),a0 + bsr resetch else - lea mt_data(pc),a0 - clr.b mt_Enable(a0) - clr.w mt_chan1+n_volume(a0) - clr.w mt_chan2+n_volume(a0) - clr.w mt_chan3+n_volume(a0) - clr.w mt_chan4+n_volume(a0) + lea mt_data(pc),a1 + clr.b mt_Enable(a1) + lea mt_chan1(a1),a0 + bsr resetch + lea mt_chan2(a1),a0 + bsr resetch + lea mt_chan3(a1),a0 + bsr resetch + lea mt_chan4(a1),a0 + bsr resetch endc - moveq #0,d0 move.w d0,AUD0VOL(a6) move.w d0,AUD1VOL(a6) move.w d0,AUD2VOL(a6) move.w d0,AUD3VOL(a6) move.w #$000f,DMACON(a6) + move.w #$c000,INTENA(a6) movem.l (sp)+,a3-a6 ; Restore registers before returning to Blitz rts @@ -769,6 +861,23 @@ _mt_MusicChannels_stub: move.b d0,(a0) endc rts + +resetch: +; a0 = channel status +; All registers are preserved! + + move.w #320,n_period(a0) ; make sure period is not illegal + clr.w n_volume(a0) + clr.w n_sfxlen(a0) + clr.w n_funk(a0) + clr.b n_sfxpri(a0) + clr.b n_looped(a0) + clr.b n_gliss(a0) + ifeq MINIMAL + clr.b n_musiconly(a0) + st n_enable(a0) + endc + rts _mt_E8Trigger_stub: ifd SDATA @@ -1267,31 +1376,82 @@ _mt_mastervol: lsl.w #6,d0 add.w d0,a0 + ; set new table and adapt all channel volumes immediately move.w #$4000,INTENA(a6) - - ; adapt all channel volumes immediately move.l a0,mt_MasterVolTab(a4) + bsr set_all_volumes + move.w #$c000,INTENA(a6) + + ifnd SDATA + move.l (sp)+,a4 + endc + + move.l (sp)+,a6 ; Restore A6 for Blitz + rts + + +;--------------------------------------------------------------------------- + xdef _mt_channelmask +_mt_channelmask: +; Define which music channels are muted. Doesn't affect sound effects. +; a6 = CUSTOM +; d0.b = channel-mask (bit 0 for channel 0, ..., bit 3 for channel 3) + + ifnd SDATA + move.l a4,-(sp) + lea mt_data(pc),a4 + endc + + move.w #$4000,INTENA(a6) + + lsl.b #5,d0 + scs mt_chan4+n_enable(a4) + add.b d0,d0 + scs mt_chan3+n_enable(a4) + add.b d0,d0 + scs mt_chan2+n_enable(a4) + add.b d0,d0 + scs mt_chan1+n_enable(a4) + move.l mt_MasterVolTab(a4),a0 + bsr set_all_volumes + + move.w #$c000,INTENA(a6) + + ifnd SDATA + move.l (sp)+,a4 + endc + rts + + +;--------------------------------------------------------------------------- +set_all_volumes: +; a0 = Master volume table + + tst.b mt_chan1+n_sfxpri(a4) + bne .1 move.w mt_chan1+n_volume(a4),d0 move.b (a0,d0.w),d0 + and.b mt_chan1+n_enable(a4),d0 move.w d0,AUD0VOL(a6) +.1: tst.b mt_chan2+n_sfxpri(a4) + bne .2 move.w mt_chan2+n_volume(a4),d0 move.b (a0,d0.w),d0 + and.b mt_chan2+n_enable(a4),d0 move.w d0,AUD1VOL(a6) +.2: tst.b mt_chan3+n_sfxpri(a4) + bne .3 move.w mt_chan3+n_volume(a4),d0 move.b (a0,d0.w),d0 + and.b mt_chan3+n_enable(a4),d0 move.w d0,AUD2VOL(a6) +.3: tst.b mt_chan4+n_sfxpri(a4) + bne .4 move.w mt_chan4+n_volume(a4),d0 move.b (a0,d0.w),d0 + and.b mt_chan4+n_enable(a4),d0 move.w d0,AUD3VOL(a6) - - move.w #$c000,INTENA(a6) - - ifnd SDATA - move.l (sp)+,a4 - endc - - move.l (sp)+,a6 ; Restore A6 for Blitz - rts +.4: rts ;--------------------------------------------------------------------------- @@ -1324,6 +1484,23 @@ _mt_samplevol: ;--------------------------------------------------------------------------- xdef _mt_music _mt_music: +; Used when called by external interrupt handler. +; Saves/restores all registers and sets up A4 when needed. +; a6 = CUSTOM + + ifd SDATA + movem.l d2-d7/a2-a3/a5,-(sp) + bsr mt_music + movem.l (sp)+,d2-d7/a2-a3/a5 + else ; !SDATA + movem.l d2-d7/a2-a5,-(sp) + lea mt_data(pc),a4 + bsr mt_music + movem.l (sp)+,d2-d7/a2-a5 + endc + rts + +mt_music: ; Called from interrupt. ; Play next position when Counter equals Speed. ; Effects are always handled. @@ -1794,6 +1971,7 @@ set_len_start: ifeq MINIMAL move.l mt_MasterVolTab(a4),a0 move.b (a0,d1.w),d1 + and.b n_enable(a2),d1 endc move.w d1,AUDVOL(a5) @@ -2045,7 +2223,7 @@ do_porta_up: move.w n_period(a2),d1 sub.w d0,d1 cmp.w #113,d1 - bhs .1 + bge .1 moveq #113,d1 .1: move.w d1,n_period(a2) move.w d1,AUDPER(a5) @@ -2262,6 +2440,7 @@ mt_tremolo: ifeq MINIMAL move.l mt_MasterVolTab(a4),a0 move.b (a0,d0.w),d0 + and.b n_enable(a2),d0 endc move.w d0,AUDVOL(a5) @@ -2300,6 +2479,7 @@ set_vol: ifeq MINIMAL move.l mt_MasterVolTab(a4),a0 move.b (a0,d0.w),d0 + and.b n_enable(a2),d0 endc move.w d0,AUDVOL(a5) rts @@ -2330,6 +2510,7 @@ mt_volchange: ifeq MINIMAL move.l mt_MasterVolTab(a4),a0 move.b (a0,d4.w),d4 + and.b n_enable(a2),d4 endc move.w d4,AUDVOL(a5) rts @@ -2360,12 +2541,16 @@ mt_setspeed: ; cmd F x y (xy<$20 new speed, xy>=$20 new tempo) ; d4 = xy + ifeq VBLANK_MUSIC cmp.b #$20,d4 bhs .1 + endc + move.b d4,mt_Speed(a4) beq _mt_end rts + ifeq VBLANK_MUSIC ; set tempo (CIA only) .1: and.w #$00ff,d4 move.l mt_timerval(a4),d0 @@ -2374,6 +2559,7 @@ mt_setspeed: lsr.w #8,d0 move.b d0,CIAB+CIATAHI rts + endc mt_e_cmds: @@ -3458,4 +3644,4 @@ _mt_SongEnd: endc ; SDATA/!SDATA - end \ No newline at end of file + end diff --git a/ptplayer.obj b/ptplayer.obj index a890ee826ca4a1881ee5814f18087f41051c1123..d78780f63619dbf548bc486e72dacb5c58013d90 100644 GIT binary patch delta 1259 zcmZvce@I(b6vxlK?|(Wylt!be1_6+*Ud$bKr0-&9>sYHqA`4^O6i<>^;8c z-1j-W^X~oL%S*j=XXtGCa~A-(`v9s7llsHEA|PKJ$C2DX4$3o=k#ETs^O7hPc(y{? z%a7%Xlu~$CjONA|1P?RPw@H%4=$mAoJ)%dcpkYV2ZYsFgEa#?0m28ujichYLHz4Bergk85>dVBM;e( z@|@mfCSRkLT>)|f^H->w&;k^`{Fyk@#x2&i!!wTXJJb)TZ;XBRhy&nad)j!{wTG-a z(Z&IQqnN#zyD?8=_G118b4L*D4wyw1FK zA$t+2BvWf$gg8L!8UY=u31x5@L4i7e>OgsX_n<4+riy8VH^}dE$A#0B)*fT2oo%$X zhV3uvAuH^IqJ85rXId*J;=j2Q@%%dWxXFq<&-{FhedazXB=1U1AYXqV^dw0`0(z3L zo{Lkt*rgBT-c$Yio)kjI6@CDX#SA*p0uaZYlGl#>)dC+JFBGO^L$L5sq3~&eugfMO zzhB_#JSPpZaZ*Shu07>;Ho~g&f%~k}?JS)DwQSqL*3R_q1zd*UU2ENvLMOqQep)BK zf5c2~iKuI4vNQzXs$egxPSDgR?7GTNQ+B2=o}o`JvBlyh`ecZuid%?@HJ0?#$4l&d z$zH3&V}b*I$o^z8L4Eu$)`bCioaxre6 z*YhKMkbe&hVBnK-1cPFMHLG`5f1K}2W7~b{nS9^BHvB+Ovaq^^+-CD?E5;4=d4ACZ zT`Vk4kOj6SR**%eD}8VNNSbLJ}joG#NO=jJNeY-u|= z&61@RFEwZW=ca9$W*Vf==u8k~_e2}i4?~7$jhbE(V3V5L6+Y3T2X{0v3R;^3ls}rF z4fi1or&zSCoQ$&Rvc33ISS~a0ae3yOWv=|7Nn2mFgv8bXf)om>;~kCsz0QP9)LmkK6^Qu{|AbFIVB{7$%}XKtuC5=R?c63BKqq9z&xyKo KFR;tj?tcKsB!;;F delta 1050 zcmZvbe@I(b6vw~!zGsrwXr3{NS=$w5HY8ba(H5=7bV(|hO8SuEU~HWx&c-2WNxOxK z1dU^KMfQjFh!oi$WAu+wqzw~_e~dMZQc`hUaIE;BGRD^ZDw35}vt`+h6NIsM`5Zo+ z%i-R8?tN?Lw%(qu+ZO?(p84onpQXIyW&6o3kMfrr>85P7Y42(sjH2T^D=Gh)Vl_ zYd}rZj~xjI{ViDa{~Db32mLjZFsO-f_zT-b<)bq$_COJ&T`YSJ2#}@f65oEGrmCWR z+d%oMPx;n)nyo&@x7ujAx}E(+vLVc$&r{TJl(}i%aKQ4sCyLU2b2lp7PT0gXYQ)vW zY1z>5qF#zgGg4YUBoE0kIRzVR@(<+*Tg3wDl$RRjdz|Qqg3pNBrpyH#9tOWG{7`1> z*^z!TN&3IAK& zQT(65TZEuC}pYN?Bsu#Xv*9y zT`w*#ereV**Jg0CS51h&=z(aztR`|Ju!QlqaW7+p=tnuHU(8!(4s!J(ec00X55|*B AO#lD@ From cffcbb680f4fefa4995155bd37706f07ef205cac Mon Sep 17 00:00:00 2001 From: Daniel Lakey Date: Tue, 18 Jul 2023 10:55:03 +0200 Subject: [PATCH 13/23] Changes to be committed: modified: README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cd3a3bc..7394261 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # blitz_ptplayer -Port of Frank Wille's Protracker player as a Blitz Basic library +Port of Frank Wille's Protracker player v6.3 as a Blitz Basic library Blitz Basic function/statement entry points added by idrougge VBR fix by phx -compiled with vasm 1.8l, with the following options: +compiled with vasm 1.9d, with the following options: vasmm68k_mot.exe -devpac -Fhunkexe -kick1hunks -nosym ptplayer.asm -o ptplayer.obj Built wit the following configuration flags: From 7c79e3169b702cd4134a1438e8fbbe18cab51647 Mon Sep 17 00:00:00 2001 From: Daniel Lakey Date: Wed, 19 Jul 2023 13:21:11 +0200 Subject: [PATCH 14/23] Changes to be committed: modified: README.md modified: ptplayer.asm --- README.md | 15 +++++++++++++++ ptplayer.asm | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7394261..29526a7 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ Port of Frank Wille's Protracker player v6.3 as a Blitz Basic library Blitz Basic function/statement entry points added by idrougge +Further development by E-Penguin VBR fix by phx @@ -15,3 +16,17 @@ MINIMAL equ 0 ENABLE_SAWRECT equ 0 NULL_IS_CLEARED equ 0 + +Command reference: +LoadBank 0,"mod.song",2 (loads module into bank 0 in chipmem (2)) +MTInstall PAL=True, NTSC=False (installs player in program) +MTInit Bank#, startpos (inserts module into player) +MTInit &module_addr, &instr_addr, startpos (inserts INCBIN module into player, set instr_addr to 0 for normal modules) +MTPlay On/Off (start/stop module playback) +MTEnd (Stop playing current module) +MTSoundFX Sound#, volume (0..64) +MTSoundFX &sample_addr.l, length.w, period.w, volume.w (0..64) +MTMasterVolume 0..64 (Master volume for all music channels) +MTMusicMask bitmask.b (Set bits 0-3 to reserve channels for music only.) +MTMusicChannels 0..4 (number of channels dedicated to music) +MTE8Trigger (Value of the last E8 command in case you want to trigger game events from a module) \ No newline at end of file diff --git a/ptplayer.asm b/ptplayer.asm index ab6af56..0d02552 100644 --- a/ptplayer.asm +++ b/ptplayer.asm @@ -4,7 +4,7 @@ ; ; Version 6.3 ; Written by Frank Wille in 2013, 2016-2023. -; Converted to BlitzBasic library by E-Penguin based on work by idrouge +; Converted to BlitzBasic library by E-Penguin based on work by idrougge ; ; I, the copyright holder of this work, hereby release it into the ; public domain. This applies worldwide. From d4b0600c1e57cb1b4bd2ab0559972e3fcb25a615 Mon Sep 17 00:00:00 2001 From: Daniel Lakey Date: Fri, 11 Aug 2023 19:44:53 +0200 Subject: [PATCH 15/23] This is broken but I wanted to save the changes somewhere. Changes to be committed: modified: README.md modified: ptplayer.asm deleted: ptplayer.obj --- README.md | 3 ++ ptplayer.asm | 82 +++++++++++++++++++++++++++++++++++++++++++++++++-- ptplayer.obj | Bin 13016 -> 0 bytes 3 files changed, 83 insertions(+), 2 deletions(-) delete mode 100644 ptplayer.obj diff --git a/README.md b/README.md index 29526a7..8b1d07a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # blitz_ptplayer Port of Frank Wille's Protracker player v6.3 as a Blitz Basic library +******** THIS VERSION HAS A BUG, DON'T USE IT YET**************** +Please take the older version, pre-2023 for now! + Blitz Basic function/statement entry points added by idrougge Further development by E-Penguin diff --git a/ptplayer.asm b/ptplayer.asm index 0d02552..a3e96d8 100644 --- a/ptplayer.asm +++ b/ptplayer.asm @@ -220,6 +220,12 @@ ptplayerlib equ 48 libs audiolib,$1080 subs _mt_soundfx_soundobject,0,0 name "MTSoundFX","Sound#, volume (0..64)| sample_addr.l, length.w, period.w, volume.w" + + afunction long + args long + libs + subs _mt_playfx_stub,0,0 + name "MTPlayFx","SfxStructurePointer, returns SfxChanStatus pointer" astatement args word @@ -256,6 +262,30 @@ ptplayerlib equ 48 libs subs _mt_stopfx_stub,0,0 name "MTStopFx","channel to stop FX loop" + + astatement + args word,byte + libs + subs _mt_samplevol_stub,0,0 + name "MTSampleVolume", "Redefine a sample's volume" + + astatement + args byte + libs + subs _mt_channelmask_stub,0,0 + name "MTChannelMask", "Clear bits 0-3 to mute channel for music" + + astatement + args byte + libs + subs _mt_SongEnd_stub,0,0 + name "MTSetSongEnd","Set value of the mt_SongEnd flag to 0xFF to end after it has finished" + + afunction byte + args + libs + subs _mt_IsEnabled_stub,0,0 + name "MTIsEnabled","Get status of playback" blitz_finit: nullsub _blitz_mt_lib_finit,0,0 ; Call deinit routine on exit @@ -878,15 +908,37 @@ resetch: st n_enable(a0) endc rts + +_mt_IsEnabled_stub: + ifd SDATA + move.b mt_Enable(a4),d0 + else + lea mt_data+mt_Enable(pc),a0 + move.b (a0),d0 + endc + rts _mt_E8Trigger_stub: ifd SDATA - move.b mt_MusicChannels(a4),d0 + move.b mt_E8Trigger(a4),d0 else - lea mt_data+mt_MusicChannels(pc),a0 + lea mt_data+mt_E8Trigger(pc),a0 move.b (a0),d0 endc rts + +_mt_SongEnd_stub: +; Set mt_SongEnd to 0xFF to stop after current song is played +; d0.w = new value + ifd SDATA + trap #0 + move.b d0,mt_SongEnd(a4) + else + lea mt_data+mt_SongEnd(pc),a0 + move.b d0,(a0) + endc + rts + ;------------------------------------- _mt_soundfx_soundobject: movem.l a3-a6,-(sp) @@ -932,6 +984,11 @@ _mt_soundfx: ;--------------------------------------------------------------------------- +_mt_playfx_stub: + movem.l a3-a6,-(sp) + lea CUSTOM,a6 + move.l d0,a0 ; sfx-structure pointer + xdef _mt_playfx _mt_playfx: ; Request playing of a prioritized external sound effect, either on a @@ -1217,6 +1274,9 @@ exit_playfx: movem.l (sp)+,d2-d7/a0-a3/a5 else movem.l (sp)+,d2-d7/a0-a5 + + movem.l (sp)+,a3-a6 ; Restore registers for Blitz + endc rts @@ -1391,6 +1451,12 @@ _mt_mastervol: ;--------------------------------------------------------------------------- +_mt_channelmask_stub: +;--- Blitz 2 call stub ----- + movem.l a3-a6,-(sp) + lea CUSTOM,a6 +;--------------------------- + xdef _mt_channelmask _mt_channelmask: ; Define which music channels are muted. Doesn't affect sound effects. @@ -1419,6 +1485,9 @@ _mt_channelmask: ifnd SDATA move.l (sp)+,a4 + + movem.l (sp)+,a3-a6 ; Restore registers for Blitz + endc rts @@ -1455,6 +1524,12 @@ set_all_volumes: ;--------------------------------------------------------------------------- +_mt_samplevol_stub: +;--- Blitz 2 call stub ----- + movem.l a3-a6,-(sp) + lea CUSTOM,a6 +;--------------------------- + xdef _mt_samplevol _mt_samplevol: ; Redefine a sample's volume. May also be done while the song is playing. @@ -1477,6 +1552,9 @@ _mt_samplevol: sub.w d1,d0 ; table index: sample number * 30 swap d1 move.b d1,12+3(a0,d0.w) ; set sample's volume + + movem.l (sp)+,a3-a6 ; Restore registers for Blitz + rts endc ; !MINIMAL diff --git a/ptplayer.obj b/ptplayer.obj deleted file mode 100644 index d78780f63619dbf548bc486e72dacb5c58013d90..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13016 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 Date: Fri, 11 Aug 2023 21:15:01 +0200 Subject: [PATCH 16/23] Fixed it. MTPlayFx and MTSoundFX now working correctly. Changes to be committed: modified: README.md modified: ptplayer.asm new file: ptplayer.obj --- README.md | 14 +++++++++++++- ptplayer.asm | 8 ++++---- ptplayer.obj | Bin 0 -> 13532 bytes 3 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 ptplayer.obj diff --git a/README.md b/README.md index 8b1d07a..0ba3157 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # blitz_ptplayer Port of Frank Wille's Protracker player v6.3 as a Blitz Basic library -******** THIS VERSION HAS A BUG, DON'T USE IT YET**************** Please take the older version, pre-2023 for now! Blitz Basic function/statement entry points added by idrougge @@ -22,14 +21,27 @@ NULL_IS_CLEARED equ 0 Command reference: LoadBank 0,"mod.song",2 (loads module into bank 0 in chipmem (2)) + MTInstall PAL=True, NTSC=False (installs player in program) + MTInit Bank#, startpos (inserts module into player) + MTInit &module_addr, &instr_addr, startpos (inserts INCBIN module into player, set instr_addr to 0 for normal modules) + MTPlay On/Off (start/stop module playback) + MTEnd (Stop playing current module) + MTSoundFX Sound#, volume (0..64) + MTSoundFX &sample_addr.l, length.w, period.w, volume.w (0..64) + +MTPlayFx &SfxStructure + MTMasterVolume 0..64 (Master volume for all music channels) + MTMusicMask bitmask.b (Set bits 0-3 to reserve channels for music only.) + MTMusicChannels 0..4 (number of channels dedicated to music) + MTE8Trigger (Value of the last E8 command in case you want to trigger game events from a module) \ No newline at end of file diff --git a/ptplayer.asm b/ptplayer.asm index a3e96d8..0f09263 100644 --- a/ptplayer.asm +++ b/ptplayer.asm @@ -977,7 +977,7 @@ _mt_soundfx: movem.w d0-d2,sfx_len(sp) move.w #$ff01,sfx_cha(sp) ; any channel, priority=1 move.l sp,a0 - bsr _mt_playfx + bsr _mt_playfx_stub lea sfx_sizeof(sp),sp movem.l (sp)+,a3-a6 ; Restore registers for Blitz rts @@ -1273,11 +1273,11 @@ exit_playfx: ifd SDATA movem.l (sp)+,d2-d7/a0-a3/a5 else - movem.l (sp)+,d2-d7/a0-a5 + movem.l (sp)+,d2-d7/a0-a5 + endc movem.l (sp)+,a3-a6 ; Restore registers for Blitz - - endc + rts channel_offsets: diff --git a/ptplayer.obj b/ptplayer.obj new file mode 100644 index 0000000000000000000000000000000000000000..061f8ae68ef4b7442f852f2739c0ce5f42f09a9a GIT binary patch literal 13532 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 literal 0 HcmV?d00001 From 2dbaf12e31a838bbe1377cbb6e90343b7faf9e4b Mon Sep 17 00:00:00 2001 From: Daniel Lakey Date: Wed, 17 Jan 2024 17:30:38 +0100 Subject: [PATCH 17/23] Changes to be committed: new file: example/STW_Test.bb2 new file: example/dragon-roar.8svx new file: example/drozerix_-_neon_techno.mod new file: example/ho-ho-hooo.8svx new file: example/ow-sound.8svx new file: example/pterodactyl.8svx new file: example/ptplayer_include.bb2 --- example/STW_Test.bb2 | 157 +++++++++++++++++++++++++++++ example/dragon-roar.8svx | Bin 0 -> 19712 bytes example/drozerix_-_neon_techno.mod | Bin 0 -> 61072 bytes example/ho-ho-hooo.8svx | Bin 0 -> 20940 bytes example/ow-sound.8svx | Bin 0 -> 11120 bytes example/pterodactyl.8svx | Bin 0 -> 11306 bytes example/ptplayer_include.bb2 | 43 ++++++++ 7 files changed, 200 insertions(+) create mode 100644 example/STW_Test.bb2 create mode 100644 example/dragon-roar.8svx create mode 100644 example/drozerix_-_neon_techno.mod create mode 100644 example/ho-ho-hooo.8svx create mode 100644 example/ow-sound.8svx create mode 100644 example/pterodactyl.8svx create mode 100644 example/ptplayer_include.bb2 diff --git a/example/STW_Test.bb2 b/example/STW_Test.bb2 new file mode 100644 index 0000000..a3a8038 --- /dev/null +++ b/example/STW_Test.bb2 @@ -0,0 +1,157 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ptplayer_test +;; +;; Demonstrates all the features of ptplayer lib +;; E-Penguin, 2023 +;; +WBStartup + +;; Include important NewTypes and convenience function +;; for converting bb Sound object to sfx structure +XINCLUDE "ptplayer_include.bb2" + +; Load a mod into bank 0 +Gosub gs_LoadMod + +; Load some sounds into Sound 0..3 +Gosub gs_LoadSfx + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; MTInstall +; Install the CIA MOD player routine (1=PAL,0=NTSC) +MTInstall 1 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; MTInit +; Initialise the player with Bank 0 +MTInit 0,0 + +; Initialise some data structures for the sfx + +Gosub gs_InitSfxStructs + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; MTPlay +; Start the play loop +MTPlay On + +; Wait 10s +VWait 500 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; MTIsEnabled +; Confirm it is playing +If MTIsEnabled = True + NPrint "Music is playing" +EndIf + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Show the three ways of playing a sound effect +; +; MTPlayFx +For i=0 To 5 +*status.SfxChanStatus = MTPlayFx(&sfx0) + Print "Playing effect on channel " + NPrint *status\n_index + VWait 50 +Next i + +; MTSoundFX (with sound object) +For i=0 To 5 + MTSoundFX 1,64 + NPrint "Playing effect with sound object 1" + VWait 50 +Next i + +; MTSoundFX (with sample pointer) +*_snd.sound = Addr Sound(2) ; Get the pointer to the sound +For i=0 To 5 + MTSoundFX *_snd\_data, *_snd\_length, *_snd\_period, 64 + NPrint "Playing effect with sound object 2" + VWait 50 +Next i + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; MTMasterVolume +; Turn down and back up +NPrint "Fade Out" +Gosub gs_FadeOut : VWait 50 +NPrint "Fade In" +Gosub gs_FadeIn : VWait 50 + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; MTMusicMask +; Reserve channels for music +MTMusicMask 5 ; Reserve channel 0 and 3 +VWait 50 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; MTMusicChannels +; Get reserved channels for music +music_chans.b = MTMusicChannels +Print "Channels reserved for music: " +NPrint music_chans +VWait 50 + + +MTEnd ; Stop playing the current mod +MTRemove ; Remove the CIA MOD player routine + + +End + +.gs_LoadMod +; Using public domain mod from +; https://modarchive.org/index.php?request=view_by_moduleid&query=178172 +LoadBank 0,"drozerix_-_neon_techno.mod",2 +Return + +.gs_LoadSfx +; SFX Credits: +; Dinosaur roar : Groadr @ freesound.org +; Pterodactyl : dinodilopho @ freesound.org +; Ho Ho Ho : sovy @ freesound.org +; Ow : Topschool @ freesound.org +LoadSound 0,"ow-sound.8svx" +LoadSound 1,"dragon-roar.8svx" +LoadSound 2,"pterodactyl.8svx" +LoadSound 3,"ho-ho-hooo.8svx" +Return + +.gs_InitSfxStructs +; Initialize a sfx pointers for ptplayer +; {*pointer.sfx, sound_index, volume, channel (-1=best), priority (>0) } +DEFTYPE .sfx sfx0 +DEFTYPE .sfx sfx1 +DEFTYPE .sfx sfx2 +DEFTYPE .sfx sfx3 + +SFXInit{&sfx0,0,64,-1,1} +SFXInit{&sfx1,1,64,-1,1} +SFXInit{&sfx2,2,64,2,1} +SFXInit{&sfx3,3,64,3,1} +Return + +.gs_FadeOut +MTMasterVolume 32 : VWait 50 +MTMasterVolume 16 : VWait 50 +MTMasterVolume 8 : VWait 50 +MTMasterVolume 4 : VWait 50 +MTMasterVolume 2 : VWait 50 +MTMasterVolume 0 : VWait 50 +Return + +.gs_FadeIn +MTMasterVolume 2 : VWait 50 +MTMasterVolume 4 : VWait 50 +MTMasterVolume 8 : VWait 50 +MTMasterVolume 16 : VWait 50 +MTMasterVolume 32 : VWait 50 +MTMasterVolume 64 : VWait 50 +Return + + + + + diff --git a/example/dragon-roar.8svx b/example/dragon-roar.8svx new file mode 100644 index 0000000000000000000000000000000000000000..4c62155a0b5827bcea7c007dd385cabd1fbb8065 GIT binary patch literal 19712 zcma*P2bdhiwKm*6J+UX}yfd5gu2#}2XOvMS=VUMj1Gd4~7zYV#494byuP?SSQ3R1e z0Z`6ywX4lJP0l%X&rHwscY0U&-tYd;^FLqj&P;bz^{IU7)T#5H_1kaQiXdCaMK|7Z z+btW{-hd#83ZCDB-a)?1rUY#ZRB1@b z6d&H6X>BM6Q&1)=xScK6_YKZG!HYBDGqs?LOm(10vkg64tZU!1DVgk4tM#PY7`)TwycnLh9DC|=5jfL%;k&JY?+G? zl+QDiP!>v4Ig-p~i9F;4WT7Q;@GcY$a-X@FasbsrJ~vwc7&?$K(;$UBK@dPGpJz&q zAW)A2#bgJC$q`wCgp5h%7m`D6GwnDt%z#v$glx&#BtUqk#hE6AJeYJ$eR7Z`)C#hq zXoStdSO|~@if};@nQRM?E6QSXxf~YFv^3NQLlLN6CY#N{6U1=YEGQRb4+$}hjUh~* zG8xeO1W%0ozCWIHZGM*#Sp|s znI>d&Ic$u><6;;JlprGvGU9OfLZMh9lZyBZl~N&JWMMo$4|2h1rqd9N!{zgZ60v~C zX2DniN<|nkEH<0N6$phwTmb(-nZv~;QiVha<>B%KJRZEo;{bJBi1Q(YFBXY}Gh`u{ z4f#R}HuNe2jltsJB8f;K;6dvkD8|DDP!R!;WIld(1$d}!CpzJwIA@lYrfjD+nYgWbJ-y{yzMekY#FTx?XYcH|eEw>%kPM8U`0&#MM-Cr7ce>Y~Ao*Igrn0qC zWUgyysxOln^h#VR#U&Mu7L(dwUA%T)O~bMkYqsC@=+A!f#JzXkc>Ts5H*Q|P<<|Rt zbnngA?|k^NAN}x=M}GYH&z^qlu_u1M>;L`w%}*YG>X%PF@slT>diHm}dG^_#|M=0L zKK zxs%`Q`|^{|j-EPp==9}Fox{_fWRB*^L~OC9T&GbA_yQ>%o$kMUesJV!|ImoTH+gRF zr{5hq)!BJ*+-dK>boRvgE0-^H4!IJjN^h*5yJ-HL2CJoU;r#ivW*J{r3`|}1qY={>S`k=A500g6uP@zTZ9hxZ-W z|Ml0OA2@Nb%Ml8vQ^}k}rqq& zW5c|KEe);p4fU?WInvY9-8(rvG8N(QRT_gz zfw*krcE8u>xct?L?vBy(d-ohUb$0Odp>Gdfar-A66Al)VOXYdirj`ZG^^0zJeAn-G zJ^PzoyPmyg``qS5wI+2b==a(ukM8;Q%sEk4=s_BEf>uRI~Mlg$tI}tBm#44Rtl*G*(LGMSPT- zEAh%2Ygg`ASE(thzi#V_O0ksX3X*9`B%mnY)l(gAyDL0$>f%^OukFgW-<>-2)#u+G z`s)0}{U5yZ>L1@beEgk1?B2cmrPp8o;Hxh`_}42h{$clXe|R23c0UJyfBvr*UfHwv z-M2sg*?;h_|1pU@A>qfZ|?u(zdm^9?{9rKIOW93 zTbEx~YgoKUMHNB;IyByW@w31Feb3u}`C|W4hwy|DYmmp_g#x%sF6yC;70 z^Iu;d|NL~%XCHiZ`qYKNuiw7n9E{biT($P$-~I2T|G&?VZrFLtnyS^e&R@8`U1^$I ztukc_i@6Y1J+FPy!WGL`ZhiEbU;j@^zukGmFMjvnW4rELP*c;mWc{k^ zw%>l|Ef3v!+kIc6W|I&yNf_rS3;C(n1=9HX5V4;?u5zHTeV*2&k1vfzX#lL^~-G6)^G!y?Hzu*1ZTYuR7!XJ0P`maCi z-gEk^6RzBN-^mZIjP@Pwhz1xsyTQU?tBew*(yS(?u8gG7 zR7S*3JNl<=J)<6PAnoY(7HP~NBgVRix^3>B58wUv^RJI}9z8tS@eih8yI=YE;HPiC z@zTrx+Vju%4jj60zPIzpq0W(0Cyspcg-O>5Y5$Lb~c>U$K-#Ruj6dm;`+u9Xa!0BSIT({}|4K*h2GHWIdJdgA)iK%cb)7wS_=~-7eSBo!yL);td-ffcpIMna6kDc3Z1L&LWe^0!M6^Gq-vB4L9C?;~h=9vN}VSj*WRU ziL1`|rBmmwe)_Ky10h{=>#~-5D<|!{df@Y;2fjOZe$4NcRq#$h#aWx;~28=tuQ-s_jIn_rcj7#K{Y3+%wan7T&9Cv4+>xlY^EP_zEG z|C7o5kNe;Lddu>5Ro)iAe5mtqZ(sMo$?hwkef9Ng%>4TASq^jU_aDDI8#WXC-0oMH zW`6zP{wo&_?Z2FmEZnel!-gg@G~NIH%kR8)Z2|b(`=9N*G|0R+b3Omkb1!~y;Os=H ztmXP!@4M&DUoz|S)Bm$DFcJUzdg{iTA9?zzXC7*;uW4D=V$~G0Jb`C?Xq;8PcvGuL zTD|P{tqt>QD%xtbxr}c(NF%{vH>cuxdc>9XwZF)naSzgSFIYN#%p3251hX?Xfg`u^E zQ?+jUtvgn&Z&|jnu|_IjWk5hNaVwjbE?ZJnyQ+0{`=Sla1~ZbVzW?qYK6dNeg%92K z$ZgwiY;G0M7guwv8Ai`OCvNazI=_f0-O2^v%-9Y!fByQ*fBna|AHKBb@CA@N@-A=2&uiJfZP~URH*aY*88sSXVd7oj z$nTx^2BI9H*1E7!BNNC{L2~rML@JRhls^y4S*xcipt@4IRF>Mc9guUfuf;j$a< zd-$#mOVrFJ7U7C8PL`)ImI*|nqJJ1B)<|CQqhkAD8? zXK%d<%h-DdKRt5!%Wp0p8ybb#@zD`ah>MI|zSwzkU~G_#TpgJ>*_(1Dik^UbU@YT2 zf92daAANi3e6ewE-F0i)+D)rA*0j_JifNZ?@arCs7;T-$PMtV>>D1oilfI(HQqfXf z+cIy{j_X#=t>&jf<3pE^|Mk`P_k6bR%yGPHmdHZ3&a>MP;skWBaQtqjv|J>IX#3TwimndT5ibi9FuBg*Txz)UI zNt#AbZ#SE#W> zG9OQp3BIO!`OclImakpA<(6A_Zd_Vtpc9UviGjYpsVnCPI!0}Mmo9z%w~ya=5t!*8 zU-{dfPd>dI4W*=0M-RO6)u9g$fAh|sb4QMxJNV_ESDu>{t^c(5P**I9h;WmR6Hhwq z>3E*Y$?$Z7^5*vStJf@PG`3!U%Mb6r2V~1_Yt2?+?Ccra>EqvAibTccH9wq@grB`{ zVTHsXOUkV(leN-PS!S3!XWcF9Hb3?KK-l%z#@1#$FF;)0SVSsRswoLiq&HO?1Zd^_ zIjgtaedp7=Zr-tZ?R|^@@T(_(!f>PCJ+teFH$J?ycIy&rv%p^}1`8OG;3`n75!FgC zHd2fYk4M~rTzuRf@GGmOnwlED*<8MKZdr9LmLj9MK=vINkhDM#KSt>TNIMr1ZE%REXa&|FJFsxa z(4~(*zSuny&K3y~7MFkmk}V>HIBhX1tLv$$xUFMwd}^Tg=<%#Sn2YAg zQ2)h&lW!mHJ3N@2Dq+(0nzltPYj)PuwzDxoPOL0zS-5%C$}L+q->{>-+H5LQ&b_Wu zC+DI9u0n(Acv)6B%Vib$7PD54lGdf`?%P-)4qfaV_tVA3yRKiodh0zux?|N`W0_Kz znG8D}&Lm0~-6=FU-qqQc4hO}&Xp(l?vz;d|?EUl0FYJDK_iLXVO~@1y>#~*0s%3I= z$QkUvc=6)pN!OK2J;%>`qp1Yadc#AHKmO2l)>7PaVehwJ{plZndhQ?ZoQ-CDW8RWN z2RrAAn%deGODk(zniY{_F9FZ}{p&C7e&wU{#VWn2ZSjIlYb$E3T4{;y*>~{m_Ya*q zeq_+?9q$};ORRJ6x%cLcWhp;j$ih>gk~#MA!7tCbDXx6O^_%az@zxuzU(wvm&84SC zx*`Qs5lI!Jj)`O(slV~34?Xhiu17Yvo6E!!%$p5NJH=Jz`5JQ-Usu~8YQAOOqSg&R zxOLZ0Z(G-_V~OJd7FiVV`Q@9J&0jIUTtG?b0R9Lfrz`5L8-g?%eg8 zN1wQ1uB?9=)UD;~RxN3glw@Wh$`%WPQ=WW?ib$tpf3-5g7sa?PO>CbL!H&f2xDVskW(>2MZN=>z8(W8$% zxa;XV@7^(Y{pR*6slYQeJ(NR232uDA;TY~Yd*ax+69k@Q>GD{qkzhurpo1<>n`@ z#oe@6p2Y~F;OyN0{^72%Jcs?A^C`?u#`{BVRO)GC$x#H%=rn~OFYg=nei%!Z?HqE_$d&|N*H*NXBruj{v-e6_L z)%A5X)t2gps;YU5%iA|Jt=P77%Of{$yQ#LqEa6e&Qko^>%FBvj$KZe~5@s8vt()hz z-?DvvQ=MSw?BRWT-u&15AN=9<mgjJ~V=~p-;~N<*_$MxZ{{H(HFP|Q8jU&x-*WPelt+ARo_3g(Wyt(htfzyW$ z^bcMb3j3l0gvxOEy7txEmem+!q;2rbhkyV3#~**P=QYrs9qVy&%Ny5kSu|(a!rE#r zHFWrm=l}ZRi=XTtlJQmZlrpukvAVgy3|F6CXkd`pA;JmbLv4)f(1JR=@FaD?)F4%VYXb>)M#E%Upsfrrdo{^&ju!7 zt9)|c^d-l{u$QI5H0Aa4+S)ZZ7VG|^WAL5NFCFg-5SBR&+kgJV4<5Z|&ARqlYmtli zBS}xc({7Jv3-Rf&(yFiAaMw+b?0V+TO>?EO-p-T%`JW&B=X(cEe|I#K%u6vMjg>c6 zS81yxA-TWA3}IMjdY5}EdMm6iqV zO}9L?dGUrip;jxeFbMEW#OX=9<0(p4*LdAiPyOWHAMLtp{T!{XkexVq{?x^xsqx8F zMq@H9ty0u2xo-1{WlMDByiSMXlFNR{Id!OSWP)FamL#=xit2_qCCmuLrL4;~bm;6? zufF@)rK`hU!hKXdeK_mz(Bq2X~?R90uQ)@#_5KqJ*QOVg#CLZw_)#+fUT?pVEi znF1lg5jWcy7wN+pPN`JaR9&?JjP>6f*)Y+Iw#n>8Fb zMM|5!hCqyzMM^Ame_l@VCd-0ENd~>MlYWI}A z6!#`4(h^)M%yXo4a%g(|n`37$cX#x;Xoa=85r$cLrGi6D4PNOTw`YoSOPRHC&cZD> z@7S{CrmdT|-wHc!s|x3`X%3Q&7j&Akh4YqfUb|!K_4nWZ*w3HY^{c0Td-vT>-?hAc zev49tCUA+UQmb6rVr<#8sQt`&BxI?BfGj?Mb&UURC<3|Xu+9`*!c)1gFGtyRx8v*a8F zHsQBT4xMuZU5HF-(kW{(Y@S}pWy^ixk~ip^m>#iBbqpty$qa?Dlv=Hct!)sR+N^Yf zR|tm(Ms2oeER;ybQaLbVz>or0C}MfYY&cF50k6&0H!yOvzgW_i8!8&w8t2y;wepfL z8Fvq!?3naM!;x$WPSO;jSS;bo$0ytqqkR{zUhegF4!An6j=1tNozbY4Dh(^TaT{o* z$1fkh+Sk|J+1uGM5s##~xDrd_aMCTvJBIqt54jJYyn4E$>+s>B{sCK>$f(Tv#)h_4 z6$q{-Qw6U(H0|JEq&y!@=VT(7cv^98&f>D=*Uw-4(9UhQuH0Fvt>9Ls?DS|)Pv?lE zdvGivO_*v;YnJa=Klk?g@7}!b-ezf8wm^qTXI@=I(Y!vNYf+c0R80a?x!fo*>DeZ| zR21jQNLDJwbBkJ;B(nv-!@wBDb7l~xJrF6c4B?um^PL9VN(~c46*o7(5KQVCO z)K#BnFcg}KvK2gqUSX=xDASYS>5+p6zkccOCy(zx_T}NDJ-&gy@ial#sf8L;S*6gc zGok*jvw!~QKXO~Wrl;U2pNV(m*|?8+3^XbKb zA%CbKtD3*$uAOVPt(voBiK4c((j*Xs;+^lF{QSMoK6qzek2?n^qb+UC^@1{mx>9c` zAw^Or%#rcFk;!wHdVGOmm7$_mvv|vj4U3!WbW%=%i}U#`gw9jBNQMkdCG$zPq`Iwb z-Nu{OtXbN=+PYvtP2(I(#nQ@hgH|brkG8qBwrTC0idJirJirpCax?-BO{Np6+|YR3 z+cn|o=sADlz&EGPk4+&+bk6#g1qxPS==e}H%@?pJv0yUErwS?F*!hueTMRcf)|=|9 zttJ_cp>!fj1;&%pUDLx}+p*rR{*jTe6ATXI8kG`Q)!=Z)cY|HSj9meZ7K(x*8Xu2) za!7%N2-#FIpGdeIp}vlxzHyhIL_sUa6A2X>u~?0XTf$&{s0u4}d$DyrI> zYRo!~L@Ly2L~1dwB$o23YUj>xt*lu#Z`p<|t11j~2`3dHvN6w8w12wjj**c9n{Vdx z&}iIUvY+bz>VpfXPYq2v0!faj7)}+l`D8p2@Qrs|8M-nY9GmFwh>y7pVt#{3E#)_< z3sNIuCyS0@r!eIsK;529kM)ELBNtE7zHXJvA4&?YgooI$Ji!RMS*x(x~J_JT*2PNM%bRby-=tzBGFF)V{Af z`g^bVWvZ$+9e3UeqdGl2a-q)~@KM}2!o{+YL^3rsI`ZAg$?<7dn1IcYUe>0NR*CZ* zu|ma{ibOPlr&v@pJs4+&3tTE-&u8pBR)pt6i*~V6Ei@1TmdE3dlM>eS)KLHM#KgeJ zuqQYLyB{%Ipwg={+=9E5caV-#exEx;Au-%iJ+HB{9?PI~hR%a(s2Cpan{e1EG?7gw z2#x@9)=-&nsOQ@QhX*>lykTr$$ju45e~b zVTg+t5Iqje9z;av)v?GVEl!hoDJqx8BjyS=mC8BBsCdk76U2SR%+LvwLzHa^MhNW$_YI!+^ zDb(fKvT{Xj+v4_ltE!E)wROvu)PmZ{T164$h-0E}#6F$CIf$@UU$=V6b&G4OD}`oN zu9##;!k7=vd&a$ep|~R-Ct_*6ns2gl^V#6UbkH&G8togmP4td|On7A^jH=Z#J|Y$d zv$4V9;|KSCeDrk(5{%PWfZFrc5gq%ak>(&GoBxtlqk^ zePNAJtLMXMBZn^mYcE=1)RmdU+Qwz`O;s&5^-JfgNM12P7jg-=6G@O3xlB`2r8U+s ztg30MYp&LzVs%MO1!%C|sHiDdTbs>Nfhv`g@no_xEuyZgQgTbB3=s`G2l-^6UNntS z@gfItc)3)OCy~bzk|HJZ3r!&+8l13^2^`P(;_)PhN=|SJV`$#(OA(PgsHZqNO{HFq z)UL9$ud6H5=r97Yr|E=5k|eSmR!oMad0--AD$C+lojD((Jf7~egK#=n66w@NjaZ3e z8a~~qW9hk=rm{gHG4a!}@sTU1FOOat^tgEfuBo!TZTXJPE0<^#C9lgT&KD)LlI2R1 ziHtww1*0_?!ZN8rw3^c{(3cwoC9|pAqE+h*a)DHv^MOf9F_#f$^J)%45pjY}XL4+w zs;auCqP#|@QWIXUZEzy!@fOGe=5@qli4q@Wvr(bZT()>&^PC!!G?m8EnSn9SWOOQz z7C1aU!XZmoQ7BhREDDi~jc4Pw6NmS`_s_q6dFZO$9-)1cbS4relF)nAB)_htRdNhFf+L|!8c(~zUUxXn5^+f9 zKxc2?ahD>ENNAEH7pq#gtf{SOs*vV{N>S9=z4zPm9~_wM>>Qp5=ZpM?xfQiisfn+W z(*is~mvJ&_TQEK{e){lKud|R>D9w6fz20C^&0TIWTjWxS&Vb}S@z{{9zt1s}E`@}2 zTCS)@Viz%tPLsK02E#cr84dPM z@-hWiLX}iPk%Y|w+r+Fl&q@j0I5#4cV=^Hv4BC^9$-dt1?x7y&QWi`cqe@;bF5+nvk4w!I8!vG9 z8W~3BxxUG)m``Pq#DqVdaFsZz0;Lfo@Tv+7$K%;z3Sp&kgpQIO0z_y5VA6GxZ6GKaE%4xZZe-R8zprHp^8c?P;Wjz;3Mth;{jjya3nuA zR%SL;*R0m7ms^AiF(=>(hOZv9_ZQNj0SVLTH08y zGgC#OuBK8W<0ncm8pHxo8JZ%dBO-p;0zDk{@^n&MCEy9N?%p$J`o~?#V7QQEDKMd+ zL}jLiZ5J4nXEvdFi2H8I4eU- zTrSGaWugPanXso6^~8O#be63WiEGP1H6T}ljxZGy(OKM})@U_4ouRhLSY0bb_!6C- zYmmER;VV}?Q%-+?Lxv4TUem()jY=u*9gK!YqUo@`*EM-z!0oo@OQBp*AY_}WRU(ZH z&Vy25Ei1?b*i;~gl0_kh&kCh|P7LS66v`w1A;pFf9YYHyaJ6904j- z0F!JHR-h)w{o~WVu}PPcLfM>HIL{Y?`LjQr!Oa?twaz-Hi6x*hoZ?1p(_?*oLymYB zZ*Hk=F0U}k)Z$#iGc`0c(LEVSa+DR7GRCCR6AgeND#Gh`iwzB#KRrL#&tlhAhIoP#IVK+4~JUTcqM&TvBQJ~aVYuo0}nbXiz zSypG(YRfcPis%~gfjHO~=egMWqFg7?%P9_vQc;1*vJk(T&aqQDUY0$;9EOSzs1!sFq9dTrT8GcvA`clzptno6Q#KEFwTqK1ZsP zsZ0j5UfE<3*IQKOT3mw5bTS2Bq2@}pMzg7^+FWJUn9Y1Pj-oh@Xv}hBWqqwmCNHsA z$pna-0Ji`$eF-arvZ*vF!qS|AGZc!1i!z>CAr==>sdRYS>2-|xGfuifF0ZP#)YKVa zjg={tV40qx^l02v#noGQh)Tsms8Y%mjQHb4UNMj183)FR@?^ZEpUaP@v6wFwjg3tZ zZbv9Z+ouDQllJMcaK6AzXX&Uf3@8wcq=K&4$e1HkfXy8%8>LZ!N~YIpwIV=3BqPZz zNyZaVzaNbFrEChLxoU}At230DcqN+51iWCVES8lU6*Xm2gO)E+Y7AyMzmzYbd}(>L zR4U4+J&7Vn6=W(EiNq+0OfKb#Sm`WilIU1C74tg+r2@q-iKHnMOR&?PoX_K#NT%ar zBuKde{7_C-67XY`Ad!;gmDOQf<}PxQ0;9gVT&&lYG%{K#D@Y7{rCDaKS6k}jB5_eD zB6xg1UlkYWNTDQ@A`^v7TE&Xdfe>Cu$dROol}PwfIYh*j>9kTqlR{e~5L!hUGEECv zQnR{Dt7xpVRLQ~iHIpFHjzBa(uu3e@0SJqcC{>6+-IIwzj*Ny0f3g_z^o`mE?3rvR z8FzY|W8EE7Q=<-V3NS2Mom`W$N5kHc{;AQ4$tjl?7IiUR%uG*516c}H<7^HXyATPI zOa@1Lu6A}@b@>A!71q4F(Vehf0Ja9=F#`ma+&*BVgzt6RCtHG~|l7 z{n?y9R!qm7fou{h=2)OAC1L^pq%Ro?I^DrkI-d3gyc6*h!38*j+MtvH%0<8?Q?Yb9 z!_KAseg4R_vu|+Jj&gWPQ+dNG*x1VCIlpbn*4Z^a)^CrFg~ZD0#>N^w&H~G~fIk4s zhs~7;$BQV!5 zOehlxc>*Ks_YAyLj`VthPM3Go;R2DeK;%^xp}0(u!OFRLq$os!v?viwxrba}34$~l z#nzQpVP##Tl4H@cSQ0cTP_bE(a&0`XLh~$WoFb8!Roz5&p*g}a+4E#(2;1#NnjpcGFf+SK| z04j-iVyT=@W{4755a0lkumBVY05%|yf^L|vlkim{xdP#G^LZj13WwvNV8T1?_Bd_s zbUuxW*lbg!0#_>dT#&3e0u_!`l>sD*&ypIDoLGnygc81@uCcPxSfRpU@sI*9!9}Gk zg~Y>j)Dwv&3T%m9r_oA<5-yj@W3egw^zekuHaRxcH{zHK1S5bT$`G6)g$wCI!WVG3 zVI7Od13rH+>ayAUhlh@xJa@UPXCzUIgo0r6KjfaY1svm3LBG!zAbd8@=)l0`t9?TO z|A-?D2a*I4%;m!AY}9Q}z^5Ns-m6f@~8LFBR&my1iqDwWEpQRyTKm4L-pO2s0;s=*Y(#eA^>I1f)H=hKjH zHVQkF5&*UUS`bS{V`LsHX1P>`Br%CbqNq}+%6TFwx2#NO)+(?Z7on9pbNT#*%}uop zt!)dJHJA(v4#uSlxWZ_VYf%DJMig5nRT=aeK!B;#73E?sJ2pK&WV1U`fk~fzVst8x zP|`|eiJcAmCX*R^oUq${Ljz;u{^>!FeQ-4BpYWE%rJ~8qp-Y8S3_v9jnoO}}YN^U% z)<`sXvOtAn8PN7d2w-beDqRGXYbKXYAuu0ND(w#h?Nh^pyouC{hNjjUYfFVj&&OFgE?*)Bkf2VhmFbOgfc?Ry5wMX(E)fMF9V+CA z)G*HrnS?8lh^A6-ewNC!iUL*~iF-Z6Q}Gmyv$^ani(67_`3gN7*3oz}BfxnqtdP!; zn1H9y2qa3W!ctz_Sl4V(SJYP3T54)RieP1MSW#(|P8N`yK&#SLSq*A|M2cWi4hzN> z;8LK6ps8|z4N9d$IYFT)1z?d%BoeDQS%jB~f23A^1s=8Fc&L^?+x zB}Bjj=7R<@MSX{)MN2!wP|ilv0PWG>6*aCBwm4NbL`7L7rv0-cRokIPg7E?`MPAc+V3 z;XooC%_IpTO9CHA<&u$j&~5j)?4gJUEZ}Uz}E$dqz`P?o85WqJ@?=^|TMrmt$ysTCqA4;C_9!po%!*))c$A-$7Z2qjwsR;T3|5D2@)Cr0IkIm zrzq5eA&|~c(bznubi$zgl z316<(fV_{Cpj)y9mUqfKJ>^Sg;bbXCrqao9Bvpce8i~b|$p`@loauBf70MCeL@5v2 zRY1oUv(W;|1_mJ!%LRE9`WIv8lL_zC=;+jh3&f6Oijmw30;Sp@Q>i#O2!wKtfK8{O z-f%XW1RQuKmGHWwnM5`ZOJyn*ie*w@Qk+Rf5)ofA5e5Pwzr!{)<#W420jL832Gb=J zEg~fv)^wTy1`~kU=CJ`jUdZN(MK0iM0ryz|Sw|q2%cWYCTr3m8QVRQQ76riXR2=wV zG7$mEDhN&Eii3=c356xD5H{2(XkWP~4<;zhrl?#g9D@m$6NxJu>)P8_ZdiZg_En2l zFR_|+D!y0_)~hlhs95>R$}&^Syt=w&%NDKJuygJ5mGi1#`49?az+G7d5Q(AyGzRsc zSST~;wN-Uh20(Y}lnOB}U}02|E}&=$WIfQB0Wg`bkSnzko&dn8Yyk^u9CY}iWIB&A zSbfkUVt~$OmB?%iAi-2F16PVj6FCBOBcN2YyL^#oERljEIB3N@8xZRpfJO?$B3#79 zxd@jrhuix ze?A>grjuC+$x;A2=gq*yv#Defc5lH5a9IR$5{f~K3gZi5Gpr2oodgNsdYHW=$a$$O z2)zZA25Vf9=TruR!D!MbbOxPDr<1643Nc@)Rh!HPgU(`v6;CeV3!qPn7*~LExTQP_ z;QSO&D$q1l;&6p>9J*LxP)n35jas9(R9LDjz?`_EqN2W{dQQ{a1$7N=4Gm48QP=7X zA~~q&bfz+eu3V+IYBjJJS6Q1H%Uk9)G|y|RHCxIhxDpr!1JCA)xKbG?A@pjHrBq^t zQm$30^a`mQ=Kz);A%WqrL9q#k);L=Ng#`FHmko${u>hn~7L@~KLaCTdrZU-RGzKeR zo=Aj4!6X?^7jj^imrTSnur9>|LB9u#ie0Xd&*^kcOpFbKsx2H0#^R9_3C<;`bT}Du zj81z&Kn%Oar)^`CHfJQ7PI!G@k3X3L2rM{`7yNGAZt97_P2oz2FDd=7$wNS30q z1dVW@lf+=MkHEn^%y>Xt!7yY2o*o##kOA5Q>*nJk;2|P_{j)fHu~MS|lTU?2q6AS0 zfb$smF9C+HQp)5wjg-J<1sE2OLxZP?A~?B zOe9q)WFRQRQVxbJ63|GASimblZbZR10+$Z}9JvCHOu5BuJe@;DA_+)m0D-0el+OoI z2=;Zz zP$J?3J5(x^5?m;fN&s*Td=YeBuqs0}aRCRIC>uN*fR6_bQp^>KEFKOFf^jav0v8Xk zZh@-?=zoxUz->Va`Wv*T(DIB^2@xC)s$dt$gAFVV^O`RPp;RhY=}l&{3AEoTF&{RB zu!vzOxQD>NF$L5fd?P&j0VU($?E!W}uq!4Z9)!zC40sQS zEMZ?5rkoE@?*#(vH%M?lkx7x6SPYQrzEIQ`Wf)liV0?hvib2vyAOvC&5KJWEi8z4f zQyJjB84y9ic|#`0IGdmd3JL>50fR0BR}tVv1bjDeQ5Lx006q(i%3RD=8Tw5k65~SV zw1^8{E@tWu%Q&!fVAL3N2aI)?5AX!i5;z_KuOl=}DQ3?Od>^8~VFmcmfG3z%Ak=|q zG2`im@lFD93?&3!mkFGrgXA{%oWMM^udO*J5+yv;(_^n}xU6Q2T>jNCXDwGcsC8aJPSb?;D*2pVIaT= z%Y%a&C5X*Lu1W=1~*&(NR!Wo8HCX9wB>T<_$e#bJst-{2L8af1V`GaClUAszymVCH`& zcINk5O2%i;wTo#>=7AXpOcygxjIST2g`v6O!w_bY%skA#2QM)f{J>aZdUe(t)4%U$ z$1TLoM9jo9w@fM9e;e@0ssY&ql$(g)ru=+5eej%*(R` zW(M}`tISaQckIl|F!27J@LCiTz?|+zICUoYV z8M@bsFhk%PiJ81+B4<-G&)3TNeH26Ce+M${aP4AJLy7)7VD^p)njv|O+V|CmiSqrM z-~Y~b)=crgPsLFDJ{s<42+f3iAO7F5v&p^>|KA0jDI4=9keHe3O!Aoqm<>Vx58s$Y AssI20 literal 0 HcmV?d00001 diff --git a/example/drozerix_-_neon_techno.mod b/example/drozerix_-_neon_techno.mod new file mode 100644 index 0000000000000000000000000000000000000000..991957c04cf8fcaad1b89fab61b630333c6b15db GIT binary patch literal 61072 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 literal 0 HcmV?d00001 diff --git a/example/ho-ho-hooo.8svx b/example/ho-ho-hooo.8svx new file mode 100644 index 0000000000000000000000000000000000000000..f197df66b5014491421d89fa4dacb99f20075794 GIT binary patch literal 20940 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 HoV5QBB)-!! literal 0 HcmV?d00001 diff --git a/example/ow-sound.8svx b/example/ow-sound.8svx new file mode 100644 index 0000000000000000000000000000000000000000..48560b8e1b8bbed053175a402be913a67f4fd9a2 GIT binary patch literal 11120 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 literal 0 HcmV?d00001 diff --git a/example/pterodactyl.8svx b/example/pterodactyl.8svx new file mode 100644 index 0000000000000000000000000000000000000000..af1a1bf43dd2dc7d17e76fb2ccbb0ccda622b084 GIT binary patch literal 11306 zcmXY%2Y4ITmA0p1&?|{TkYERUCnbs!sorJDwp`@MHL(&WapEMqiJeU$oVL48;$&l| z$aN#zl5ESiB+Kd&B~heAkraFH1PB5|FN5hb|7G?Ei3fw3J9E$d&UX$8%)8^j!w*1E zd0yke#|}NVYum#R1f@YxStoe?*JJZzFa&|$&HMKs06TJaeEY>CIp6rvk>~#X$n!a0 z|7p&_mwx>H=X18d@{Jds`~J6%*T#DU>FO>;n0Uczt)Oav0CkP9ruixSIzuU8%?Ij1e|U_IZUk(B$!Rb zXapoS!6Aek$3+Q(5m*utkh>TSfiqE&Nx`s$^MZ&fP$9`l;5Y#@G^#=+QdCGW5hLk1 zqQ)c%Hi$Xp@oE>(T~9E6T%421M7dF?;6+@b6OxQ!g#^v>8Y7`pVgxXfKqWYu;o~eM2?|1{Ltz;T;uQpz zO;9us%j7Dp#BdS;5($YE$--PLAt)3m#4{AHHz?$yK&O%%%CVA6;>aW~NJ_m9=R}ZE z5=9{#gXw6T6%j%sgV>ZH3X)ERpaLC_CV<~zoIpTM1W2LM$XGHS4KsvZD@P$Z8e>pG zt<^~JXoRL&O_~PcQ;A?gRH@WfJRYSIArUvGp^24H7)A((QJwOMqAw)CdV+w-6(7mT zu}49i=fDm5}LV0*5L<-bFSU ziIFTK#{>!C+5g)qp;9u&5jZUJ6i5w@gQSA0;aEZBM43tfkx5Pjixkn-Qic-@2LY9t z6p1Jao)b4Ns*+j7=nxNkXgDV**P`3KW+p z&;~d%o?uXwiU3QdSeE5L0|4y@0%ZuA4zAN6keEn78o3yc0I@JQ8BanAy%F>-f1a6pBE!M^gYTM=g!n?iqD@WSlOv&vgdVv=MsU_F%iOpgJ)p zC}N9z2HicY%(aOW6r#!-WaC|Ny8)4ym8BT2r{)*<+&o2sm&tLMzsKbkP+~trJNeNr@I?cwkyrtc`^^82DZw-2p>kT6l`J z=qOgBh%Lbuez>1BS`^%}57uR8XOOoCbn8lxm9fE;uCNeXctxc8lfe z$q=g2$u#m54Vo20GMG`%`(j2tr7|-?(pnsE_h(`eg;5;8C#%(mlS)0pnDSUpl4Yfc z;LP`&?!>BV3(L6w>3HVZyjWN72)XN^=cA4lM?q0#Y=SnWMf_%zl(C*!v7rz#&Y$kF zKiL%Oo^_AgpUs*YUtsbJaB5^kSES)+_k6^(y|n+jq9inD-CK6;D3l$t?OH$OYWvBP zh1*hpbxr>K6^0GCep}l5)Xn#1uiPtd-FyBMN?P)*`x-kDp%*X0&+p8deY-oF^;9uV z{_T|c@fX6E3GAz}C!Zu=|Eh6+e9YnOI%a8Y{_7WYWw-7=_TrLj^6T2u`n#h$TATiHfgiaf{N(A-F;C&Zm+O99 zaOEp3eR5&T!GX7pJ0n++2NWmCr>)1%uYF|f()t5uk5+E-+!33L?u<5s`m-JUY*4EX z6mFjW?Yo9gBX3M^ykmN z`F3+_&Z{HPp`N#)$M(KHpZ2{+?zUyroEo(M$U5noZaopPm8; zwRg#a`T^IDXQ|I;HveGSrD#>VLeKm};hJw<|Eu<|zpQ-q#Ni(v{j}~E4gd8{X=~um z;cq>C{O>j2R-Yc&^CW%Dy)Suw(*B6y=!NHMr`_?~I`WF8c=04%T&z46+g`M!TAMZc zZv0To&<(CkxWmuFs`!_5V&@`vCMi ze{0$?dc%>7Uv<8+^P_X>e|zSit_{Du^}fFKi4!hsEA;x{_n-UHMYfjy_l4(Lr(8YX zJ8=5irk^ETZQnoKce}b(={kAn+3^nI@uD~Xvi`dZmyIvA9Q$ql5pSFAo8{-;O5Z)% zne%jU+k4`U&>j2s(&b~)1F?(2l4`^G(0B&DPx4ZACYxf4Ppz9Z}YVn->LGEiyA232%H5{+=9)q;00=vR6I8;*P zA*g7R(9Pm0QfSz;a+ynm&zOH9V%-M&Zr1JvPjB#u6 z$2~UYxV-TfSD(Fx~F99Yl|MAIuu)hRPJP z?)>uEc6~LqqPL-o3prUciCndBZpl=^s&+2_k$KcMeAQE0Rb`&IH6^d9#og`0a=lE> z=Qe!&k6q6$e0amw?2``XeDIq1)0d|H5zqzWyeX@1E>A-k+PmuNORTru^$Qmrn@pqc zCZC5tO*K`fddMn%R@4zO+(<4axqxC@-QBn1TOFRI%*KT)=Jly-<3$_T3&DM9SEdc6 zm1Aw|>bm-{JfnZqv{%*XHfvG~aa$HDggrLGUD7;p++DRB?Vg|%qP&QRKr-9nC2QtT zR?S?PS(IyH`oa?N#N>|h>Go7nnq-v6d}HPp^NyafR>4c*!m^IRvLa*Xis9+dMaFDb zEo5$5?3z|?%e+34ZNY|y>MExcg;eI;(cB*_cj{7kig!}aD*EEuJv!HoZ`I9&(T&`z zgHJf8eagM64`Q3x8PwF2dAU!uvSip?GdIcMQqt|%CWK&eZkk#5H2Pi#_)BGM+{FI+ z;tT69#_HB-{_FI=?>y6|e7yNTFT9-p*)96ehEJ|;FJVHmrob87uE6B{w7ewEnC#0a zO=E_k>hbrA%e(oqqT(~Bk310Z-&XGLII}6gYm9qf!~1ty>?u+zQ=gluwyM1M3+==H zQsX`MnsxqxIc>QZQ#+uMadY0~xrZv;T|NWc8*15%woNwrKSmEdweb7v+f<`azJ+Ul z{pCY{>e}}>>iXd2CqKRLbE5k-x^?Euj#t(WxQwlVk*OUYU8{KM^pbdP_m%QS?Sx-h z5&HmH>pZ*SQ|PSaxCvKV~%PMBBW!F^SJe^QQ$R`huAMM$3z@scqyw_E- zi$CtDAiK~i*3)4xA*5v7El(q|7Dlvz5~q7g)Y|P!fh86zNGQ?*N!YOv46L{HUt}9R zu&FlO*1xHCqK`KZm`yg%n8}=_=B8-B+UTKTup%p%vdS&9Gqp`u#@0LCXB7WY;kw?s z_xDE+{A}8Vzk<7ZUit3q8~HEw{wA|E?=Pn-o%7@ATg&Ii)>NOl?tD)7Nmp*pFj2gF z=~l#6a|&AP=(O5nh`+ zVT4)Fc%x}Fv|n@bdfp?RTcP#XNzbOTdqSh5=TrE(rjzH{8S}%Az}$?iScZiL*-Cj& zU}IyK4>i;*O$GYet^2g9v4x77B3MXtoJp^l^vM&M4yw$u=uK7TT|dT`f`9zJmUTj2 znZ0yBsvGk(XVNP~hJGH&>sr*Ai$lGo_U`oD*=cWa=KZL#C~bZv&z_myc5^{qCW|jS zo!)dcRZ-YEGTQ;w7bAvv8&nY_ZqD89L(N0FXV@ObsCz(l zTj4o@tS3db&+=eoW}l{aX_=C+=^FH=+_6?|^?TFrcZyn^P^H2Ow6L1@B}Y4yxGay0 z3dST-ml_Gh>7vMFwRslJnT${|1sRvGlcr-1E1l??^lF-`sU@1zY_trt4;sg#4Vsel z9>Y%FseR}Cps~2;nzZ8uQ@^O#R`A)_$hdkrq19!Un^$~=`~98wyMwG%W6E(y?ZW!a zXWQBSr7*i8Yf5RtmlQ{ISNjJi(i!|}*`Lt<%zbP%-EMt~Z16&?UD1GsKB3^(l$A;C7dbhtKZ7HBoL1cf1 zGtI=(y>wy0(z&2ItXbo1>V3DmrNr^~_hGkF-EENXslVmPY-;ZQlb9kcL&|dep^P)j z)+3Hn_ucMMY5hTUhz#?J=hvO@=vqvhBpxuciGftzmhxLZ5C2-zn8lJDL${+{qj)XYWzFhF5od3LQ+j<~;iR>AH=>>4T}4Y`*eAC=^l0pF7xhX^^f^Yp-1T%iKgeSpbeZm}$;Ns& zQO)N2gAUKlRA@0T9TyAoI3de0Iy>tV0{%4{qUtm#Upqzk&mcCzs$lF4l%5QQ5}ZU@ zGb5Jl?pSU->h~F{WY8Mfbu#n*SY*kvuGowX#yLt8#wy(J$4l$hXN-K@Te`QZWb&=S z{IBhCpJ}^^ZQZ$H?k#yu_744g&+*wpirFpwUA3|Cr-y5~8*qHu98*E$YmMj3O8B5!ze>}9%s6!WpMy26uZ?YQ=9O|jG6 zr8RSrT=kW+4d1a|oSD3--*UkC*^NRQYb?tg>JeqVo?OHqFdS|?H@DgBf!0cIUUhiS z=YHS$`OGgHKcmXYvv-n-vB=ivm7nCc%KyGx=6+*#JMn(}$9qF#hhDn*r>Jr8x;n4F z!}-nP73W%V&hPKPJ>OUufV1c%if)Q?9Aa8XETd=xMLs9M(Qu z5JSXZaFRE9?#R^0#w;~4;k9t%lSo<8W6aD*kH+JR;bm5xV@Tl&StK>RVoj#fm7{)x zHqKNPk+TdnK4Ra=UW^w;$%;LWFW%JJ>4NPwZytT9Y8)EyLj4;aWxNI_-aeXRG*F}S zalJ7kotkHI;|oPOb4ywH;4r0OF>CH}Ur|+HN`|>3cE^(6WcIg7>ous8nwzD|G)U4L z6H&qt9UgU5F`R{%n6hsnZYN8keW`~wT^`(Eyc!E#Bdw2q{mNg{nu{r0Rq*b;AQxdD zvK;kp&XudiVZv=KpFbln*Dr}`B+V06Dhk!7rVNnNWX9%~iS+QS*OpEZv|71RuxYtl z;h#=Tvs$dC*0;!JSCHc9X>X5f+wZ{S(kTUWX)nxrY;8SR$^Zl-F>;OU4! zXX6t=Rrwm<^}wQhS0yq%FJB%d^2((2cu-7PxXi+AP1jjN z<=`@n5FSTz28$_*^`13ww6d zxPK$xcRw@iwvUAztYBNBHyJh#fZ5TQ@lG#@rguQnti3;!Epx>@7o`Zk1n|vM{a*Q_1t#|MWOqR!L{3H zL(UA=q6w9k>U1^8SJd8huzYRn`9rO9W_-?c5&J9!ff&A(2 zLFX1IVi#h4Jz)yD_r*(FG0ecdiyyyEA_5yn2JFln;dRt2Jn;`V92EY61>L2D>VVv8KY? zk-!K}%4>;B9Zb-6cvt%6n~c1rtZ$&WW~KwLd7`!Xwc}dD!?xdip7!_yJ@*OQ5%B5s zpIN)J$AA4#$1k5v{r%RV)~|V8Z?9>Rn>u?c8>s1p`op`-A6&LsH4UnOLNL-yMk&g` zW1Dl&lLdy7eQB4kruO`z`qEXZ%U0DFOxyJn_#Gd0wFYr@xCtLXFpn9e9ykHb6S-ALght@}^7?u#Y0$9{Vj{pruV ze`BT1FHE2Qs^4LW4-e!&$G(#)Qsr96Yj1`v1576PmUAk&zTD|QHe9#H6YRR@tY9f- z1l>yf<qOGd{YdHS3SJbM14+*;j|ReIq`ZDk@BsCMiQA-1})y^RCkH7x22ZH6@d76%~GC za%0U_`A1hWUi|Lxv0%hj=l$3NnYU$O?V)d)h9^v0nE*L<%bDhFOmsFp?i=t~%H?S9 zFsqd~9I--c(>ez%+XuO@*13oz>fOr)YV-0Om+k4&KcE~%=2sfyOL{|#?E3veY|YCv ze+VI&TReZhvAMPS)vL2PtuI7=(@yNl9DVCAkNmCx* zrP0RMRNWiV=4kpq>#?_N3|&uVI33ai8u2&EZVYKkDkI11cP&SXZNI(o*tYih#Im2! zparV>P``hu;(6PRGcytOqZ-$RZ#`HLKQT!^_T%x3ANcK9-SHFaA1&f1Z;L`Z)QDK;?Z{1O5Id3~qv-YWd8FxAmm&DFr!PHNan#jO-_)!b zbH!TMf8siopYi#tZ%9OKvE#{yTYuL2t!bD4>tzHpd)^!S^0hw?fA!hGXRgW0>{`os zV*fs2dcL>Q3v;BGs8L{AgUo1`l~_4L1|5}aq4BZQLb$xxlvwhMHjCAK@gs#XQ?amw zIQKq8+-_&9ieV><0Yf89nZ)RZHv^igf+#gD}rzTEM9}()6_aM)Kgz7 z9HV0zYlJs>Jy}{Lots)PWz(Zw3u>nWUHo#fsCuog^Y%&)l||Fmw4i%TbMOgkTlZ*R z!9)Fjm|z+XW-Q;53e3)|g*FJieu=Hsqj~=SmA%+fE_1iH<;r@9N@m({KNCxD-E`)f&^x=w_{LRmJHo7$74Hyc5Z;81kN32jA2qHdw42LVt10fG zjH8owWLoW1HMPuoOS00X<>aKV%6@0sSyz*9@Ugoayvxk(P;-W7ddbk7Z!(%tpSoTV zUWq0~e2(TC1*znsmiiX-{_N5yQXtPNTAXs`!37V&?P%Xv6`qnUN!js|I>uR;O2}7M zbkV`l^x}YJGuv(3Z%Og!;@v(vU6BDTle3=nzjT}x%6iUbY3>CSHU$@Qt9NB2HaXrq zZzQIpj+j@u63fm|G(JGTcN+;*jq=opJ2XhfQdGbaU9g?mS2Zp)}*~A0SD=+pT zG7?>x1UDHBO1k=BR^M%9E_r`=;zHu_wXRtCcXR$pQ!{mGws7|*6f({Y zg=O%HPozgkrCMjn;+RTJ`$}?A&d#gU^YPqtNKw6ky5oxm5hSvR^Ann+bS$*g!K1d4?h=hO&9tL@~7te;3J#v-Bz2H6h+Z)F4x^H<(A`@WgG5{ z@88W_nrIL{&wK(I=?oX+bJnNjAG+62SH;ZWGDB&#N_(bqBEC@`&zipDPuU5NB~L?5 zGX|ZbfWLP>RIF4L&WF>xR~+iiJEuRL3WjSASG9d!pzO@eUsUJBdZ!dZW^3WAec$kn zw#=Q6$brfpSbB*ztd@=jc|<*;#qkD=&7m`Xmhf9OImGe?56S8f#Ly?@JC^W3`2 zrw}AwvpMtf8wDHQo_KW2rkihVkbPNK2gko0uhI*Vs@8GWhHrmJ7L9iH)jT ztnQ4;!++HCg(8_zvh>AhlTxzkl9@rGMnCQOlE z)-d$>!_Qtj1#77Q$`7au6Xwd<#a$%>T_IJ9R}Q*sGQCN8);2pv{_{dno^?yt?~Qrm z^K~y2U%nijk=4HP_`9yg?@;Ex9)rAo=j6w1epPecnOAdKr`~hc1eTVf;dsHzhdy=5 zcV`gSCo|Rgo=Z0E%rmX$Uafg~;T)!2&~5na9cQum7mvU3n{D6L1n&9#@J{bv6&unw zzUXnqTXT-}sLV>J^Q>{F%JFrN%l;pRcdulEo6mQbD>V^=77bYr8;@O4*74FAJpf#dY1R_fQV=A!W6&vmXpm+oOKWlylLF7Kk|vq7hLy$xU27PtekF?eR>=@xLKJ!eDjH2!)K?6ELCFo9CpYy7bfy5@@Ct|h1??X z>{ZJ;sn6T|xGJ=KcEnnL0R7W@hBX$S=ErGocB3So_rr?c{BFMfaH)3F;@ds3HMQt> zt*(!TgqkA%j`TZYBXPIUF zFNa)rjh2Gyea-K_HdnqQ8$VLvdJC(}h6|p&>WW&^nkv?0zxlaev#!LkJK`F)TZ`*T zYtJ5ygiY1f9NnplQA2hvZXw!wBakg8CcuHXEVBq(#uNg>8jUL}6ik@Y)Y5{RQ&E^s z8yxkLGF_%4Z8;S1K_;!qPA-r6d6lDBDJNzYBBauhV+b$0ml7F;*%|4={ae%O(qf%~ z@4e>9D|Zx>!WT{kOb&~qg1K=mnC7%pG)FFU`Vp0JO;x;aU{R5gU6%`u-VFq4dw#yo zJ2V|OThj}SvGJLy706bfDFo-1X(A&tO~Lpl=2EELVURC{(I~DbY-l9Byd1$TnRr~G zCD{b8NWe-u>JO^*NV=8aQlXU;rdCNpoW(hfP6KYeCj-lJqmZH`l}x7QSuw<^q6xK( zN|K09E<+fYPl@y@z_Chth(^R0NzdinF)0~=tvVWf*%qJXDzl}?N-!Li>k*m2!=Xhf zqsZu=@`Jna;I2L(Y2sm--Kks-L}DDP(E`SX@dh+GW-%FCBqhWoB;_y}2WrbPCKv_S zj(||CB&Pw|q37r%50;>UqZpwR1CS1uP?<=lc!|U0S}B}_1z4e!k`xt<5k?)LU*b_% ztyBYafeh0oJtiZWRFqKBN(D($6eL4cA|s?k1uw`DB*nv=s06q08BvZ>G(aH$A{65Q z@Pfm*KrtjpxgH6|Ktw8~lBW!3P0HV${;%Fo((H7=`12*We_T zf(<1hREaVyfENHAf+*4ON=&X-5K^3mc^sG2`jkHksnmo<^fL&CBCs4vtV96Iq?D7f zlw8FNN|d2|Qqq07b(IAQqYjyp<$D@gy!uutLEDWJiWVC>u|Rh=j;+ zCIL7l1>mm2BqyK%aS&sI3gsaU5++ejf>St33OFVt5h9XQU^I}MNe~)bWMDNN7gPvf zGx#uMP{R-_!4!%JJfM$A0g=gIgy&%bVR!%aIEF%C1O;dagFU@j2?<`atzy7@z{d5?3XGK>k)M6%dI$ z2P6S=2ml-cQi1QM!60#UZV)9vBOz3$5L1A30m?u;038__29||D5GR2xjsc>8_qbdJ zieohsG)r?N2`&Z!oKFPxfq*Q6s{^r7OsT{Lz%0d*AXgM%_)s}0TLcD-A%qHmj-rw@ z3#JFZAOPS}pgVxoz*7Q~)3G=ONHP(DAr1t9A~J{r7FnGYoB?>h)oy^WJiQ7?QY;6= z0f-hT9iT5uukwT<3%mjRf@7$}L!h^EBF_Pw3jl@)jEVqHq(I(5hmt@>P~c<;KyL9A z19~nifiVF1THOcmLV)1_hl#6{8E`#dF9im;2mqUL67(S87lhF3<)}MA zz2Km?fHF!lz*@-ai2{H?f!bU$MW;v-WJ3^TGNl5=z*K;TWMi>aLK8>?at-iO;4WZN zpav*Ha198?K<*ea$uOw|$@4U@E#Lzo1kjF20QYggUtl1>I7KiJJb>9ykZ2J!Ti|oh z?IZ~_I{=CTH=r2sJq~a_5r_^fzyn5*VS%FH4 Date: Wed, 15 Jan 2025 14:52:56 +0100 Subject: [PATCH 18/23] Changes to be committed: renamed: ptplayer.asm -> blitz_ptplayer.asm --- ptplayer.asm => blitz_ptplayer.asm | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ptplayer.asm => blitz_ptplayer.asm (100%) diff --git a/ptplayer.asm b/blitz_ptplayer.asm similarity index 100% rename from ptplayer.asm rename to blitz_ptplayer.asm From b627515477a1a2180a6d01e63a82fe78e5454e6e Mon Sep 17 00:00:00 2001 From: Daniel Lakey Date: Wed, 15 Jan 2025 15:55:35 +0100 Subject: [PATCH 19/23] Changes to be committed: modified: blitz_ptplayer.asm new file: ptplayer.asm modified: ptplayer.obj --- blitz_ptplayer.asm | 3726 ++--------------------------------------- ptplayer.asm | 3974 ++++++++++++++++++++++++++++++++++++++++++++ ptplayer.obj | Bin 13532 -> 13668 bytes 3 files changed, 4115 insertions(+), 3585 deletions(-) create mode 100644 ptplayer.asm diff --git a/blitz_ptplayer.asm b/blitz_ptplayer.asm index 0f09263..c9dceb7 100644 --- a/blitz_ptplayer.asm +++ b/blitz_ptplayer.asm @@ -1,180 +1,14 @@ -;************************************************** -;* ----- Protracker V2.3B Playroutine ----- * -;************************************************** -; -; Version 6.3 -; Written by Frank Wille in 2013, 2016-2023. +; Amiga Blitz Basic Library wrapper of PT Player by Frank Wille (PHX) in 2013, 2016-2024. ; Converted to BlitzBasic library by E-Penguin based on work by idrougge -; -; I, the copyright holder of this work, hereby release it into the -; public domain. This applies worldwide. -; -; The default version (single section, local base register) should -; work with most assemblers. Tested are: Devpac, vasm, PhxAss, -; Barfly-Asm, SNMA, AsmOne, AsmPro. -; -; The small data model can be enabled by defining the symbol SDATA. In -; this case all functions expect a4 to be initialized with the small -; data base register. Interrupt functions restore a4 from _LinkerDB. -; Small data may work with vasm and PhxAss only. -; -; Exported functions and variables: -; (CUSTOM is the custom-chip register set base address $dff000.) -; -; _mt_install_cia(a6=CUSTOM, a0=VectorBase, d0=PALflag.b) -; Install a CIA-B interrupt for calling mt_music or mt_sfxonly. -; The music module is replayed via mt_music when _mt_Enable is non-zero. -; Otherwise the interrupt handler calls mt_sfxonly to play sound -; effects only. VectorBase is 0 for 68000, otherwise set it to the CPU's -; VBR register. A non-zero PALflag selects PAL-clock for the CIA timers -; (NTSC otherwise). -; -; _mt_remove_cia(a6=CUSTOM) -; Remove the CIA-B music interrupt, restore the previous handler and -; reset the CIA timer registers to their original values. -; -; _mt_init(a6=CUSTOM, a0=TrackerModule, a1=Samples|NULL, d0=InitialSongPos.b) -; Initialize a new module. -; Reset speed to 6, tempo to 125 and start at the given song position. -; Master volume is at 64 (maximum). -; When a1 is NULL the samples are assumed to be stored after the patterns. -; -; _mt_end(a6=CUSTOM) -; Stop playing current module and sound effects. -; -; _mt_soundfx(a6=CUSTOM, a0=SamplePointer, -; d0=SampleLength.w, d1=SamplePeriod.w, d2=SampleVolume.w) -; Request playing of an external sound effect on the most unused channel. -; This function is for compatibility with the old API only. -; You may prefer _mt_playfx instead. MINIMAL=0 only. -; -; channelStatus(d0) = _mt_playfx(a6=CUSTOM, a0=SfxStructurePointer) -; Request playing of a prioritized external sound effect, either on a -; fixed channel or on the most unused one. -; Structure layout of SfxStructure: -; void *sfx_ptr (pointer to sample start in Chip RAM, even address) -; WORD sfx_len (sample length in words) -; WORD sfx_per (hardware replay period for sample) -; WORD sfx_vol (volume 0..64, is unaffected by the song's master volume) -; BYTE sfx_cha (0..3 selected replay channel, -1 selects best channel) -; BYTE sfx_pri (priority, must be in the range 1..127) -; When multiple samples are assigned to the same channel the lower -; priority sample will be replaced. When priorities are the same, then -; the older sample is replaced. -; The chosen channel is blocked for music until the effect has -; completely been replayed. -; Returns a pointer to a channel-status structure when the sample -; is scheduled for playing, and NULL when the request was ignored. -; MINIMAL=0 only. -; -; _mt_loopfx(a6=CUSTOM, a0=SfxStructurePointer) -; Request playing of a looped sound effect on a fixed channel, which -; will be blocked for music until the effect is stopped (_mt_stopfx). -; It uses the same sfx-structure as _mt_playfx, but the priority is -; ignored. A looped sound effect has always highest priority and will -; replace a previous effect on the same channel. No automatic channel -; selection possible! -; Also make sure the sample starts with a zero-word, which is used -; for idling when the effect is stopped. This word is included in the -; total length calculation, but excluded when actually playing the loop. -; MINIMAL=0 only. -; -; _mt_stopfx(a6=CUSTOM, d0=Channel.b) -; Immediately stop a currently playing sound effect on a channel (0..3) -; and make it available for music, or other effects, again. This is the -; only way to stop a looped sound effect (_mt_loopfx) besides stopping -; replay completely (_mt_end). MINIMAL=0 only. -; -; _mt_musicmask(a6=CUSTOM, d0=ChannelMask.b) -; Bits set in the ChannelMask define which specific channels are reserved -; for music only. Set bit 0 for channel 0, ..., bit 3 for channel 3. -; When calling _mt_soundfx or _mt_playfx with automatic channel selection -; (sfx_cha=-1) then these masked channels will never be picked. -; The mask defaults to 0. MINIMAL=0 only. -; -; _mt_mastervol(a6=CUSTOM, d0=MasterVolume.w) -; Set a master volume from 0 to 64 for all music channels. -; Note that the master volume does not affect the volume of external -; sound effects (which is desired). MINIMAL=0 only. -; -; _mt_samplevol(d0=SampleNumber.w, d1=Volume.b) -; Redefine a sample's volume. May also be done while the song is playing. -; Warning: Does not check arguments for valid range! You must have done -; _mt_init before calling this function! -; The new volume is persistent. Even when the song is restarted. -; MINIMAL=0 only. -; -; _mt_channelmask(a6=CUSTOM, d0=ChannelMask.b) -; Bits cleared in the ChannelMask define which specific channels are muted -; for music replay. Clear bit 0 for channel 0, ..., bit 3 for channel 3. -; Additionally a cleared bit prevents any access to the sample pointers -; of this channel. -; The mask defaults to all channels unmuted (bits set) and is reset to -; this state on _mt_init and _mt_end. MINIMAL=0 only. -; -; _mt_music(a6=CUSTOM) -; The replayer routine. Can be called from your own VBlank interrupt -; handler when VBLANK_MUSIC is set. Is otherwise called automatically -; by Timer-A interrupts after _mt_install_cia. -; -; Byte Variables: -; -; _mt_Enable -; Set this byte to non-zero to play music, zero to pause playing. -; Note that you can still play sound effects, while music is stopped. -; It is set to 0 by _mt_install_cia. VBLANK_MUSIC=0 only. -; -; _mt_E8Trigger -; This byte reflects the value of the last E8 command. -; It is reset to 0 after _mt_init. -; -; _mt_MusicChannels -; This byte defines the number of channels which should be dedicated -; for playing music. So sound effects will never use more -; than 4 - _mt_MusicChannels channels at once. Defaults to 0. -; MINIMAL=0 only. -; - -; Optionally you can build a minimal version, which includes only -; the basic player. No sound effects insertion, no master volume, no -; sample volume, etc. Define the symbol MINIMAL=1 for it. - ifnd MINIMAL -MINIMAL equ 0 - endc - -; You may disable sawtooth and rectangle vibratos/tremolos here, which -; will be replaced by sine-waves. They are rarely used and disabling -; them will free a lot of memory for the tables. - ifnd ENABLE_SAWRECT -ENABLE_SAWRECT equ 0 - endc - -; Set this if you can guarantee that the word at $0 is cleared and if -; you want to use it for idle-looping of samples. - ifnd NULL_IS_CLEARED -NULL_IS_CLEARED equ 0 - endc - -; This disables initialization of CIA Timer-A interrupts for music -; replay, which means you cannot set the tempo with the F-command -; anymore and you have to call _mt_music yourself out of your own -; VBlank interrupt handler. Also sound effects will no longer work, -; when no music is playing (_mt_Enable=0). - ifnd VBLANK_MUSIC -VBLANK_MUSIC equ 0 - endc - -; Delay in CIA-ticks, which guarantees that at least one Audio-DMA -; took place, even with the lowest periods. -; 496 should be the correct value. But there are some A1200 which -; need at least 550. -DMADELAY equ 576 ; was 496 +; Wrapper concept and macros by earok include "blitz.i" - +; Blitz Libraries used audiolib equ 116 banklib equ 76 + +; This Blitz library ptplayerlib equ 48 libheader ptplayerlib,0,0,blitz_finit,0 @@ -182,109 +16,109 @@ ptplayerlib equ 48 astatement args word,byte libs banklib,$1080 - subs _mt_init_bank,0,0 + subs MTInit_bank,0,0 args long,long,byte libs - subs _mt_init_stub,0,0 + subs MTInit,0,0 name "MTInit","Bank#, startpos | module addr, instr addr, startpos (inserts module into player)",0 astatement args byte libs - subs _mt_install_cia,0,0 + subs MTInstall,0,0 name "MTInstall","Install player routine. PAL=true, NTSC=false",0 astatement args byte libs - subs _mt_enable_stub,0,0 + subs MTPlay,0,0 name "MTPlay","On/Off for module playback",0 astatement args libs - subs _mt_remove_cia,0,0 + subs MTRemove,0,0 name "MTRemove","Remove player from system",0 astatement args libs - subs _mt_end_stub,0,0 + subs MTEnd,0,0 name "MTEnd","Stop playing current module",0 astatement args long,word,word,word libs - subs _mt_soundfx_stub,0,0 + subs MTSoundFX,0,0 args word,word libs audiolib,$1080 - subs _mt_soundfx_soundobject,0,0 + subs MTSoundFX_soundobject,0,0 name "MTSoundFX","Sound#, volume (0..64)| sample_addr.l, length.w, period.w, volume.w" afunction long args long libs - subs _mt_playfx_stub,0,0 + subs MTPlayFx,0,0 name "MTPlayFx","SfxStructurePointer, returns SfxChanStatus pointer" astatement args word libs - subs _mt_mastervol,0,0 + subs MTMasterVolume,0,0 name "MTMasterVolume","Master volume (0..64) for all music channels." astatement args byte libs - subs _mt_musicmask,0,0 + subs MTMusicMask,0,0 name "MTMusicMask","Set bits 0-3 to reserve channels for music only." astatement args byte libs - subs _mt_MusicChannels_stub,0,0 + subs MTMusicChannels,0,0 name "MTMusicChannels","number of channels dedicated to music." afunction byte args libs - subs _mt_E8Trigger_stub,0,0 + subs MTE8Trigger,0,0 name "MTE8Trigger","Value of the last E8 command." astatement args long libs - subs _mt_loopfx_stub,0,0 + subs MTLoopFx,0,0 name "MTLoopFx","SfxStructurePointer" astatement args byte libs - subs _mt_stopfx_stub,0,0 + subs MTStopFx,0,0 name "MTStopFx","channel to stop FX loop" astatement args word,byte libs - subs _mt_samplevol_stub,0,0 + subs MTSampleVolume,0,0 name "MTSampleVolume", "Redefine a sample's volume" astatement args byte libs - subs _mt_channelmask_stub,0,0 + subs MTChannelMask,0,0 name "MTChannelMask", "Clear bits 0-3 to mute channel for music" astatement args byte libs - subs _mt_SongEnd_stub,0,0 + subs MTSetSongEnd,0,0 name "MTSetSongEnd","Set value of the mt_SongEnd flag to 0xFF to end after it has finished" afunction byte args libs - subs _mt_IsEnabled_stub,0,0 + subs MTIsEnabled,0,0 name "MTIsEnabled","Get status of playback" blitz_finit: @@ -297,114 +131,31 @@ _blitz_mt_lib_finit: bra _mt_remove_cia ;-------------------- -; Custom chip registers -CUSTOM equ $dff000 -INTREQR equ $01e -INTENAR equ $01c -DMACON equ $096 -INTENA equ $09a -INTREQ equ $09c -AUD0LC equ $0a0 -AUD0LEN equ $0a4 -AUD0VOL equ $0a8 -AUD1LC equ $0b0 -AUD1LEN equ $0b4 -AUD1VOL equ $0b8 -AUD2LC equ $0c0 -AUD2LEN equ $0c4 -AUD2VOL equ $0c8 -AUD3LC equ $0d0 -AUD3LEN equ $0d4 -AUD3VOL equ $0d8 - -; Audio channel registers -AUDLC equ 0 -AUDLEN equ 4 -AUDPER equ 6 -AUDVOL equ 8 - -; CIA registers -CIAA equ $bfe001 -CIAB equ $bfd000 -CIAPRA equ $000 -CIATALO equ $400 -CIATAHI equ $500 -CIATBLO equ $600 -CIATBHI equ $700 -CIAICR equ $d00 -CIACRA equ $e00 -CIACRB equ $f00 - - -; Sound effects structure, passed into _mt_playfx - rsreset -sfx_ptr rs.l 1 -sfx_len rs.w 1 -sfx_per rs.w 1 -sfx_vol rs.w 1 -sfx_cha rs.b 1 -sfx_pri rs.b 1 -sfx_sizeof rs.b 0 - - -; Channel Status - rsreset -n_note rs.w 1 -n_cmd rs.b 1 -n_cmdlo rs.b 1 -n_index rs.b 1 -n_sfxpri rs.b 1 -n_reserved1 rs.b 2 -n_start rs.l 1 -n_loopstart rs.l 1 -n_length rs.w 1 -n_replen rs.w 1 -n_period rs.w 1 -n_volume rs.w 1 -n_pertab rs.l 1 -n_dmabit rs.w 1 -n_noteoff rs.w 1 -n_toneportspeed rs.w 1 -n_wantedperiod rs.w 1 -n_pattpos rs.w 1 -n_funk rs.w 1 -n_wavestart rs.l 1 -n_reallength rs.w 1 -n_intbit rs.w 1 -n_sfxptr rs.l 1 -n_sfxlen rs.w 1 -n_sfxper rs.w 1 -n_sfxvol rs.w 1 -n_looped rs.b 1 -n_minusft rs.b 1 -n_vibratoamp rs.b 1 -n_vibratospd rs.b 1 -n_vibratopos rs.b 1 -n_vibratoctrl rs.b 1 -n_tremoloamp rs.b 1 -n_tremolospd rs.b 1 -n_tremolopos rs.b 1 -n_tremoloctrl rs.b 1 -n_gliss rs.b 1 -n_sampleoffset rs.b 1 -n_loopcount rs.b 1 -n_funkoffset rs.b 1 -n_retrigcount rs.b 1 - ifeq MINIMAL -n_freecnt rs.b 1 -n_musiconly rs.b 1 -n_enable rs.b 1 - else -n_reserved2 rs.b 3 - endc -n_sizeof rs.b 0 +; Macros by earok / Scorpion +; https://github.com/earok/ptplayer_blitzwrapper_scorpion +storeAddressRegisters macro + movem.l a4-a6,-(sp) ; Save registers for Blitz 2 + lea CUSTOM,a6 ;Store the custom register in A6 + endm + +storeAddressRegistersVBR macro + movem.l a4-a6,-(sp) ; Save registers for Blitz 2 + ;Load vector base into A0 + sub.l a0,a0 + move.l 4.w,a6 + btst #0,297(a6) ; check for 68010 + beq .novbr + lea getvbr(pc),a5 + jsr -30(a6) ; Supervisor() +.novbr: lea CUSTOM,a6 + endM +restoreAddressRegisters macro + movem.l (sp)+,a4-a6 ; Restore registers for Blitz + rts ;Return to Blitz + endm +;-------------------- - ifd SDATA - xref _LinkerDB ; small data base from linker - near a4 - code - endc ; VBR Hack by phx ; https://eab.abime.net/showpost.php?p=1516508&postcount=7 @@ -416,3310 +167,115 @@ getvbr: mc68000 ;------------------------ -;--------------------------------------------------------------------------- - xdef _mt_install_cia -_mt_install_cia: ; Install a CIA-B interrupt for calling mt_music. -; a6 = CUSTOM -; a0 = VectorBase -; d0 = PALflag.b (0 is NTSC) - -;----- Save registers for Blitz 2 --- -; Part of the VBR Hack by phx - movem.l a3-a6,-(sp) - sub.l a0,a0 - move.l 4.w,a6 - btst #0,297(a6) ; check for 68010 - beq .novbr - lea getvbr(pc),a5 - jsr -30(a6) ; Supervisor() -.novbr: lea CUSTOM,a6 -;------------------------------------- - - ifnd SDATA - move.l a4,-(sp) - lea mt_data(pc),a4 - endc - - clr.b mt_Enable(a4) - - ; remember level 6 vector and interrupt enable - lea $78(a0),a0 - move.l a0,mt_Lev6Int(a4) - move.w #$2000,d1 - and.w INTENAR(a6),d1 - or.w #$8000,d1 - move.w d1,mt_Lev6Ena(a4) - - ; disable level 6 EXTER interrupts, set player interrupt vector - move.w #$2000,INTENA(a6) - move.l (a0),mt_oldLev6(a4) - lea mt_TimerAInt(pc),a1 - move.l a1,(a0) - - ; reset TimerB toggle - lea TB_toggle(pc),a0 - clr.b (a0) - - ; disable CIA-B interrupts, stop and save all timers - lea CIAB,a0 - moveq #0,d1 - move.b #$7f,CIAICR(a0) - move.b d1,CIACRA(a0) - move.b d1,CIACRB(a0) - lea mt_oldtimers(a4),a1 - move.b CIATALO(a0),(a1)+ - move.b CIATAHI(a0),(a1)+ - move.b CIATBLO(a0),(a1)+ - move.b CIATBHI(a0),(a1) - - ifeq VBLANK_MUSIC - ; determine if 02 clock for timers is based on PAL or NTSC - tst.b d0 - bne .1 - move.l #1789773,d0 ; NTSC - bra .2 -.1: move.l #1773447,d0 ; PAL -.2: move.l d0,mt_timerval(a4) - - ; load TimerA in continuous mode for the default tempo of 125 - divu #125,d0 - move.b d0,CIATALO(a0) - lsr.w #8,d0 - move.b d0,CIATAHI(a0) - move.b #$11,CIACRA(a0) ; load timer, start continuous - endc ; !VBLANK_MUSIC - - ; load TimerB with DMADELAY ticks for setting DMA and repeat - move.b #DMADELAY&255,CIATBLO(a0) - move.b #DMADELAY>>8,CIATBHI(a0) - - ; Ack. pending interrupts, TimerA and TimerB interrupt enable - tst.b CIAICR(a0) - move.w #$2000,INTREQ(a6) - ifne VBLANK_MUSIC - move.b #$82,CIAICR(a0) ; TimerB only for VBLANK_MUSIC - else - move.b #$83,CIAICR(a0) - endc - - ; enable level 6 interrupts - move.w #$a000,INTENA(a6) - - bra mt_reset - - -;--------------------------------------------------------------------------- - xdef _mt_remove_cia -_mt_remove_cia: -; Remove CIA-B music interrupt and restore the old vector. -; a6 = CUSTOM - movem.l a3-a6,-(sp) ; Save registers for Blitz 2 - lea CUSTOM,a6 - - ifnd SDATA - move.l a4,-(sp) - lea mt_data(pc),a4 - endc - - ; disable level 6 and CIA-B interrupts - lea CIAB,a0 - move.w #$2000,d0 - move.b #$7f,CIAICR(a0) - move.w d0,INTENA(a6) - tst.b CIAICR(a0) - move.w d0,INTREQ(a6) - - ; restore old timer values - lea mt_oldtimers(a4),a1 - move.b (a1)+,CIATALO(a0) - move.b (a1)+,CIATAHI(a0) - move.b (a1)+,CIATBLO(a0) - move.b (a1),CIATBHI(a0) - move.b #$10,CIACRA(a0) - move.b #$10,CIACRB(a0) - - ; restore original level 6 interrupt vector - move.l mt_Lev6Int(a4),a1 - move.l mt_oldLev6(a4),(a1) - - ; reenable CIA-B ALRM interrupt, which was set by AmigaOS - move.b #$84,CIAICR(a0) - - ; reenable previous level 6 interrupt - move.w mt_Lev6Ena(a4),INTENA(a6) - - ifnd SDATA - move.l (sp)+,a4 - endc - - movem.l (sp)+,a3-a6 ; Restore registers for Blitz - rts - - -;--------------------------------------------------------------------------- -mt_TimerAInt: -; TimerA interrupt calls mt_music at a selectable tempo (Fxx command), -; which defaults to 50 times per second. - - ; check for TB interrupt and clear CIAB interrupt flags - btst #1,CIAB+CIAICR - bne mt_TimerBInt - - ifne VBLANK_MUSIC - move.w #$2000,CUSTOM+INTREQ ; ack interrupt and leave - nop - rte - - else - ; Now it should be a TA interrupt. - ; Other level 6 interrupt sources have to be handled elsewhere. - movem.l d0-d7/a0-a6,-(sp) - lea CUSTOM,a6 - ifd SDATA - lea _LinkerDB,a4 - else - lea mt_data(pc),a4 - endc - - ; clear EXTER interrupt flag - move.w #$2000,INTREQ(a6) - - ; do music when enabled - tst.b mt_Enable(a4) - ifeq MINIMAL - beq .2 - else - beq .1 - endc - - bsr mt_music ; music with sfx inserted -.1: movem.l (sp)+,d0-d7/a0-a6 - nop - rte - - ifeq MINIMAL -.2: bsr mt_sfxonly ; no music, only sfx - movem.l (sp)+,d0-d7/a0-a6 - nop - rte - endc - endc ; !VBLANK_MUSIC - - -;--------------------------------------------------------------------------- -mt_TimerBInt: -; Handle one-shot TimerB interrupt. -; TB_toggle-technique suggested by Ross/EAB. - - move.l a0,-(sp) - lea TB_toggle(pc),a0 - not.b (a0) - lea CUSTOM+INTREQ,a0 - beq mt_TimerBsetrep - - ; restart timer for repeat, enable audio DMA after DMADELAY ticks - move.w #$2000,(a0) ; clear EXTER interrupt flag - move.b #$19,CIAB+CIACRB - move.w mt_dmaon(pc),DMACON-INTREQ(a0) - - move.l (sp)+,a0 - nop - rte - -mt_dmaon: - dc.w $8000 -TB_toggle: - dc.b 0 - even - - -;--------------------------------------------------------------------------- -mt_TimerBsetrep: -; Oneshot TimerB interrupt to set repeat samples after another DMADELAY ticks. -; a0 = INTREQ - - ; clear EXTER and possible audio interrupt flags - move.l a4,-(sp) - move.l d0,a4 - moveq #$2000>>7,d0 ; EXTER-flag - or.b mt_dmaon+1(pc),d0 - lsl.w #7,d0 - move.w d0,(a0) - move.l a4,d0 - - ; set repeat sample pointers and lengths - ifd SDATA - lea _LinkerDB,a4 - else - lea mt_data(pc),a4 - endc - - ifne MINIMAL - move.l mt_chan1+n_loopstart(a4),AUD0LC-INTREQ(a0) - move.w mt_chan1+n_replen(a4),AUD0LEN-INTREQ(a0) - move.l mt_chan2+n_loopstart(a4),AUD1LC-INTREQ(a0) - move.w mt_chan2+n_replen(a4),AUD1LEN-INTREQ(a0) - move.l mt_chan3+n_loopstart(a4),AUD2LC-INTREQ(a0) - move.w mt_chan3+n_replen(a4),AUD2LEN-INTREQ(a0) - move.l mt_chan4+n_loopstart(a4),AUD3LC-INTREQ(a0) - move.w mt_chan4+n_replen(a4),AUD3LEN-INTREQ(a0) - - else ; !MINIMAL - tst.b mt_chan1+n_enable(a4) - beq .1 - move.l mt_chan1+n_loopstart(a4),AUD0LC-INTREQ(a0) - move.w mt_chan1+n_replen(a4),AUD0LEN-INTREQ(a0) -.1: tst.b mt_chan2+n_enable(a4) - beq .2 - move.l mt_chan2+n_loopstart(a4),AUD1LC-INTREQ(a0) - move.w mt_chan2+n_replen(a4),AUD1LEN-INTREQ(a0) -.2: tst.b mt_chan3+n_enable(a4) - beq .3 - move.l mt_chan3+n_loopstart(a4),AUD2LC-INTREQ(a0) - move.w mt_chan3+n_replen(a4),AUD2LEN-INTREQ(a0) -.3: tst.b mt_chan4+n_enable(a4) - beq .4 - move.l mt_chan4+n_loopstart(a4),AUD3LC-INTREQ(a0) - move.w mt_chan4+n_replen(a4),AUD3LEN-INTREQ(a0) -.4: - endc - - move.l (sp)+,a4 - move.l (sp)+,a0 - nop - rte +MTInstall: + storeAddressRegistersVBR + jsr _mt_install_cia + restoreAddressRegisters +MTRemove: + storeAddressRegisters + jsr _mt_remove_cia + restoreAddressRegisters ;--------------------------------------------------------------------------- - xdef _mt_init -_mt_init: +;_mt_init: ; Initialize new module. ; Reset speed to 6, tempo to 125 and start at given song position. ; Master volume is at 64 (maximum). -; a6 = CUSTOM -; a0 = module pointer -; a1 = sample pointer (NULL means samples are stored within the module) -; d0 = initial song position - -; --- Init for Blitz ---- -_mt_init_bank: - move.l #0,a1 ; Set sample pointer to NULL +; --- Init for Blitz ---- +MTInit_bank: + move.l #0,a1 ; Set sample pointer to NULL move.b d1,d0 ; Set starting position move.l (a0),a0 ; Set module address - bra _mt_init_blitz_done -_mt_init_stub: - move.l d0,a0 ; Set module address - move.l d1,a1 ; Set sample pointer - move.b d2,d0 ; Set starting position -_mt_init_blitz_done: - movem.l a3-a6,-(sp) - lea CUSTOM,a6 -; ----------------------- - - ifnd SDATA - move.l a4,-(sp) - lea mt_data(pc),a4 - endc - - move.l a0,mt_mod(a4) - movem.l d2/a2,-(sp) - - ; set initial song position - cmp.b 950(a0),d0 - blo .1 - moveq #0,d0 -.1: move.b d0,mt_SongPos(a4) - - move.l a1,d0 ; sample data location is given? - bne .4 - - ; get number of highest pattern - lea 952(a0),a1 ; song arrangement list - moveq #127,d0 - moveq #0,d2 -.2: move.b (a1)+,d1 - cmp.b d2,d1 - bls .3 - move.b d1,d2 -.3: dbf d0,.2 - addq.b #1,d2 ; number of patterns - - ; now we can calculate the base address of the sample data - moveq #10,d0 - asl.l d0,d2 - lea (a0,d2.l),a1 - add.w #1084,a1 - - ; save start address of each sample and do some fixes for broken mods -.4: lea mt_SampleStarts(a4),a2 - moveq #1,d2 - moveq #31-1,d0 -.5: move.l a1,(a2)+ - moveq #0,d1 - move.w 42(a0),d1 - cmp.w d2,d1 ; length 0 and 1 define unused samples - bls .6 - add.l d1,d1 - add.l d1,a1 - bra .7 -.6: clr.w 42(a0) ; length 1 means zero -> no sample -.7: lea 30(a0),a0 - dbf d0,.5 - - movem.l (sp)+,d2/a2 - - ifeq VBLANK_MUSIC - ; reset CIA timer A to default (125) - move.l mt_timerval(a4),d0 - divu #125,d0 - move.b d0,CIAB+CIATALO - lsr.w #8,d0 - move.b d0,CIAB+CIATAHI - endc - -mt_reset: -; a4 must be initialized with base register - - ; reset speed and counters - move.b #6,mt_Speed(a4) - clr.b mt_Counter(a4) - clr.w mt_PatternPos(a4) - clr.b mt_PattDelTime(a4) - clr.b mt_PattDelTime2(a4) - clr.w mt_PBreakPos(a4) - clr.b mt_PBreakFlag(a4) - clr.b mt_PosJumpFlag(a4) - - ; disable the filter - or.b #2,CIAA+CIAPRA - - ifeq MINIMAL - ; set master volume to 64 - lea MasterVolTab64(pc),a0 - move.l a0,mt_MasterVolTab(a4) - endc - - ; set channel index - clr.b mt_chan1+n_index(a4) - move.b #1,mt_chan2+n_index(a4) - move.b #2,mt_chan3+n_index(a4) - move.b #3,mt_chan4+n_index(a4) - - ; initialize channel DMA and interrupt bits - move.w #$0001,mt_chan1+n_dmabit(a4) - move.w #$0002,mt_chan2+n_dmabit(a4) - move.w #$0004,mt_chan3+n_dmabit(a4) - move.w #$0008,mt_chan4+n_dmabit(a4) - move.w #$0080,mt_chan1+n_intbit(a4) - move.w #$0100,mt_chan2+n_intbit(a4) - move.w #$0200,mt_chan3+n_intbit(a4) - move.w #$0400,mt_chan4+n_intbit(a4) - - clr.b mt_E8Trigger(a4) - ifeq MINIMAL - clr.b mt_SongEnd(a4) - clr.b mt_SilCntValid(a4) - endc - - ifnd SDATA - move.l (sp)+,a4 - endc - - -;--------------------------------------------------------------------------- - xdef _mt_end -_mt_end: -; Stop playing current module. -; a6 = CUSTOM - - move.w #$4000,INTENA(a6) - ifd SDATA - clr.b mt_Enable(a4) - lea mt_chan1(a4),a0 - bsr resetch - lea mt_chan2(a4),a0 - bsr resetch - lea mt_chan3(a4),a0 - bsr resetch - lea mt_chan4(a4),a0 - bsr resetch - else - lea mt_data(pc),a1 - clr.b mt_Enable(a1) - lea mt_chan1(a1),a0 - bsr resetch - lea mt_chan2(a1),a0 - bsr resetch - lea mt_chan3(a1),a0 - bsr resetch - lea mt_chan4(a1),a0 - bsr resetch - endc - moveq #0,d0 - move.w d0,AUD0VOL(a6) - move.w d0,AUD1VOL(a6) - move.w d0,AUD2VOL(a6) - move.w d0,AUD3VOL(a6) - move.w #$000f,DMACON(a6) - move.w #$c000,INTENA(a6) - - movem.l (sp)+,a3-a6 ; Restore registers before returning to Blitz - rts - -;------------ Blitz2 stubs ----------- -_mt_enable_stub: - ifd SDATA - trap #0 - move.b d0,mt_Enable(a4) - else - lea mt_data+mt_Enable(pc),a0 - move.b d0,(a0) - endc - rts - -_mt_end_stub: - movem.l a3-a6,-(sp) - lea CUSTOM,a6 - bra _mt_end - -_mt_MusicChannels_stub: - ifd SDATA - move.b d0,mt_MusicChannels(a4) - else - lea mt_data+mt_MusicChannels(pc),a0 - move.b d0,(a0) - endc - rts - -resetch: -; a0 = channel status -; All registers are preserved! - - move.w #320,n_period(a0) ; make sure period is not illegal - clr.w n_volume(a0) - clr.w n_sfxlen(a0) - clr.w n_funk(a0) - clr.b n_sfxpri(a0) - clr.b n_looped(a0) - clr.b n_gliss(a0) - ifeq MINIMAL - clr.b n_musiconly(a0) - st n_enable(a0) - endc - rts - -_mt_IsEnabled_stub: - ifd SDATA - move.b mt_Enable(a4),d0 - else - lea mt_data+mt_Enable(pc),a0 - move.b (a0),d0 - endc - rts - -_mt_E8Trigger_stub: - ifd SDATA - move.b mt_E8Trigger(a4),d0 - else - lea mt_data+mt_E8Trigger(pc),a0 - move.b (a0),d0 - endc - rts - -_mt_SongEnd_stub: + bra MTInit_done +MTInit: + move.l D0,A0 ; a0 = module pointer + move.l D1,A1 ; a1 = sample pointer (NULL means samples are stored within the module) + move.w D2,D0 ; d0 = initial song position +MTInit_done: + storeAddressRegisters + jsr _mt_init + restoreAddressRegisters + +MTEnd: + storeAddressRegisters + jsr _mt_end + restoreAddressRegisters + +MTPlay: + move.b d0,_mt_Enable + rts + +MTIsEnabled: + move.b _mt_Enable,d0 + rts + +MTSetSongEnd: ; Set mt_SongEnd to 0xFF to stop after current song is played -; d0.w = new value - ifd SDATA - trap #0 move.b d0,mt_SongEnd(a4) - else - lea mt_data+mt_SongEnd(pc),a0 - move.b d0,(a0) - endc rts ;------------------------------------- -_mt_soundfx_soundobject: - movem.l a3-a6,-(sp) - lea CUSTOM,a6 +MTSoundFX_soundobject: move.w d1,d2 ; volume move.w 4(a0),d1 ; period move.w 6(a0),d0 ; length move.l (a0),a0 ; sample data - bra _mt_soundfx - - ifeq MINIMAL -;--------------------------------------------------------------------------- - xdef _mt_soundfx -_mt_soundfx_stub: -; Request playing of an external sound effect on the most unused channel. -; This function is for compatibility with the old API only! -; You should call _mt_playfx instead! -; a6 = CUSTOM -; a0 = sample pointer -; d0.w = sample length in words -; d1.w = sample period -; d2.w = sample volume - -;--- Blitz 2 call stub ----- - movem.l a3-a6,-(sp) - lea CUSTOM,a6 - move.l d0,a0 - move.w d1,d0 - move.w d2,d1 - move.w d3,d2 -;--------------------------- - -_mt_soundfx: - lea -sfx_sizeof(sp),sp - move.l a0,sfx_ptr(sp) - movem.w d0-d2,sfx_len(sp) - move.w #$ff01,sfx_cha(sp) ; any channel, priority=1 - move.l sp,a0 - bsr _mt_playfx_stub - lea sfx_sizeof(sp),sp - movem.l (sp)+,a3-a6 ; Restore registers for Blitz - rts - - -;--------------------------------------------------------------------------- -_mt_playfx_stub: - movem.l a3-a6,-(sp) - lea CUSTOM,a6 - move.l d0,a0 ; sfx-structure pointer - - xdef _mt_playfx -_mt_playfx: -; Request playing of a prioritized external sound effect, either on a -; fixed channel or on the most unused one. -; A negative channel specification means to use the best one. -; The priority is unsigned and should be greater than zero. -; This channel will be blocked for music until the effect has finished. -; a6 = CUSTOM -; a0 = sfx-structure pointer with the following layout: -; 0: ptr, 4: len.w, 6: period.w, 8: vol.w, 10: channel.b, 11: priority.b -; -> d0 = pointer to channel status or NULL when sample was ignored - - ifd SDATA - movem.l d2-d7/a0-a3/a5,-(sp) - else - movem.l d2-d7/a0-a5,-(sp) - lea mt_data(pc),a4 - endc - - move.w #$4000,INTENA(a6) - moveq #0,d0 - move.b sfx_cha(a0),d0 - bpl channelsfx ; use fixed channel for effect - - ; Did we already calculate the n_freecnt values for all channels? - tst.b mt_SilCntValid(a4) - bne freecnt_valid - - ; Look at the next 8 pattern steps to find the longest sequence - ; of silence (no new note or instrument). - moveq #8,d2 - move.l #$fffff000,d3 ; mask to ignore effects - - ; reset freecnts for all channels - moveq #0,d0 - move.b d0,mt_chan1+n_freecnt(a4) - move.b d0,mt_chan2+n_freecnt(a4) - move.b d0,mt_chan3+n_freecnt(a4) - move.b d0,mt_chan4+n_freecnt(a4) - moveq #-1,d4 - moveq #-1,d5 - moveq #-1,d6 - moveq #-1,d7 - - ; get pattern pointer - move.l mt_mod(a4),a3 ; a3 mod pointer - lea 952(a3),a5 ; a5 song arrangement list - move.w mt_PatternPos(a4),d1 - move.b mt_SongPos(a4),d0 -.1: move.b (a5,d0.w),d0 - swap d0 - lea 1084(a3),a1 - lsr.l #6,d0 - add.l d0,a1 - lea 1024(a1),a2 ; a2 end of pattern - add.w d1,a1 ; a1 current pattern pos - -.2: moveq #0,d0 - - move.l (a1)+,d1 - and.l d3,d1 - sne d1 - and.b d1,d4 - sub.b d4,mt_chan1+n_freecnt(a4) - add.b d4,d0 - - move.l (a1)+,d1 - and.l d3,d1 - sne d1 - and.b d1,d5 - sub.b d5,mt_chan2+n_freecnt(a4) - add.b d5,d0 - - move.l (a1)+,d1 - and.l d3,d1 - sne d1 - and.b d1,d6 - sub.b d6,mt_chan3+n_freecnt(a4) - add.b d6,d0 - - move.l (a1)+,d1 - and.l d3,d1 - sne d1 - and.b d1,d7 - sub.b d7,mt_chan4+n_freecnt(a4) - add.b d7,d0 - - ; break the loop when no channel has any more free pattern steps - beq .7 - - ; otherwise break after 8 pattern steps - subq.w #1,d2 - beq .7 - - ; End of pattern reached? Then load next pattern pointer. - cmp.l a2,a1 - blo .2 - moveq #0,d1 - moveq #1,d0 - add.b mt_SongPos(a4),d0 - and.w #$007f,d0 - cmp.b 950(a3),d0 ; end of song reached? - blo .1 - moveq #0,d0 - bra .1 - -.7: st mt_SilCntValid(a4) - -freecnt_valid: - sub.l a2,a2 - move.b sfx_pri(a0),d2 - - ; Determine which channels are already allocated for sound - ; effects and check if the limit was reached. In this case only - ; replace sound effect channels by higher priority. - moveq #3,d0 - sub.b mt_MusicChannels(a4),d0 - move.b mt_chan1+n_sfxpri(a4),d4 - or.b mt_chan1+n_musiconly(a4),d4 - sne d1 - add.b d1,d0 - move.b mt_chan2+n_sfxpri(a4),d5 - or.b mt_chan2+n_musiconly(a4),d5 - sne d1 - add.b d1,d0 - move.b mt_chan3+n_sfxpri(a4),d6 - or.b mt_chan3+n_musiconly(a4),d6 - sne d1 - add.b d1,d0 - move.b mt_chan4+n_sfxpri(a4),d7 - or.b mt_chan4+n_musiconly(a4),d7 - sne d1 - add.b d1,d0 - bmi .overwrite ; all channels reserved/playing effects - - ; We will prefer a music channel which had an audio interrupt, - ; because that means the last instrument sample has been played - ; completely, and the channel is now in an idle loop. - ; Also exclude channels which have set a repeat loop. - ; Try not to break them! - moveq #0,d3 - tst.b mt_chan1+n_looped(a4) - bne .1 - or.w #$0080,d3 -.1: tst.b mt_chan2+n_looped(a4) - bne .2 - or.w #$0100,d3 -.2: tst.b mt_chan3+n_looped(a4) - bne .3 - or.w #$0200,d3 -.3: tst.b mt_chan4+n_looped(a4) - bne .4 - or.w #$0400,d3 -.4: move.w INTREQR(a6),d1 - and.w d3,d1 - bne .6 - - ; All channels are busy. Then break the non-looped ones first... - move.w d3,d1 - bne .6 - - ; ..except there are none. Then it doesn't matter. :| - move.w #$0780,d1 - - ; first look for the best unused channel -.6: moveq #0,d3 - btst #7,d1 - seq d0 - or.b d4,d0 - bne .7 - lea mt_chan1+n_freecnt(a4),a1 - cmp.b (a1),d3 - bhi .7 - move.l a1,a2 - move.b (a1),d3 -.7: btst #8,d1 - seq d0 - or.b d5,d0 - bne .8 - lea mt_chan2+n_freecnt(a4),a1 - cmp.b (a1),d3 - bhi .8 - move.l a1,a2 - move.b (a1),d3 -.8: btst #9,d1 - seq d0 - or.b d6,d0 - bne .9 - lea mt_chan3+n_freecnt(a4),a1 - cmp.b (a1),d3 - bhi .9 - move.l a1,a2 - move.b (a1),d3 -.9: btst #10,d1 - seq d0 - or.b d7,d0 - bne .10 - lea mt_chan4+n_freecnt(a4),a1 - cmp.b (a1),d3 - bhi .10 - move.l a1,a2 - bra found_sfx_ch - -.10: move.l a2,d3 - bne found_sfx_ch - -.overwrite: - ; finally try to overwrite a sound effect with lower/equal priority - moveq #0,d3 - tst.b d4 - beq .11 - cmp.b d4,d2 - blo .11 - lea mt_chan1+n_freecnt(a4),a1 - cmp.b (a1),d3 - bhi .11 - move.l a1,a2 - move.b (a1),d3 -.11: tst.b d5 - beq .12 - cmp.b d5,d2 - blo .12 - lea mt_chan2+n_freecnt(a4),a1 - cmp.b (a1),d3 - bhi .12 - move.l a1,a2 - move.b (a1),d3 -.12: tst.b d6 - beq .13 - cmp.b d6,d2 - blo .13 - lea mt_chan3+n_freecnt(a4),a1 - cmp.b (a1),d3 - bhi .13 - move.l a1,a2 - move.b (a1),d3 -.13: tst.b d7 - beq .14 - cmp.b d7,d2 - blo .14 - lea mt_chan4+n_freecnt(a4),a1 - cmp.b (a1),d3 - bhi .14 - move.l a1,a2 - -.14: move.l a2,d3 - beq exit_playfx ; ignore new sfx due to low priority - -found_sfx_ch: - lea -n_freecnt(a2),a2 - bra set_sfx - -channelsfx: -; a0 = sfx structure -; d0 = fixed channel for new sound effect - add.w d0,d0 - lea mt_chan1(a4),a2 - add.w channel_offsets(pc,d0.w),a2 - - ; priority high enough to replace a present effect on this channel? - move.b sfx_pri(a0),d2 - cmp.b n_sfxpri(a2),d2 - bhs set_sfx - sub.l a2,a2 - bra exit_playfx - -set_sfx: -; activate the sound effect on this channel -; a0 = sfx structure -; d2 = sfx priority -; a2 = channel status - move.l (a0)+,n_sfxptr(a2) ; sfx_ptr - move.w (a0)+,n_sfxlen(a2) ; sfx_len - move.w (a0)+,n_sfxper(a2) ; sfx_per - move.w (a0),n_sfxvol(a2) ; sfx_vol - move.b d2,n_sfxpri(a2) - -exit_playfx: - move.w #$c000,INTENA(a6) - move.l a2,d0 ; ptr to selected channel or NULL - - ifd SDATA - movem.l (sp)+,d2-d7/a0-a3/a5 - else - movem.l (sp)+,d2-d7/a0-a5 - endc - - movem.l (sp)+,a3-a6 ; Restore registers for Blitz - - rts - -channel_offsets: - dc.w 0*n_sizeof,1*n_sizeof,2*n_sizeof,3*n_sizeof - -_mt_loopfx_stub: -;--- Blitz 2 call stub ----- - movem.l a3-a6,-(sp) - lea CUSTOM,a6 - move.l d0,a0 ; sfx-structure pointer -;--------------------------- -;--------------------------------------------------------------------------- - xdef _mt_loopfx -_mt_loopfx: -; Request playing of a looped sound effect on a fixed channel, which -; will be blocked for music until the effect is stopped (_mt_stopfx). -; It uses the same sfx-structure as _mt_playfx, but the priority is -; ignored. A looped sound effect has always highest priority and will -; replace a previous effect on the same channel. No automatic channel -; selection possible! -; Also make sure the sample starts with a zero-word, which is used -; for idling when the effect is stopped. This word is included in the -; total length calculation, but excluded when actually playing the loop. -; a6 = CUSTOM -; a0 = sfx-structure pointer with the following layout: -; 0: ptr, 4: len.w, 6: period.w, 8: vol.w, 10: channel.b - - ifnd SDATA - lea mt_data+mt_chan1(pc),a1 - else - lea mt_chan1(a4),a1 - endc - - moveq #3,d0 - and.b sfx_cha(a0),d0 - add.w d0,d0 - add.w channel_offsets(pc,d0.w),a1 - - move.w #$4000,INTENA(a6) - move.l (a0)+,n_sfxptr(a1) ; sfx_ptr - move.w (a0)+,n_sfxlen(a1) ; sfx_len - move.w (a0)+,n_sfxper(a1) ; sfx_per - move.w (a0),n_sfxvol(a1) ; sfx_vol - st n_sfxpri(a1) ; sfx_pri -1 enables looped mode - move.w #$c000,INTENA(a6) - - movem.l (sp)+,a3-a6 ; Restore registers before returning to Blitz - - rts - -_mt_stopfx_stub: -;--- Blitz 2 call stub ----- - movem.l a3-a6,-(sp) - lea CUSTOM,a6 -;--------------------------- -;--------------------------------------------------------------------------- - xdef _mt_stopfx -_mt_stopfx: -; Immediately stop a currently playing sound effect on a channel. -; a6 = CUSTOM -; d0.b = channel (0..3) - - ifnd SDATA - lea mt_data+mt_chan1(pc),a0 - else - lea mt_chan1(a4),a0 - endc - - and.w #3,d0 - add.w d0,d0 - add.w channel_offsets(pc,d0.w),a0 - - move.w #$4000,INTENA(a6) - tst.b n_sfxpri(a0) - beq .1 ; no sfx playing anyway - moveq #1,d0 - move.b d0,n_sfxpri(a0) - move.w d0,n_sfxlen(a0) ; idle loop - move.w #108,n_sfxper(a0) ; enter idle as quickly as possible - clr.w n_sfxvol(a0) ; and cut volume - ifne NULL_IS_CLEARED - clr.b n_looped(a0) - clr.l n_sfxptr(a0) ; use $0 for idle-looping - else - tst.b n_looped(a0) - beq .1 - clr.b n_looped(a0) - subq.l #2,n_sfxptr(a0) ; idle loop at sample-start - 2 - endc -.1: move.w #$c000,INTENA(a6) - - movem.l (sp)+,a3-a6 ; Restore registers before returning to Blitz - rts - - -;--------------------------------------------------------------------------- - xdef _mt_musicmask -_mt_musicmask: -; Set bits in the mask define which specific channels are reserved -; for music only. -; a6 = CUSTOM -; d0.b = channel-mask (bit 0 for channel 0, ..., bit 3 for channel 3) - - move.l a6,-(sp) ; Save A6 for Blitz - lea CUSTOM,a6 ; Prepare A6 for player - - ifnd SDATA - move.l a4,-(sp) - lea mt_data(pc),a4 - endc - - move.w #$4000,INTENA(a6) - - lsl.b #5,d0 - scs mt_chan4+n_musiconly(a4) - add.b d0,d0 - scs mt_chan3+n_musiconly(a4) - add.b d0,d0 - scs mt_chan2+n_musiconly(a4) - add.b d0,d0 - scs mt_chan1+n_musiconly(a4) - - move.w #$c000,INTENA(a6) - - ifnd SDATA - move.l (sp)+,a4 - endc - - move.l (sp)+,a6 ; Restore A6 for Blitz - - rts - - -;--------------------------------------------------------------------------- - xdef _mt_mastervol -_mt_mastervol: -; Set a master volume from 0 to 64 for all music channels. -; Note that the master volume does not affect the volume of external -; sound effects (which is desired). -; a6 = CUSTOM -; d0.w = master volume - - move.l a6,-(sp) ; Save A6 for Blitz - lea CUSTOM,a6 ; Prepare A6 for player - - ifnd SDATA - move.l a4,-(sp) - lea mt_data(pc),a4 - endc - - ; stingray, since each volume table has a size of 65 bytes - ; we simply multiply (optimised of course) by 65 to get the - ; offset to the correct table - lea MasterVolTab0(pc),a0 - add.w d0,a0 - lsl.w #6,d0 - add.w d0,a0 - - ; set new table and adapt all channel volumes immediately - move.w #$4000,INTENA(a6) - move.l a0,mt_MasterVolTab(a4) - bsr set_all_volumes - move.w #$c000,INTENA(a6) - - ifnd SDATA - move.l (sp)+,a4 - endc - - move.l (sp)+,a6 ; Restore A6 for Blitz - rts - - -;--------------------------------------------------------------------------- -_mt_channelmask_stub: -;--- Blitz 2 call stub ----- - movem.l a3-a6,-(sp) - lea CUSTOM,a6 -;--------------------------- - - xdef _mt_channelmask -_mt_channelmask: -; Define which music channels are muted. Doesn't affect sound effects. -; a6 = CUSTOM -; d0.b = channel-mask (bit 0 for channel 0, ..., bit 3 for channel 3) - - ifnd SDATA - move.l a4,-(sp) - lea mt_data(pc),a4 - endc - - move.w #$4000,INTENA(a6) - - lsl.b #5,d0 - scs mt_chan4+n_enable(a4) - add.b d0,d0 - scs mt_chan3+n_enable(a4) - add.b d0,d0 - scs mt_chan2+n_enable(a4) - add.b d0,d0 - scs mt_chan1+n_enable(a4) - move.l mt_MasterVolTab(a4),a0 - bsr set_all_volumes - - move.w #$c000,INTENA(a6) - - ifnd SDATA - move.l (sp)+,a4 - - movem.l (sp)+,a3-a6 ; Restore registers for Blitz - - endc - rts - - -;--------------------------------------------------------------------------- -set_all_volumes: -; a0 = Master volume table - - tst.b mt_chan1+n_sfxpri(a4) - bne .1 - move.w mt_chan1+n_volume(a4),d0 - move.b (a0,d0.w),d0 - and.b mt_chan1+n_enable(a4),d0 - move.w d0,AUD0VOL(a6) -.1: tst.b mt_chan2+n_sfxpri(a4) - bne .2 - move.w mt_chan2+n_volume(a4),d0 - move.b (a0,d0.w),d0 - and.b mt_chan2+n_enable(a4),d0 - move.w d0,AUD1VOL(a6) -.2: tst.b mt_chan3+n_sfxpri(a4) - bne .3 - move.w mt_chan3+n_volume(a4),d0 - move.b (a0,d0.w),d0 - and.b mt_chan3+n_enable(a4),d0 - move.w d0,AUD2VOL(a6) -.3: tst.b mt_chan4+n_sfxpri(a4) - bne .4 - move.w mt_chan4+n_volume(a4),d0 - move.b (a0,d0.w),d0 - and.b mt_chan4+n_enable(a4),d0 - move.w d0,AUD3VOL(a6) -.4: rts - - -;--------------------------------------------------------------------------- -_mt_samplevol_stub: -;--- Blitz 2 call stub ----- - movem.l a3-a6,-(sp) - lea CUSTOM,a6 -;--------------------------- - - xdef _mt_samplevol -_mt_samplevol: -; Redefine a sample's volume. May also be done while the song is playing. -; Warning: Does not check arguments for valid range! You must have done -; _mt_init before calling this function! -; The new volume is persistent. Even when the song is restarted. -; d0.w = sample number (0-31) -; d1.b = volume (0-64) - - ifd SDATA - move.l mt_mod(a4),a0 - else - move.l mt_data+mt_mod(pc),a0 - endc - - swap d1 - move.w d0,d1 - add.w d1,d1 - lsl.w #5,d0 - sub.w d1,d0 ; table index: sample number * 30 - swap d1 - move.b d1,12+3(a0,d0.w) ; set sample's volume - - movem.l (sp)+,a3-a6 ; Restore registers for Blitz - - rts - endc ; !MINIMAL - - -;--------------------------------------------------------------------------- - xdef _mt_music -_mt_music: -; Used when called by external interrupt handler. -; Saves/restores all registers and sets up A4 when needed. -; a6 = CUSTOM - - ifd SDATA - movem.l d2-d7/a2-a3/a5,-(sp) - bsr mt_music - movem.l (sp)+,d2-d7/a2-a3/a5 - else ; !SDATA - movem.l d2-d7/a2-a5,-(sp) - lea mt_data(pc),a4 - bsr mt_music - movem.l (sp)+,d2-d7/a2-a5 - endc - rts - -mt_music: -; Called from interrupt. -; Play next position when Counter equals Speed. -; Effects are always handled. -; a6 = CUSTOM - - moveq #0,d7 ; d7 is always zero - - lea mt_dmaon+1(pc),a0 - move.b d7,(a0) - - addq.b #1,mt_Counter(a4) - - move.b mt_Counter(a4),d0 - cmp.b mt_Speed(a4),d0 - blo no_new_note - - ; handle a new note - move.b d7,mt_Counter(a4) - tst.b mt_PattDelTime2(a4) - beq get_new_note - - ; we have a pattern delay, check effects then step - lea AUD0LC(a6),a5 - lea mt_chan1(a4),a2 - bsr mt_checkfx - lea AUD1LC(a6),a5 - lea mt_chan2(a4),a2 - bsr mt_checkfx - lea AUD2LC(a6),a5 - lea mt_chan3(a4),a2 - bsr mt_checkfx - lea AUD3LC(a6),a5 - lea mt_chan4(a4),a2 - bsr mt_checkfx - bra settb_step - -no_new_note: - ; no new note, just check effects, don't step to next position - lea AUD0LC(a6),a5 - lea mt_chan1(a4),a2 - bsr mt_checkfx - lea AUD1LC(a6),a5 - lea mt_chan2(a4),a2 - bsr mt_checkfx - lea AUD2LC(a6),a5 - lea mt_chan3(a4),a2 - bsr mt_checkfx - lea AUD3LC(a6),a5 - lea mt_chan4(a4),a2 - bsr mt_checkfx - - ; set one-shot TimerB interrupt for enabling DMA, when needed - move.b mt_dmaon+1(pc),d0 - beq same_pattern - move.b #$19,CIAB+CIACRB ; load/start timer B, one-shot - bra same_pattern - -get_new_note: - ; determine pointer to current pattern line - move.l mt_mod(a4),a0 - lea 12(a0),a3 ; sample info table - lea 1084(a0),a1 ; pattern data - lea 952(a0),a0 - moveq #0,d0 - move.b mt_SongPos(a4),d0 - move.b (a0,d0.w),d0 ; current pattern number - swap d0 - lsr.l #6,d0 - add.l d0,a1 ; pattern base - add.w mt_PatternPos(a4),a1 ; a1 pattern line - - ; play new note for each channel, apply some effects - lea AUD0LC(a6),a5 - lea mt_chan1(a4),a2 - bsr mt_playvoice - lea AUD1LC(a6),a5 - lea mt_chan2(a4),a2 - bsr mt_playvoice - lea AUD2LC(a6),a5 - lea mt_chan3(a4),a2 - bsr mt_playvoice - lea AUD3LC(a6),a5 - lea mt_chan4(a4),a2 - bsr mt_playvoice - -settb_step: - ; set one-shot TimerB interrupt for enabling DMA, when needed - move.b mt_dmaon+1(pc),d0 - beq pattern_step - move.b #$19,CIAB+CIACRB ; load/start timer B, one-shot - -pattern_step: - ; next pattern line, handle delay and break - ifeq MINIMAL - clr.b mt_SilCntValid(a4) ; recalculate silence counters - endc - moveq #16,d2 ; offset to next pattern line - - move.b mt_PattDelTime2(a4),d1 - move.b mt_PattDelTime(a4),d0 - beq .1 - move.b d0,d1 - move.b d7,mt_PattDelTime(a4) -.1: tst.b d1 - beq .3 - subq.b #1,d1 - beq .2 - moveq #0,d2 ; do not advance to next line -.2: move.b d1,mt_PattDelTime2(a4) - -.3: add.w mt_PatternPos(a4),d2 ; d2 PatternPos - - ; check for break - bclr d7,mt_PBreakFlag(a4) - beq .4 - move.w mt_PBreakPos(a4),d2 - move.w d7,mt_PBreakPos(a4) - - ; check whether end of pattern is reached -.4: move.w d2,mt_PatternPos(a4) - cmp.w #1024,d2 - blo same_pattern - -song_step: - move.w mt_PBreakPos(a4),mt_PatternPos(a4) - move.w d7,mt_PBreakPos(a4) - move.b d7,mt_PosJumpFlag(a4) - - ; next position in song - moveq #1,d0 - add.b mt_SongPos(a4),d0 - and.w #$007f,d0 - move.l mt_mod(a4),a0 - cmp.b 950(a0),d0 ; end of song reached? - blo .1 - moveq #0,d0 ; restart the song from the beginning - ifeq MINIMAL - addq.b #1,mt_SongEnd(a4) - bne .2 - clr.b mt_Enable(a4) ; stop the song when mt_SongEnd was -1 -.2: and.b #$7f,mt_SongEnd(a4) - endc -.1: move.b d0,mt_SongPos(a4) - -same_pattern: - tst.b mt_PosJumpFlag(a4) - bne song_step - - rts - - - ifeq MINIMAL -;--------------------------------------------------------------------------- -mt_sfxonly: -; Called from interrupt. -; Plays sound effects on free channels. -; a6 = CUSTOM - - moveq #0,d7 ; d7 is always zero - - lea mt_dmaon+1(pc),a0 - move.b d7,(a0) - - lea AUD0LC(a6),a5 - lea mt_chan1(a4),a2 - bsr chan_sfx_only - lea AUD1LC(a6),a5 - lea mt_chan2(a4),a2 - bsr chan_sfx_only - lea AUD2LC(a6),a5 - lea mt_chan3(a4),a2 - bsr chan_sfx_only - lea AUD3LC(a6),a5 - lea mt_chan4(a4),a2 - bsr chan_sfx_only - - move.b mt_dmaon+1(pc),d0 - beq .1 - move.b #$19,CIAB+CIACRB ; load/start timer B, one-shot - -.1: rts - - -chan_sfx_only: -; Check for new sound samples. Check if previous ones are finished. -; a2 = channel data -; a5 = audio registers - - tst.b n_sfxpri(a2) - beq .1 - - move.w n_sfxlen(a2),d0 - bne start_sfx - tst.b n_looped(a2) - bne .1 - - move.w n_intbit(a2),d0 - and.w INTREQR(a6),d0 - beq .1 - move.w n_dmabit(a2),d0 - and.w mt_dmaon(pc),d0 - bne .1 - - ; last sound effect sample has played, so unblock this channel again - move.b d7,n_sfxpri(a2) - -.1: rts - - -;--------------------------------------------------------------------------- -start_sfx: -; d0 = sfx_len in words -; a2 = channel data -; a5 = audio registers - - ; play new sound effect on this channel - move.w n_dmabit(a2),d1 - move.w d1,DMACON(a6) - - move.l n_sfxptr(a2),a0 - tst.b n_sfxpri(a2) - bpl .1 - - ; looped sound effect - st n_looped(a2) - addq.l #2,a0 ; skip first word, used for idling - subq.w #1,d0 - move.l a0,AUDLC(a5) - move.w d0,AUDLEN(a5) - bra .2 - - ; normal sound effect -.1: move.b d7,n_looped(a2) - move.l a0,AUDLC(a5) - move.w d0,AUDLEN(a5) - moveq #1,d0 ; idles after playing once - ifne NULL_IS_CLEARED - sub.l a0,a0 - endc - - ; save repeat and period for TimerB interrupt -.2: move.l a0,n_loopstart(a2) - move.w d0,n_replen(a2) - move.w n_sfxper(a2),d0 - move.w d0,AUDPER(a5) - move.w d0,n_period(a2) - move.w n_sfxvol(a2),AUDVOL(a5) - - move.w d7,n_sfxlen(a2) ; don't call start_sfx again - - lea mt_dmaon(pc),a0 - or.w d1,(a0) ; DMA-channel to enable on TimerB - rts - endc ; !MINIMAL - - -;--------------------------------------------------------------------------- -mt_checkfx: -; a2 = channel data -; a5 = audio registers - - ifeq MINIMAL - tst.b n_sfxpri(a2) - beq .3 - - move.w n_sfxlen(a2),d0 - beq .2 - bsr start_sfx - - ; channel is blocked, only check some E-commands -.1: move.w #$0fff,d4 - and.w n_cmd(a2),d4 - move.w d4,d0 - clr.b d0 - cmp.w #$0e00,d0 - bne mt_nop - and.w #$00ff,d4 - bra blocked_e_cmds - -.2: tst.b n_looped(a2) - bne .1 - move.w n_intbit(a2),d0 - and.w INTREQR(a6),d0 - beq .1 - move.w n_dmabit(a2),d0 - and.w mt_dmaon(pc),d0 - bne .1 - - ; sound effect sample has played, so unblock this channel again - move.b d7,n_sfxpri(a2) - endc ; !MINIMAL - - ; do channel effects between notes -.3: move.w n_funk(a2),d0 - beq .4 - bsr mt_updatefunk - -.4: move.w #$0fff,d4 - and.w n_cmd(a2),d4 - beq mt_pernop - and.w #$00ff,d4 - - moveq #$0f,d0 - and.b n_cmd(a2),d0 - add.w d0,d0 - move.w fx_tab(pc,d0.w),d0 - jmp fx_tab(pc,d0.w) - -fx_tab: - dc.w mt_arpeggio-fx_tab ; $0 - dc.w mt_portaup-fx_tab - dc.w mt_portadown-fx_tab - dc.w mt_toneporta-fx_tab - dc.w mt_vibrato-fx_tab ; $4 - dc.w mt_tonevolslide-fx_tab - dc.w mt_vibrvolslide-fx_tab - dc.w mt_tremolo-fx_tab - dc.w mt_nop-fx_tab ; $8 - dc.w mt_nop-fx_tab - dc.w mt_volumeslide-fx_tab - dc.w mt_nop-fx_tab - dc.w mt_nop-fx_tab ; $C - dc.w mt_nop-fx_tab - dc.w mt_e_cmds-fx_tab - dc.w mt_nop-fx_tab - - -mt_pernop: -; just set the current period - - move.w n_period(a2),AUDPER(a5) -mt_nop: - rts - - -;--------------------------------------------------------------------------- -mt_playvoice: -; a1 = pattern ptr -; a2 = channel data -; a3 = sample info table -; a5 = audio registers - - move.l (a1)+,d6 ; d6 current note/cmd words - - ifeq MINIMAL - ; channel blocked by external sound effect? - tst.b n_sfxpri(a2) - beq .2 - - move.w n_sfxlen(a2),d0 - beq .1 - bsr start_sfx - bra moreblockedfx - - ; do only some limited commands, while sound effect is in progress -.1: tst.b n_looped(a2) - bne moreblockedfx - move.w n_intbit(a2),d0 - and.w INTREQR(a6),d0 - beq moreblockedfx - move.w n_dmabit(a2),d0 - and.w mt_dmaon(pc),d0 - bne moreblockedfx - - ; sound effect sample has played, so unblock this channel again - move.b d7,n_sfxpri(a2) - endc ; !MINIMAL - -.2: tst.l (a2) ; n_note/cmd: any note or cmd set? - bne .3 - move.w n_period(a2),AUDPER(a5) -.3: move.l d6,(a2) - - moveq #15,d5 - and.b n_cmd(a2),d5 - add.w d5,d5 ; d5 cmd*2 - - moveq #0,d4 - move.b d6,d4 ; d4 cmd argument (in MSW) - swap d4 - move.w #$0ff0,d4 - and.w d6,d4 ; d4 for checking E-cmd (in LSW) - - swap d6 - move.l d6,d0 ; S...S... - clr.b d0 - rol.w #4,d0 - rol.l #4,d0 ; ....00SS - - and.w #$0fff,d6 ; d6 note - - ; get sample start address - add.w d0,d0 ; sample number * 2 - beq set_regs - move.w mult30tab(pc,d0.w),d1 ; d1 sample info table offset - lea mt_SampleStarts(a4),a0 - add.w d0,d0 - move.l -4(a0,d0.w),d2 - - ; read length, volume and repeat from sample info table - lea (a3,d1.w),a0 - move.w (a0)+,d0 ; length - bne .4 - - ifne NULL_IS_CLEARED - moveq #0,d2 ; use $0 for empty samples - else - ; use the first two bytes from the first sample for empty samples - move.l mt_SampleStarts(a4),d2 - endc - addq.w #1,d0 - -.4: move.l d2,n_start(a2) - move.w d0,n_reallength(a2) - - ; determine period table from fine-tune parameter - moveq #0,d3 - move.b (a0)+,d3 - add.w d3,d3 - move.l a0,d1 - lea mt_PerFineTune(pc),a0 - add.w (a0,d3.w),a0 - move.l a0,n_pertab(a2) - move.l d1,a0 - cmp.w #2*8,d3 - shs n_minusft(a2) - - moveq #0,d1 - move.b (a0)+,d1 ; volume - move.w d1,n_volume(a2) - move.w (a0)+,d3 ; repeat offset - beq no_offset - - ; set repeat - add.l d3,d2 - add.l d3,d2 - move.w (a0),d0 - move.w d0,n_replen(a2) - exg d0,d3 ; n_replen to d3 - add.w d3,d0 - bra set_len_start - -mult30tab: - dc.w 0*30,1*30,2*30,3*30,4*30,5*30,6*30,7*30 - dc.w 8*30,9*30,10*30,11*30,12*30,13*30,14*30,15*30 - dc.w 16*30,17*30,18*30,19*30,20*30,21*30,22*30,23*30 - dc.w 24*30,25*30,26*30,27*30,28*30,29*30,30*30,31*30 - -no_offset: - move.w (a0),d3 - ifne NULL_IS_CLEARED - cmp.w #1,d3 - beq .1 - bhi set_replen - else - bne set_replen - endc - ; repeat length zero means idle-looping - addq.w #1,d3 -.1: moveq #0,d2 ; expect two zero bytes at $0 -set_replen: - move.w d3,n_replen(a2) -set_len_start: - move.w d0,n_length(a2) - move.l d2,n_loopstart(a2) - move.l d2,n_wavestart(a2) - - ifeq MINIMAL - move.l mt_MasterVolTab(a4),a0 - move.b (a0,d1.w),d1 - and.b n_enable(a2),d1 - endc - move.w d1,AUDVOL(a5) - - ; remember if sample is looped - ; @@@ FIXME: also need to check if n_loopstart equals n_start - subq.w #1,d3 - sne n_looped(a2) - -set_regs: -; d4 = cmd argument | masked E-cmd -; d5 = cmd*2 -; d6 = cmd.w | note.w - - move.w d4,d3 ; d3 masked E-cmd - swap d4 ; d4 cmd argument into LSW - - tst.w d6 - beq checkmorefx ; no new note - - cmp.w #$0e50,d3 - beq set_finetune - - move.w prefx_tab(pc,d5.w),d0 - jmp prefx_tab(pc,d0.w) - -prefx_tab: - dc.w set_period-prefx_tab,set_period-prefx_tab,set_period-prefx_tab - dc.w set_toneporta-prefx_tab ; $3 - dc.w set_period-prefx_tab - dc.w set_toneporta-prefx_tab ; $5 - dc.w set_period-prefx_tab,set_period-prefx_tab,set_period-prefx_tab - dc.w set_sampleoffset-prefx_tab ; $9 - dc.w set_period-prefx_tab,set_period-prefx_tab,set_period-prefx_tab - dc.w set_period-prefx_tab,set_period-prefx_tab,set_period-prefx_tab - - -mt_sampleoffset: -; cmd 9 x y (xy = offset in 256 bytes) -; d4 = xy - - moveq #0,d0 - move.b d4,d0 - bne .1 - move.b n_sampleoffset(a2),d0 - bra .2 -.1: move.b d0,n_sampleoffset(a2) - -.2: lsl.w #7,d0 - cmp.w n_length(a2),d0 - bhs .3 - sub.w d0,n_length(a2) - add.w d0,d0 - add.l d0,n_start(a2) - rts - -.3: move.w #1,n_length(a2) - rts - - -set_sampleoffset: - bsr mt_sampleoffset - bra set_period - -set_finetune: - lea mt_PerFineTune(pc),a0 - moveq #$0f,d0 - and.b d4,d0 - add.w d0,d0 - add.w (a0,d0.w),a0 - move.l a0,n_pertab(a2) - cmp.w #2*8,d0 - shs n_minusft(a2) - -set_period: -; find nearest period for a note value, then apply finetuning -; d3 = masked E-cmd -; d4 = cmd argument -; d5 = cmd*2 -; d6 = note.w - - lea mt_PeriodTable(pc),a0 - moveq #36-1,d0 - moveq #-2,d1 -.1: addq.w #2,d1 ; table offset - cmp.w (a0)+,d6 - dbhs d0,.1 - - ; apply finetuning, set period and note-offset - move.l n_pertab(a2),a0 - move.w (a0,d1.w),d2 - move.w d2,n_period(a2) - move.w d1,n_noteoff(a2) - - ; check for notedelay - cmp.w #$0ed0,d3 ; notedelay - beq checkmorefx - - ; disable DMA - move.w n_dmabit(a2),d0 - move.w d0,DMACON(a6) - - btst #2,n_vibratoctrl(a2) - bne .2 - move.b d7,n_vibratopos(a2) - -.2: btst #2,n_tremoloctrl(a2) - bne .3 - move.b d7,n_tremolopos(a2) - -.3: move.l n_start(a2),AUDLC(a5) - move.w n_length(a2),AUDLEN(a5) - move.w d2,AUDPER(a5) - lea mt_dmaon(pc),a0 - or.w d0,(a0) - -checkmorefx: -; d4 = cmd argument -; d5 = cmd*2 -; d6 = note.w - - move.w n_funk(a2),d0 - beq .1 - bsr mt_updatefunk - -.1: move.w morefx_tab(pc,d5.w),d0 - jmp morefx_tab(pc,d0.w) - -morefx_tab: - dc.w mt_pernop-morefx_tab,mt_pernop-morefx_tab,mt_pernop-morefx_tab - dc.w mt_pernop-morefx_tab,mt_pernop-morefx_tab,mt_pernop-morefx_tab - dc.w mt_pernop-morefx_tab,mt_pernop-morefx_tab,mt_pernop-morefx_tab - dc.w mt_sampleoffset-morefx_tab ; $9 - dc.w mt_pernop-morefx_tab - dc.w mt_posjump-morefx_tab ; $B - dc.w mt_volchange-morefx_tab - dc.w mt_patternbrk-morefx_tab ; $D - dc.w mt_e_cmds-morefx_tab - dc.w mt_setspeed-morefx_tab - - -set_toneporta: - move.l n_pertab(a2),a0 ; tuned period table - - ; find first period which is less or equal the note in d6 - moveq #36-1,d0 - moveq #-2,d1 -.1: addq.w #2,d1 - cmp.w (a0)+,d6 - dbhs d0,.1 - - tst.b n_minusft(a2) ; negative fine tune? - beq .2 - tst.w d1 - beq .2 - subq.l #2,a0 ; then take previous period - subq.w #2,d1 - -.2: move.w d1,n_noteoff(a2) ; note offset in period table - move.w n_period(a2),d2 - move.w -(a0),d1 - cmp.w d1,d2 - bne .3 - moveq #0,d1 -.3: move.w d1,n_wantedperiod(a2) - - move.w n_funk(a2),d0 - beq .4 - bsr mt_updatefunk - -.4: move.w d2,AUDPER(a5) - rts - - - ifeq MINIMAL -moreblockedfx: -; d6 = note.w | cmd.w - - moveq #0,d4 - move.b d6,d4 ; cmd argument - and.w #$0f00,d6 - lsr.w #7,d6 - move.w blmorefx_tab(pc,d6.w),d0 - jmp blmorefx_tab(pc,d0.w) - -blmorefx_tab: - dc.w mt_nop-blmorefx_tab,mt_nop-blmorefx_tab - dc.w mt_nop-blmorefx_tab,mt_nop-blmorefx_tab - dc.w mt_nop-blmorefx_tab,mt_nop-blmorefx_tab - dc.w mt_nop-blmorefx_tab,mt_nop-blmorefx_tab - dc.w mt_nop-blmorefx_tab,mt_nop-blmorefx_tab - dc.w mt_nop-blmorefx_tab - dc.w mt_posjump-blmorefx_tab ; $B - dc.w mt_nop-blmorefx_tab - dc.w mt_patternbrk-blmorefx_tab ; $D - dc.w blocked_e_cmds-blmorefx_tab - dc.w mt_setspeed-blmorefx_tab ; $F - endc ; !MINIMAL - - -mt_arpeggio: -; cmd 0 x y (x = first arpeggio offset, y = second arpeggio offset) -; d4 = xy - - moveq #0,d0 - move.b mt_Counter(a4),d0 - move.b arptab(pc,d0.w),d0 - beq mt_pernop ; step 0, just use normal period - bmi .1 - - ; step 1, arpeggio by left nibble - lsr.b #4,d4 - bra .2 - - ; step 2, arpeggio by right nibble -.1: and.w #$000f,d4 - - ; offset current note -.2: add.w d4,d4 - add.w n_noteoff(a2),d4 - cmp.w #2*36,d4 - bhs .4 - - ; set period with arpeggio offset from note table - move.l n_pertab(a2),a0 - move.w (a0,d4.w),AUDPER(a5) -.4: rts - -arptab: - dc.b 0,1,-1,0,1,-1,0,1,-1,0,1,-1,0,1,-1,0 - dc.b 1,-1,0,1,-1,0,1,-1,0,1,-1,0,1,-1,0,1 - - -mt_fineportaup: -; cmd E 1 x (subtract x from period) -; d0 = x - - tst.b mt_Counter(a4) - beq do_porta_up - rts - - -mt_portaup: -; cmd 1 x x (subtract xx from period) -; d4 = xx - - move.w d4,d0 - -do_porta_up: - move.w n_period(a2),d1 - sub.w d0,d1 - cmp.w #113,d1 - bge .1 - moveq #113,d1 -.1: move.w d1,n_period(a2) - move.w d1,AUDPER(a5) - rts - - -mt_fineportadn: -; cmd E 2 x (add x to period) -; d0 = x - - tst.b mt_Counter(a4) - beq do_porta_down - rts - - -mt_portadown: -; cmd 2 x x (add xx to period) -; d4 = xx - - move.w d4,d0 - -do_porta_down: - move.w n_period(a2),d1 - add.w d0,d1 - cmp.w #856,d1 - bls .1 - move.w #856,d1 -.1: move.w d1,n_period(a2) - move.w d1,AUDPER(a5) - rts - - -mt_toneporta: -; cmd 3 x y (xy = tone portamento speed) -; d4 = xy - - tst.b d4 - beq mt_toneporta_nc - move.w d4,n_toneportspeed(a2) - move.b d7,n_cmdlo(a2) - -mt_toneporta_nc: - move.w n_wantedperiod(a2),d1 - beq .6 - - move.w n_toneportspeed(a2),d0 - move.w n_period(a2),d2 - cmp.w d1,d2 - blo .2 - - ; tone porta up - sub.w d0,d2 - cmp.w d1,d2 - bgt .3 - move.w d1,d2 - move.w d7,n_wantedperiod(a2) - bra .3 - - ; tone porta down -.2: add.w d0,d2 - cmp.w d1,d2 - blt .3 - move.w d1,d2 - move.w d7,n_wantedperiod(a2) - -.3: move.w d2,n_period(a2) - - tst.b n_gliss(a2) - beq .5 - - ; glissando: find nearest note for new period - move.l n_pertab(a2),a0 - moveq #36-1,d0 - moveq #-2,d1 -.4: addq.w #2,d1 - cmp.w (a0)+,d2 - dbhs d0,.4 - - move.w d1,n_noteoff(a2) ; @@@ needed? - move.w -(a0),d2 - -.5: move.w d2,AUDPER(a5) -.6 rts - - -mt_vibrato: -; cmd 4 x y (x = speed, y = amplitude) -; d4 = xy - - moveq #$0f,d2 - and.b d4,d2 - beq .1 - move.b d2,n_vibratoamp(a2) - bra .2 -.1: move.b n_vibratoamp(a2),d2 - -.2: lsr.b #4,d4 - beq .3 - move.b d4,n_vibratospd(a2) - bra mt_vibrato_nc -.3: move.b n_vibratospd(a2),d4 - -mt_vibrato_nc: - ; calculate vibrato table offset: 64 * amplitude + (pos & 63) - lsl.w #6,d2 - moveq #63,d0 - and.b n_vibratopos(a2),d0 - add.w d0,d2 - - ifne ENABLE_SAWRECT - ; select vibrato waveform - moveq #3,d1 - and.b n_vibratoctrl(a2),d1 - beq .6 - subq.b #1,d1 - beq .5 - - ; ctrl 2 & 3 select a rectangle vibrato - lea mt_VibratoRectTable(pc),a0 - bra .9 - - ; ctrl 1 selects a sawtooth vibrato -.5: lea mt_VibratoSawTable(pc),a0 - bra .9 - endc ; ENABLE_SAWRECT - - ; ctrl 0 selects a sine vibrato -.6: lea mt_VibratoSineTable(pc),a0 - - ; add vibrato-offset to period -.9: move.b (a0,d2.w),d0 - ext.w d0 - add.w n_period(a2),d0 - move.w d0,AUDPER(a5) - - ; increase vibratopos by speed - add.b d4,n_vibratopos(a2) - rts - - -mt_tonevolslide: -; cmd 5 x y (x = volume-up, y = volume-down) -; d4 = xy - - pea mt_volumeslide(pc) - bra mt_toneporta_nc - - -mt_vibrvolslide: -; cmd 6 x y (x = volume-up, y = volume-down) -; d4 = xy - - move.w d4,d3 - move.b n_vibratoamp(a2),d2 - move.b n_vibratospd(a2),d4 - bsr mt_vibrato_nc - - move.w d3,d4 - bra mt_volumeslide - - -mt_tremolo: -; cmd 7 x y (x = speed, y = amplitude) -; d4 = xy - - moveq #$0f,d2 - and.b d4,d2 - beq .1 - move.b d2,n_tremoloamp(a2) - bra .2 -.1: move.b n_tremoloamp(a2),d2 - -.2: lsr.b #4,d4 - beq .3 - move.b d4,n_tremolospd(a2) - bra .4 -.3: move.b n_tremolospd(a2),d4 - - ; calculate tremolo table offset: 64 * amplitude + (pos & 63) -.4: lsl.w #6,d2 - moveq #63,d0 - and.b n_tremolopos(a2),d0 - add.w d0,d2 - - ifne ENABLE_SAWRECT - ; select tremolo waveform - moveq #3,d1 - and.b n_tremoloctrl(a2),d1 - beq .6 - subq.b #1,d1 - beq .5 - - ; ctrl 2 & 3 select a rectangle tremolo - lea mt_VibratoRectTable(pc),a0 - bra .9 - - ; ctrl 1 selects a sawtooth tremolo -.5: lea mt_VibratoSawTable(pc),a0 - bra .9 - endc ; ENABLE_SAWRECT - - ; ctrl 0 selects a sine tremolo -.6: lea mt_VibratoSineTable(pc),a0 - - ; add tremolo-offset to volume -.9: move.w n_volume(a2),d0 - add.b (a0,d2.w),d0 - bpl .10 - moveq #0,d0 -.10: cmp.w #64,d0 - bls .11 - moveq #64,d0 -.11: move.w n_period(a2),AUDPER(a5) - ifeq MINIMAL - move.l mt_MasterVolTab(a4),a0 - move.b (a0,d0.w),d0 - and.b n_enable(a2),d0 - endc - move.w d0,AUDVOL(a5) - - ; increase tremolopos by speed - add.b d4,n_tremolopos(a2) - rts - - -mt_volumeslide: -; cmd A x y (x = volume-up, y = volume-down) -; d4 = xy - - move.w n_volume(a2),d0 - moveq #$0f,d1 - and.b d4,d1 - lsr.b #4,d4 - beq vol_slide_down - - ; slide up, until 64 - add.b d4,d0 -vol_slide_up: - cmp.b #64,d0 - bls set_vol - moveq #64,d0 - bra set_vol - - ; slide down, until 0 -vol_slide_down: - sub.b d1,d0 - bpl set_vol - moveq #0,d0 - -set_vol: - move.w d0,n_volume(a2) - move.w n_period(a2),AUDPER(a5) - ifeq MINIMAL - move.l mt_MasterVolTab(a4),a0 - move.b (a0,d0.w),d0 - and.b n_enable(a2),d0 - endc - move.w d0,AUDVOL(a5) - rts - - -mt_posjump: -; cmd B x y (xy = new song position) -; d4 = xy - - move.b d4,d0 - subq.b #1,d0 - move.b d0,mt_SongPos(a4) - -jump_pos0: - move.w d7,mt_PBreakPos(a4) - st mt_PosJumpFlag(a4) - rts - - -mt_volchange: -; cmd C x y (xy = new volume) -; d4 = xy - - cmp.w #64,d4 - bls .1 - moveq #64,d4 -.1: move.w d4,n_volume(a2) - ifeq MINIMAL - move.l mt_MasterVolTab(a4),a0 - move.b (a0,d4.w),d4 - and.b n_enable(a2),d4 - endc - move.w d4,AUDVOL(a5) - rts - - -mt_patternbrk: -; cmd D x y (xy = break pos in decimal) -; d4 = xy - - moveq #$0f,d0 - and.w d4,d0 - move.w d4,d1 - lsr.w #4,d1 - add.b mult10tab(pc,d1.w),d0 - cmp.b #63,d0 - bhi jump_pos0 - - lsl.w #4,d0 - move.w d0,mt_PBreakPos(a4) - st mt_PosJumpFlag(a4) - rts - -mult10tab: - dc.b 0,10,20,30,40,50,60,70,80,90,0,0,0,0,0,0 - - -mt_setspeed: -; cmd F x y (xy<$20 new speed, xy>=$20 new tempo) -; d4 = xy - - ifeq VBLANK_MUSIC - cmp.b #$20,d4 - bhs .1 - endc - - move.b d4,mt_Speed(a4) - beq _mt_end - rts - - ifeq VBLANK_MUSIC - ; set tempo (CIA only) -.1: and.w #$00ff,d4 - move.l mt_timerval(a4),d0 - divu d4,d0 - move.b d0,CIAB+CIATALO - lsr.w #8,d0 - move.b d0,CIAB+CIATAHI - rts - endc - - -mt_e_cmds: -; cmd E x y (x=command, y=argument) -; d4 = xy - - moveq #$0f,d0 - and.w d4,d0 ; pass E-cmd argument in d0 - - move.w d4,d1 - lsr.w #4,d1 - add.w d1,d1 - move.w ecmd_tab(pc,d1.w),d1 - jmp ecmd_tab(pc,d1.w) - -ecmd_tab: - dc.w mt_filter-ecmd_tab - dc.w mt_fineportaup-ecmd_tab - dc.w mt_fineportadn-ecmd_tab - dc.w mt_glissctrl-ecmd_tab - dc.w mt_vibratoctrl-ecmd_tab - dc.w mt_finetune-ecmd_tab - dc.w mt_jumploop-ecmd_tab - dc.w mt_tremoctrl-ecmd_tab - dc.w mt_e8-ecmd_tab - dc.w mt_retrignote-ecmd_tab - dc.w mt_volfineup-ecmd_tab - dc.w mt_volfinedn-ecmd_tab - dc.w mt_notecut-ecmd_tab - dc.w mt_notedelay-ecmd_tab - dc.w mt_patterndelay-ecmd_tab - dc.w mt_funk-ecmd_tab - - -blocked_e_cmds: -; cmd E x y (x=command, y=argument) -; d4 = xy - - moveq #$0f,d0 - and.w d4,d0 ; pass E-cmd argument in d0 - - move.w d4,d1 - lsr.w #4,d1 - add.w d1,d1 - move.w blecmd_tab(pc,d1.w),d1 - jmp blecmd_tab(pc,d1.w) - -blecmd_tab: - dc.w mt_filter-blecmd_tab - dc.w mt_rts-blecmd_tab - dc.w mt_rts-blecmd_tab - dc.w mt_glissctrl-blecmd_tab - dc.w mt_vibratoctrl-blecmd_tab - dc.w mt_finetune-blecmd_tab - dc.w mt_jumploop-blecmd_tab - dc.w mt_tremoctrl-blecmd_tab - dc.w mt_e8-blecmd_tab - dc.w mt_rts-blecmd_tab - dc.w mt_rts-blecmd_tab - dc.w mt_rts-blecmd_tab - dc.w mt_rts-blecmd_tab - dc.w mt_rts-blecmd_tab - dc.w mt_patterndelay-blecmd_tab - dc.w mt_rts-blecmd_tab - - -mt_filter: -; cmd E 0 x (x=1 disable, x=0 enable) -; d0 = x - - lsr.b #1,d0 - bcs .1 - bclr #2,CIAA+CIAPRA - rts -.1: bset #2,CIAA+CIAPRA -mt_rts: - rts - - -mt_glissctrl: -; cmd E 3 x (x gliss) -; d0 = x - - move.b d0,n_gliss(a2) - rts - - -mt_vibratoctrl: -; cmd E 4 x (x = vibrato) -; d0 = x - - move.b d0,n_vibratoctrl(a2) - rts - - -mt_finetune: -; cmd E 5 x (x = finetune) -; d0 = x - - lea mt_PerFineTune(pc),a0 - add.w d0,d0 - add.w (a0,d0.w),a0 - move.l a0,n_pertab(a2) - cmp.w #2*8,d0 - shs n_minusft(a2) - rts - - -mt_jumploop: -; cmd E 6 x (x=0 loop start, else loop count) -; d0 = x - - tst.b mt_Counter(a4) - bne .4 - -.1: tst.b d0 - beq .3 ; set start - - ; otherwise we are at the end of the loop - subq.b #1,n_loopcount(a2) - beq .4 ; loop finished - bpl .2 - - ; initialize loop counter - move.b d0,n_loopcount(a2) - - ; jump back to start of loop -.2: move.w n_pattpos(a2),mt_PBreakPos(a4) - st mt_PBreakFlag(a4) - rts - - ; remember start of loop position -.3: move.w mt_PatternPos(a4),n_pattpos(a2) -.4: rts - - -mt_tremoctrl: -; cmd E 7 x (x = tremolo) -; d0 = x - - move.b d0,n_tremoloctrl(a2) - rts - - -mt_e8: -; cmd E 8 x (x = trigger value) -; d0 = x - - move.b d0,mt_E8Trigger(a4) - rts - - -mt_retrignote: -; cmd E 9 x (x = retrigger count) -; d0 = x - - tst.b d0 - beq .1 - - ; set new retrigger count when Counter=0 - tst.b mt_Counter(a4) - bne .2 - move.b d0,n_retrigcount(a2) - - ; avoid double retrigger, when Counter=0 and a note was set - move.w #$0fff,d2 - and.w (a2),d2 - beq do_retrigger -.1: rts - - ; check if retrigger count is reached -.2: subq.b #1,n_retrigcount(a2) - bne .1 - move.b d0,n_retrigcount(a2) ; reset - -do_retrigger: - ; DMA off, set sample pointer and length - move.w n_dmabit(a2),d0 - move.w d0,DMACON(a6) - move.l n_start(a2),AUDLC(a5) - move.w n_length(a2),AUDLEN(a5) - lea mt_dmaon(pc),a0 - or.w d0,(a0) - rts - - -mt_volfineup: -; cmd E A x (x = volume add) -; d0 = x - - tst.b mt_Counter(a4) - beq .1 - rts - -.1: add.w n_volume(a2),d0 - bra vol_slide_up - - -mt_volfinedn: -; cmd E B x (x = volume sub) -; d0 = x - - tst.b mt_Counter(a4) - beq .1 - rts - -.1: move.b d0,d1 - move.w n_volume(a2),d0 - bra vol_slide_down - - -mt_notecut: -; cmd E C x (x = counter to cut at) -; d0 = x - - cmp.b mt_Counter(a4),d0 - bne .1 - move.w d7,n_volume(a2) - move.w d7,AUDVOL(a5) -.1: rts - - -mt_notedelay: -; cmd E D x (x = counter to retrigger at) -; d0 = x - - cmp.b mt_Counter(a4),d0 - bne .1 - tst.w (a2) ; trigger note when given - bne .2 -.1: rts -.2: move.w n_period(a2),AUDPER(a5) - bra do_retrigger - - -mt_patterndelay: -; cmd E E x (x = delay count) -; d0 = x - - tst.b mt_Counter(a4) - bne .1 - tst.b mt_PattDelTime2(a4) - bne .1 - addq.b #1,d0 - move.b d0,mt_PattDelTime(a4) -.1: rts - - -mt_funk: -; cmd E F x (x = funk speed) -; d0 = x - - tst.b mt_Counter(a4) - bne .1 - move.w d0,n_funk(a2) - bne mt_updatefunk -.1: rts - -mt_updatefunk: -; d0 = funk speed - - move.b mt_FunkTable(pc,d0.w),d0 - add.b d0,n_funkoffset(a2) - bpl .2 - move.b d7,n_funkoffset(a2) - - move.l n_loopstart(a2),d0 - moveq #0,d1 - move.w n_replen(a2),d1 - add.l d1,d1 - add.l d0,d1 - move.l n_wavestart(a2),a0 - addq.l #1,a0 - cmp.l d1,a0 - blo .1 - move.l d0,a0 -.1: move.l a0,n_wavestart(a2) - not.b (a0) - -.2: rts - - -mt_FunkTable: - dc.b 0,5,6,7,8,10,11,13,16,19,22,26,32,43,64,128 - -mt_VibratoSineTable: - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1 - dc.b 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0 - dc.b 0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - dc.b -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,0,0,0 - dc.b 0,0,0,1,1,1,2,2,2,3,3,3,3,3,3,3 - dc.b 3,3,3,3,3,3,3,3,2,2,2,1,1,1,0,0 - dc.b 0,0,0,-1,-1,-1,-2,-2,-2,-3,-3,-3,-3,-3,-3,-3 - dc.b -3,-3,-3,-3,-3,-3,-3,-3,-2,-2,-2,-1,-1,-1,0,0 - dc.b 0,0,1,1,2,2,3,3,4,4,4,5,5,5,5,5 - dc.b 5,5,5,5,5,5,4,4,4,3,3,2,2,1,1,0 - dc.b 0,0,-1,-1,-2,-2,-3,-3,-4,-4,-4,-5,-5,-5,-5,-5 - dc.b -5,-5,-5,-5,-5,-5,-4,-4,-4,-3,-3,-2,-2,-1,-1,0 - dc.b 0,0,1,2,3,3,4,5,5,6,6,7,7,7,7,7 - dc.b 7,7,7,7,7,7,6,6,5,5,4,3,3,2,1,0 - dc.b 0,0,-1,-2,-3,-3,-4,-5,-5,-6,-6,-7,-7,-7,-7,-7 - dc.b -7,-7,-7,-7,-7,-7,-6,-6,-5,-5,-4,-3,-3,-2,-1,0 - dc.b 0,0,1,2,3,4,5,6,7,7,8,8,9,9,9,9 - dc.b 9,9,9,9,9,8,8,7,7,6,5,4,3,2,1,0 - dc.b 0,0,-1,-2,-3,-4,-5,-6,-7,-7,-8,-8,-9,-9,-9,-9 - dc.b -9,-9,-9,-9,-9,-8,-8,-7,-7,-6,-5,-4,-3,-2,-1,0 - dc.b 0,1,2,3,4,5,6,7,8,9,9,10,11,11,11,11 - dc.b 11,11,11,11,11,10,9,9,8,7,6,5,4,3,2,1 - dc.b 0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-9,-10,-11,-11,-11,-11 - dc.b -11,-11,-11,-11,-11,-10,-9,-9,-8,-7,-6,-5,-4,-3,-2,-1 - dc.b 0,1,2,4,5,6,7,8,9,10,11,12,12,13,13,13 - dc.b 13,13,13,13,12,12,11,10,9,8,7,6,5,4,2,1 - dc.b 0,-1,-2,-4,-5,-6,-7,-8,-9,-10,-11,-12,-12,-13,-13,-13 - dc.b -13,-13,-13,-13,-12,-12,-11,-10,-9,-8,-7,-6,-5,-4,-2,-1 - dc.b 0,1,3,4,6,7,8,10,11,12,13,14,14,15,15,15 - dc.b 15,15,15,15,14,14,13,12,11,10,8,7,6,4,3,1 - dc.b 0,-1,-3,-4,-6,-7,-8,-10,-11,-12,-13,-14,-14,-15,-15,-15 - dc.b -15,-15,-15,-15,-14,-14,-13,-12,-11,-10,-8,-7,-6,-4,-3,-1 - dc.b 0,1,3,5,6,8,9,11,12,13,14,15,16,17,17,17 - dc.b 17,17,17,17,16,15,14,13,12,11,9,8,6,5,3,1 - dc.b 0,-1,-3,-5,-6,-8,-9,-11,-12,-13,-14,-15,-16,-17,-17,-17 - dc.b -17,-17,-17,-17,-16,-15,-14,-13,-12,-11,-9,-8,-6,-5,-3,-1 - dc.b 0,1,3,5,7,9,11,12,14,15,16,17,18,19,19,19 - dc.b 19,19,19,19,18,17,16,15,14,12,11,9,7,5,3,1 - dc.b 0,-1,-3,-5,-7,-9,-11,-12,-14,-15,-16,-17,-18,-19,-19,-19 - dc.b -19,-19,-19,-19,-18,-17,-16,-15,-14,-12,-11,-9,-7,-5,-3,-1 - dc.b 0,2,4,6,8,10,12,13,15,16,18,19,20,20,21,21 - dc.b 21,21,21,20,20,19,18,16,15,13,12,10,8,6,4,2 - dc.b 0,-2,-4,-6,-8,-10,-12,-13,-15,-16,-18,-19,-20,-20,-21,-21 - dc.b -21,-21,-21,-20,-20,-19,-18,-16,-15,-13,-12,-10,-8,-6,-4,-2 - dc.b 0,2,4,6,9,11,13,15,16,18,19,21,22,22,23,23 - dc.b 23,23,23,22,22,21,19,18,16,15,13,11,9,6,4,2 - dc.b 0,-2,-4,-6,-9,-11,-13,-15,-16,-18,-19,-21,-22,-22,-23,-23 - dc.b -23,-23,-23,-22,-22,-21,-19,-18,-16,-15,-13,-11,-9,-6,-4,-2 - dc.b 0,2,4,7,9,12,14,16,18,20,21,22,23,24,25,25 - dc.b 25,25,25,24,23,22,21,20,18,16,14,12,9,7,4,2 - dc.b 0,-2,-4,-7,-9,-12,-14,-16,-18,-20,-21,-22,-23,-24,-25,-25 - dc.b -25,-25,-25,-24,-23,-22,-21,-20,-18,-16,-14,-12,-9,-7,-4,-2 - dc.b 0,2,5,8,10,13,15,17,19,21,23,24,25,26,27,27 - dc.b 27,27,27,26,25,24,23,21,19,17,15,13,10,8,5,2 - dc.b 0,-2,-5,-8,-10,-13,-15,-17,-19,-21,-23,-24,-25,-26,-27,-27 - dc.b -27,-27,-27,-26,-25,-24,-23,-21,-19,-17,-15,-13,-10,-8,-5,-2 - dc.b 0,2,5,8,11,14,16,18,21,23,24,26,27,28,29,29 - dc.b 29,29,29,28,27,26,24,23,21,18,16,14,11,8,5,2 - dc.b 0,-2,-5,-8,-11,-14,-16,-18,-21,-23,-24,-26,-27,-28,-29,-29 - dc.b -29,-29,-29,-28,-27,-26,-24,-23,-21,-18,-16,-14,-11,-8,-5,-2 - - ifne ENABLE_SAWRECT -mt_VibratoSawTable: - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 - dc.b -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1 - dc.b 2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 - dc.b -3,-3,-3,-3,-3,-3,-3,-3,-2,-2,-2,-2,-2,-2,-2,-2 - dc.b -1,-1,-1,-1,-1,-1,-1,-1,0,0,0,0,0,0,0,0 - dc.b 0,0,0,0,0,0,1,1,1,1,1,2,2,2,2,2 - dc.b 3,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5 - dc.b -5,-5,-5,-5,-5,-5,-4,-4,-4,-4,-4,-3,-3,-3,-3,-3 - dc.b -2,-2,-2,-2,-2,-2,-1,-1,-1,-1,-1,0,0,0,0,0 - dc.b 0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3 - dc.b 4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7 - dc.b -7,-7,-7,-7,-6,-6,-6,-6,-5,-5,-5,-5,-4,-4,-4,-4 - dc.b -3,-3,-3,-3,-2,-2,-2,-2,-1,-1,-1,-1,0,0,0,0 - dc.b 0,0,0,0,1,1,1,2,2,2,3,3,3,4,4,4 - dc.b 5,5,5,5,6,6,6,7,7,7,8,8,8,9,9,9 - dc.b -9,-9,-9,-9,-8,-8,-8,-7,-7,-7,-6,-6,-6,-5,-5,-5 - dc.b -4,-4,-4,-4,-3,-3,-3,-2,-2,-2,-1,-1,-1,0,0,0 - dc.b 0,0,0,1,1,1,2,2,3,3,3,4,4,4,5,5 - dc.b 6,6,6,7,7,7,8,8,9,9,9,10,10,10,11,11 - dc.b -11,-11,-11,-10,-10,-10,-9,-9,-8,-8,-8,-7,-7,-7,-6,-6 - dc.b -5,-5,-5,-4,-4,-4,-3,-3,-2,-2,-2,-1,-1,-1,0,0 - dc.b 0,0,0,1,1,2,2,3,3,3,4,4,5,5,6,6 - dc.b 7,7,7,8,8,9,9,10,10,10,11,11,12,12,13,13 - dc.b -13,-13,-13,-12,-12,-11,-11,-10,-10,-10,-9,-9,-8,-8,-7,-7 - dc.b -6,-6,-6,-5,-5,-4,-4,-3,-3,-3,-2,-2,-1,-1,0,0 - dc.b 0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7 - dc.b 8,8,9,9,10,10,11,11,12,12,13,13,14,14,15,15 - dc.b -15,-15,-14,-14,-13,-13,-12,-12,-11,-11,-10,-10,-9,-9,-8,-8 - dc.b -7,-7,-6,-6,-5,-5,-4,-4,-3,-3,-2,-2,-1,-1,0,0 - dc.b 0,0,1,1,2,2,3,3,4,5,5,6,6,7,7,8 - dc.b 9,9,10,10,11,11,12,12,13,14,14,15,15,16,16,17 - dc.b -17,-17,-16,-16,-15,-15,-14,-13,-13,-12,-12,-11,-11,-10,-10,-9 - dc.b -8,-8,-7,-7,-6,-6,-5,-4,-4,-3,-3,-2,-2,-1,-1,0 - dc.b 0,0,1,1,2,3,3,4,5,5,6,6,7,8,8,9 - dc.b 10,10,11,11,12,13,13,14,15,15,16,16,17,18,18,19 - dc.b -19,-19,-18,-18,-17,-16,-16,-15,-14,-14,-13,-13,-12,-11,-11,-10 - dc.b -9,-9,-8,-8,-7,-6,-6,-5,-4,-4,-3,-3,-2,-1,-1,0 - dc.b 0,0,1,2,2,3,4,4,5,6,6,7,8,8,9,10 - dc.b 11,11,12,13,13,14,15,15,16,17,17,18,19,19,20,21 - dc.b -21,-21,-20,-19,-19,-18,-17,-17,-16,-15,-15,-14,-13,-12,-12,-11 - dc.b -10,-10,-9,-8,-8,-7,-6,-6,-5,-4,-4,-3,-2,-1,-1,0 - dc.b 0,0,1,2,3,3,4,5,6,6,7,8,9,9,10,11 - dc.b 12,12,13,14,15,15,16,17,18,18,19,20,21,21,22,23 - dc.b -23,-23,-22,-21,-20,-20,-19,-18,-17,-17,-16,-15,-14,-14,-13,-12 - dc.b -11,-11,-10,-9,-8,-8,-7,-6,-5,-5,-4,-3,-2,-2,-1,0 - dc.b 0,0,1,2,3,4,4,5,6,7,8,8,9,10,11,12 - dc.b 13,13,14,15,16,17,17,18,19,20,21,21,22,23,24,25 - dc.b -25,-25,-24,-23,-22,-21,-21,-20,-19,-18,-17,-16,-16,-15,-14,-13 - dc.b -12,-12,-11,-10,-9,-8,-8,-7,-6,-5,-4,-3,-3,-2,-1,0 - dc.b 0,0,1,2,3,4,5,6,7,7,8,9,10,11,12,13 - dc.b 14,14,15,16,17,18,19,20,21,21,22,23,24,25,26,27 - dc.b -27,-27,-26,-25,-24,-23,-22,-21,-20,-20,-19,-18,-17,-16,-15,-14 - dc.b -13,-13,-12,-11,-10,-9,-8,-7,-6,-6,-5,-4,-3,-2,-1,0 - dc.b 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14 - dc.b 15,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29 - dc.b -29,-28,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15 - dc.b -14,-13,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0 - -mt_VibratoRectTable: - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 - dc.b 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 - dc.b -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - dc.b -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - dc.b 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 - dc.b 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 - dc.b -3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3 - dc.b -3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3 - dc.b 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 - dc.b 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 - dc.b -5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5 - dc.b -5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5 - dc.b 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 - dc.b 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 - dc.b -7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7 - dc.b -7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7 - dc.b 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 - dc.b 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 - dc.b -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 - dc.b -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 - dc.b 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 - dc.b 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 - dc.b -11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11 - dc.b -11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11 - dc.b 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 - dc.b 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 - dc.b -13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13 - dc.b -13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13 - dc.b 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15 - dc.b 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15 - dc.b -15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15 - dc.b -15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15 - dc.b 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 - dc.b 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 - dc.b -17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17 - dc.b -17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17 - dc.b 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19 - dc.b 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19 - dc.b -19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19 - dc.b -19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19 - dc.b 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21 - dc.b 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21 - dc.b -21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21 - dc.b -21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21 - dc.b 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23 - dc.b 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23 - dc.b -23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23 - dc.b -23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23 - dc.b 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25 - dc.b 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25 - dc.b -25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25 - dc.b -25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25 - dc.b 27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27 - dc.b 27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27 - dc.b -27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27 - dc.b -27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27 - dc.b 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 - dc.b 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 - dc.b -29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29 - dc.b -29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29 - endc ; ENABLE_SAWRECT - - -mt_PerFineTune: - dc.w mt_Tuning0-mt_PerFineTune,mt_Tuning1-mt_PerFineTune - dc.w mt_Tuning2-mt_PerFineTune,mt_Tuning3-mt_PerFineTune - dc.w mt_Tuning4-mt_PerFineTune,mt_Tuning5-mt_PerFineTune - dc.w mt_Tuning6-mt_PerFineTune,mt_Tuning7-mt_PerFineTune - dc.w mt_TuningM8-mt_PerFineTune,mt_TuningM7-mt_PerFineTune - dc.w mt_TuningM6-mt_PerFineTune,mt_TuningM5-mt_PerFineTune - dc.w mt_TuningM4-mt_PerFineTune,mt_TuningM3-mt_PerFineTune - dc.w mt_TuningM2-mt_PerFineTune,mt_TuningM1-mt_PerFineTune - -mt_PeriodTable: -mt_Tuning0: ; Tuning 0, Normal c-1 - b3 - dc.w 856,808,762,720,678,640,604,570,538,508,480,453 - dc.w 428,404,381,360,339,320,302,285,269,254,240,226 - dc.w 214,202,190,180,170,160,151,143,135,127,120,113 -mt_Tuning1: - dc.w 850,802,757,715,674,637,601,567,535,505,477,450 - dc.w 425,401,379,357,337,318,300,284,268,253,239,225 - dc.w 213,201,189,179,169,159,150,142,134,126,119,113 -mt_Tuning2: - dc.w 844,796,752,709,670,632,597,563,532,502,474,447 - dc.w 422,398,376,355,335,316,298,282,266,251,237,224 - dc.w 211,199,188,177,167,158,149,141,133,125,118,112 -mt_Tuning3: - dc.w 838,791,746,704,665,628,592,559,528,498,470,444 - dc.w 419,395,373,352,332,314,296,280,264,249,235,222 - dc.w 209,198,187,176,166,157,148,140,132,125,118,111 -mt_Tuning4: - dc.w 832,785,741,699,660,623,588,555,524,495,467,441 - dc.w 416,392,370,350,330,312,294,278,262,247,233,220 - dc.w 208,196,185,175,165,156,147,139,131,124,117,110 -mt_Tuning5: - dc.w 826,779,736,694,655,619,584,551,520,491,463,437 - dc.w 413,390,368,347,328,309,292,276,260,245,232,219 - dc.w 206,195,184,174,164,155,146,138,130,123,116,109 -mt_Tuning6: - dc.w 820,774,730,689,651,614,580,547,516,487,460,434 - dc.w 410,387,365,345,325,307,290,274,258,244,230,217 - dc.w 205,193,183,172,163,154,145,137,129,122,115,109 -mt_Tuning7: - dc.w 814,768,725,684,646,610,575,543,513,484,457,431 - dc.w 407,384,363,342,323,305,288,272,256,242,228,216 - dc.w 204,192,181,171,161,152,144,136,128,121,114,108 -mt_TuningM8: - dc.w 907,856,808,762,720,678,640,604,570,538,508,480 - dc.w 453,428,404,381,360,339,320,302,285,269,254,240 - dc.w 226,214,202,190,180,170,160,151,143,135,127,120 -mt_TuningM7: - dc.w 900,850,802,757,715,675,636,601,567,535,505,477 - dc.w 450,425,401,379,357,337,318,300,284,268,253,238 - dc.w 225,212,200,189,179,169,159,150,142,134,126,119 -mt_TuningM6: - dc.w 894,844,796,752,709,670,632,597,563,532,502,474 - dc.w 447,422,398,376,355,335,316,298,282,266,251,237 - dc.w 223,211,199,188,177,167,158,149,141,133,125,118 -mt_TuningM5: - dc.w 887,838,791,746,704,665,628,592,559,528,498,470 - dc.w 444,419,395,373,352,332,314,296,280,264,249,235 - dc.w 222,209,198,187,176,166,157,148,140,132,125,118 -mt_TuningM4: - dc.w 881,832,785,741,699,660,623,588,555,524,494,467 - dc.w 441,416,392,370,350,330,312,294,278,262,247,233 - dc.w 220,208,196,185,175,165,156,147,139,131,123,117 -mt_TuningM3: - dc.w 875,826,779,736,694,655,619,584,551,520,491,463 - dc.w 437,413,390,368,347,328,309,292,276,260,245,232 - dc.w 219,206,195,184,174,164,155,146,138,130,123,116 -mt_TuningM2: - dc.w 868,820,774,730,689,651,614,580,547,516,487,460 - dc.w 434,410,387,365,345,325,307,290,274,258,244,230 - dc.w 217,205,193,183,172,163,154,145,137,129,122,115 -mt_TuningM1: - dc.w 862,814,768,725,684,646,610,575,543,513,484,457 - dc.w 431,407,384,363,342,323,305,288,272,256,242,228 - dc.w 216,203,192,181,171,161,152,144,136,128,121,114 - - ifeq MINIMAL -MasterVolTab0: - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 0 -MasterVolTab1: - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 1 -MasterVolTab2: - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 - dc.b 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 - dc.b 2 -MasterVolTab3: - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1 - dc.b 1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2 - dc.b 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 - dc.b 3 -MasterVolTab4: - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - dc.b 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 - dc.b 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 - dc.b 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 - dc.b 4 -MasterVolTab5: - dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1 - dc.b 1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2 - dc.b 2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3 - dc.b 3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4 - dc.b 5 -MasterVolTab6: - dc.b 0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1 - dc.b 1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2 - dc.b 3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4 - dc.b 4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5 - dc.b 6 -MasterVolTab7: - dc.b 0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1 - dc.b 1,1,1,2,2,2,2,2,2,2,2,2,3,3,3,3 - dc.b 3,3,3,3,3,4,4,4,4,4,4,4,4,4,5,5 - dc.b 5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6 - dc.b 7 -MasterVolTab8: - dc.b 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1 - dc.b 2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 - dc.b 4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5 - dc.b 6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7 - dc.b 8 -MasterVolTab9: - dc.b 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,2 - dc.b 2,2,2,2,2,2,3,3,3,3,3,3,3,4,4,4 - dc.b 4,4,4,4,5,5,5,5,5,5,5,6,6,6,6,6 - dc.b 6,6,7,7,7,7,7,7,7,8,8,8,8,8,8,8 - dc.b 9 -MasterVolTab10: - dc.b 0,0,0,0,0,0,0,1,1,1,1,1,1,2,2,2 - dc.b 2,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4 - dc.b 5,5,5,5,5,5,5,6,6,6,6,6,6,7,7,7 - dc.b 7,7,7,7,8,8,8,8,8,8,9,9,9,9,9,9 - dc.b 10 -MasterVolTab11: - dc.b 0,0,0,0,0,0,1,1,1,1,1,1,2,2,2,2 - dc.b 2,2,3,3,3,3,3,3,4,4,4,4,4,4,5,5 - dc.b 5,5,5,6,6,6,6,6,6,7,7,7,7,7,7,8 - dc.b 8,8,8,8,8,9,9,9,9,9,9,10,10,10,10,10 - dc.b 11 -MasterVolTab12: - dc.b 0,0,0,0,0,0,1,1,1,1,1,2,2,2,2,2 - dc.b 3,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5 - dc.b 6,6,6,6,6,6,7,7,7,7,7,8,8,8,8,8 - dc.b 9,9,9,9,9,9,10,10,10,10,10,11,11,11,11,11 - dc.b 12 -MasterVolTab13: - dc.b 0,0,0,0,0,1,1,1,1,1,2,2,2,2,2,3 - dc.b 3,3,3,3,4,4,4,4,4,5,5,5,5,5,6,6 - dc.b 6,6,6,7,7,7,7,7,8,8,8,8,8,9,9,9 - dc.b 9,9,10,10,10,10,10,11,11,11,11,11,12,12,12,12 - dc.b 13 -MasterVolTab14: - dc.b 0,0,0,0,0,1,1,1,1,1,2,2,2,2,3,3 - dc.b 3,3,3,4,4,4,4,5,5,5,5,5,6,6,6,6 - dc.b 7,7,7,7,7,8,8,8,8,8,9,9,9,9,10,10 - dc.b 10,10,10,11,11,11,11,12,12,12,12,12,13,13,13,13 - dc.b 14 -MasterVolTab15: - dc.b 0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3 - dc.b 3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7 - dc.b 7,7,7,8,8,8,8,9,9,9,9,10,10,10,10,11 - dc.b 11,11,11,11,12,12,12,12,13,13,13,13,14,14,14,14 - dc.b 15 -MasterVolTab16: - dc.b 0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3 - dc.b 4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7 - dc.b 8,8,8,8,9,9,9,9,10,10,10,10,11,11,11,11 - dc.b 12,12,12,12,13,13,13,13,14,14,14,14,15,15,15,15 - dc.b 16 -MasterVolTab17: - dc.b 0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3 - dc.b 4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,8 - dc.b 8,8,9,9,9,9,10,10,10,10,11,11,11,11,12,12 - dc.b 12,13,13,13,13,14,14,14,14,15,15,15,15,16,16,16 - dc.b 17 -MasterVolTab18: - dc.b 0,0,0,0,1,1,1,1,2,2,2,3,3,3,3,4 - dc.b 4,4,5,5,5,5,6,6,6,7,7,7,7,8,8,8 - dc.b 9,9,9,9,10,10,10,10,11,11,11,12,12,12,12,13 - dc.b 13,13,14,14,14,14,15,15,15,16,16,16,16,17,17,17 - dc.b 18 -MasterVolTab19: - dc.b 0,0,0,0,1,1,1,2,2,2,2,3,3,3,4,4 - dc.b 4,5,5,5,5,6,6,6,7,7,7,8,8,8,8,9 - dc.b 9,9,10,10,10,10,11,11,11,12,12,12,13,13,13,13 - dc.b 14,14,14,15,15,15,16,16,16,16,17,17,17,18,18,18 - dc.b 19 -MasterVolTab20: - dc.b 0,0,0,0,1,1,1,2,2,2,3,3,3,4,4,4 - dc.b 5,5,5,5,6,6,6,7,7,7,8,8,8,9,9,9 - dc.b 10,10,10,10,11,11,11,12,12,12,13,13,13,14,14,14 - dc.b 15,15,15,15,16,16,16,17,17,17,18,18,18,19,19,19 - dc.b 20 -MasterVolTab21: - dc.b 0,0,0,0,1,1,1,2,2,2,3,3,3,4,4,4 - dc.b 5,5,5,6,6,6,7,7,7,8,8,8,9,9,9,10 - dc.b 10,10,11,11,11,12,12,12,13,13,13,14,14,14,15,15 - dc.b 15,16,16,16,17,17,17,18,18,18,19,19,19,20,20,20 - dc.b 21 -MasterVolTab22: - dc.b 0,0,0,1,1,1,2,2,2,3,3,3,4,4,4,5 - dc.b 5,5,6,6,6,7,7,7,8,8,8,9,9,9,10,10 - dc.b 11,11,11,12,12,12,13,13,13,14,14,14,15,15,15,16 - dc.b 16,16,17,17,17,18,18,18,19,19,19,20,20,20,21,21 - dc.b 22 -MasterVolTab23: - dc.b 0,0,0,1,1,1,2,2,2,3,3,3,4,4,5,5 - dc.b 5,6,6,6,7,7,7,8,8,8,9,9,10,10,10,11 - dc.b 11,11,12,12,12,13,13,14,14,14,15,15,15,16,16,16 - dc.b 17,17,17,18,18,19,19,19,20,20,20,21,21,21,22,22 - dc.b 23 -MasterVolTab24: - dc.b 0,0,0,1,1,1,2,2,3,3,3,4,4,4,5,5 - dc.b 6,6,6,7,7,7,8,8,9,9,9,10,10,10,11,11 - dc.b 12,12,12,13,13,13,14,14,15,15,15,16,16,16,17,17 - dc.b 18,18,18,19,19,19,20,20,21,21,21,22,22,22,23,23 - dc.b 24 -MasterVolTab25: - dc.b 0,0,0,1,1,1,2,2,3,3,3,4,4,5,5,5 - dc.b 6,6,7,7,7,8,8,8,9,9,10,10,10,11,11,12 - dc.b 12,12,13,13,14,14,14,15,15,16,16,16,17,17,17,18 - dc.b 18,19,19,19,20,20,21,21,21,22,22,23,23,23,24,24 - dc.b 25 -MasterVolTab26: - dc.b 0,0,0,1,1,2,2,2,3,3,4,4,4,5,5,6 - dc.b 6,6,7,7,8,8,8,9,9,10,10,10,11,11,12,12 - dc.b 13,13,13,14,14,15,15,15,16,16,17,17,17,18,18,19 - dc.b 19,19,20,20,21,21,21,22,22,23,23,23,24,24,25,25 - dc.b 26 -MasterVolTab27: - dc.b 0,0,0,1,1,2,2,2,3,3,4,4,5,5,5,6 - dc.b 6,7,7,8,8,8,9,9,10,10,10,11,11,12,12,13 - dc.b 13,13,14,14,15,15,16,16,16,17,17,18,18,18,19,19 - dc.b 20,20,21,21,21,22,22,23,23,24,24,24,25,25,26,26 - dc.b 27 -MasterVolTab28: - dc.b 0,0,0,1,1,2,2,3,3,3,4,4,5,5,6,6 - dc.b 7,7,7,8,8,9,9,10,10,10,11,11,12,12,13,13 - dc.b 14,14,14,15,15,16,16,17,17,17,18,18,19,19,20,20 - dc.b 21,21,21,22,22,23,23,24,24,24,25,25,26,26,27,27 - dc.b 28 -MasterVolTab29: - dc.b 0,0,0,1,1,2,2,3,3,4,4,4,5,5,6,6 - dc.b 7,7,8,8,9,9,9,10,10,11,11,12,12,13,13,14 - dc.b 14,14,15,15,16,16,17,17,18,18,19,19,19,20,20,21 - dc.b 21,22,22,23,23,24,24,24,25,25,26,26,27,27,28,28 - dc.b 29 -MasterVolTab30: - dc.b 0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7 - dc.b 7,7,8,8,9,9,10,10,11,11,12,12,13,13,14,14 - dc.b 15,15,15,16,16,17,17,18,18,19,19,20,20,21,21,22 - dc.b 22,22,23,23,24,24,25,25,26,26,27,27,28,28,29,29 - dc.b 30 -MasterVolTab31: - dc.b 0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7 - dc.b 7,8,8,9,9,10,10,11,11,12,12,13,13,14,14,15 - dc.b 15,15,16,16,17,17,18,18,19,19,20,20,21,21,22,22 - dc.b 23,23,24,24,25,25,26,26,27,27,28,28,29,29,30,30 - dc.b 31 -MasterVolTab32: - dc.b 0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7 - dc.b 8,8,9,9,10,10,11,11,12,12,13,13,14,14,15,15 - dc.b 16,16,17,17,18,18,19,19,20,20,21,21,22,22,23,23 - dc.b 24,24,25,25,26,26,27,27,28,28,29,29,30,30,31,31 - dc.b 32 -MasterVolTab33: - dc.b 0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7 - dc.b 8,8,9,9,10,10,11,11,12,12,13,13,14,14,15,15 - dc.b 16,17,17,18,18,19,19,20,20,21,21,22,22,23,23,24 - dc.b 24,25,25,26,26,27,27,28,28,29,29,30,30,31,31,32 - dc.b 33 -MasterVolTab34: - dc.b 0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7 - dc.b 8,9,9,10,10,11,11,12,12,13,13,14,14,15,15,16 - dc.b 17,17,18,18,19,19,20,20,21,21,22,22,23,23,24,24 - dc.b 25,26,26,27,27,28,28,29,29,30,30,31,31,32,32,33 - dc.b 34 -MasterVolTab35: - dc.b 0,0,1,1,2,2,3,3,4,4,5,6,6,7,7,8 - dc.b 8,9,9,10,10,11,12,12,13,13,14,14,15,15,16,16 - dc.b 17,18,18,19,19,20,20,21,21,22,22,23,24,24,25,25 - dc.b 26,26,27,27,28,28,29,30,30,31,31,32,32,33,33,34 - dc.b 35 -MasterVolTab36: - dc.b 0,0,1,1,2,2,3,3,4,5,5,6,6,7,7,8 - dc.b 9,9,10,10,11,11,12,12,13,14,14,15,15,16,16,17 - dc.b 18,18,19,19,20,20,21,21,22,23,23,24,24,25,25,26 - dc.b 27,27,28,28,29,29,30,30,31,32,32,33,33,34,34,35 - dc.b 36 -MasterVolTab37: - dc.b 0,0,1,1,2,2,3,4,4,5,5,6,6,7,8,8 - dc.b 9,9,10,10,11,12,12,13,13,14,15,15,16,16,17,17 - dc.b 18,19,19,20,20,21,21,22,23,23,24,24,25,26,26,27 - dc.b 27,28,28,29,30,30,31,31,32,32,33,34,34,35,35,36 - dc.b 37 -MasterVolTab38: - dc.b 0,0,1,1,2,2,3,4,4,5,5,6,7,7,8,8 - dc.b 9,10,10,11,11,12,13,13,14,14,15,16,16,17,17,18 - dc.b 19,19,20,20,21,21,22,23,23,24,24,25,26,26,27,27 - dc.b 28,29,29,30,30,31,32,32,33,33,34,35,35,36,36,37 - dc.b 38 -MasterVolTab39: - dc.b 0,0,1,1,2,3,3,4,4,5,6,6,7,7,8,9 - dc.b 9,10,10,11,12,12,13,14,14,15,15,16,17,17,18,18 - dc.b 19,20,20,21,21,22,23,23,24,24,25,26,26,27,28,28 - dc.b 29,29,30,31,31,32,32,33,34,34,35,35,36,37,37,38 - dc.b 39 -MasterVolTab40: - dc.b 0,0,1,1,2,3,3,4,5,5,6,6,7,8,8,9 - dc.b 10,10,11,11,12,13,13,14,15,15,16,16,17,18,18,19 - dc.b 20,20,21,21,22,23,23,24,25,25,26,26,27,28,28,29 - dc.b 30,30,31,31,32,33,33,34,35,35,36,36,37,38,38,39 - dc.b 40 -MasterVolTab41: - dc.b 0,0,1,1,2,3,3,4,5,5,6,7,7,8,8,9 - dc.b 10,10,11,12,12,13,14,14,15,16,16,17,17,18,19,19 - dc.b 20,21,21,22,23,23,24,24,25,26,26,27,28,28,29,30 - dc.b 30,31,32,32,33,33,34,35,35,36,37,37,38,39,39,40 - dc.b 41 -MasterVolTab42: - dc.b 0,0,1,1,2,3,3,4,5,5,6,7,7,8,9,9 - dc.b 10,11,11,12,13,13,14,15,15,16,17,17,18,19,19,20 - dc.b 21,21,22,22,23,24,24,25,26,26,27,28,28,29,30,30 - dc.b 31,32,32,33,34,34,35,36,36,37,38,38,39,40,40,41 - dc.b 42 -MasterVolTab43: - dc.b 0,0,1,2,2,3,4,4,5,6,6,7,8,8,9,10 - dc.b 10,11,12,12,13,14,14,15,16,16,17,18,18,19,20,20 - dc.b 21,22,22,23,24,24,25,26,26,27,28,28,29,30,30,31 - dc.b 32,32,33,34,34,35,36,36,37,38,38,39,40,40,41,42 - dc.b 43 -MasterVolTab44: - dc.b 0,0,1,2,2,3,4,4,5,6,6,7,8,8,9,10 - dc.b 11,11,12,13,13,14,15,15,16,17,17,18,19,19,20,21 - dc.b 22,22,23,24,24,25,26,26,27,28,28,29,30,30,31,32 - dc.b 33,33,34,35,35,36,37,37,38,39,39,40,41,41,42,43 - dc.b 44 -MasterVolTab45: - dc.b 0,0,1,2,2,3,4,4,5,6,7,7,8,9,9,10 - dc.b 11,11,12,13,14,14,15,16,16,17,18,18,19,20,21,21 - dc.b 22,23,23,24,25,26,26,27,28,28,29,30,30,31,32,33 - dc.b 33,34,35,35,36,37,37,38,39,40,40,41,42,42,43,44 - dc.b 45 -MasterVolTab46: - dc.b 0,0,1,2,2,3,4,5,5,6,7,7,8,9,10,10 - dc.b 11,12,12,13,14,15,15,16,17,17,18,19,20,20,21,22 - dc.b 23,23,24,25,25,26,27,28,28,29,30,30,31,32,33,33 - dc.b 34,35,35,36,37,38,38,39,40,40,41,42,43,43,44,45 - dc.b 46 -MasterVolTab47: - dc.b 0,0,1,2,2,3,4,5,5,6,7,8,8,9,10,11 - dc.b 11,12,13,13,14,15,16,16,17,18,19,19,20,21,22,22 - dc.b 23,24,24,25,26,27,27,28,29,30,30,31,32,33,33,34 - dc.b 35,35,36,37,38,38,39,40,41,41,42,43,44,44,45,46 - dc.b 47 -MasterVolTab48: - dc.b 0,0,1,2,3,3,4,5,6,6,7,8,9,9,10,11 - dc.b 12,12,13,14,15,15,16,17,18,18,19,20,21,21,22,23 - dc.b 24,24,25,26,27,27,28,29,30,30,31,32,33,33,34,35 - dc.b 36,36,37,38,39,39,40,41,42,42,43,44,45,45,46,47 - dc.b 48 -MasterVolTab49: - dc.b 0,0,1,2,3,3,4,5,6,6,7,8,9,9,10,11 - dc.b 12,13,13,14,15,16,16,17,18,19,19,20,21,22,22,23 - dc.b 24,25,26,26,27,28,29,29,30,31,32,32,33,34,35,35 - dc.b 36,37,38,39,39,40,41,42,42,43,44,45,45,46,47,48 - dc.b 49 -MasterVolTab50: - dc.b 0,0,1,2,3,3,4,5,6,7,7,8,9,10,10,11 - dc.b 12,13,14,14,15,16,17,17,18,19,20,21,21,22,23,24 - dc.b 25,25,26,27,28,28,29,30,31,32,32,33,34,35,35,36 - dc.b 37,38,39,39,40,41,42,42,43,44,45,46,46,47,48,49 - dc.b 50 -MasterVolTab51: - dc.b 0,0,1,2,3,3,4,5,6,7,7,8,9,10,11,11 - dc.b 12,13,14,15,15,16,17,18,19,19,20,21,22,23,23,24 - dc.b 25,26,27,27,28,29,30,31,31,32,33,34,35,35,36,37 - dc.b 38,39,39,40,41,42,43,43,44,45,46,47,47,48,49,50 - dc.b 51 -MasterVolTab52: - dc.b 0,0,1,2,3,4,4,5,6,7,8,8,9,10,11,12 - dc.b 13,13,14,15,16,17,17,18,19,20,21,21,22,23,24,25 - dc.b 26,26,27,28,29,30,30,31,32,33,34,34,35,36,37,38 - dc.b 39,39,40,41,42,43,43,44,45,46,47,47,48,49,50,51 - dc.b 52 -MasterVolTab53: - dc.b 0,0,1,2,3,4,4,5,6,7,8,9,9,10,11,12 - dc.b 13,14,14,15,16,17,18,19,19,20,21,22,23,24,24,25 - dc.b 26,27,28,28,29,30,31,32,33,33,34,35,36,37,38,38 - dc.b 39,40,41,42,43,43,44,45,46,47,48,48,49,50,51,52 - dc.b 53 -MasterVolTab54: - dc.b 0,0,1,2,3,4,5,5,6,7,8,9,10,10,11,12 - dc.b 13,14,15,16,16,17,18,19,20,21,21,22,23,24,25,26 - dc.b 27,27,28,29,30,31,32,32,33,34,35,36,37,37,38,39 - dc.b 40,41,42,43,43,44,45,46,47,48,48,49,50,51,52,53 - dc.b 54 -MasterVolTab55: - dc.b 0,0,1,2,3,4,5,6,6,7,8,9,10,11,12,12 - dc.b 13,14,15,16,17,18,18,19,20,21,22,23,24,24,25,26 - dc.b 27,28,29,30,30,31,32,33,34,35,36,36,37,38,39,40 - dc.b 41,42,42,43,44,45,46,47,48,48,49,50,51,52,53,54 - dc.b 55 -MasterVolTab56: - dc.b 0,0,1,2,3,4,5,6,7,7,8,9,10,11,12,13 - dc.b 14,14,15,16,17,18,19,20,21,21,22,23,24,25,26,27 - dc.b 28,28,29,30,31,32,33,34,35,35,36,37,38,39,40,41 - dc.b 42,42,43,44,45,46,47,48,49,49,50,51,52,53,54,55 - dc.b 56 -MasterVolTab57: - dc.b 0,0,1,2,3,4,5,6,7,8,8,9,10,11,12,13 - dc.b 14,15,16,16,17,18,19,20,21,22,23,24,24,25,26,27 - dc.b 28,29,30,31,32,32,33,34,35,36,37,38,39,40,40,41 - dc.b 42,43,44,45,46,47,48,48,49,50,51,52,53,54,55,56 - dc.b 57 -MasterVolTab58: - dc.b 0,0,1,2,3,4,5,6,7,8,9,9,10,11,12,13 - dc.b 14,15,16,17,18,19,19,20,21,22,23,24,25,26,27,28 - dc.b 29,29,30,31,32,33,34,35,36,37,38,38,39,40,41,42 - dc.b 43,44,45,46,47,48,48,49,50,51,52,53,54,55,56,57 - dc.b 58 -MasterVolTab59: - dc.b 0,0,1,2,3,4,5,6,7,8,9,10,11,11,12,13 - dc.b 14,15,16,17,18,19,20,21,22,23,23,24,25,26,27,28 - dc.b 29,30,31,32,33,34,35,35,36,37,38,39,40,41,42,43 - dc.b 44,45,46,47,47,48,49,50,51,52,53,54,55,56,57,58 - dc.b 59 -MasterVolTab60: - dc.b 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14 - dc.b 15,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29 - dc.b 30,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44 - dc.b 45,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59 - dc.b 60 -MasterVolTab61: - dc.b 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14 - dc.b 15,16,17,18,19,20,20,21,22,23,24,25,26,27,28,29 - dc.b 30,31,32,33,34,35,36,37,38,39,40,40,41,42,43,44 - dc.b 45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60 - dc.b 61 -MasterVolTab62: - dc.b 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14 - dc.b 15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30 - dc.b 31,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45 - dc.b 46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61 - dc.b 62 -MasterVolTab63: - dc.b 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14 - dc.b 15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30 - dc.b 31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46 - dc.b 47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62 - dc.b 63 -MasterVolTab64: - dc.b 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 - dc.b 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31 - dc.b 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47 - dc.b 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63 - dc.b 64 - even - endc ; !MINIMAL - - - ifd SDATA - - section __MERGED,bss - -mt_chan1: - ds.b n_sizeof -mt_chan2: - ds.b n_sizeof -mt_chan3: - ds.b n_sizeof -mt_chan4: - ds.b n_sizeof - -mt_SampleStarts: - ds.l 31 - -mt_mod: - ds.l 1 -mt_oldLev6: - ds.l 1 -mt_timerval: - ds.l 1 -mt_oldtimers: - ds.b 4 -mt_Lev6Int: - ds.l 1 -mt_Lev6Ena: - ds.w 1 -mt_PatternPos: - ds.w 1 -mt_PBreakPos: - ds.w 1 -mt_PosJumpFlag: - ds.b 1 -mt_PBreakFlag: - ds.b 1 -mt_Speed: - ds.b 1 -mt_Counter: - ds.b 1 -mt_SongPos: - ds.b 1 -mt_PattDelTime: - ds.b 1 -mt_PattDelTime2: - ds.b 1 - - ifeq MINIMAL -mt_SilCntValid: - ds.b 1 -mt_MasterVolTab: - ds.l 1 - endc - - xdef _mt_Enable -_mt_Enable: -mt_Enable: - ds.b 1 - - xdef _mt_E8Trigger -_mt_E8Trigger: -mt_E8Trigger: - ds.b 1 - - ifeq MINIMAL - xdef _mt_MusicChannels -_mt_MusicChannels: -mt_MusicChannels: - ds.b 1 - - xdef _mt_SongEnd -_mt_SongEnd: -mt_SongEnd: - ds.b 1 - endc ; !MINIMAL - - else ; !SDATA : single section with local base register - - rsreset -mt_chan1 rs.b n_sizeof -mt_chan2 rs.b n_sizeof -mt_chan3 rs.b n_sizeof -mt_chan4 rs.b n_sizeof -mt_SampleStarts rs.l 31 -mt_mod rs.l 1 -mt_oldLev6 rs.l 1 -mt_timerval rs.l 1 -mt_oldtimers rs.b 4 -mt_Lev6Int rs.l 1 -mt_Lev6Ena rs.w 1 -mt_PatternPos rs.w 1 -mt_PBreakPos rs.w 1 -mt_PosJumpFlag rs.b 1 -mt_PBreakFlag rs.b 1 -mt_Speed rs.b 1 -mt_Counter rs.b 1 -mt_SongPos rs.b 1 -mt_PattDelTime rs.b 1 -mt_PattDelTime2 rs.b 1 - ifeq MINIMAL -mt_SilCntValid rs.b 1 -mt_MasterVolTab rs.l 1 - endc -mt_Enable rs.b 1 ; exported as _mt_Enable -mt_E8Trigger rs.b 1 ; exported as _mt_E8Trigger -mt_MusicChannels rs.b 1 ; exported as _mt_MusicChannels -mt_SongEnd rs.b 1 ; exported as _mt_SongEnd - -mt_data: - ds.b mt_Enable - xdef _mt_Enable -_mt_Enable: - ds.b 1 - xdef _mt_E8Trigger -_mt_E8Trigger: - ds.b 1 - ifeq MINIMAL - xdef _mt_MusicChannels -_mt_MusicChannels: - ds.b 1 - xdef _mt_SongEnd -_mt_SongEnd: - ds.b 1 - endc ; !MINIMAL - - endc ; SDATA/!SDATA - - end + bra MTSoundFX_done +MTSoundFX: + move.l D0,A0 ;a0 = sample pointer + move.w D1,D0 ;d0.w = sample length in words + move.w D2,D1 ;d1.w = sample period + Move.w D3,D2 ;d2.w = sample volume +MTSoundFX_done: + storeAddressRegisters + jsr _mt_soundfx + restoreAddressRegisters + +MTPlayFx: + storeAddressRegisters + move.l D0,A0 ;a0=SfxStructurePointer + jsr _mt_playfx + restoreAddressRegisters + +MTLoopFx: + storeAddressRegisters + move.l D0,A0 ;a0=SfxStructurePointer + jsr _mt_loopfx + restoreAddressRegisters + +MTStopFx: + storeAddressRegisters + jsr _mt_stopfx + restoreAddressRegisters + +MTMusicMask: + storeAddressRegisters + jsr _mt_musicmask + restoreAddressRegisters + +MTMasterVolume: + storeAddressRegisters + jsr _mt_mastervol + restoreAddressRegisters + +MTSampleVolume: + storeAddressRegisters + jsr _mt_samplevol + restoreAddressRegisters + +MTChannelMask: + storeAddressRegisters + jsr _mt_channelmask + restoreAddressRegisters + +MTMusicChannels: + move.b d0,_mt_MusicChannels + rts + +MTE8Trigger: + move.b _mt_E8Trigger,d0 + rts + + include "ptplayer.asm" \ No newline at end of file diff --git a/ptplayer.asm b/ptplayer.asm new file mode 100644 index 0000000..2c23a13 --- /dev/null +++ b/ptplayer.asm @@ -0,0 +1,3974 @@ +;************************************************** +;* ----- Protracker V2.3B Playroutine ----- * +;************************************************** +; +; Version 6.4 +; Written by Frank Wille in 2013, 2016-2024. +; +; I, the copyright holder of this work, hereby release it into the +; public domain. This applies worldwide. +; +; The default version (single section, local base register) should +; work with most assemblers. Tested are: Devpac, vasm, PhxAss, +; Barfly-Asm, SNMA, AsmOne, AsmPro. +; +; The small data model can be enabled by defining the symbol SDATA. In +; this case all functions expect a4 to be initialized with the small +; data base register. Interrupt functions restore a4 from _LinkerDB. +; Small data may work with vasm and PhxAss only. +; +; Exported functions and variables: +; (CUSTOM is the custom-chip register set base address $dff000.) +; +; _mt_install(a6=CUSTOM, a0=VectorBase, d0=PALflag.b) [OSCOMPAT=0] +; Install a CIA-B interrupt for calling mt_music or mt_sfxonly. +; The music module is replayed via mt_music when _mt_Enable is non-zero. +; Otherwise the interrupt handler calls mt_sfxonly to play sound +; effects only. VectorBase is 0 for 68000, otherwise set it to the CPU's +; VBR register. A non-zero PALflag selects PAL-clock for the CIA timers +; (NTSC otherwise). +; +; ok(d0) = _mt_install() [OSCOMPAT=1] +; Register CIA-B interrupts with AmigaOS for calling mt_music or +; mt_sfxonly. Allocate all Paula audio channels via audio.device. +; The music module is replayed via mt_music when _mt_Enable is non-zero. +; Otherwise the interrupt handler calls mt_sfxonly to play sound +; effects only. +; Returns true (1) on success, false (0) otherwise. You must not call +; _mt_remove(), when _mt_install() failed! +; +; _mt_remove(a6=CUSTOM) [OSCOMPAT=0] +; Remove the CIA-B music interrupt, restore the previous handler and +; reset the CIA timer registers to their original values. +; +; _mt_remove() [OSCOMPAT=1] +; Unregister the CIA-B interrupts handlers and deallocate all +; audio channels. +; +; _mt_init(a6=CUSTOM, a0=TrackerModule, a1=Samples|NULL, d0=InitialSongPos.b) +; Initialize a new module. +; Reset speed to 6, tempo to 125 and start at the given song position. +; Master volume is at 64 (maximum). +; When a1 is NULL the samples are assumed to be stored after the patterns. +; +; _mt_end(a6=CUSTOM) +; Stop playing current module and sound effects. +; +; _mt_soundfx(a6=CUSTOM, a0=SamplePointer, +; d0=SampleLength.w, d1=SamplePeriod.w, d2=SampleVolume.w) +; Request playing of an external sound effect on the most unused channel. +; This function is for compatibility with the old API only. +; You may prefer _mt_playfx instead. MINIMAL=0 only. +; +; channelStatus(d0) = _mt_playfx(a6=CUSTOM, a0=SfxStructurePointer) +; Request playing of a prioritized external sound effect, either on a +; fixed channel or on the most unused one. +; Structure layout of SfxStructure: +; void *sfx_ptr (pointer to sample start in Chip RAM, even address) +; WORD sfx_len (sample length in words) +; WORD sfx_per (hardware replay period for sample) +; WORD sfx_vol (volume 0..64, is unaffected by the song's master volume) +; BYTE sfx_cha (0..3 selected replay channel, -1 selects best channel) +; BYTE sfx_pri (priority, must be in the range 1..127) +; When multiple samples are assigned to the same channel the lower +; priority sample will be replaced. When priorities are the same, then +; the older sample is replaced. +; The chosen channel is blocked for music until the effect has +; completely been replayed. +; Returns a pointer to a channel-status structure when the sample +; is scheduled for playing, and NULL when the request was ignored. +; MINIMAL=0 only. +; +; _mt_loopfx(a6=CUSTOM, a0=SfxStructurePointer) +; Request playing of a looped sound effect on a fixed channel, which +; will be blocked for music until the effect is stopped (_mt_stopfx). +; It uses the same sfx-structure as _mt_playfx, but the priority is +; ignored. A looped sound effect has always highest priority and will +; replace a previous effect on the same channel. No automatic channel +; selection possible! +; Also make sure the sample starts with a zero-word, which is used +; for idling when the effect is stopped. This word is included in the +; total length calculation, but excluded when actually playing the loop. +; MINIMAL=0 only. +; +; _mt_stopfx(a6=CUSTOM, d0=Channel.b) [MINIMAL=0] +; Immediately stop a currently playing sound effect on a channel (0..3) +; and make it available for music, or other effects, again. This is the +; only way to stop a looped sound effect (_mt_loopfx) besides stopping +; replay completely (_mt_end). +; +; _mt_musicmask(a6=CUSTOM, d0=ChannelMask.b) [MINIMAL=0] +; Bits set in the ChannelMask define which specific channels are reserved +; for music only. Set bit 0 for channel 0, ..., bit 3 for channel 3. +; When calling _mt_soundfx or _mt_playfx with automatic channel selection +; (sfx_cha=-1) then these masked channels will never be picked. +; The mask defaults to 0. +; +; _mt_mastervol(a6=CUSTOM, d0=MasterVolume.w) [MINIMAL=0] +; Set a master volume from 0 to 64 for all music channels. +; Note that the master volume does not affect the volume of external +; sound effects (which is desired). +; +; _mt_samplevol(d0=SampleNumber.w, d1=Volume.b) [MINIMAL=0] +; Redefine a sample's volume. May also be done while the song is playing. +; Warning: Does not check arguments for valid range! You must have done +; _mt_init before calling this function! +; The new volume is persistent. Even when the song is restarted. +; +; _mt_channelmask(a6=CUSTOM, d0=ChannelMask.b) [MINIMAL=0] +; Bits cleared in the ChannelMask define which specific channels are muted +; for music replay. Clear bit 0 for channel 0, ..., bit 3 for channel 3. +; Additionally a cleared bit prevents any access to the sample pointers +; of this channel. +; The mask defaults to all channels unmuted (bits set) and is reset to +; this state on _mt_init and _mt_end. +; +; _mt_music(a6=CUSTOM) +; The replayer routine. Can be called from your own VBlank interrupt +; handler when VBLANK_MUSIC is set. Is otherwise called automatically +; by Timer-A interrupts after _mt_install. +; +; _mt_dmaon() [NO_TIMERS=1] +; MUST be called ca. 550 ticks after calling _mt_music. Enables Audio +; DMA to play a new note. +; +; _mt_setrep() [NO_TIMERS=1] +; MUST be called ca. 550 ticks after calling _mt_dmaon. Sets the +; repetition pointers and lengths for looped samples. +; +; Byte Variables: +; +; _mt_Enable [VBLANK_MUSIC=0] +; Set this byte to non-zero to play music, zero to pause playing. +; Note that you can still play sound effects, while music is stopped. +; It is set to 0 by _mt_install. +; +; _mt_E8Trigger +; This byte reflects the value of the last E8 command. +; It is reset to 0 after _mt_init. +; +; _mt_MusicChannels [MINIMAL=0] +; This byte defines the number of channels which should be dedicated +; for playing music. So sound effects will never use more +; than 4 - _mt_MusicChannels channels at once. Defaults to 0. +; + +; Set this if you want to build a version which uses AmigaOS to +; register interrupts and reserve audio channels. + ifnd OSCOMPAT +OSCOMPAT equ 0 + endc + +; Optionally you can build a minimal version, which includes only +; the basic player. No sound effects insertion, no master volume, no +; sample volume, etc. Define the symbol MINIMAL=1 for it. + ifnd MINIMAL +MINIMAL equ 0 + endc + +; You may disable sawtooth and rectangle vibratos/tremolos here, which +; will be replaced by sine-waves. They are rarely used and disabling +; them will free a lot of memory for the tables. + ifnd ENABLE_SAWRECT +ENABLE_SAWRECT equ 0 + endc + +; Set this if you can guarantee that the word at $0 is cleared, and if +; you want to use it for idle-looping of samples. + ifnd NULL_IS_CLEARED +NULL_IS_CLEARED equ 0 + endc + +; Setting NO_TIMERS disables the use of both CIA-B timers completely, which +; means that the player doesn't depend on level-6 EXTER interrupts anymore. +; NO_TIMERS=1 automatically includes VBLANK_MUSIC=1 (see below). +; With NO_TIMERS set, your main program has to call all music subroutines +; itself. The typical procedure in a VERTB-based main loop would be: +; 1. call _mt_music +; 2. wait at least 550 ticks, then call _mt_dmaon +; 3. wait at least 550 ticks again, then call _mt_setrep +; The last two steps could for example be performed by Copper interrupts. + ifnd NO_TIMERS +NO_TIMERS equ 0 + else + ifne NO_TIMERS + ifd VBLANK_MUSIC + ifeq VBLANK_MUSIC + fail "NO_TIMERS=1 requires VBLANK_MUSIC=1" + endc + endc ; VBLANK_MUSIC + endc ; NO_TIMERS=1 + endc ; NO_TIMERS + +; This disables only initialization of CIA Timer-A interrupts for music +; replay, which means you cannot set the tempo with the F-command +; anymore and you have to call _mt_music yourself out of your own +; VBlank interrupt handler. Also sound effects will no longer work, +; when no music is playing (_mt_Enable=0). + ifnd VBLANK_MUSIC +VBLANK_MUSIC equ NO_TIMERS + endc + +; Delay in CIA-ticks, which guarantees that at least one Audio-DMA +; took place, even with the lowest periods. +; 496 should be the correct value. But there are some A1200 which +; need at least 550. +DMADELAY equ 576 ; was 496 + + +; exec.library (OSCOMPAT) +_LVOAllocSignal equ -330 +_LVOFreeSignal equ -336 +_LVOOpenDevice equ -444 +_LVOCloseDevice equ -450 +_LVOOpenResource equ -498 + +; cia.resource (OSCOMPAT) +_LVOAddICRVector equ -6 +_LVORemICRVector equ -12 +_LVOAbleICR equ -18 +CIAICRB_TA equ 0 +CIAICRB_TB equ 1 + + +; Custom chip registers +CUSTOM equ $dff000 +INTREQR equ $01e +INTENAR equ $01c +DMACON equ $096 +INTENA equ $09a +INTREQ equ $09c +AUD0LC equ $0a0 +AUD0LEN equ $0a4 +AUD0VOL equ $0a8 +AUD1LC equ $0b0 +AUD1LEN equ $0b4 +AUD1VOL equ $0b8 +AUD2LC equ $0c0 +AUD2LEN equ $0c4 +AUD2VOL equ $0c8 +AUD3LC equ $0d0 +AUD3LEN equ $0d4 +AUD3VOL equ $0d8 + +; Audio channel registers +AUDLC equ 0 +AUDLEN equ 4 +AUDPER equ 6 +AUDVOL equ 8 + +; CIA registers +CIAA equ $bfe001 +CIAB equ $bfd000 +CIAPRA equ $000 +CIATALO equ $400 +CIATAHI equ $500 +CIATBLO equ $600 +CIATBHI equ $700 +CIAICR equ $d00 +CIACRA equ $e00 +CIACRB equ $f00 + + +; Sound effects structure, passed into _mt_playfx + rsreset +sfx_ptr rs.l 1 +sfx_len rs.w 1 +sfx_per rs.w 1 +sfx_vol rs.w 1 +sfx_cha rs.b 1 +sfx_pri rs.b 1 +sfx_sizeof rs.b 0 + + +; Channel Status + rsreset +n_note rs.w 1 +n_cmd rs.b 1 +n_cmdlo rs.b 1 +n_index rs.b 1 +n_sfxpri rs.b 1 +n_reserved1 rs.b 2 +n_start rs.l 1 +n_loopstart rs.l 1 +n_length rs.w 1 +n_replen rs.w 1 +n_period rs.w 1 +n_volume rs.w 1 +n_pertab rs.l 1 +n_dmabit rs.w 1 +n_noteoff rs.w 1 +n_toneportspeed rs.w 1 +n_wantedperiod rs.w 1 +n_pattpos rs.w 1 +n_funk rs.w 1 +n_wavestart rs.l 1 +n_reallength rs.w 1 +n_intbit rs.w 1 +n_sfxptr rs.l 1 +n_sfxlen rs.w 1 +n_sfxper rs.w 1 +n_sfxvol rs.w 1 +n_looped rs.b 1 +n_minusft rs.b 1 +n_vibratoamp rs.b 1 +n_vibratospd rs.b 1 +n_vibratopos rs.b 1 +n_vibratoctrl rs.b 1 +n_tremoloamp rs.b 1 +n_tremolospd rs.b 1 +n_tremolopos rs.b 1 +n_tremoloctrl rs.b 1 +n_gliss rs.b 1 +n_sampleoffset rs.b 1 +n_loopcount rs.b 1 +n_funkoffset rs.b 1 +n_retrigcount rs.b 1 + ifeq MINIMAL +n_freecnt rs.b 1 +n_musiconly rs.b 1 +n_enable rs.b 1 + else +n_reserved2 rs.b 3 + endc +n_sizeof rs.b 0 + + +DISABLE macro + ifeq NO_TIMERS + move.w #$4000,INTENA(a6) + endc + endm + +ENABLE macro + ifeq NO_TIMERS + move.w #$c000,INTENA(a6) + endc + endm + + + ifd SDATA + xref _LinkerDB ; small data base from linker + near a4 + code + endc + + + + ifne OSCOMPAT +;--------------------------------------------------------------------------- + xdef _mt_install +_mt_install: +; Register a CIA-B interrupt with AmigaOS for calling mt_music. +; Reserve all audio channels for our use. +; -> d0.l = status (1 for success, 0 for error) + + move.l a6,-(sp) + + ifnd SDATA + move.l a4,-(sp) + lea mt_data(pc),a4 + endc + + ; open audio.device and allocate all four audio channels + move.l 4.w,a6 + moveq #-1,d0 + jsr _LVOAllocSignal(a6) + lea mt_ioport+20(pc),a0 + move.b d0,-5(a0) ; MP_SIGBIT + bmi .fail + move.l a0,8(a0) ; NewList MP_MSGLIST + addq.l #4,a0 + clr.l (a0) + move.l a0,-(a0) + move.l 276(a6),-(a0) ; MP_SIGTASK = ThisTask + lea mt_audio_name(pc),a0 + lea mt_ioaudio(pc),a1 + moveq #0,d0 + moveq #0,d1 + jsr _LVOOpenDevice(a6) + tst.b d0 + bne .no_device + + ifeq NO_TIMERS + ; attempt to allocate CIA-B timers A and B + lea mt_ciab_name(pc),a1 + jsr _LVOOpenResource(a6) + tst.l d0 + beq .no_rsrc + + move.l d0,a6 + + clr.b mt_Enable(a4) + lea TB_toggle(pc),a0 + clr.b (a0) + + ifeq VBLANK_MUSIC + moveq #CIAICRB_TA,d0 + lea mt_extern_timer_a_is(pc),a1 + jsr _LVOAddICRVector(a6) + tst.l d0 + bne .no_timer_a + endc ; !VBLANK_MUSIC + + moveq #CIAICRB_TB,d0 + lea mt_extern_timer_b_is(pc),a1 + jsr _LVOAddICRVector(a6) + tst.l d0 + bne .no_timer_b + + ; TimerA and TimerB interrupt disable + ifne VBLANK_MUSIC + moveq #$2,d0 ; TimerB only for VBLANK_MUSIC + else + moveq #$3,d0 + endc + jsr _LVOAbleICR(a6) + + lea CIAB,a0 + + ; Stop Timer B + moveq #0,d0 + move.b d0,CIACRB(a0) + + ifeq VBLANK_MUSIC + ; Stop Timer A + move.b d0,CIACRA(a0) + + ; determine if 02 clock for timers is based on PAL or NTSC + move.l 4.w,a1 + cmp.b #50,531(a1) ; PowerSupplyFrequency + beq .1 + move.l #1789773,d0 ; NTSC + bra .2 +.1: move.l #1773447,d0 ; PAL +.2: move.l d0,mt_timerval(a4) + + ; load TimerA in continuous mode for the default tempo of 125 + divu #125,d0 + move.b d0,CIATALO(a0) + lsr.w #8,d0 + move.b d0,CIATAHI(a0) + move.b #$11,CIACRA(a0) ; load timer, start continuous + endc ; !VBLANK_MUSIC + + ; load TimerB with DMADELAY ticks for setting DMA and repeat + move.b #DMADELAY&255,CIATBLO(a0) + move.b #DMADELAY>>8,CIATBHI(a0) + + ; TimerA and TimerB interrupt enable + ifne VBLANK_MUSIC + move.w #$82,d0 ; TimerB only for VBLANK_MUSIC + else + move.w #$83,d0 + endc + jsr _LVOAbleICR(a6) + + endc ; !NO_TIMERS + + ; reset the player + pea .popout(pc) + ifnd SDATA + move.l a4,-(sp) + endc + lea CUSTOM,a6 + bra mt_reset + + ifeq NO_TIMERS +.no_timer_b: + ifeq VBLANK_MUSIC + moveq #CIAICRB_TA,d0 + lea mt_extern_timer_a_is(pc),a1 + jsr _LVORemICRVector(a6) +.no_timer_a: + endc ; !VBLANK_MUSIC + endc ; !NO_TIMERS + + move.l 4.w,a6 +.no_rsrc: + lea mt_ioaudio(pc),a1 + jsr _LVOCloseDevice(a6) +.no_device: + moveq #0,d0 + move.b mt_ioport+15(pc),d0 + jsr _LVOFreeSignal(a6) +.fail: + moveq #0,d0 ; Indicate failure + bra .out +.popout: + moveq #1,d0 ; Indicate success +.out: + ifnd SDATA + move.l (sp)+,a4 + endc + move.l (sp)+,a6 + rts + + +mt_ioport: + dc.l 0,0 + dc.b 4,0 ; MT_MSGPORT + dc.l 0 + dc.b 0,-1 ; PA_SIGNAL + dc.l 0 +.head: dc.l .tail +.tail: dc.l 0 + dc.l .head + +mt_ioaudio: + dc.l 0,0 + dc.b 7,127 ; NT_REPLYMSG, ADALLOC_MAXPREC + dc.l 0,mt_ioport + dc.w .end-mt_ioaudio + dc.l 0,0 + dc.w $20 ; ADCMD_ALLOCATE + dc.b $40,0 ; ADIOF_NOWAIT + dc.w 0 ; ioa_AllocKey + dc.l mt_ch_alloc ; ioa_Data + dc.l 1 ; ioa_Length + dc.w 0,0,0 + ds.b 20 ; ioa_WriteMsg +.end: + + ifeq NO_TIMERS + ifeq VBLANK_MUSIC +mt_extern_timer_a_is: + dc.l 0,0 + dc.b 2 ; NT_INTERRUPT + dc.b 0 ; priority + dc.l mt_timer_int_name + dc.l 0 + dc.l mt_cia_timer_a_code + endc ; !VBLANK_MUSIC + +mt_extern_timer_b_is: + dc.l 0,0 + dc.b 2 ; NT_INTERRUPT + dc.b 0 ; priority + dc.l mt_timer_int_name + dc.l 0 + dc.l mt_cia_timer_b_code + +mt_ciab_name: + dc.b "ciab.resource",0 +mt_timer_int_name: + dc.b "ptplayer timer",0 + endc ; !NO_TIMERS + +mt_audio_name: + dc.b "audio.device",0 +mt_ch_alloc: + dc.b %1111 ; allocate all four channels + even + + +;--------------------------------------------------------------------------- + xdef _mt_remove +_mt_remove: +; Unregister our CIA-B interrupt with AmigaOS. Free all audio channels. + + move.l a6,-(sp) + + ifeq NO_TIMERS + move.l 4.w,a6 + lea mt_ciab_name(pc),a1 + jsr _LVOOpenResource(a6) + tst.l d0 + beq .nociares + + move.l d0,a6 + + ; TimerA and TimerB interrupt disable + ifne VBLANK_MUSIC + moveq #$02,d0 ; TimerB only for VBLANK_MUSIC + else + moveq #$03,d0 + endc + jsr _LVOAbleICR(a6) + + moveq #CIAICRB_TB,d0 + lea mt_extern_timer_b_is(pc),a1 + jsr _LVORemICRVector(a6) + + ifeq VBLANK_MUSIC + moveq #CIAICRB_TA,d0 + lea mt_extern_timer_a_is(pc),a1 + jsr _LVORemICRVector(a6) + endc ; !VBLANK_MUSIC +.nociares: + endc ; !NO_TIMERS + + ; close audio.device, free channels + move.l 4.w,a6 + lea mt_ioaudio(pc),a1 + jsr _LVOCloseDevice(a6) + moveq #0,d0 + move.b mt_ioport+15(pc),d0 + jsr _LVOFreeSignal(a6) + + move.l (sp)+,a6 + rts + + + ifeq NO_TIMERS + ifeq VBLANK_MUSIC +;--------------------------------------------------------------------------- +mt_cia_timer_a_code: +; TimerA interrupt calls mt_music at a selectable tempo (Fxx command), +; which defaults to 50 times per second. + + movem.l d2-d7/a2-a6,-(sp) + lea CUSTOM,a6 + ifd SDATA + lea _LinkerDB,a4 + else + lea mt_data(pc),a4 + endc + + ; do music when enabled + tst.b mt_Enable(a4) + ifeq MINIMAL + beq .2 + else + beq .1 + endc + + bsr mt_music ; music with sfx inserted +.1: movem.l (sp)+,d2-d7/a2-a6 + rts + + ifeq MINIMAL +.2: bsr mt_sfxonly ; no music, only sfx + movem.l (sp)+,d2-d7/a2-a6 + rts + endc + endc ; !VBLANK_MUSIC + + +;--------------------------------------------------------------------------- +mt_cia_timer_b_code: +; Handle one-shot TimerB interrupt. +; TB_toggle-technique suggested by Ross/EAB. + + lea TB_toggle(pc),a0 + not.b (a0) + lea CUSTOM+INTREQ,a0 + beq mt_TimerBsetrep + + ; restart timer for repeat, enable audio DMA after DMADELAY ticks + move.b #$19,CIAB+CIACRB + move.w mt_dmaon(pc),DMACON-INTREQ(a0) + rts + +TB_toggle: + dc.b 0 + even + + +mt_TimerBsetrep: +; Oneshot TimerB interrupt to set repeat samples after another DMADELAY ticks. + + move.l a4,-(sp) + + ifd SDATA + lea _LinkerDB,a4 + else + lea mt_data(pc),a4 + endc + + ; clear possible audio interrupt flags + moveq #0,d0 + or.b mt_dmaon+1(pc),d0 + lsl.w #7,d0 + move.w d0,(a0) + + ; set repeat sample pointers and lengths + ifne MINIMAL + move.l mt_chan1+n_loopstart(a4),AUD0LC-INTREQ(a0) + move.w mt_chan1+n_replen(a4),AUD0LEN-INTREQ(a0) + move.l mt_chan2+n_loopstart(a4),AUD1LC-INTREQ(a0) + move.w mt_chan2+n_replen(a4),AUD1LEN-INTREQ(a0) + move.l mt_chan3+n_loopstart(a4),AUD2LC-INTREQ(a0) + move.w mt_chan3+n_replen(a4),AUD2LEN-INTREQ(a0) + move.l mt_chan4+n_loopstart(a4),AUD3LC-INTREQ(a0) + move.w mt_chan4+n_replen(a4),AUD3LEN-INTREQ(a0) + + else ; !MINIMAL + tst.b mt_chan1+n_enable(a4) + beq .1 + move.l mt_chan1+n_loopstart(a4),AUD0LC-INTREQ(a0) + move.w mt_chan1+n_replen(a4),AUD0LEN-INTREQ(a0) +.1: tst.b mt_chan2+n_enable(a4) + beq .2 + move.l mt_chan2+n_loopstart(a4),AUD1LC-INTREQ(a0) + move.w mt_chan2+n_replen(a4),AUD1LEN-INTREQ(a0) +.2: tst.b mt_chan3+n_enable(a4) + beq .3 + move.l mt_chan3+n_loopstart(a4),AUD2LC-INTREQ(a0) + move.w mt_chan3+n_replen(a4),AUD2LEN-INTREQ(a0) +.3: tst.b mt_chan4+n_enable(a4) + beq .4 + move.l mt_chan4+n_loopstart(a4),AUD3LC-INTREQ(a0) + move.w mt_chan4+n_replen(a4),AUD3LEN-INTREQ(a0) +.4: + endc + + move.l (sp)+,a4 + rts + endc ; !NO_TIMERS + + + else !OSCOMPAT +;--------------------------------------------------------------------------- + xdef _mt_install + xdef _mt_install_cia +_mt_install: +_mt_install_cia: +; Install a CIA-B interrupt for calling mt_music. +; a6 = CUSTOM +; a0 = VectorBase +; d0 = PALflag.b (0 is NTSC) + + ifnd SDATA + move.l a4,-(sp) + lea mt_data(pc),a4 + endc + + clr.b mt_Enable(a4) + + ifeq NO_TIMERS + ; remember level 6 vector and interrupt enable + lea $78(a0),a0 + move.l a0,mt_Lev6Int(a4) + move.w #$2000,d1 + and.w INTENAR(a6),d1 + or.w #$8000,d1 + move.w d1,mt_Lev6Ena(a4) + + ; disable level 6 EXTER interrupts, set player interrupt vector + move.w #$2000,INTENA(a6) + move.l (a0),mt_oldLev6(a4) + lea mt_TimerAInt(pc),a1 + move.l a1,(a0) + + ; reset TimerB toggle + lea TB_toggle(pc),a0 + clr.b (a0) + + ; disable CIA-B interrupts, stop and save all timers + lea CIAB,a0 + moveq #0,d1 + move.b #$7f,CIAICR(a0) + move.b d1,CIACRA(a0) + move.b d1,CIACRB(a0) + lea mt_oldtimers(a4),a1 + move.b CIATALO(a0),(a1)+ + move.b CIATAHI(a0),(a1)+ + move.b CIATBLO(a0),(a1)+ + move.b CIATBHI(a0),(a1) + + ifeq VBLANK_MUSIC + ; determine if 02 clock for timers is based on PAL or NTSC + tst.b d0 + bne .1 + move.l #1789773,d0 ; NTSC + bra .2 +.1: move.l #1773447,d0 ; PAL +.2: move.l d0,mt_timerval(a4) + + ; load TimerA in continuous mode for the default tempo of 125 + divu #125,d0 + move.b d0,CIATALO(a0) + lsr.w #8,d0 + move.b d0,CIATAHI(a0) + move.b #$11,CIACRA(a0) ; load timer, start continuous + endc ; !VBLANK_MUSIC + + ; load TimerB with DMADELAY ticks for setting DMA and repeat + move.b #DMADELAY&255,CIATBLO(a0) + move.b #DMADELAY>>8,CIATBHI(a0) + + ; Ack. pending interrupts, TimerA and TimerB interrupt enable + tst.b CIAICR(a0) + move.w #$2000,INTREQ(a6) + ifne VBLANK_MUSIC + move.b #$82,CIAICR(a0) ; TimerB only for VBLANK_MUSIC + else + move.b #$83,CIAICR(a0) + endc + + ; enable level 6 interrupts + move.w #$a000,INTENA(a6) + endc ; !NO_TIMERS + + bra mt_reset + + +;--------------------------------------------------------------------------- + xdef _mt_remove_cia + xdef _mt_remove +_mt_remove_cia: +_mt_remove: +; Remove CIA-B music interrupt and restore the old vector. +; a6 = CUSTOM + + ifeq NO_TIMERS + ifnd SDATA + move.l a4,-(sp) + lea mt_data(pc),a4 + endc + + ; disable level 6 and CIA-B interrupts + lea CIAB,a0 + move.w #$2000,d0 + move.b #$7f,CIAICR(a0) + move.w d0,INTENA(a6) + tst.b CIAICR(a0) + move.w d0,INTREQ(a6) + + ; restore old timer values + lea mt_oldtimers(a4),a1 + move.b (a1)+,CIATALO(a0) + move.b (a1)+,CIATAHI(a0) + move.b (a1)+,CIATBLO(a0) + move.b (a1),CIATBHI(a0) + move.b #$10,CIACRA(a0) + move.b #$10,CIACRB(a0) + + ; restore original level 6 interrupt vector + move.l mt_Lev6Int(a4),a1 + move.l mt_oldLev6(a4),(a1) + + ; reenable CIA-B ALRM interrupt, which was set by AmigaOS + move.b #$84,CIAICR(a0) + + ; reenable previous level 6 interrupt + move.w mt_Lev6Ena(a4),INTENA(a6) + + ifnd SDATA + move.l (sp)+,a4 + endc + endc ; !NO_TIMERS + rts + + + ifeq NO_TIMERS +;--------------------------------------------------------------------------- +mt_TimerAInt: +; TimerA interrupt calls mt_music at a selectable tempo (Fxx command), +; which defaults to 50 times per second. + + ; check for TB interrupt and clear CIAB interrupt flags + btst #1,CIAB+CIAICR + bne mt_TimerBInt + + ifne VBLANK_MUSIC + move.w #$2000,CUSTOM+INTREQ ; ack interrupt and leave + nop + rte + + else + ; Now it should be a TA interrupt. + ; Other level 6 interrupt sources have to be handled elsewhere. + movem.l d0-d7/a0-a6,-(sp) + lea CUSTOM,a6 + ifd SDATA + lea _LinkerDB,a4 + else + lea mt_data(pc),a4 + endc + + ; clear EXTER interrupt flag + move.w #$2000,INTREQ(a6) + + ; do music when enabled + tst.b mt_Enable(a4) + ifeq MINIMAL + beq .2 + else + beq .1 + endc + + bsr mt_music ; music with sfx inserted +.1: movem.l (sp)+,d0-d7/a0-a6 + nop + rte + + ifeq MINIMAL +.2: bsr mt_sfxonly ; no music, only sfx + movem.l (sp)+,d0-d7/a0-a6 + nop + rte + endc + endc ; !VBLANK_MUSIC + + +;--------------------------------------------------------------------------- +mt_TimerBInt: +; Handle one-shot TimerB interrupt. +; TB_toggle-technique suggested by Ross/EAB. + + move.l a0,-(sp) + lea TB_toggle(pc),a0 + not.b (a0) + lea CUSTOM+INTREQ,a0 + beq mt_TimerBsetrep + + ; restart timer for repeat, enable audio DMA after DMADELAY ticks + move.w #$2000,(a0) ; clear EXTER interrupt flag + move.b #$19,CIAB+CIACRB + move.w mt_dmaon(pc),DMACON-INTREQ(a0) + + move.l (sp)+,a0 + nop + rte + + +mt_TimerBsetrep: +; Oneshot TimerB interrupt to set repeat samples after another DMADELAY ticks. + + ; clear EXTER and possible audio interrupt flags + move.l a4,-(sp) + move.l d0,a4 + moveq #$2000>>7,d0 ; EXTER-flag + or.b mt_dmaon+1(pc),d0 + lsl.w #7,d0 + move.w d0,(a0) + move.l a4,d0 + + ifd SDATA + lea _LinkerDB,a4 + else + lea mt_data(pc),a4 + endc + + ; set repeat sample pointers and lengths + ifne MINIMAL + move.l mt_chan1+n_loopstart(a4),AUD0LC-INTREQ(a0) + move.w mt_chan1+n_replen(a4),AUD0LEN-INTREQ(a0) + move.l mt_chan2+n_loopstart(a4),AUD1LC-INTREQ(a0) + move.w mt_chan2+n_replen(a4),AUD1LEN-INTREQ(a0) + move.l mt_chan3+n_loopstart(a4),AUD2LC-INTREQ(a0) + move.w mt_chan3+n_replen(a4),AUD2LEN-INTREQ(a0) + move.l mt_chan4+n_loopstart(a4),AUD3LC-INTREQ(a0) + move.w mt_chan4+n_replen(a4),AUD3LEN-INTREQ(a0) + + else ; !MINIMAL + tst.b mt_chan1+n_enable(a4) + beq .1 + move.l mt_chan1+n_loopstart(a4),AUD0LC-INTREQ(a0) + move.w mt_chan1+n_replen(a4),AUD0LEN-INTREQ(a0) +.1: tst.b mt_chan2+n_enable(a4) + beq .2 + move.l mt_chan2+n_loopstart(a4),AUD1LC-INTREQ(a0) + move.w mt_chan2+n_replen(a4),AUD1LEN-INTREQ(a0) +.2: tst.b mt_chan3+n_enable(a4) + beq .3 + move.l mt_chan3+n_loopstart(a4),AUD2LC-INTREQ(a0) + move.w mt_chan3+n_replen(a4),AUD2LEN-INTREQ(a0) +.3: tst.b mt_chan4+n_enable(a4) + beq .4 + move.l mt_chan4+n_loopstart(a4),AUD3LC-INTREQ(a0) + move.w mt_chan4+n_replen(a4),AUD3LEN-INTREQ(a0) +.4: + endc + + move.l (sp)+,a4 + move.l (sp)+,a0 + nop + rte + +TB_toggle: + dc.b 0 + even + endc ; !NO_TIMERS + endc ; !OSCOMPAT + + + ifne NO_TIMERS +;--------------------------------------------------------------------------- + xdef _mt_dmaon +_mt_dmaon: +; Enable audio DMA after some delay. +; Called by main program, for example out of a copper-interrupt. + + move.w mt_dmaon(pc),CUSTOM+DMACON + rts + + + xdef _mt_setrep +_mt_setrep: +; Set repeat sample pointers and lengths. +; Called by main program, for example out of a copper-interrupt. + + movem.l a0/a4,-(sp) + + ; reset audio interrupt flags + lea CUSTOM+INTREQ,a0 + move.l d0,a4 + move.w mt_dmaon(pc),d0 + lsl.w #7,d0 + move.w d0,(a0) + move.l a4,d0 + + ifd SDATA + lea _LinkerDB,a4 + else + lea mt_data(pc),a4 + endc + + ; set repeat sample pointers and lengths + ifne MINIMAL + move.l mt_chan1+n_loopstart(a4),AUD0LC-INTREQ(a0) + move.w mt_chan1+n_replen(a4),AUD0LEN-INTREQ(a0) + move.l mt_chan2+n_loopstart(a4),AUD1LC-INTREQ(a0) + move.w mt_chan2+n_replen(a4),AUD1LEN-INTREQ(a0) + move.l mt_chan3+n_loopstart(a4),AUD2LC-INTREQ(a0) + move.w mt_chan3+n_replen(a4),AUD2LEN-INTREQ(a0) + move.l mt_chan4+n_loopstart(a4),AUD3LC-INTREQ(a0) + move.w mt_chan4+n_replen(a4),AUD3LEN-INTREQ(a0) + + else ; !MINIMAL + tst.b mt_chan1+n_enable(a4) + beq .1 + move.l mt_chan1+n_loopstart(a4),AUD0LC-INTREQ(a0) + move.w mt_chan1+n_replen(a4),AUD0LEN-INTREQ(a0) +.1: tst.b mt_chan2+n_enable(a4) + beq .2 + move.l mt_chan2+n_loopstart(a4),AUD1LC-INTREQ(a0) + move.w mt_chan2+n_replen(a4),AUD1LEN-INTREQ(a0) +.2: tst.b mt_chan3+n_enable(a4) + beq .3 + move.l mt_chan3+n_loopstart(a4),AUD2LC-INTREQ(a0) + move.w mt_chan3+n_replen(a4),AUD2LEN-INTREQ(a0) +.3: tst.b mt_chan4+n_enable(a4) + beq .4 + move.l mt_chan4+n_loopstart(a4),AUD3LC-INTREQ(a0) + move.w mt_chan4+n_replen(a4),AUD3LEN-INTREQ(a0) +.4: + endc + + movem.l (sp)+,a0/a4 + rts + endc ; NO_TIMERS + +mt_dmaon: + dc.w $8000 + + +;--------------------------------------------------------------------------- + xdef _mt_init +_mt_init: +; Initialize new module. +; Reset speed to 6, tempo to 125 and start at given song position. +; Master volume is at 64 (maximum). +; a6 = CUSTOM +; a0 = module pointer +; a1 = sample pointer (NULL means samples are stored within the module) +; d0 = initial song position + + ifnd SDATA + move.l a4,-(sp) + lea mt_data(pc),a4 + endc + + move.l a0,mt_mod(a4) + movem.l d2/a2,-(sp) + + ; set initial song position + cmp.b 950(a0),d0 + blo .1 + moveq #0,d0 +.1: move.b d0,mt_SongPos(a4) + + move.l a1,d0 ; sample data location is given? + bne .4 + + ; get number of highest pattern + lea 952(a0),a1 ; song arrangement list + moveq #127,d0 + moveq #0,d2 +.2: move.b (a1)+,d1 + cmp.b d2,d1 + bls .3 + move.b d1,d2 +.3: dbf d0,.2 + addq.b #1,d2 ; number of patterns + + ; now we can calculate the base address of the sample data + moveq #10,d0 + asl.l d0,d2 + lea (a0,d2.l),a1 + add.w #1084,a1 + + ; save start address of each sample and do some fixes for broken mods +.4: lea mt_SampleStarts(a4),a2 + moveq #1,d2 + moveq #31-1,d0 +.5: move.l a1,(a2)+ + moveq #0,d1 + move.w 42(a0),d1 + cmp.w d2,d1 ; length 0 and 1 define unused samples + bls .6 + add.l d1,d1 + add.l d1,a1 + bra .7 +.6: clr.w 42(a0) ; length 1 means zero -> no sample +.7: lea 30(a0),a0 + dbf d0,.5 + + movem.l (sp)+,d2/a2 + + ifeq VBLANK_MUSIC + ; reset CIA timer A to default (125) + move.l mt_timerval(a4),d0 + divu #125,d0 + move.b d0,CIAB+CIATALO + lsr.w #8,d0 + move.b d0,CIAB+CIATAHI + endc + +mt_reset: +; a4 must be initialized with base register + + ; reset speed and counters + move.b #6,mt_Speed(a4) + clr.b mt_Counter(a4) + clr.w mt_PatternPos(a4) + clr.b mt_PattDelTime(a4) + clr.b mt_PattDelTime2(a4) + clr.w mt_PBreakPos(a4) + clr.b mt_PBreakFlag(a4) + clr.b mt_PosJumpFlag(a4) + + ; disable the filter + or.b #2,CIAA+CIAPRA + + ifeq MINIMAL + ; set master volume to 64 + lea MasterVolTab64(pc),a0 + move.l a0,mt_MasterVolTab(a4) + endc + + ; set channel index + clr.b mt_chan1+n_index(a4) + move.b #1,mt_chan2+n_index(a4) + move.b #2,mt_chan3+n_index(a4) + move.b #3,mt_chan4+n_index(a4) + + ; initialize channel DMA and interrupt bits + move.w #$0001,mt_chan1+n_dmabit(a4) + move.w #$0002,mt_chan2+n_dmabit(a4) + move.w #$0004,mt_chan3+n_dmabit(a4) + move.w #$0008,mt_chan4+n_dmabit(a4) + move.w #$0080,mt_chan1+n_intbit(a4) + move.w #$0100,mt_chan2+n_intbit(a4) + move.w #$0200,mt_chan3+n_intbit(a4) + move.w #$0400,mt_chan4+n_intbit(a4) + + clr.b mt_E8Trigger(a4) + ifeq MINIMAL + clr.b mt_SongEnd(a4) + clr.b mt_SilCntValid(a4) + endc + + ifnd SDATA + move.l (sp)+,a4 + endc + + +;--------------------------------------------------------------------------- + xdef _mt_end +_mt_end: +; Stop playing current module. +; a6 = CUSTOM + + DISABLE + ifd SDATA + clr.b mt_Enable(a4) + lea mt_chan1(a4),a0 + bsr resetch + lea mt_chan2(a4),a0 + bsr resetch + lea mt_chan3(a4),a0 + bsr resetch + lea mt_chan4(a4),a0 + bsr resetch + else + lea mt_data(pc),a1 + clr.b mt_Enable(a1) + lea mt_chan1(a1),a0 + bsr resetch + lea mt_chan2(a1),a0 + bsr resetch + lea mt_chan3(a1),a0 + bsr resetch + lea mt_chan4(a1),a0 + bsr resetch + endc + moveq #0,d0 + move.w d0,AUD0VOL(a6) + move.w d0,AUD1VOL(a6) + move.w d0,AUD2VOL(a6) + move.w d0,AUD3VOL(a6) + move.w #$000f,DMACON(a6) + ENABLE + rts + + +resetch: +; a0 = channel status +; All registers are preserved! + + move.w #320,n_period(a0) ; make sure period is not illegal + clr.w n_volume(a0) + clr.w n_sfxlen(a0) + clr.w n_funk(a0) + clr.b n_sfxpri(a0) + clr.b n_looped(a0) + clr.b n_gliss(a0) + ifeq MINIMAL + clr.b n_musiconly(a0) + st n_enable(a0) + endc + rts + + + ifeq MINIMAL +;--------------------------------------------------------------------------- + xdef _mt_soundfx +_mt_soundfx: +; Request playing of an external sound effect on the most unused channel. +; This function is for compatibility with the old API only! +; You should call _mt_playfx instead! +; a6 = CUSTOM +; a0 = sample pointer +; d0.w = sample length in words +; d1.w = sample period +; d2.w = sample volume + + lea -sfx_sizeof(sp),sp + move.l a0,sfx_ptr(sp) + movem.w d0-d2,sfx_len(sp) + move.w #$ff01,sfx_cha(sp) ; any channel, priority=1 + move.l sp,a0 + bsr _mt_playfx + lea sfx_sizeof(sp),sp + rts + + +;--------------------------------------------------------------------------- + xdef _mt_playfx +_mt_playfx: +; Request playing of a prioritized external sound effect, either on a +; fixed channel or on the most unused one. +; A negative channel specification means to use the best one. +; The priority is unsigned and should be greater than zero. +; This channel will be blocked for music until the effect has finished. +; a6 = CUSTOM +; a0 = sfx-structure pointer with the following layout: +; 0: ptr, 4: len.w, 6: period.w, 8: vol.w, 10: channel.b, 11: priority.b +; -> d0 = pointer to channel status or NULL when sample was ignored + + ifd SDATA + movem.l d2-d7/a0-a3/a5,-(sp) + else + movem.l d2-d7/a0-a5,-(sp) + lea mt_data(pc),a4 + endc + + DISABLE + moveq #0,d0 + move.b sfx_cha(a0),d0 + bpl channelsfx ; use fixed channel for effect + + ; Did we already calculate the n_freecnt values for all channels? + tst.b mt_SilCntValid(a4) + bne freecnt_valid + + ; Look at the next 8 pattern steps to find the longest sequence + ; of silence (no new note or instrument). + moveq #8,d2 + move.l #$fffff000,d3 ; mask to ignore effects + + ; reset freecnts for all channels + moveq #0,d0 + move.b d0,mt_chan1+n_freecnt(a4) + move.b d0,mt_chan2+n_freecnt(a4) + move.b d0,mt_chan3+n_freecnt(a4) + move.b d0,mt_chan4+n_freecnt(a4) + moveq #-1,d4 + moveq #-1,d5 + moveq #-1,d6 + moveq #-1,d7 + + ; get pattern pointer + move.l mt_mod(a4),a3 ; a3 mod pointer + lea 952(a3),a5 ; a5 song arrangement list + move.w mt_PatternPos(a4),d1 + move.b mt_SongPos(a4),d0 +.1: move.b (a5,d0.w),d0 + swap d0 + lea 1084(a3),a1 + lsr.l #6,d0 + add.l d0,a1 + lea 1024(a1),a2 ; a2 end of pattern + add.w d1,a1 ; a1 current pattern pos + +.2: moveq #0,d0 + + move.l (a1)+,d1 + and.l d3,d1 + seq d1 + and.b d1,d4 + sub.b d4,mt_chan1+n_freecnt(a4) + add.b d4,d0 + + move.l (a1)+,d1 + and.l d3,d1 + seq d1 + and.b d1,d5 + sub.b d5,mt_chan2+n_freecnt(a4) + add.b d5,d0 + + move.l (a1)+,d1 + and.l d3,d1 + seq d1 + and.b d1,d6 + sub.b d6,mt_chan3+n_freecnt(a4) + add.b d6,d0 + + move.l (a1)+,d1 + and.l d3,d1 + seq d1 + and.b d1,d7 + sub.b d7,mt_chan4+n_freecnt(a4) + add.b d7,d0 + + ; break the loop when no channel has any more free pattern steps + beq .7 + + ; otherwise break after 8 pattern steps + subq.w #1,d2 + beq .7 + + ; End of pattern reached? Then load next pattern pointer. + cmp.l a2,a1 + blo .2 + moveq #0,d1 + moveq #1,d0 + add.b mt_SongPos(a4),d0 + and.w #$007f,d0 + cmp.b 950(a3),d0 ; end of song reached? + blo .1 + moveq #0,d0 + bra .1 + +.7: st mt_SilCntValid(a4) + +freecnt_valid: + sub.l a2,a2 + move.b sfx_pri(a0),d2 + + ; Determine which channels are already allocated for sound + ; effects and check if the limit was reached. In this case only + ; replace sound effect channels by higher priority. + moveq #3,d0 + sub.b mt_MusicChannels(a4),d0 + move.b mt_chan1+n_sfxpri(a4),d4 + or.b mt_chan1+n_musiconly(a4),d4 + sne d1 + add.b d1,d0 + move.b mt_chan2+n_sfxpri(a4),d5 + or.b mt_chan2+n_musiconly(a4),d5 + sne d1 + add.b d1,d0 + move.b mt_chan3+n_sfxpri(a4),d6 + or.b mt_chan3+n_musiconly(a4),d6 + sne d1 + add.b d1,d0 + move.b mt_chan4+n_sfxpri(a4),d7 + or.b mt_chan4+n_musiconly(a4),d7 + sne d1 + add.b d1,d0 + bmi .overwrite ; all channels reserved/playing effects + + ; We will prefer a music channel which had an audio interrupt, + ; because that means the last instrument sample has been played + ; completely, and the channel is now in an idle loop. + ; Also exclude channels which have set a repeat loop. + ; Try not to break them! + moveq #0,d3 + tst.b mt_chan1+n_looped(a4) + bne .1 + or.w #$0080,d3 +.1: tst.b mt_chan2+n_looped(a4) + bne .2 + or.w #$0100,d3 +.2: tst.b mt_chan3+n_looped(a4) + bne .3 + or.w #$0200,d3 +.3: tst.b mt_chan4+n_looped(a4) + bne .4 + or.w #$0400,d3 +.4: move.w INTREQR(a6),d1 + and.w d3,d1 + bne .6 + + ; All channels are busy. Then break the non-looped ones first... + move.w d3,d1 + bne .6 + + ; ..except there are none. Then it doesn't matter. :| + move.w #$0780,d1 + + ; first look for the best unused channel +.6: moveq #0,d3 + btst #7,d1 + seq d0 + or.b d4,d0 + bne .7 + lea mt_chan1+n_freecnt(a4),a1 + cmp.b (a1),d3 + bhi .7 + move.l a1,a2 + move.b (a1),d3 +.7: btst #8,d1 + seq d0 + or.b d5,d0 + bne .8 + lea mt_chan2+n_freecnt(a4),a1 + cmp.b (a1),d3 + bhi .8 + move.l a1,a2 + move.b (a1),d3 +.8: btst #9,d1 + seq d0 + or.b d6,d0 + bne .9 + lea mt_chan3+n_freecnt(a4),a1 + cmp.b (a1),d3 + bhi .9 + move.l a1,a2 + move.b (a1),d3 +.9: btst #10,d1 + seq d0 + or.b d7,d0 + bne .10 + lea mt_chan4+n_freecnt(a4),a1 + cmp.b (a1),d3 + bhi .10 + move.l a1,a2 + bra found_sfx_ch + +.10: move.l a2,d3 + bne found_sfx_ch + +.overwrite: + ; finally try to overwrite a sound effect with lower/equal priority + moveq #0,d3 + tst.b d4 + beq .11 + cmp.b d4,d2 + blo .11 + lea mt_chan1+n_freecnt(a4),a2 + move.b (a2),d3 +.11: tst.b d5 + beq .12 + cmp.b d5,d2 + blo .12 + lea mt_chan2+n_freecnt(a4),a1 + cmp.b (a1),d3 + bhi .12 + move.l a1,a2 + move.b (a1),d3 +.12: tst.b d6 + beq .13 + cmp.b d6,d2 + blo .13 + lea mt_chan3+n_freecnt(a4),a1 + cmp.b (a1),d3 + bhi .13 + move.l a1,a2 + move.b (a1),d3 +.13: tst.b d7 + beq .14 + cmp.b d7,d2 + blo .14 + lea mt_chan4+n_freecnt(a4),a1 + cmp.b (a1),d3 + bhi .14 + move.l a1,a2 + +.14: move.l a2,d3 + beq exit_playfx ; ignore new sfx due to low priority + +found_sfx_ch: + lea -n_freecnt(a2),a2 + bra set_sfx + +channelsfx: +; a0 = sfx structure +; d0 = fixed channel for new sound effect + add.w d0,d0 + lea mt_chan1(a4),a2 + add.w channel_offsets(pc,d0.w),a2 + + ; priority high enough to replace a present effect on this channel? + move.b sfx_pri(a0),d2 + cmp.b n_sfxpri(a2),d2 + bhs set_sfx + sub.l a2,a2 + bra exit_playfx + +set_sfx: +; activate the sound effect on this channel +; a0 = sfx structure +; d2 = sfx priority +; a2 = channel status + move.l (a0)+,n_sfxptr(a2) ; sfx_ptr + move.w (a0)+,n_sfxlen(a2) ; sfx_len + move.w (a0)+,n_sfxper(a2) ; sfx_per + move.w (a0),n_sfxvol(a2) ; sfx_vol + move.b d2,n_sfxpri(a2) + +exit_playfx: + ENABLE + move.l a2,d0 ; ptr to selected channel or NULL + + ifd SDATA + movem.l (sp)+,d2-d7/a0-a3/a5 + else + movem.l (sp)+,d2-d7/a0-a5 + endc + rts + +channel_offsets: + dc.w 0*n_sizeof,1*n_sizeof,2*n_sizeof,3*n_sizeof + + +;--------------------------------------------------------------------------- + xdef _mt_loopfx +_mt_loopfx: +; Request playing of a looped sound effect on a fixed channel, which +; will be blocked for music until the effect is stopped (_mt_stopfx). +; It uses the same sfx-structure as _mt_playfx, but the priority is +; ignored. A looped sound effect has always highest priority and will +; replace a previous effect on the same channel. No automatic channel +; selection possible! +; Also make sure the sample starts with a zero-word, which is used +; for idling when the effect is stopped. This word is included in the +; total length calculation, but excluded when actually playing the loop. +; a6 = CUSTOM +; a0 = sfx-structure pointer with the following layout: +; 0: ptr, 4: len.w, 6: period.w, 8: vol.w, 10: channel.b + + ifnd SDATA + lea mt_data+mt_chan1(pc),a1 + else + lea mt_chan1(a4),a1 + endc + + moveq #3,d0 + and.b sfx_cha(a0),d0 + add.w d0,d0 + add.w channel_offsets(pc,d0.w),a1 + + DISABLE + move.l (a0)+,n_sfxptr(a1) ; sfx_ptr + move.w (a0)+,n_sfxlen(a1) ; sfx_len + move.w (a0)+,n_sfxper(a1) ; sfx_per + move.w (a0),n_sfxvol(a1) ; sfx_vol + st n_sfxpri(a1) ; sfx_pri -1 enables looped mode + ENABLE + rts + + +;--------------------------------------------------------------------------- + xdef _mt_stopfx +_mt_stopfx: +; Immediately stop a currently playing sound effect on a channel. +; a6 = CUSTOM +; d0.b = channel (0..3) + + ifnd SDATA + lea mt_data+mt_chan1(pc),a0 + else + lea mt_chan1(a4),a0 + endc + + and.w #3,d0 + add.w d0,d0 + add.w channel_offsets(pc,d0.w),a0 + + DISABLE + tst.b n_sfxpri(a0) + beq .1 ; no sfx playing anyway + moveq #1,d0 + move.b d0,n_sfxpri(a0) + move.w d0,n_sfxlen(a0) ; idle loop + move.w #108,n_sfxper(a0) ; enter idle as quickly as possible + clr.w n_sfxvol(a0) ; and cut volume + ifne NULL_IS_CLEARED + clr.b n_looped(a0) + clr.l n_sfxptr(a0) ; use $0 for idle-looping + else + tst.b n_looped(a0) + beq .1 + clr.b n_looped(a0) + subq.l #2,n_sfxptr(a0) ; idle loop at sample-start - 2 + endc +.1: ENABLE + + rts + + +;--------------------------------------------------------------------------- + xdef _mt_musicmask +_mt_musicmask: +; Set bits in the mask define which specific channels are reserved +; for music only. +; a6 = CUSTOM +; d0.b = channel-mask (bit 0 for channel 0, ..., bit 3 for channel 3) + + ifnd SDATA + move.l a4,-(sp) + lea mt_data(pc),a4 + endc + + DISABLE + + lsl.b #5,d0 + scs mt_chan4+n_musiconly(a4) + add.b d0,d0 + scs mt_chan3+n_musiconly(a4) + add.b d0,d0 + scs mt_chan2+n_musiconly(a4) + add.b d0,d0 + scs mt_chan1+n_musiconly(a4) + + ENABLE + + ifnd SDATA + move.l (sp)+,a4 + endc + rts + + +;--------------------------------------------------------------------------- + xdef _mt_mastervol +_mt_mastervol: +; Set a master volume from 0 to 64 for all music channels. +; Note that the master volume does not affect the volume of external +; sound effects (which is desired). +; a6 = CUSTOM +; d0.w = master volume + + ifnd SDATA + move.l a4,-(sp) + lea mt_data(pc),a4 + endc + + ; stingray, since each volume table has a size of 65 bytes + ; we simply multiply (optimised of course) by 65 to get the + ; offset to the correct table + lea MasterVolTab0(pc),a0 + add.w d0,a0 + lsl.w #6,d0 + add.w d0,a0 + + ; set new table and adapt all channel volumes immediately + DISABLE + move.l a0,mt_MasterVolTab(a4) + bsr set_all_volumes + ENABLE + + ifnd SDATA + move.l (sp)+,a4 + endc + rts + + +;--------------------------------------------------------------------------- + xdef _mt_channelmask +_mt_channelmask: +; Define which music channels are muted. Doesn't affect sound effects. +; a6 = CUSTOM +; d0.b = channel-mask (bit 0 for channel 0, ..., bit 3 for channel 3) + + ifnd SDATA + move.l a4,-(sp) + lea mt_data(pc),a4 + endc + + DISABLE + + lsl.b #5,d0 + scs mt_chan4+n_enable(a4) + add.b d0,d0 + scs mt_chan3+n_enable(a4) + add.b d0,d0 + scs mt_chan2+n_enable(a4) + add.b d0,d0 + scs mt_chan1+n_enable(a4) + move.l mt_MasterVolTab(a4),a0 + bsr set_all_volumes + + ENABLE + + ifnd SDATA + move.l (sp)+,a4 + endc + rts + + +;--------------------------------------------------------------------------- +set_all_volumes: +; a0 = Master volume table + + tst.b mt_chan1+n_sfxpri(a4) + bne .1 + move.w mt_chan1+n_volume(a4),d0 + move.b (a0,d0.w),d0 + and.b mt_chan1+n_enable(a4),d0 + move.w d0,AUD0VOL(a6) + ifd VOL0_UNLOOPS + bne .1 + clr.b mt_chan1+n_looped(a4) + endc +.1: tst.b mt_chan2+n_sfxpri(a4) + bne .2 + move.w mt_chan2+n_volume(a4),d0 + move.b (a0,d0.w),d0 + and.b mt_chan2+n_enable(a4),d0 + move.w d0,AUD1VOL(a6) + ifd VOL0_UNLOOPS + bne .2 + clr.b mt_chan2+n_looped(a4) + endc +.2: tst.b mt_chan3+n_sfxpri(a4) + bne .3 + move.w mt_chan3+n_volume(a4),d0 + move.b (a0,d0.w),d0 + and.b mt_chan3+n_enable(a4),d0 + move.w d0,AUD2VOL(a6) + ifd VOL0_UNLOOPS + bne .3 + clr.b mt_chan3+n_looped(a4) + endc +.3: tst.b mt_chan4+n_sfxpri(a4) + bne .4 + move.w mt_chan4+n_volume(a4),d0 + move.b (a0,d0.w),d0 + and.b mt_chan4+n_enable(a4),d0 + move.w d0,AUD3VOL(a6) + ifd VOL0_UNLOOPS + bne .4 + clr.b mt_chan4+n_looped(a4) + endc +.4: rts + + +;--------------------------------------------------------------------------- + xdef _mt_samplevol +_mt_samplevol: +; Redefine a sample's volume. May also be done while the song is playing. +; Warning: Does not check arguments for valid range! You must have done +; _mt_init before calling this function! +; The new volume is persistent. Even when the song is restarted. +; d0.w = sample number (0-31) +; d1.b = volume (0-64) + + ifd SDATA + move.l mt_mod(a4),a0 + else + move.l mt_data+mt_mod(pc),a0 + endc + + swap d1 + move.w d0,d1 + add.w d1,d1 + lsl.w #5,d0 + sub.w d1,d0 ; table index: sample number * 30 + swap d1 + move.b d1,12+3(a0,d0.w) ; set sample's volume + rts + endc ; !MINIMAL + + +;--------------------------------------------------------------------------- + xdef _mt_music +_mt_music: +; Used when called by external interrupt handler. +; Saves/restores all registers and sets up A4 when needed. +; a6 = CUSTOM + + ifd SDATA + movem.l d2-d7/a2-a3/a5,-(sp) + bsr mt_music + movem.l (sp)+,d2-d7/a2-a3/a5 + else ; !SDATA + movem.l d2-d7/a2-a5,-(sp) + lea mt_data(pc),a4 + bsr mt_music + movem.l (sp)+,d2-d7/a2-a5 + endc + rts + +mt_music: +; Called from interrupt. +; Play next position when Counter equals Speed. +; Effects are always handled. +; a6 = CUSTOM + + moveq #0,d7 ; d7 is always zero + + lea mt_dmaon+1(pc),a0 + move.b d7,(a0) + + addq.b #1,mt_Counter(a4) + + move.b mt_Counter(a4),d0 + cmp.b mt_Speed(a4),d0 + blo no_new_note + + ; handle a new note + move.b d7,mt_Counter(a4) + tst.b mt_PattDelTime2(a4) + beq get_new_note + + ; we have a pattern delay, check effects then step + lea AUD0LC(a6),a5 + lea mt_chan1(a4),a2 + bsr mt_checkfx + lea AUD1LC(a6),a5 + lea mt_chan2(a4),a2 + bsr mt_checkfx + lea AUD2LC(a6),a5 + lea mt_chan3(a4),a2 + bsr mt_checkfx + lea AUD3LC(a6),a5 + lea mt_chan4(a4),a2 + bsr mt_checkfx + bra settb_step + +no_new_note: + ; no new note, just check effects, don't step to next position + lea AUD0LC(a6),a5 + lea mt_chan1(a4),a2 + bsr mt_checkfx + lea AUD1LC(a6),a5 + lea mt_chan2(a4),a2 + bsr mt_checkfx + lea AUD2LC(a6),a5 + lea mt_chan3(a4),a2 + bsr mt_checkfx + lea AUD3LC(a6),a5 + lea mt_chan4(a4),a2 + bsr mt_checkfx + + ifeq NO_TIMERS + ; set one-shot TimerB interrupt for enabling DMA, when needed + move.b mt_dmaon+1(pc),d0 + beq same_pattern + move.b #$19,CIAB+CIACRB ; load/start timer B, one-shot + endc + bra same_pattern + +get_new_note: + ; determine pointer to current pattern line + move.l mt_mod(a4),a0 + lea 12(a0),a3 ; sample info table + lea 1084(a0),a1 ; pattern data + lea 952(a0),a0 + moveq #0,d0 + move.b mt_SongPos(a4),d0 + move.b (a0,d0.w),d0 ; current pattern number + swap d0 + lsr.l #6,d0 + add.l d0,a1 ; pattern base + add.w mt_PatternPos(a4),a1 ; a1 pattern line + + ; play new note for each channel, apply some effects + lea AUD0LC(a6),a5 + lea mt_chan1(a4),a2 + bsr mt_playvoice + lea AUD1LC(a6),a5 + lea mt_chan2(a4),a2 + bsr mt_playvoice + lea AUD2LC(a6),a5 + lea mt_chan3(a4),a2 + bsr mt_playvoice + lea AUD3LC(a6),a5 + lea mt_chan4(a4),a2 + bsr mt_playvoice + +settb_step: + ifeq NO_TIMERS + ; set one-shot TimerB interrupt for enabling DMA, when needed + move.b mt_dmaon+1(pc),d0 + beq pattern_step + move.b #$19,CIAB+CIACRB ; load/start timer B, one-shot + endc + +pattern_step: + ; next pattern line, handle delay and break + ifeq MINIMAL + clr.b mt_SilCntValid(a4) ; recalculate silence counters + endc + moveq #16,d2 ; offset to next pattern line + + move.b mt_PattDelTime2(a4),d1 + move.b mt_PattDelTime(a4),d0 + beq .1 + move.b d0,d1 + move.b d7,mt_PattDelTime(a4) +.1: tst.b d1 + beq .3 + subq.b #1,d1 + beq .2 + moveq #0,d2 ; do not advance to next line +.2: move.b d1,mt_PattDelTime2(a4) + +.3: add.w mt_PatternPos(a4),d2 ; d2 PatternPos + + ; check for break + bclr d7,mt_PBreakFlag(a4) + beq .4 + move.w mt_PBreakPos(a4),d2 + move.w d7,mt_PBreakPos(a4) + + ; check whether end of pattern is reached +.4: move.w d2,mt_PatternPos(a4) + cmp.w #1024,d2 + blo same_pattern + +song_step: + move.w mt_PBreakPos(a4),mt_PatternPos(a4) + move.w d7,mt_PBreakPos(a4) + move.b d7,mt_PosJumpFlag(a4) + + ; next position in song + moveq #1,d0 + add.b mt_SongPos(a4),d0 + and.w #$007f,d0 + move.l mt_mod(a4),a0 + cmp.b 950(a0),d0 ; end of song reached? + blo .1 + moveq #0,d0 ; restart the song from the beginning + ifeq MINIMAL + addq.b #1,mt_SongEnd(a4) + bne .2 + clr.b mt_Enable(a4) ; stop the song when mt_SongEnd was -1 +.2: and.b #$7f,mt_SongEnd(a4) + endc +.1: move.b d0,mt_SongPos(a4) + +same_pattern: + tst.b mt_PosJumpFlag(a4) + bne song_step + + rts + + + ifeq MINIMAL +;--------------------------------------------------------------------------- +mt_sfxonly: +; Called from interrupt. +; Plays sound effects on free channels. +; a6 = CUSTOM + + moveq #0,d7 ; d7 is always zero + + lea mt_dmaon+1(pc),a0 + move.b d7,(a0) + + lea AUD0LC(a6),a5 + lea mt_chan1(a4),a2 + bsr chan_sfx_only + lea AUD1LC(a6),a5 + lea mt_chan2(a4),a2 + bsr chan_sfx_only + lea AUD2LC(a6),a5 + lea mt_chan3(a4),a2 + bsr chan_sfx_only + lea AUD3LC(a6),a5 + lea mt_chan4(a4),a2 + bsr chan_sfx_only + + ifeq NO_TIMERS + move.b mt_dmaon+1(pc),d0 + beq .1 + move.b #$19,CIAB+CIACRB ; load/start timer B, one-shot + endc + +.1: rts + + +chan_sfx_only: +; Check for new sound samples. Check if previous ones are finished. +; a2 = channel data +; a5 = audio registers + + tst.b n_sfxpri(a2) + beq .1 + + move.w n_sfxlen(a2),d0 + bne start_sfx + tst.b n_looped(a2) + bne .1 + + move.w n_intbit(a2),d0 + and.w INTREQR(a6),d0 + beq .1 + move.w n_dmabit(a2),d0 + and.w mt_dmaon(pc),d0 + bne .1 + + ; last sound effect sample has played, so unblock this channel again + move.b d7,n_sfxpri(a2) + +.1: rts + + +;--------------------------------------------------------------------------- +start_sfx: +; d0 = sfx_len in words +; a2 = channel data +; a5 = audio registers + + ; play new sound effect on this channel + move.w n_dmabit(a2),d1 + move.w d1,DMACON(a6) + + move.l n_sfxptr(a2),a0 + tst.b n_sfxpri(a2) + bpl .1 + + ; looped sound effect + st n_looped(a2) + addq.l #2,a0 ; skip first word, used for idling + subq.w #1,d0 + move.l a0,AUDLC(a5) + move.w d0,AUDLEN(a5) + bra .2 + + ; normal sound effect +.1: move.b d7,n_looped(a2) + move.l a0,AUDLC(a5) + move.w d0,AUDLEN(a5) + moveq #1,d0 ; idles after playing once + ifne NULL_IS_CLEARED + sub.l a0,a0 + endc + + ; save repeat and period for TimerB interrupt +.2: move.l a0,n_loopstart(a2) + move.w d0,n_replen(a2) + move.w n_sfxper(a2),d0 + move.w d0,AUDPER(a5) + move.w d0,n_period(a2) + move.w n_sfxvol(a2),AUDVOL(a5) + + move.w d7,n_sfxlen(a2) ; don't call start_sfx again + + lea mt_dmaon(pc),a0 + or.w d1,(a0) ; DMA-channel to enable on TimerB + rts + endc ; !MINIMAL + + +;--------------------------------------------------------------------------- +mt_checkfx: +; a2 = channel data +; a5 = audio registers + + ifeq MINIMAL + tst.b n_sfxpri(a2) + beq .3 + + move.w n_sfxlen(a2),d0 + beq .2 + bsr start_sfx + + ; channel is blocked, only check some E-commands +.1: move.w #$0fff,d4 + and.w n_cmd(a2),d4 + move.w d4,d0 + clr.b d0 + cmp.w #$0e00,d0 + bne mt_nop + and.w #$00ff,d4 + bra blocked_e_cmds + +.2: tst.b n_looped(a2) + bne .1 + move.w n_intbit(a2),d0 + and.w INTREQR(a6),d0 + beq .1 + move.w n_dmabit(a2),d0 + and.w mt_dmaon(pc),d0 + bne .1 + + ; sound effect sample has played, so unblock this channel again + move.b d7,n_sfxpri(a2) + endc ; !MINIMAL + + ; do channel effects between notes +.3: move.w n_funk(a2),d0 + beq .4 + bsr mt_updatefunk + +.4: move.w #$0fff,d4 + and.w n_cmd(a2),d4 + beq mt_pernop + and.w #$00ff,d4 + + moveq #$0f,d0 + and.b n_cmd(a2),d0 + add.w d0,d0 + move.w fx_tab(pc,d0.w),d0 + jmp fx_tab(pc,d0.w) + +fx_tab: + dc.w mt_arpeggio-fx_tab ; $0 + dc.w mt_portaup-fx_tab + dc.w mt_portadown-fx_tab + dc.w mt_toneporta-fx_tab + dc.w mt_vibrato-fx_tab ; $4 + dc.w mt_tonevolslide-fx_tab + dc.w mt_vibrvolslide-fx_tab + dc.w mt_tremolo-fx_tab + dc.w mt_nop-fx_tab ; $8 + dc.w mt_nop-fx_tab + dc.w mt_volumeslide-fx_tab + dc.w mt_nop-fx_tab + dc.w mt_nop-fx_tab ; $C + dc.w mt_nop-fx_tab + dc.w mt_e_cmds-fx_tab + dc.w mt_nop-fx_tab + + +mt_pernop: +; just set the current period + + move.w n_period(a2),AUDPER(a5) +mt_nop: + rts + + +;--------------------------------------------------------------------------- +mt_playvoice: +; a1 = pattern ptr +; a2 = channel data +; a3 = sample info table +; a5 = audio registers + + move.l (a1)+,d6 ; d6 current note/cmd words + + ifeq MINIMAL + ; channel blocked by external sound effect? + tst.b n_sfxpri(a2) + beq .2 + + move.w n_sfxlen(a2),d0 + beq .1 + bsr start_sfx + bra moreblockedfx + + ; do only some limited commands, while sound effect is in progress +.1: tst.b n_looped(a2) + bne moreblockedfx + move.w n_intbit(a2),d0 + and.w INTREQR(a6),d0 + beq moreblockedfx + move.w n_dmabit(a2),d0 + and.w mt_dmaon(pc),d0 + bne moreblockedfx + + ; sound effect sample has played, so unblock this channel again + move.b d7,n_sfxpri(a2) + endc ; !MINIMAL + +.2: tst.l (a2) ; n_note/cmd: any note or cmd set? + bne .3 + move.w n_period(a2),AUDPER(a5) +.3: move.l d6,(a2) + + moveq #15,d5 + and.b n_cmd(a2),d5 + add.w d5,d5 ; d5 cmd*2 + + moveq #0,d4 + move.b d6,d4 ; d4 cmd argument (in MSW) + swap d4 + move.w #$0ff0,d4 + and.w d6,d4 ; d4 for checking E-cmd (in LSW) + + swap d6 + move.l d6,d0 ; S...S... + clr.b d0 + rol.w #4,d0 + rol.l #4,d0 ; ....00SS + + and.w #$0fff,d6 ; d6 note + + ; get sample start address + add.w d0,d0 ; sample number * 2 + beq set_regs + move.w mult30tab(pc,d0.w),d1 ; d1 sample info table offset + lea mt_SampleStarts(a4),a0 + add.w d0,d0 + move.l -4(a0,d0.w),d2 + + ; read length, volume and repeat from sample info table + lea (a3,d1.w),a0 + move.w (a0)+,d0 ; length + bne .4 + + ifne NULL_IS_CLEARED + moveq #0,d2 ; use $0 for empty samples + else + ; use the first two bytes from the first sample for empty samples + move.l mt_SampleStarts(a4),d2 + endc + addq.w #1,d0 + +.4: move.l d2,n_start(a2) + move.w d0,n_reallength(a2) + + ; determine period table from fine-tune parameter + moveq #0,d3 + move.b (a0)+,d3 + add.w d3,d3 + move.l a0,d1 + lea mt_PerFineTune(pc),a0 + add.w (a0,d3.w),a0 + move.l a0,n_pertab(a2) + move.l d1,a0 + cmp.w #2*8,d3 + shs n_minusft(a2) + + moveq #0,d1 + move.b (a0)+,d1 ; volume + move.w d1,n_volume(a2) + move.w (a0)+,d3 ; repeat offset + beq no_offset + + ; set repeat + add.l d3,d2 + add.l d3,d2 + move.w (a0),d0 + move.w d0,n_replen(a2) + exg d0,d3 ; n_replen to d3 + add.w d3,d0 + bra set_len_start + +mult30tab: + dc.w 0*30,1*30,2*30,3*30,4*30,5*30,6*30,7*30 + dc.w 8*30,9*30,10*30,11*30,12*30,13*30,14*30,15*30 + dc.w 16*30,17*30,18*30,19*30,20*30,21*30,22*30,23*30 + dc.w 24*30,25*30,26*30,27*30,28*30,29*30,30*30,31*30 + +no_offset: + move.w (a0),d3 + ifne NULL_IS_CLEARED + cmp.w #1,d3 + beq .1 + bhi set_replen + else + bne set_replen + endc + ; repeat length zero means idle-looping + addq.w #1,d3 +.1: moveq #0,d2 ; expect two zero bytes at $0 +set_replen: + move.w d3,n_replen(a2) +set_len_start: + move.w d0,n_length(a2) + move.l d2,n_loopstart(a2) + move.l d2,n_wavestart(a2) + + ifeq MINIMAL + move.l mt_MasterVolTab(a4),a0 + move.b (a0,d1.w),d1 + and.b n_enable(a2),d1 + endc + move.w d1,AUDVOL(a5) + + ; remember if sample is looped + ; @@@ FIXME: also need to check if n_loopstart equals n_start + subq.w #1,d3 + sne n_looped(a2) + +set_regs: +; d4 = cmd argument | masked E-cmd +; d5 = cmd*2 +; d6 = cmd.w | note.w + + move.w d4,d3 ; d3 masked E-cmd + swap d4 ; d4 cmd argument into LSW + + tst.w d6 + beq checkmorefx ; no new note + + cmp.w #$0e50,d3 + beq set_finetune + + move.w prefx_tab(pc,d5.w),d0 + jmp prefx_tab(pc,d0.w) + +prefx_tab: + dc.w set_period-prefx_tab,set_period-prefx_tab,set_period-prefx_tab + dc.w set_toneporta-prefx_tab ; $3 + dc.w set_period-prefx_tab + dc.w set_toneporta-prefx_tab ; $5 + dc.w set_period-prefx_tab,set_period-prefx_tab,set_period-prefx_tab + dc.w set_sampleoffset-prefx_tab ; $9 + dc.w set_period-prefx_tab,set_period-prefx_tab,set_period-prefx_tab + dc.w set_period-prefx_tab,set_period-prefx_tab,set_period-prefx_tab + + +mt_sampleoffset: +; cmd 9 x y (xy = offset in 256 bytes) +; d4 = xy + + moveq #0,d0 + move.b d4,d0 + bne .1 + move.b n_sampleoffset(a2),d0 + bra .2 +.1: move.b d0,n_sampleoffset(a2) + +.2: lsl.w #7,d0 + cmp.w n_length(a2),d0 + bhs .3 + sub.w d0,n_length(a2) + add.w d0,d0 + add.l d0,n_start(a2) + rts + +.3: move.w #1,n_length(a2) + rts + + +set_sampleoffset: + bsr mt_sampleoffset + bra set_period + +set_finetune: + lea mt_PerFineTune(pc),a0 + moveq #$0f,d0 + and.b d4,d0 + add.w d0,d0 + add.w (a0,d0.w),a0 + move.l a0,n_pertab(a2) + cmp.w #2*8,d0 + shs n_minusft(a2) + +set_period: +; find nearest period for a note value, then apply finetuning +; d3 = masked E-cmd +; d4 = cmd argument +; d5 = cmd*2 +; d6 = note.w + + lea mt_PeriodTable(pc),a0 + moveq #36-1,d0 + moveq #-2,d1 +.1: addq.w #2,d1 ; table offset + cmp.w (a0)+,d6 + dbhs d0,.1 + + ; apply finetuning, set period and note-offset + move.l n_pertab(a2),a0 + move.w (a0,d1.w),d2 + move.w d2,n_period(a2) + move.w d1,n_noteoff(a2) + + ; check for notedelay + cmp.w #$0ed0,d3 ; notedelay + beq checkmorefx + + ; disable DMA + move.w n_dmabit(a2),d0 + move.w d0,DMACON(a6) + + btst #2,n_vibratoctrl(a2) + bne .2 + move.b d7,n_vibratopos(a2) + +.2: btst #2,n_tremoloctrl(a2) + bne .3 + move.b d7,n_tremolopos(a2) + +.3: move.l n_start(a2),AUDLC(a5) + move.w n_length(a2),AUDLEN(a5) + move.w d2,AUDPER(a5) + lea mt_dmaon(pc),a0 + or.w d0,(a0) + +checkmorefx: +; d4 = cmd argument +; d5 = cmd*2 +; d6 = note.w + + move.w n_funk(a2),d0 + beq .1 + bsr mt_updatefunk + +.1: move.w morefx_tab(pc,d5.w),d0 + jmp morefx_tab(pc,d0.w) + +morefx_tab: + dc.w mt_pernop-morefx_tab,mt_pernop-morefx_tab,mt_pernop-morefx_tab + dc.w mt_pernop-morefx_tab,mt_pernop-morefx_tab,mt_pernop-morefx_tab + dc.w mt_pernop-morefx_tab,mt_pernop-morefx_tab,mt_pernop-morefx_tab + dc.w mt_sampleoffset-morefx_tab ; $9 + dc.w mt_pernop-morefx_tab + dc.w mt_posjump-morefx_tab ; $B + dc.w mt_volchange-morefx_tab + dc.w mt_patternbrk-morefx_tab ; $D + dc.w mt_e_cmds-morefx_tab + dc.w mt_setspeed-morefx_tab + + +set_toneporta: + move.l n_pertab(a2),a0 ; tuned period table + + ; find first period which is less or equal the note in d6 + moveq #36-1,d0 + moveq #-2,d1 +.1: addq.w #2,d1 + cmp.w (a0)+,d6 + dbhs d0,.1 + + tst.b n_minusft(a2) ; negative fine tune? + beq .2 + tst.w d1 + beq .2 + subq.l #2,a0 ; then take previous period + subq.w #2,d1 + +.2: move.w d1,n_noteoff(a2) ; note offset in period table + move.w n_period(a2),d2 + move.w -(a0),d1 + cmp.w d1,d2 + bne .3 + moveq #0,d1 +.3: move.w d1,n_wantedperiod(a2) + + move.w n_funk(a2),d0 + beq .4 + bsr mt_updatefunk + +.4: move.w d2,AUDPER(a5) + rts + + + ifeq MINIMAL +moreblockedfx: +; d6 = note.w | cmd.w + + moveq #0,d4 + move.b d6,d4 ; cmd argument + and.w #$0f00,d6 + lsr.w #7,d6 + move.w blmorefx_tab(pc,d6.w),d0 + jmp blmorefx_tab(pc,d0.w) + +blmorefx_tab: + dc.w mt_nop-blmorefx_tab,mt_nop-blmorefx_tab + dc.w mt_nop-blmorefx_tab,mt_nop-blmorefx_tab + dc.w mt_nop-blmorefx_tab,mt_nop-blmorefx_tab + dc.w mt_nop-blmorefx_tab,mt_nop-blmorefx_tab + dc.w mt_nop-blmorefx_tab,mt_nop-blmorefx_tab + dc.w mt_nop-blmorefx_tab + dc.w mt_posjump-blmorefx_tab ; $B + dc.w mt_nop-blmorefx_tab + dc.w mt_patternbrk-blmorefx_tab ; $D + dc.w blocked_e_cmds-blmorefx_tab + dc.w mt_setspeed-blmorefx_tab ; $F + endc ; !MINIMAL + + +mt_arpeggio: +; cmd 0 x y (x = first arpeggio offset, y = second arpeggio offset) +; d4 = xy + + moveq #0,d0 + move.b mt_Counter(a4),d0 + move.b arptab(pc,d0.w),d0 + beq mt_pernop ; step 0, just use normal period + bmi .1 + + ; step 1, arpeggio by left nibble + lsr.b #4,d4 + bra .2 + + ; step 2, arpeggio by right nibble +.1: and.w #$000f,d4 + + ; offset current note +.2: add.w d4,d4 + add.w n_noteoff(a2),d4 + cmp.w #2*36,d4 + bhs .4 + + ; set period with arpeggio offset from note table + move.l n_pertab(a2),a0 + move.w (a0,d4.w),AUDPER(a5) +.4: rts + +arptab: + dc.b 0,1,-1,0,1,-1,0,1,-1,0,1,-1,0,1,-1,0 + dc.b 1,-1,0,1,-1,0,1,-1,0,1,-1,0,1,-1,0,1 + + +mt_fineportaup: +; cmd E 1 x (subtract x from period) +; d0 = x + + tst.b mt_Counter(a4) + beq do_porta_up + rts + + +mt_portaup: +; cmd 1 x x (subtract xx from period) +; d4 = xx + + move.w d4,d0 + +do_porta_up: + move.w n_period(a2),d1 + sub.w d0,d1 + cmp.w #113,d1 + bge .1 + moveq #113,d1 +.1: move.w d1,n_period(a2) + move.w d1,AUDPER(a5) + rts + + +mt_fineportadn: +; cmd E 2 x (add x to period) +; d0 = x + + tst.b mt_Counter(a4) + beq do_porta_down + rts + + +mt_portadown: +; cmd 2 x x (add xx to period) +; d4 = xx + + move.w d4,d0 + +do_porta_down: + move.w n_period(a2),d1 + add.w d0,d1 + cmp.w #856,d1 + bls .1 + move.w #856,d1 +.1: move.w d1,n_period(a2) + move.w d1,AUDPER(a5) + rts + + +mt_toneporta: +; cmd 3 x y (xy = tone portamento speed) +; d4 = xy + + tst.b d4 + beq mt_toneporta_nc + move.w d4,n_toneportspeed(a2) + move.b d7,n_cmdlo(a2) + +mt_toneporta_nc: + move.w n_wantedperiod(a2),d1 + beq .6 + + move.w n_toneportspeed(a2),d0 + move.w n_period(a2),d2 + cmp.w d1,d2 + blo .2 + + ; tone porta up + sub.w d0,d2 + cmp.w d1,d2 + bgt .3 + move.w d1,d2 + move.w d7,n_wantedperiod(a2) + bra .3 + + ; tone porta down +.2: add.w d0,d2 + cmp.w d1,d2 + blt .3 + move.w d1,d2 + move.w d7,n_wantedperiod(a2) + +.3: move.w d2,n_period(a2) + + tst.b n_gliss(a2) + beq .5 + + ; glissando: find nearest note for new period + move.l n_pertab(a2),a0 + moveq #36-1,d0 + moveq #-2,d1 +.4: addq.w #2,d1 + cmp.w (a0)+,d2 + dbhs d0,.4 + + move.w d1,n_noteoff(a2) ; @@@ needed? + move.w -(a0),d2 + +.5: move.w d2,AUDPER(a5) +.6 rts + + +mt_vibrato: +; cmd 4 x y (x = speed, y = amplitude) +; d4 = xy + + moveq #$0f,d2 + and.b d4,d2 + beq .1 + move.b d2,n_vibratoamp(a2) + bra .2 +.1: move.b n_vibratoamp(a2),d2 + +.2: lsr.b #4,d4 + beq .3 + move.b d4,n_vibratospd(a2) + bra mt_vibrato_nc +.3: move.b n_vibratospd(a2),d4 + +mt_vibrato_nc: + ; calculate vibrato table offset: 64 * amplitude + (pos & 63) + lsl.w #6,d2 + moveq #63,d0 + and.b n_vibratopos(a2),d0 + add.w d0,d2 + + ifne ENABLE_SAWRECT + ; select vibrato waveform + moveq #3,d1 + and.b n_vibratoctrl(a2),d1 + beq .6 + subq.b #1,d1 + beq .5 + + ; ctrl 2 & 3 select a rectangle vibrato + lea mt_VibratoRectTable(pc),a0 + bra .9 + + ; ctrl 1 selects a sawtooth vibrato +.5: lea mt_VibratoSawTable(pc),a0 + bra .9 + endc ; ENABLE_SAWRECT + + ; ctrl 0 selects a sine vibrato +.6: lea mt_VibratoSineTable(pc),a0 + + ; add vibrato-offset to period +.9: move.b (a0,d2.w),d0 + ext.w d0 + add.w n_period(a2),d0 + move.w d0,AUDPER(a5) + + ; increase vibratopos by speed + add.b d4,n_vibratopos(a2) + rts + + +mt_tonevolslide: +; cmd 5 x y (x = volume-up, y = volume-down) +; d4 = xy + + pea mt_volumeslide(pc) + bra mt_toneporta_nc + + +mt_vibrvolslide: +; cmd 6 x y (x = volume-up, y = volume-down) +; d4 = xy + + move.w d4,d3 + move.b n_vibratoamp(a2),d2 + move.b n_vibratospd(a2),d4 + bsr mt_vibrato_nc + + move.w d3,d4 + bra mt_volumeslide + + +mt_tremolo: +; cmd 7 x y (x = speed, y = amplitude) +; d4 = xy + + moveq #$0f,d2 + and.b d4,d2 + beq .1 + move.b d2,n_tremoloamp(a2) + bra .2 +.1: move.b n_tremoloamp(a2),d2 + +.2: lsr.b #4,d4 + beq .3 + move.b d4,n_tremolospd(a2) + bra .4 +.3: move.b n_tremolospd(a2),d4 + + ; calculate tremolo table offset: 64 * amplitude + (pos & 63) +.4: lsl.w #6,d2 + moveq #63,d0 + and.b n_tremolopos(a2),d0 + add.w d0,d2 + + ifne ENABLE_SAWRECT + ; select tremolo waveform + moveq #3,d1 + and.b n_tremoloctrl(a2),d1 + beq .6 + subq.b #1,d1 + beq .5 + + ; ctrl 2 & 3 select a rectangle tremolo + lea mt_VibratoRectTable(pc),a0 + bra .9 + + ; ctrl 1 selects a sawtooth tremolo +.5: lea mt_VibratoSawTable(pc),a0 + bra .9 + endc ; ENABLE_SAWRECT + + ; ctrl 0 selects a sine tremolo +.6: lea mt_VibratoSineTable(pc),a0 + + ; add tremolo-offset to volume +.9: move.w n_volume(a2),d0 + add.b (a0,d2.w),d0 + bpl .10 + moveq #0,d0 +.10: cmp.w #64,d0 + bls .11 + moveq #64,d0 +.11: move.w n_period(a2),AUDPER(a5) + ifeq MINIMAL + move.l mt_MasterVolTab(a4),a0 + move.b (a0,d0.w),d0 + and.b n_enable(a2),d0 + endc + move.w d0,AUDVOL(a5) + ifd VOL0_UNLOOPS + bne .12 + move.b d7,n_looped(a2) + endc + + ; increase tremolopos by speed +.12: add.b d4,n_tremolopos(a2) + rts + + +mt_volumeslide: +; cmd A x y (x = volume-up, y = volume-down) +; d4 = xy + + move.w n_volume(a2),d0 + moveq #$0f,d1 + and.b d4,d1 + lsr.b #4,d4 + beq vol_slide_down + + ; slide up, until 64 + add.b d4,d0 +vol_slide_up: + cmp.b #64,d0 + bls set_pervol + moveq #64,d0 + bra set_pervol + + ; slide down, until 0 +vol_slide_down: + sub.b d1,d0 + bpl set_pervol + moveq #0,d0 + +set_pervol: + move.w n_period(a2),AUDPER(a5) +set_vol: + move.w d0,n_volume(a2) + ifeq MINIMAL + move.l mt_MasterVolTab(a4),a0 + move.b (a0,d0.w),d0 + and.b n_enable(a2),d0 + endc + move.w d0,AUDVOL(a5) + ifd VOL0_UNLOOPS + bne .1 + move.b d7,n_looped(a2) + endc +.1: rts + + +mt_posjump: +; cmd B x y (xy = new song position) +; d4 = xy + + move.b d4,d0 + subq.b #1,d0 + move.b d0,mt_SongPos(a4) + +jump_pos0: + move.w d7,mt_PBreakPos(a4) + st mt_PosJumpFlag(a4) + rts + + +mt_volchange: +; cmd C x y (xy = new volume) +; d4 = xy + + move.w d4,d0 + cmp.w #64,d0 + bls set_vol + moveq #64,d0 + bra set_vol + + +mt_patternbrk: +; cmd D x y (xy = break pos in decimal) +; d4 = xy + + moveq #$0f,d0 + and.w d4,d0 + move.w d4,d1 + lsr.w #4,d1 + add.b mult10tab(pc,d1.w),d0 + cmp.b #63,d0 + bhi jump_pos0 + + lsl.w #4,d0 + move.w d0,mt_PBreakPos(a4) + st mt_PosJumpFlag(a4) + rts + +mult10tab: + dc.b 0,10,20,30,40,50,60,70,80,90,0,0,0,0,0,0 + + +mt_setspeed: +; cmd F x y (xy<$20 new speed, xy>=$20 new tempo) +; d4 = xy + + ifeq VBLANK_MUSIC + cmp.b #$20,d4 + bhs .1 + endc + + move.b d4,mt_Speed(a4) + beq _mt_end + rts + + ifeq VBLANK_MUSIC + ; set tempo (CIA only) +.1: and.w #$00ff,d4 + move.l mt_timerval(a4),d0 + divu d4,d0 + move.b d0,CIAB+CIATALO + lsr.w #8,d0 + move.b d0,CIAB+CIATAHI + rts + endc + + +mt_e_cmds: +; cmd E x y (x=command, y=argument) +; d4 = xy + + moveq #$0f,d0 + and.w d4,d0 ; pass E-cmd argument in d0 + + move.w d4,d1 + lsr.w #4,d1 + add.w d1,d1 + move.w ecmd_tab(pc,d1.w),d1 + jmp ecmd_tab(pc,d1.w) + +ecmd_tab: + dc.w mt_filter-ecmd_tab + dc.w mt_fineportaup-ecmd_tab + dc.w mt_fineportadn-ecmd_tab + dc.w mt_glissctrl-ecmd_tab + dc.w mt_vibratoctrl-ecmd_tab + dc.w mt_finetune-ecmd_tab + dc.w mt_jumploop-ecmd_tab + dc.w mt_tremoctrl-ecmd_tab + dc.w mt_e8-ecmd_tab + dc.w mt_retrignote-ecmd_tab + dc.w mt_volfineup-ecmd_tab + dc.w mt_volfinedn-ecmd_tab + dc.w mt_notecut-ecmd_tab + dc.w mt_notedelay-ecmd_tab + dc.w mt_patterndelay-ecmd_tab + dc.w mt_funk-ecmd_tab + + +blocked_e_cmds: +; cmd E x y (x=command, y=argument) +; d4 = xy + + moveq #$0f,d0 + and.w d4,d0 ; pass E-cmd argument in d0 + + move.w d4,d1 + lsr.w #4,d1 + add.w d1,d1 + move.w blecmd_tab(pc,d1.w),d1 + jmp blecmd_tab(pc,d1.w) + +blecmd_tab: + dc.w mt_filter-blecmd_tab + dc.w mt_rts-blecmd_tab + dc.w mt_rts-blecmd_tab + dc.w mt_glissctrl-blecmd_tab + dc.w mt_vibratoctrl-blecmd_tab + dc.w mt_finetune-blecmd_tab + dc.w mt_jumploop-blecmd_tab + dc.w mt_tremoctrl-blecmd_tab + dc.w mt_e8-blecmd_tab + dc.w mt_rts-blecmd_tab + dc.w mt_rts-blecmd_tab + dc.w mt_rts-blecmd_tab + dc.w mt_rts-blecmd_tab + dc.w mt_rts-blecmd_tab + dc.w mt_patterndelay-blecmd_tab + dc.w mt_rts-blecmd_tab + + +mt_filter: +; cmd E 0 x (x=1 disable, x=0 enable) +; d0 = x + + lsr.b #1,d0 + bcs .1 + bclr #1,CIAA+CIAPRA + rts +.1: bset #1,CIAA+CIAPRA +mt_rts: + rts + + +mt_glissctrl: +; cmd E 3 x (x gliss) +; d0 = x + + move.b d0,n_gliss(a2) + rts + + +mt_vibratoctrl: +; cmd E 4 x (x = vibrato) +; d0 = x + + move.b d0,n_vibratoctrl(a2) + rts + + +mt_finetune: +; cmd E 5 x (x = finetune) +; d0 = x + + lea mt_PerFineTune(pc),a0 + add.w d0,d0 + add.w (a0,d0.w),a0 + move.l a0,n_pertab(a2) + cmp.w #2*8,d0 + shs n_minusft(a2) + rts + + +mt_jumploop: +; cmd E 6 x (x=0 loop start, else loop count) +; d0 = x + + tst.b mt_Counter(a4) + bne .4 + +.1: tst.b d0 + beq .3 ; set start + + ; otherwise we are at the end of the loop + subq.b #1,n_loopcount(a2) + beq .4 ; loop finished + bpl .2 + + ; initialize loop counter + move.b d0,n_loopcount(a2) + + ; jump back to start of loop +.2: move.w n_pattpos(a2),mt_PBreakPos(a4) + st mt_PBreakFlag(a4) + rts + + ; remember start of loop position +.3: move.w mt_PatternPos(a4),n_pattpos(a2) +.4: rts + + +mt_tremoctrl: +; cmd E 7 x (x = tremolo) +; d0 = x + + move.b d0,n_tremoloctrl(a2) + rts + + +mt_e8: +; cmd E 8 x (x = trigger value) +; d0 = x + + move.b d0,mt_E8Trigger(a4) + rts + + +mt_retrignote: +; cmd E 9 x (x = retrigger count) +; d0 = x + + tst.b d0 + beq .1 + + ; set new retrigger count when Counter=0 + tst.b mt_Counter(a4) + bne .2 + move.b d0,n_retrigcount(a2) + + ; avoid double retrigger, when Counter=0 and a note was set + move.w #$0fff,d2 + and.w (a2),d2 + beq do_retrigger +.1: rts + + ; check if retrigger count is reached +.2: subq.b #1,n_retrigcount(a2) + bne .1 + move.b d0,n_retrigcount(a2) ; reset + +do_retrigger: + ; DMA off, set sample pointer and length + move.w n_dmabit(a2),d0 + move.w d0,DMACON(a6) + move.l n_start(a2),AUDLC(a5) + move.w n_length(a2),AUDLEN(a5) + lea mt_dmaon(pc),a0 + or.w d0,(a0) + rts + + +mt_volfineup: +; cmd E A x (x = volume add) +; d0 = x + + tst.b mt_Counter(a4) + beq .1 + rts + +.1: add.w n_volume(a2),d0 + bra vol_slide_up + + +mt_volfinedn: +; cmd E B x (x = volume sub) +; d0 = x + + tst.b mt_Counter(a4) + beq .1 + rts + +.1: move.b d0,d1 + move.w n_volume(a2),d0 + bra vol_slide_down + + +mt_notecut: +; cmd E C x (x = counter to cut at) +; d0 = x + + cmp.b mt_Counter(a4),d0 + bne .1 + move.w d7,n_volume(a2) + move.w d7,AUDVOL(a5) + ifd VOL0_UNLOOPS + move.b d7,n_looped(a2) + endc +.1: rts + + +mt_notedelay: +; cmd E D x (x = counter to retrigger at) +; d0 = x + + cmp.b mt_Counter(a4),d0 + bne .1 + tst.w (a2) ; trigger note when given + bne .2 +.1: rts +.2: move.w n_period(a2),AUDPER(a5) + bra do_retrigger + + +mt_patterndelay: +; cmd E E x (x = delay count) +; d0 = x + + tst.b mt_Counter(a4) + bne .1 + tst.b mt_PattDelTime2(a4) + bne .1 + addq.b #1,d0 + move.b d0,mt_PattDelTime(a4) +.1: rts + + +mt_funk: +; cmd E F x (x = funk speed) +; d0 = x + + tst.b mt_Counter(a4) + bne .1 + move.w d0,n_funk(a2) + bne mt_updatefunk +.1: rts + +mt_updatefunk: +; d0 = funk speed + + move.b mt_FunkTable(pc,d0.w),d0 + add.b d0,n_funkoffset(a2) + bpl .2 + move.b d7,n_funkoffset(a2) + + move.l n_loopstart(a2),d0 + moveq #0,d1 + move.w n_replen(a2),d1 + add.l d1,d1 + add.l d0,d1 + move.l n_wavestart(a2),a0 + addq.l #1,a0 + cmp.l d1,a0 + blo .1 + move.l d0,a0 +.1: move.l a0,n_wavestart(a2) + not.b (a0) + +.2: rts + + +mt_FunkTable: + dc.b 0,5,6,7,8,10,11,13,16,19,22,26,32,43,64,128 + +mt_VibratoSineTable: + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1 + dc.b 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0 + dc.b 0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + dc.b -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,0,0,0 + dc.b 0,0,0,1,1,1,2,2,2,3,3,3,3,3,3,3 + dc.b 3,3,3,3,3,3,3,3,2,2,2,1,1,1,0,0 + dc.b 0,0,0,-1,-1,-1,-2,-2,-2,-3,-3,-3,-3,-3,-3,-3 + dc.b -3,-3,-3,-3,-3,-3,-3,-3,-2,-2,-2,-1,-1,-1,0,0 + dc.b 0,0,1,1,2,2,3,3,4,4,4,5,5,5,5,5 + dc.b 5,5,5,5,5,5,4,4,4,3,3,2,2,1,1,0 + dc.b 0,0,-1,-1,-2,-2,-3,-3,-4,-4,-4,-5,-5,-5,-5,-5 + dc.b -5,-5,-5,-5,-5,-5,-4,-4,-4,-3,-3,-2,-2,-1,-1,0 + dc.b 0,0,1,2,3,3,4,5,5,6,6,7,7,7,7,7 + dc.b 7,7,7,7,7,7,6,6,5,5,4,3,3,2,1,0 + dc.b 0,0,-1,-2,-3,-3,-4,-5,-5,-6,-6,-7,-7,-7,-7,-7 + dc.b -7,-7,-7,-7,-7,-7,-6,-6,-5,-5,-4,-3,-3,-2,-1,0 + dc.b 0,0,1,2,3,4,5,6,7,7,8,8,9,9,9,9 + dc.b 9,9,9,9,9,8,8,7,7,6,5,4,3,2,1,0 + dc.b 0,0,-1,-2,-3,-4,-5,-6,-7,-7,-8,-8,-9,-9,-9,-9 + dc.b -9,-9,-9,-9,-9,-8,-8,-7,-7,-6,-5,-4,-3,-2,-1,0 + dc.b 0,1,2,3,4,5,6,7,8,9,9,10,11,11,11,11 + dc.b 11,11,11,11,11,10,9,9,8,7,6,5,4,3,2,1 + dc.b 0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-9,-10,-11,-11,-11,-11 + dc.b -11,-11,-11,-11,-11,-10,-9,-9,-8,-7,-6,-5,-4,-3,-2,-1 + dc.b 0,1,2,4,5,6,7,8,9,10,11,12,12,13,13,13 + dc.b 13,13,13,13,12,12,11,10,9,8,7,6,5,4,2,1 + dc.b 0,-1,-2,-4,-5,-6,-7,-8,-9,-10,-11,-12,-12,-13,-13,-13 + dc.b -13,-13,-13,-13,-12,-12,-11,-10,-9,-8,-7,-6,-5,-4,-2,-1 + dc.b 0,1,3,4,6,7,8,10,11,12,13,14,14,15,15,15 + dc.b 15,15,15,15,14,14,13,12,11,10,8,7,6,4,3,1 + dc.b 0,-1,-3,-4,-6,-7,-8,-10,-11,-12,-13,-14,-14,-15,-15,-15 + dc.b -15,-15,-15,-15,-14,-14,-13,-12,-11,-10,-8,-7,-6,-4,-3,-1 + dc.b 0,1,3,5,6,8,9,11,12,13,14,15,16,17,17,17 + dc.b 17,17,17,17,16,15,14,13,12,11,9,8,6,5,3,1 + dc.b 0,-1,-3,-5,-6,-8,-9,-11,-12,-13,-14,-15,-16,-17,-17,-17 + dc.b -17,-17,-17,-17,-16,-15,-14,-13,-12,-11,-9,-8,-6,-5,-3,-1 + dc.b 0,1,3,5,7,9,11,12,14,15,16,17,18,19,19,19 + dc.b 19,19,19,19,18,17,16,15,14,12,11,9,7,5,3,1 + dc.b 0,-1,-3,-5,-7,-9,-11,-12,-14,-15,-16,-17,-18,-19,-19,-19 + dc.b -19,-19,-19,-19,-18,-17,-16,-15,-14,-12,-11,-9,-7,-5,-3,-1 + dc.b 0,2,4,6,8,10,12,13,15,16,18,19,20,20,21,21 + dc.b 21,21,21,20,20,19,18,16,15,13,12,10,8,6,4,2 + dc.b 0,-2,-4,-6,-8,-10,-12,-13,-15,-16,-18,-19,-20,-20,-21,-21 + dc.b -21,-21,-21,-20,-20,-19,-18,-16,-15,-13,-12,-10,-8,-6,-4,-2 + dc.b 0,2,4,6,9,11,13,15,16,18,19,21,22,22,23,23 + dc.b 23,23,23,22,22,21,19,18,16,15,13,11,9,6,4,2 + dc.b 0,-2,-4,-6,-9,-11,-13,-15,-16,-18,-19,-21,-22,-22,-23,-23 + dc.b -23,-23,-23,-22,-22,-21,-19,-18,-16,-15,-13,-11,-9,-6,-4,-2 + dc.b 0,2,4,7,9,12,14,16,18,20,21,22,23,24,25,25 + dc.b 25,25,25,24,23,22,21,20,18,16,14,12,9,7,4,2 + dc.b 0,-2,-4,-7,-9,-12,-14,-16,-18,-20,-21,-22,-23,-24,-25,-25 + dc.b -25,-25,-25,-24,-23,-22,-21,-20,-18,-16,-14,-12,-9,-7,-4,-2 + dc.b 0,2,5,8,10,13,15,17,19,21,23,24,25,26,27,27 + dc.b 27,27,27,26,25,24,23,21,19,17,15,13,10,8,5,2 + dc.b 0,-2,-5,-8,-10,-13,-15,-17,-19,-21,-23,-24,-25,-26,-27,-27 + dc.b -27,-27,-27,-26,-25,-24,-23,-21,-19,-17,-15,-13,-10,-8,-5,-2 + dc.b 0,2,5,8,11,14,16,18,21,23,24,26,27,28,29,29 + dc.b 29,29,29,28,27,26,24,23,21,18,16,14,11,8,5,2 + dc.b 0,-2,-5,-8,-11,-14,-16,-18,-21,-23,-24,-26,-27,-28,-29,-29 + dc.b -29,-29,-29,-28,-27,-26,-24,-23,-21,-18,-16,-14,-11,-8,-5,-2 + + ifne ENABLE_SAWRECT +mt_VibratoSawTable: + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + dc.b -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1 + dc.b 2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 + dc.b -3,-3,-3,-3,-3,-3,-3,-3,-2,-2,-2,-2,-2,-2,-2,-2 + dc.b -1,-1,-1,-1,-1,-1,-1,-1,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,1,1,1,1,1,2,2,2,2,2 + dc.b 3,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5 + dc.b -5,-5,-5,-5,-5,-5,-4,-4,-4,-4,-4,-3,-3,-3,-3,-3 + dc.b -2,-2,-2,-2,-2,-2,-1,-1,-1,-1,-1,0,0,0,0,0 + dc.b 0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3 + dc.b 4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7 + dc.b -7,-7,-7,-7,-6,-6,-6,-6,-5,-5,-5,-5,-4,-4,-4,-4 + dc.b -3,-3,-3,-3,-2,-2,-2,-2,-1,-1,-1,-1,0,0,0,0 + dc.b 0,0,0,0,1,1,1,2,2,2,3,3,3,4,4,4 + dc.b 5,5,5,5,6,6,6,7,7,7,8,8,8,9,9,9 + dc.b -9,-9,-9,-9,-8,-8,-8,-7,-7,-7,-6,-6,-6,-5,-5,-5 + dc.b -4,-4,-4,-4,-3,-3,-3,-2,-2,-2,-1,-1,-1,0,0,0 + dc.b 0,0,0,1,1,1,2,2,3,3,3,4,4,4,5,5 + dc.b 6,6,6,7,7,7,8,8,9,9,9,10,10,10,11,11 + dc.b -11,-11,-11,-10,-10,-10,-9,-9,-8,-8,-8,-7,-7,-7,-6,-6 + dc.b -5,-5,-5,-4,-4,-4,-3,-3,-2,-2,-2,-1,-1,-1,0,0 + dc.b 0,0,0,1,1,2,2,3,3,3,4,4,5,5,6,6 + dc.b 7,7,7,8,8,9,9,10,10,10,11,11,12,12,13,13 + dc.b -13,-13,-13,-12,-12,-11,-11,-10,-10,-10,-9,-9,-8,-8,-7,-7 + dc.b -6,-6,-6,-5,-5,-4,-4,-3,-3,-3,-2,-2,-1,-1,0,0 + dc.b 0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7 + dc.b 8,8,9,9,10,10,11,11,12,12,13,13,14,14,15,15 + dc.b -15,-15,-14,-14,-13,-13,-12,-12,-11,-11,-10,-10,-9,-9,-8,-8 + dc.b -7,-7,-6,-6,-5,-5,-4,-4,-3,-3,-2,-2,-1,-1,0,0 + dc.b 0,0,1,1,2,2,3,3,4,5,5,6,6,7,7,8 + dc.b 9,9,10,10,11,11,12,12,13,14,14,15,15,16,16,17 + dc.b -17,-17,-16,-16,-15,-15,-14,-13,-13,-12,-12,-11,-11,-10,-10,-9 + dc.b -8,-8,-7,-7,-6,-6,-5,-4,-4,-3,-3,-2,-2,-1,-1,0 + dc.b 0,0,1,1,2,3,3,4,5,5,6,6,7,8,8,9 + dc.b 10,10,11,11,12,13,13,14,15,15,16,16,17,18,18,19 + dc.b -19,-19,-18,-18,-17,-16,-16,-15,-14,-14,-13,-13,-12,-11,-11,-10 + dc.b -9,-9,-8,-8,-7,-6,-6,-5,-4,-4,-3,-3,-2,-1,-1,0 + dc.b 0,0,1,2,2,3,4,4,5,6,6,7,8,8,9,10 + dc.b 11,11,12,13,13,14,15,15,16,17,17,18,19,19,20,21 + dc.b -21,-21,-20,-19,-19,-18,-17,-17,-16,-15,-15,-14,-13,-12,-12,-11 + dc.b -10,-10,-9,-8,-8,-7,-6,-6,-5,-4,-4,-3,-2,-1,-1,0 + dc.b 0,0,1,2,3,3,4,5,6,6,7,8,9,9,10,11 + dc.b 12,12,13,14,15,15,16,17,18,18,19,20,21,21,22,23 + dc.b -23,-23,-22,-21,-20,-20,-19,-18,-17,-17,-16,-15,-14,-14,-13,-12 + dc.b -11,-11,-10,-9,-8,-8,-7,-6,-5,-5,-4,-3,-2,-2,-1,0 + dc.b 0,0,1,2,3,4,4,5,6,7,8,8,9,10,11,12 + dc.b 13,13,14,15,16,17,17,18,19,20,21,21,22,23,24,25 + dc.b -25,-25,-24,-23,-22,-21,-21,-20,-19,-18,-17,-16,-16,-15,-14,-13 + dc.b -12,-12,-11,-10,-9,-8,-8,-7,-6,-5,-4,-3,-3,-2,-1,0 + dc.b 0,0,1,2,3,4,5,6,7,7,8,9,10,11,12,13 + dc.b 14,14,15,16,17,18,19,20,21,21,22,23,24,25,26,27 + dc.b -27,-27,-26,-25,-24,-23,-22,-21,-20,-20,-19,-18,-17,-16,-15,-14 + dc.b -13,-13,-12,-11,-10,-9,-8,-7,-6,-6,-5,-4,-3,-2,-1,0 + dc.b 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14 + dc.b 15,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29 + dc.b -29,-28,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15 + dc.b -14,-13,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0 + +mt_VibratoRectTable: + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + dc.b 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + dc.b -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + dc.b -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + dc.b 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 + dc.b 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 + dc.b -3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3 + dc.b -3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3 + dc.b 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 + dc.b 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 + dc.b -5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5 + dc.b -5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5 + dc.b 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 + dc.b 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 + dc.b -7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7 + dc.b -7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7 + dc.b 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 + dc.b 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 + dc.b -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 + dc.b -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 + dc.b 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 + dc.b 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 + dc.b -11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11 + dc.b -11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11 + dc.b 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 + dc.b 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 + dc.b -13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13 + dc.b -13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13 + dc.b 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15 + dc.b 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15 + dc.b -15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15 + dc.b -15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15 + dc.b 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 + dc.b 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 + dc.b -17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17 + dc.b -17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17 + dc.b 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19 + dc.b 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19 + dc.b -19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19 + dc.b -19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19 + dc.b 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21 + dc.b 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21 + dc.b -21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21 + dc.b -21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21 + dc.b 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23 + dc.b 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23 + dc.b -23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23 + dc.b -23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23 + dc.b 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25 + dc.b 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25 + dc.b -25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25 + dc.b -25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25 + dc.b 27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27 + dc.b 27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27 + dc.b -27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27 + dc.b -27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27 + dc.b 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 + dc.b 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 + dc.b -29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29 + dc.b -29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29 + endc ; ENABLE_SAWRECT + + +mt_PerFineTune: + dc.w mt_Tuning0-mt_PerFineTune,mt_Tuning1-mt_PerFineTune + dc.w mt_Tuning2-mt_PerFineTune,mt_Tuning3-mt_PerFineTune + dc.w mt_Tuning4-mt_PerFineTune,mt_Tuning5-mt_PerFineTune + dc.w mt_Tuning6-mt_PerFineTune,mt_Tuning7-mt_PerFineTune + dc.w mt_TuningM8-mt_PerFineTune,mt_TuningM7-mt_PerFineTune + dc.w mt_TuningM6-mt_PerFineTune,mt_TuningM5-mt_PerFineTune + dc.w mt_TuningM4-mt_PerFineTune,mt_TuningM3-mt_PerFineTune + dc.w mt_TuningM2-mt_PerFineTune,mt_TuningM1-mt_PerFineTune + +mt_PeriodTable: +mt_Tuning0: ; Tuning 0, Normal c-1 - b3 + dc.w 856,808,762,720,678,640,604,570,538,508,480,453 + dc.w 428,404,381,360,339,320,302,285,269,254,240,226 + dc.w 214,202,190,180,170,160,151,143,135,127,120,113 +mt_Tuning1: + dc.w 850,802,757,715,674,637,601,567,535,505,477,450 + dc.w 425,401,379,357,337,318,300,284,268,253,239,225 + dc.w 213,201,189,179,169,159,150,142,134,126,119,113 +mt_Tuning2: + dc.w 844,796,752,709,670,632,597,563,532,502,474,447 + dc.w 422,398,376,355,335,316,298,282,266,251,237,224 + dc.w 211,199,188,177,167,158,149,141,133,125,118,112 +mt_Tuning3: + dc.w 838,791,746,704,665,628,592,559,528,498,470,444 + dc.w 419,395,373,352,332,314,296,280,264,249,235,222 + dc.w 209,198,187,176,166,157,148,140,132,125,118,111 +mt_Tuning4: + dc.w 832,785,741,699,660,623,588,555,524,495,467,441 + dc.w 416,392,370,350,330,312,294,278,262,247,233,220 + dc.w 208,196,185,175,165,156,147,139,131,124,117,110 +mt_Tuning5: + dc.w 826,779,736,694,655,619,584,551,520,491,463,437 + dc.w 413,390,368,347,328,309,292,276,260,245,232,219 + dc.w 206,195,184,174,164,155,146,138,130,123,116,109 +mt_Tuning6: + dc.w 820,774,730,689,651,614,580,547,516,487,460,434 + dc.w 410,387,365,345,325,307,290,274,258,244,230,217 + dc.w 205,193,183,172,163,154,145,137,129,122,115,109 +mt_Tuning7: + dc.w 814,768,725,684,646,610,575,543,513,484,457,431 + dc.w 407,384,363,342,323,305,288,272,256,242,228,216 + dc.w 204,192,181,171,161,152,144,136,128,121,114,108 +mt_TuningM8: + dc.w 907,856,808,762,720,678,640,604,570,538,508,480 + dc.w 453,428,404,381,360,339,320,302,285,269,254,240 + dc.w 226,214,202,190,180,170,160,151,143,135,127,120 +mt_TuningM7: + dc.w 900,850,802,757,715,675,636,601,567,535,505,477 + dc.w 450,425,401,379,357,337,318,300,284,268,253,238 + dc.w 225,212,200,189,179,169,159,150,142,134,126,119 +mt_TuningM6: + dc.w 894,844,796,752,709,670,632,597,563,532,502,474 + dc.w 447,422,398,376,355,335,316,298,282,266,251,237 + dc.w 223,211,199,188,177,167,158,149,141,133,125,118 +mt_TuningM5: + dc.w 887,838,791,746,704,665,628,592,559,528,498,470 + dc.w 444,419,395,373,352,332,314,296,280,264,249,235 + dc.w 222,209,198,187,176,166,157,148,140,132,125,118 +mt_TuningM4: + dc.w 881,832,785,741,699,660,623,588,555,524,494,467 + dc.w 441,416,392,370,350,330,312,294,278,262,247,233 + dc.w 220,208,196,185,175,165,156,147,139,131,123,117 +mt_TuningM3: + dc.w 875,826,779,736,694,655,619,584,551,520,491,463 + dc.w 437,413,390,368,347,328,309,292,276,260,245,232 + dc.w 219,206,195,184,174,164,155,146,138,130,123,116 +mt_TuningM2: + dc.w 868,820,774,730,689,651,614,580,547,516,487,460 + dc.w 434,410,387,365,345,325,307,290,274,258,244,230 + dc.w 217,205,193,183,172,163,154,145,137,129,122,115 +mt_TuningM1: + dc.w 862,814,768,725,684,646,610,575,543,513,484,457 + dc.w 431,407,384,363,342,323,305,288,272,256,242,228 + dc.w 216,203,192,181,171,161,152,144,136,128,121,114 + + ifeq MINIMAL +MasterVolTab0: + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0 +MasterVolTab1: + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 1 +MasterVolTab2: + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + dc.b 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + dc.b 2 +MasterVolTab3: + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1 + dc.b 1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2 + dc.b 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 + dc.b 3 +MasterVolTab4: + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + dc.b 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 + dc.b 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 + dc.b 4 +MasterVolTab5: + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1 + dc.b 1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2 + dc.b 2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3 + dc.b 3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4 + dc.b 5 +MasterVolTab6: + dc.b 0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1 + dc.b 1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2 + dc.b 3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4 + dc.b 4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5 + dc.b 6 +MasterVolTab7: + dc.b 0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1 + dc.b 1,1,1,2,2,2,2,2,2,2,2,2,3,3,3,3 + dc.b 3,3,3,3,3,4,4,4,4,4,4,4,4,4,5,5 + dc.b 5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6 + dc.b 7 +MasterVolTab8: + dc.b 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1 + dc.b 2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 + dc.b 4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5 + dc.b 6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7 + dc.b 8 +MasterVolTab9: + dc.b 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,2 + dc.b 2,2,2,2,2,2,3,3,3,3,3,3,3,4,4,4 + dc.b 4,4,4,4,5,5,5,5,5,5,5,6,6,6,6,6 + dc.b 6,6,7,7,7,7,7,7,7,8,8,8,8,8,8,8 + dc.b 9 +MasterVolTab10: + dc.b 0,0,0,0,0,0,0,1,1,1,1,1,1,2,2,2 + dc.b 2,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4 + dc.b 5,5,5,5,5,5,5,6,6,6,6,6,6,7,7,7 + dc.b 7,7,7,7,8,8,8,8,8,8,9,9,9,9,9,9 + dc.b 10 +MasterVolTab11: + dc.b 0,0,0,0,0,0,1,1,1,1,1,1,2,2,2,2 + dc.b 2,2,3,3,3,3,3,3,4,4,4,4,4,4,5,5 + dc.b 5,5,5,6,6,6,6,6,6,7,7,7,7,7,7,8 + dc.b 8,8,8,8,8,9,9,9,9,9,9,10,10,10,10,10 + dc.b 11 +MasterVolTab12: + dc.b 0,0,0,0,0,0,1,1,1,1,1,2,2,2,2,2 + dc.b 3,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5 + dc.b 6,6,6,6,6,6,7,7,7,7,7,8,8,8,8,8 + dc.b 9,9,9,9,9,9,10,10,10,10,10,11,11,11,11,11 + dc.b 12 +MasterVolTab13: + dc.b 0,0,0,0,0,1,1,1,1,1,2,2,2,2,2,3 + dc.b 3,3,3,3,4,4,4,4,4,5,5,5,5,5,6,6 + dc.b 6,6,6,7,7,7,7,7,8,8,8,8,8,9,9,9 + dc.b 9,9,10,10,10,10,10,11,11,11,11,11,12,12,12,12 + dc.b 13 +MasterVolTab14: + dc.b 0,0,0,0,0,1,1,1,1,1,2,2,2,2,3,3 + dc.b 3,3,3,4,4,4,4,5,5,5,5,5,6,6,6,6 + dc.b 7,7,7,7,7,8,8,8,8,8,9,9,9,9,10,10 + dc.b 10,10,10,11,11,11,11,12,12,12,12,12,13,13,13,13 + dc.b 14 +MasterVolTab15: + dc.b 0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3 + dc.b 3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7 + dc.b 7,7,7,8,8,8,8,9,9,9,9,10,10,10,10,11 + dc.b 11,11,11,11,12,12,12,12,13,13,13,13,14,14,14,14 + dc.b 15 +MasterVolTab16: + dc.b 0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3 + dc.b 4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7 + dc.b 8,8,8,8,9,9,9,9,10,10,10,10,11,11,11,11 + dc.b 12,12,12,12,13,13,13,13,14,14,14,14,15,15,15,15 + dc.b 16 +MasterVolTab17: + dc.b 0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3 + dc.b 4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,8 + dc.b 8,8,9,9,9,9,10,10,10,10,11,11,11,11,12,12 + dc.b 12,13,13,13,13,14,14,14,14,15,15,15,15,16,16,16 + dc.b 17 +MasterVolTab18: + dc.b 0,0,0,0,1,1,1,1,2,2,2,3,3,3,3,4 + dc.b 4,4,5,5,5,5,6,6,6,7,7,7,7,8,8,8 + dc.b 9,9,9,9,10,10,10,10,11,11,11,12,12,12,12,13 + dc.b 13,13,14,14,14,14,15,15,15,16,16,16,16,17,17,17 + dc.b 18 +MasterVolTab19: + dc.b 0,0,0,0,1,1,1,2,2,2,2,3,3,3,4,4 + dc.b 4,5,5,5,5,6,6,6,7,7,7,8,8,8,8,9 + dc.b 9,9,10,10,10,10,11,11,11,12,12,12,13,13,13,13 + dc.b 14,14,14,15,15,15,16,16,16,16,17,17,17,18,18,18 + dc.b 19 +MasterVolTab20: + dc.b 0,0,0,0,1,1,1,2,2,2,3,3,3,4,4,4 + dc.b 5,5,5,5,6,6,6,7,7,7,8,8,8,9,9,9 + dc.b 10,10,10,10,11,11,11,12,12,12,13,13,13,14,14,14 + dc.b 15,15,15,15,16,16,16,17,17,17,18,18,18,19,19,19 + dc.b 20 +MasterVolTab21: + dc.b 0,0,0,0,1,1,1,2,2,2,3,3,3,4,4,4 + dc.b 5,5,5,6,6,6,7,7,7,8,8,8,9,9,9,10 + dc.b 10,10,11,11,11,12,12,12,13,13,13,14,14,14,15,15 + dc.b 15,16,16,16,17,17,17,18,18,18,19,19,19,20,20,20 + dc.b 21 +MasterVolTab22: + dc.b 0,0,0,1,1,1,2,2,2,3,3,3,4,4,4,5 + dc.b 5,5,6,6,6,7,7,7,8,8,8,9,9,9,10,10 + dc.b 11,11,11,12,12,12,13,13,13,14,14,14,15,15,15,16 + dc.b 16,16,17,17,17,18,18,18,19,19,19,20,20,20,21,21 + dc.b 22 +MasterVolTab23: + dc.b 0,0,0,1,1,1,2,2,2,3,3,3,4,4,5,5 + dc.b 5,6,6,6,7,7,7,8,8,8,9,9,10,10,10,11 + dc.b 11,11,12,12,12,13,13,14,14,14,15,15,15,16,16,16 + dc.b 17,17,17,18,18,19,19,19,20,20,20,21,21,21,22,22 + dc.b 23 +MasterVolTab24: + dc.b 0,0,0,1,1,1,2,2,3,3,3,4,4,4,5,5 + dc.b 6,6,6,7,7,7,8,8,9,9,9,10,10,10,11,11 + dc.b 12,12,12,13,13,13,14,14,15,15,15,16,16,16,17,17 + dc.b 18,18,18,19,19,19,20,20,21,21,21,22,22,22,23,23 + dc.b 24 +MasterVolTab25: + dc.b 0,0,0,1,1,1,2,2,3,3,3,4,4,5,5,5 + dc.b 6,6,7,7,7,8,8,8,9,9,10,10,10,11,11,12 + dc.b 12,12,13,13,14,14,14,15,15,16,16,16,17,17,17,18 + dc.b 18,19,19,19,20,20,21,21,21,22,22,23,23,23,24,24 + dc.b 25 +MasterVolTab26: + dc.b 0,0,0,1,1,2,2,2,3,3,4,4,4,5,5,6 + dc.b 6,6,7,7,8,8,8,9,9,10,10,10,11,11,12,12 + dc.b 13,13,13,14,14,15,15,15,16,16,17,17,17,18,18,19 + dc.b 19,19,20,20,21,21,21,22,22,23,23,23,24,24,25,25 + dc.b 26 +MasterVolTab27: + dc.b 0,0,0,1,1,2,2,2,3,3,4,4,5,5,5,6 + dc.b 6,7,7,8,8,8,9,9,10,10,10,11,11,12,12,13 + dc.b 13,13,14,14,15,15,16,16,16,17,17,18,18,18,19,19 + dc.b 20,20,21,21,21,22,22,23,23,24,24,24,25,25,26,26 + dc.b 27 +MasterVolTab28: + dc.b 0,0,0,1,1,2,2,3,3,3,4,4,5,5,6,6 + dc.b 7,7,7,8,8,9,9,10,10,10,11,11,12,12,13,13 + dc.b 14,14,14,15,15,16,16,17,17,17,18,18,19,19,20,20 + dc.b 21,21,21,22,22,23,23,24,24,24,25,25,26,26,27,27 + dc.b 28 +MasterVolTab29: + dc.b 0,0,0,1,1,2,2,3,3,4,4,4,5,5,6,6 + dc.b 7,7,8,8,9,9,9,10,10,11,11,12,12,13,13,14 + dc.b 14,14,15,15,16,16,17,17,18,18,19,19,19,20,20,21 + dc.b 21,22,22,23,23,24,24,24,25,25,26,26,27,27,28,28 + dc.b 29 +MasterVolTab30: + dc.b 0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7 + dc.b 7,7,8,8,9,9,10,10,11,11,12,12,13,13,14,14 + dc.b 15,15,15,16,16,17,17,18,18,19,19,20,20,21,21,22 + dc.b 22,22,23,23,24,24,25,25,26,26,27,27,28,28,29,29 + dc.b 30 +MasterVolTab31: + dc.b 0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7 + dc.b 7,8,8,9,9,10,10,11,11,12,12,13,13,14,14,15 + dc.b 15,15,16,16,17,17,18,18,19,19,20,20,21,21,22,22 + dc.b 23,23,24,24,25,25,26,26,27,27,28,28,29,29,30,30 + dc.b 31 +MasterVolTab32: + dc.b 0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7 + dc.b 8,8,9,9,10,10,11,11,12,12,13,13,14,14,15,15 + dc.b 16,16,17,17,18,18,19,19,20,20,21,21,22,22,23,23 + dc.b 24,24,25,25,26,26,27,27,28,28,29,29,30,30,31,31 + dc.b 32 +MasterVolTab33: + dc.b 0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7 + dc.b 8,8,9,9,10,10,11,11,12,12,13,13,14,14,15,15 + dc.b 16,17,17,18,18,19,19,20,20,21,21,22,22,23,23,24 + dc.b 24,25,25,26,26,27,27,28,28,29,29,30,30,31,31,32 + dc.b 33 +MasterVolTab34: + dc.b 0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7 + dc.b 8,9,9,10,10,11,11,12,12,13,13,14,14,15,15,16 + dc.b 17,17,18,18,19,19,20,20,21,21,22,22,23,23,24,24 + dc.b 25,26,26,27,27,28,28,29,29,30,30,31,31,32,32,33 + dc.b 34 +MasterVolTab35: + dc.b 0,0,1,1,2,2,3,3,4,4,5,6,6,7,7,8 + dc.b 8,9,9,10,10,11,12,12,13,13,14,14,15,15,16,16 + dc.b 17,18,18,19,19,20,20,21,21,22,22,23,24,24,25,25 + dc.b 26,26,27,27,28,28,29,30,30,31,31,32,32,33,33,34 + dc.b 35 +MasterVolTab36: + dc.b 0,0,1,1,2,2,3,3,4,5,5,6,6,7,7,8 + dc.b 9,9,10,10,11,11,12,12,13,14,14,15,15,16,16,17 + dc.b 18,18,19,19,20,20,21,21,22,23,23,24,24,25,25,26 + dc.b 27,27,28,28,29,29,30,30,31,32,32,33,33,34,34,35 + dc.b 36 +MasterVolTab37: + dc.b 0,0,1,1,2,2,3,4,4,5,5,6,6,7,8,8 + dc.b 9,9,10,10,11,12,12,13,13,14,15,15,16,16,17,17 + dc.b 18,19,19,20,20,21,21,22,23,23,24,24,25,26,26,27 + dc.b 27,28,28,29,30,30,31,31,32,32,33,34,34,35,35,36 + dc.b 37 +MasterVolTab38: + dc.b 0,0,1,1,2,2,3,4,4,5,5,6,7,7,8,8 + dc.b 9,10,10,11,11,12,13,13,14,14,15,16,16,17,17,18 + dc.b 19,19,20,20,21,21,22,23,23,24,24,25,26,26,27,27 + dc.b 28,29,29,30,30,31,32,32,33,33,34,35,35,36,36,37 + dc.b 38 +MasterVolTab39: + dc.b 0,0,1,1,2,3,3,4,4,5,6,6,7,7,8,9 + dc.b 9,10,10,11,12,12,13,14,14,15,15,16,17,17,18,18 + dc.b 19,20,20,21,21,22,23,23,24,24,25,26,26,27,28,28 + dc.b 29,29,30,31,31,32,32,33,34,34,35,35,36,37,37,38 + dc.b 39 +MasterVolTab40: + dc.b 0,0,1,1,2,3,3,4,5,5,6,6,7,8,8,9 + dc.b 10,10,11,11,12,13,13,14,15,15,16,16,17,18,18,19 + dc.b 20,20,21,21,22,23,23,24,25,25,26,26,27,28,28,29 + dc.b 30,30,31,31,32,33,33,34,35,35,36,36,37,38,38,39 + dc.b 40 +MasterVolTab41: + dc.b 0,0,1,1,2,3,3,4,5,5,6,7,7,8,8,9 + dc.b 10,10,11,12,12,13,14,14,15,16,16,17,17,18,19,19 + dc.b 20,21,21,22,23,23,24,24,25,26,26,27,28,28,29,30 + dc.b 30,31,32,32,33,33,34,35,35,36,37,37,38,39,39,40 + dc.b 41 +MasterVolTab42: + dc.b 0,0,1,1,2,3,3,4,5,5,6,7,7,8,9,9 + dc.b 10,11,11,12,13,13,14,15,15,16,17,17,18,19,19,20 + dc.b 21,21,22,22,23,24,24,25,26,26,27,28,28,29,30,30 + dc.b 31,32,32,33,34,34,35,36,36,37,38,38,39,40,40,41 + dc.b 42 +MasterVolTab43: + dc.b 0,0,1,2,2,3,4,4,5,6,6,7,8,8,9,10 + dc.b 10,11,12,12,13,14,14,15,16,16,17,18,18,19,20,20 + dc.b 21,22,22,23,24,24,25,26,26,27,28,28,29,30,30,31 + dc.b 32,32,33,34,34,35,36,36,37,38,38,39,40,40,41,42 + dc.b 43 +MasterVolTab44: + dc.b 0,0,1,2,2,3,4,4,5,6,6,7,8,8,9,10 + dc.b 11,11,12,13,13,14,15,15,16,17,17,18,19,19,20,21 + dc.b 22,22,23,24,24,25,26,26,27,28,28,29,30,30,31,32 + dc.b 33,33,34,35,35,36,37,37,38,39,39,40,41,41,42,43 + dc.b 44 +MasterVolTab45: + dc.b 0,0,1,2,2,3,4,4,5,6,7,7,8,9,9,10 + dc.b 11,11,12,13,14,14,15,16,16,17,18,18,19,20,21,21 + dc.b 22,23,23,24,25,26,26,27,28,28,29,30,30,31,32,33 + dc.b 33,34,35,35,36,37,37,38,39,40,40,41,42,42,43,44 + dc.b 45 +MasterVolTab46: + dc.b 0,0,1,2,2,3,4,5,5,6,7,7,8,9,10,10 + dc.b 11,12,12,13,14,15,15,16,17,17,18,19,20,20,21,22 + dc.b 23,23,24,25,25,26,27,28,28,29,30,30,31,32,33,33 + dc.b 34,35,35,36,37,38,38,39,40,40,41,42,43,43,44,45 + dc.b 46 +MasterVolTab47: + dc.b 0,0,1,2,2,3,4,5,5,6,7,8,8,9,10,11 + dc.b 11,12,13,13,14,15,16,16,17,18,19,19,20,21,22,22 + dc.b 23,24,24,25,26,27,27,28,29,30,30,31,32,33,33,34 + dc.b 35,35,36,37,38,38,39,40,41,41,42,43,44,44,45,46 + dc.b 47 +MasterVolTab48: + dc.b 0,0,1,2,3,3,4,5,6,6,7,8,9,9,10,11 + dc.b 12,12,13,14,15,15,16,17,18,18,19,20,21,21,22,23 + dc.b 24,24,25,26,27,27,28,29,30,30,31,32,33,33,34,35 + dc.b 36,36,37,38,39,39,40,41,42,42,43,44,45,45,46,47 + dc.b 48 +MasterVolTab49: + dc.b 0,0,1,2,3,3,4,5,6,6,7,8,9,9,10,11 + dc.b 12,13,13,14,15,16,16,17,18,19,19,20,21,22,22,23 + dc.b 24,25,26,26,27,28,29,29,30,31,32,32,33,34,35,35 + dc.b 36,37,38,39,39,40,41,42,42,43,44,45,45,46,47,48 + dc.b 49 +MasterVolTab50: + dc.b 0,0,1,2,3,3,4,5,6,7,7,8,9,10,10,11 + dc.b 12,13,14,14,15,16,17,17,18,19,20,21,21,22,23,24 + dc.b 25,25,26,27,28,28,29,30,31,32,32,33,34,35,35,36 + dc.b 37,38,39,39,40,41,42,42,43,44,45,46,46,47,48,49 + dc.b 50 +MasterVolTab51: + dc.b 0,0,1,2,3,3,4,5,6,7,7,8,9,10,11,11 + dc.b 12,13,14,15,15,16,17,18,19,19,20,21,22,23,23,24 + dc.b 25,26,27,27,28,29,30,31,31,32,33,34,35,35,36,37 + dc.b 38,39,39,40,41,42,43,43,44,45,46,47,47,48,49,50 + dc.b 51 +MasterVolTab52: + dc.b 0,0,1,2,3,4,4,5,6,7,8,8,9,10,11,12 + dc.b 13,13,14,15,16,17,17,18,19,20,21,21,22,23,24,25 + dc.b 26,26,27,28,29,30,30,31,32,33,34,34,35,36,37,38 + dc.b 39,39,40,41,42,43,43,44,45,46,47,47,48,49,50,51 + dc.b 52 +MasterVolTab53: + dc.b 0,0,1,2,3,4,4,5,6,7,8,9,9,10,11,12 + dc.b 13,14,14,15,16,17,18,19,19,20,21,22,23,24,24,25 + dc.b 26,27,28,28,29,30,31,32,33,33,34,35,36,37,38,38 + dc.b 39,40,41,42,43,43,44,45,46,47,48,48,49,50,51,52 + dc.b 53 +MasterVolTab54: + dc.b 0,0,1,2,3,4,5,5,6,7,8,9,10,10,11,12 + dc.b 13,14,15,16,16,17,18,19,20,21,21,22,23,24,25,26 + dc.b 27,27,28,29,30,31,32,32,33,34,35,36,37,37,38,39 + dc.b 40,41,42,43,43,44,45,46,47,48,48,49,50,51,52,53 + dc.b 54 +MasterVolTab55: + dc.b 0,0,1,2,3,4,5,6,6,7,8,9,10,11,12,12 + dc.b 13,14,15,16,17,18,18,19,20,21,22,23,24,24,25,26 + dc.b 27,28,29,30,30,31,32,33,34,35,36,36,37,38,39,40 + dc.b 41,42,42,43,44,45,46,47,48,48,49,50,51,52,53,54 + dc.b 55 +MasterVolTab56: + dc.b 0,0,1,2,3,4,5,6,7,7,8,9,10,11,12,13 + dc.b 14,14,15,16,17,18,19,20,21,21,22,23,24,25,26,27 + dc.b 28,28,29,30,31,32,33,34,35,35,36,37,38,39,40,41 + dc.b 42,42,43,44,45,46,47,48,49,49,50,51,52,53,54,55 + dc.b 56 +MasterVolTab57: + dc.b 0,0,1,2,3,4,5,6,7,8,8,9,10,11,12,13 + dc.b 14,15,16,16,17,18,19,20,21,22,23,24,24,25,26,27 + dc.b 28,29,30,31,32,32,33,34,35,36,37,38,39,40,40,41 + dc.b 42,43,44,45,46,47,48,48,49,50,51,52,53,54,55,56 + dc.b 57 +MasterVolTab58: + dc.b 0,0,1,2,3,4,5,6,7,8,9,9,10,11,12,13 + dc.b 14,15,16,17,18,19,19,20,21,22,23,24,25,26,27,28 + dc.b 29,29,30,31,32,33,34,35,36,37,38,38,39,40,41,42 + dc.b 43,44,45,46,47,48,48,49,50,51,52,53,54,55,56,57 + dc.b 58 +MasterVolTab59: + dc.b 0,0,1,2,3,4,5,6,7,8,9,10,11,11,12,13 + dc.b 14,15,16,17,18,19,20,21,22,23,23,24,25,26,27,28 + dc.b 29,30,31,32,33,34,35,35,36,37,38,39,40,41,42,43 + dc.b 44,45,46,47,47,48,49,50,51,52,53,54,55,56,57,58 + dc.b 59 +MasterVolTab60: + dc.b 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14 + dc.b 15,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29 + dc.b 30,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44 + dc.b 45,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59 + dc.b 60 +MasterVolTab61: + dc.b 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14 + dc.b 15,16,17,18,19,20,20,21,22,23,24,25,26,27,28,29 + dc.b 30,31,32,33,34,35,36,37,38,39,40,40,41,42,43,44 + dc.b 45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60 + dc.b 61 +MasterVolTab62: + dc.b 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14 + dc.b 15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30 + dc.b 31,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45 + dc.b 46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61 + dc.b 62 +MasterVolTab63: + dc.b 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14 + dc.b 15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30 + dc.b 31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46 + dc.b 47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62 + dc.b 63 +MasterVolTab64: + dc.b 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 + dc.b 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31 + dc.b 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47 + dc.b 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63 + dc.b 64 + even + endc ; !MINIMAL + + + ifd SDATA + + section __MERGED,bss + +mt_chan1: + ds.b n_sizeof +mt_chan2: + ds.b n_sizeof +mt_chan3: + ds.b n_sizeof +mt_chan4: + ds.b n_sizeof + +mt_SampleStarts: + ds.l 31 + +mt_mod: + ds.l 1 +mt_oldLev6: + ds.l 1 +mt_timerval: + ds.l 1 +mt_oldtimers: + ds.b 4 +mt_Lev6Int: + ds.l 1 +mt_Lev6Ena: + ds.w 1 +mt_PatternPos: + ds.w 1 +mt_PBreakPos: + ds.w 1 +mt_PosJumpFlag: + ds.b 1 +mt_PBreakFlag: + ds.b 1 +mt_Speed: + ds.b 1 +mt_Counter: + ds.b 1 +mt_SongPos: + ds.b 1 +mt_PattDelTime: + ds.b 1 +mt_PattDelTime2: + ds.b 1 + + ifeq MINIMAL +mt_SilCntValid: + ds.b 1 +mt_MasterVolTab: + ds.l 1 + endc + + xdef _mt_Enable +_mt_Enable: +mt_Enable: + ds.b 1 + + xdef _mt_E8Trigger +_mt_E8Trigger: +mt_E8Trigger: + ds.b 1 + + ifeq MINIMAL + xdef _mt_MusicChannels +_mt_MusicChannels: +mt_MusicChannels: + ds.b 1 + + xdef _mt_SongEnd +_mt_SongEnd: +mt_SongEnd: + ds.b 1 + endc ; !MINIMAL + + else ; !SDATA : single section with local base register + + rsreset +mt_chan1 rs.b n_sizeof +mt_chan2 rs.b n_sizeof +mt_chan3 rs.b n_sizeof +mt_chan4 rs.b n_sizeof +mt_SampleStarts rs.l 31 +mt_mod rs.l 1 +mt_oldLev6 rs.l 1 +mt_timerval rs.l 1 +mt_oldtimers rs.b 4 +mt_Lev6Int rs.l 1 +mt_Lev6Ena rs.w 1 +mt_PatternPos rs.w 1 +mt_PBreakPos rs.w 1 +mt_PosJumpFlag rs.b 1 +mt_PBreakFlag rs.b 1 +mt_Speed rs.b 1 +mt_Counter rs.b 1 +mt_SongPos rs.b 1 +mt_PattDelTime rs.b 1 +mt_PattDelTime2 rs.b 1 + ifeq MINIMAL +mt_SilCntValid rs.b 1 +mt_MasterVolTab rs.l 1 + endc +mt_Enable rs.b 1 ; exported as _mt_Enable +mt_E8Trigger rs.b 1 ; exported as _mt_E8Trigger +mt_MusicChannels rs.b 1 ; exported as _mt_MusicChannels +mt_SongEnd rs.b 1 ; exported as _mt_SongEnd + +mt_data: + ds.b mt_Enable + xdef _mt_Enable +_mt_Enable: + ds.b 1 + xdef _mt_E8Trigger +_mt_E8Trigger: + ds.b 1 + ifeq MINIMAL + xdef _mt_MusicChannels +_mt_MusicChannels: + ds.b 1 + xdef _mt_SongEnd +_mt_SongEnd: + ds.b 1 + endc ; !MINIMAL + + endc ; SDATA/!SDATA + + end diff --git a/ptplayer.obj b/ptplayer.obj index 061f8ae68ef4b7442f852f2739c0ce5f42f09a9a..119c44fd90e8f0033dd20ff36d6f0f47d8cb711c 100644 GIT binary patch delta 1499 zcmZuxeM}rh6n`_jx62*Jv3z)Z8M0D7j*q~VBB6YgU0U%D&*fsUZ3x;!lvdKzv+Xqv zxfF=ls3C^-r34TY)ASEw2of+RqDikNHnlP2Lh66$DIvBZ3Td#w6**@)nx>UWe)H!2 z_RX93-n`klw--A4Q;c~4+#djOd6(pRtI!Pk;5d$KFZ4+`amhbJg<*7&T5<^ch(d*4 zoFR3oLO}lxlu|e=b#JfPBut=zv#(*CGSQ^-%6 zuZ)!1fVX$qK-hL{k`+5CrDfccu1%XyyOtF_xY%cR=i>&R=5_11Tf4Cq$rWn(f8Le@ zyBcrp%C<+@?Hq@9avaipC1z*1upD0(^2K@iLp4k$@nfM&4Cs78H)e{jbZNer{XpRQ z&(ID~?zkJ-ik6-kB-Z$4fRTnGGJ71r(h7pd73zv;?`mgSL0ovWE{RdYaJgPm^(z}; zmg)9yWk}kDG`Jpuy`6AA5|)qYyGqP9#jA8z!trH?y<28MkaFuA~F7LFM+amiRv(YPF0EpY%iz5o&U zT7QIqNp55&F^$V$*88e<(;3dYR^wLyhP@!!z|;XW6R(<@3Xj~lMTUyN8RWClzI*S; zjoajtBDfMXM71Bur$z8vP}tU{o{P1Sj8nNLRw`5sOWhH;d)YoYM}q z6lT>w9sR09P3VF6?P^pz!+XZ``nW>Xo0{(esNwKSxN7O88{cDRd^_EE4d>%OqwC*b zuT)0Y-^XdGg045m^VJavy4{&avYF4;RmT{5vtU%aT4c}nx%h)9`|EM`~ub| z?=j4A6`UDQB_AZ`aWZ+I!9)M1{ZorfCkhr@+sGVVwlVd8F$4nxyh;=S`YCd%+gXEyUthrvOWVLWJbTafa jj4)hdxWll-{&_q@I)j~|o}q=|G{Xgki>!Cp;O_qiL08o* delta 1394 zcmZ8hZ%A8L6hHUAmzcyPzNAUiRV4EtNenaEbcmYzUKE#T_Uek0Au_dYwiVejx|AZJ z+HDj!{9t!jv8^k9lzu3pV@Q!Tl_K335;7S3P-<6%Zq8M4Yid(xc3)hv?!kHI+{5pl z^SkGqdsq7Jof^y4S^>D<0SwlQ_9mOg1Iov79LX5UPjlpAbct%GF+}RgV;m(S)z0A( z@#pCr84#ruJ{E>EHv8#}=%52s_by(d9n=uRP1;EfBiPDyQtcw%;M%Bm2YEg~^&2Sj zb<|LWqr6CsTaVCh?&vq=yIc;0TdV2OdEo)~E~B75ZSx?Z=rc zXV!-Ob16mI@nP1GX-xsV`3#mfKtYs$f+sb{_|WoMz?+&^hROwB)DAGw>?Vm50LC83 zl0(ZwY561hbPs3}FYF49lmbVSASsb17_!haZ-)chkPo+(p>hy(>6E=o*`=JiLhlg6 z;&4s+$$G#uY-f30Q_XH@Q8K(b0D9mn2fz%PW<08GC@G4vEg%qkYd5Q{T`ZCWx}UYn zjT72w>a0dt*FEIS^E*jGVlCxT3d=KVtPRz`^k7XL}GG4V1f^E*K0$LwrVBg4ES2$=|P z{ij>@NAi*?2k;&Ts59rX?B%eF9#TcU92R8fSLlzfgMOFI%ZR|E`t4xKVe~~s#x@Ls z19bgB%djoGt-SsAU2@qC!8l)(zwl}M?H|ZhH+&UW?XG?#U%TPgxH_|%{VCf)iqDkk z`T)XbpqXbauzkb7&+6T<2ElJl#{twOL7NF}hF(&GgN7>CE*sX85RAjh%d8qwrNdz^ z#<;}A#B;chiw1k)|H(_KQZW_i2VOIGo9V_B2COOEVw&qyhY zJ0ANp5RdI1-5DTDtewYp+vey}cI;77M!Mu2jv7l1Y><`4I=Yl(c+>b^-5$(7Sb8BK zRY5}fBQPcfB-44wc|p6rE|@BwXDKwH<+^P-at@@_);flAKBHTwFp<+ow+Pks}oGZ@gwHR;_UXt^gM;4P*p$)sd>Z6j#p2xHP%#s3zJ*9x}jI;cMm z={tBSx16YPA-9qn|K~eQ=DwXx;no8pM>NlXVtd{cU3Yz%Z?)>!gYrE)EH6jQzzEyA WVTYe} Date: Wed, 15 Jan 2025 16:57:25 +0100 Subject: [PATCH 20/23] Changes to be committed: new file: blitz_ptplayer_oscompat.asm new file: ptplayer_oscompat.asm new file: ptplayer_oscompat.obj --- blitz_ptplayer_oscompat.asm | 281 +++ ptplayer_oscompat.asm | 3974 +++++++++++++++++++++++++++++++++++ ptplayer_oscompat.obj | Bin 0 -> 13900 bytes 3 files changed, 4255 insertions(+) create mode 100644 blitz_ptplayer_oscompat.asm create mode 100644 ptplayer_oscompat.asm create mode 100644 ptplayer_oscompat.obj diff --git a/blitz_ptplayer_oscompat.asm b/blitz_ptplayer_oscompat.asm new file mode 100644 index 0000000..0dec850 --- /dev/null +++ b/blitz_ptplayer_oscompat.asm @@ -0,0 +1,281 @@ +; Amiga Blitz Basic Library wrapper of PT Player by Frank Wille (PHX) in 2013, 2016-2024. +; Converted to BlitzBasic library by E-Penguin based on work by idrougge +; Wrapper concept and macros by earok + + include "blitz.i" + +; Blitz Libraries used +audiolib equ 116 +banklib equ 76 + +; This Blitz library +ptplayerlib equ 48 + + libheader ptplayerlib,0,0,blitz_finit,0 + + astatement + args word,byte + libs banklib,$1080 + subs MTInit_bank,0,0 + args long,long,byte + libs + subs MTInit,0,0 + name "MTInit","Bank#, startpos | module addr, instr addr, startpos (inserts module into player)",0 + + afunction long + args + libs + subs MTInstall,0,0 + name "MTInstall","Install player routine, OS compatible",0 + + astatement + args byte + libs + subs MTPlay,0,0 + name "MTPlay","On/Off for module playback",0 + + astatement + args + libs + subs MTRemove,0,0 + name "MTRemove","Remove player from system",0 + + astatement + args + libs + subs MTEnd,0,0 + name "MTEnd","Stop playing current module",0 + + astatement + args long,word,word,word + libs + subs MTSoundFX,0,0 + args word,word + libs audiolib,$1080 + subs MTSoundFX_soundobject,0,0 + name "MTSoundFX","Sound#, volume (0..64)| sample_addr.l, length.w, period.w, volume.w" + + afunction long + args long + libs + subs MTPlayFx,0,0 + name "MTPlayFx","SfxStructurePointer, returns SfxChanStatus pointer" + + astatement + args word + libs + subs MTMasterVolume,0,0 + name "MTMasterVolume","Master volume (0..64) for all music channels." + + astatement + args byte + libs + subs MTMusicMask,0,0 + name "MTMusicMask","Set bits 0-3 to reserve channels for music only." + + astatement + args byte + libs + subs MTMusicChannels,0,0 + name "MTMusicChannels","number of channels dedicated to music." + + afunction byte + args + libs + subs MTE8Trigger,0,0 + name "MTE8Trigger","Value of the last E8 command." + + astatement + args long + libs + subs MTLoopFx,0,0 + name "MTLoopFx","SfxStructurePointer" + + astatement + args byte + libs + subs MTStopFx,0,0 + name "MTStopFx","channel to stop FX loop" + + astatement + args word,byte + libs + subs MTSampleVolume,0,0 + name "MTSampleVolume", "Redefine a sample's volume" + + astatement + args byte + libs + subs MTChannelMask,0,0 + name "MTChannelMask", "Clear bits 0-3 to mute channel for music" + + astatement + args byte + libs + subs MTSetSongEnd,0,0 + name "MTSetSongEnd","Set value of the mt_SongEnd flag to 0xFF to end after it has finished" + + afunction byte + args + libs + subs MTIsEnabled,0,0 + name "MTIsEnabled","Get status of playback" + +blitz_finit: + nullsub _blitz_mt_lib_finit,0,0 ; Call deinit routine on exit + + libfin ; End of Blitz library header + +; Deinitialisation for Blitz so that user doesn't have to call MTRemove +_blitz_mt_lib_finit: + bra _mt_remove +;-------------------- + +; Macros by earok / Scorpion +; https://github.com/earok/ptplayer_blitzwrapper_scorpion +storeAddressRegisters macro + movem.l a4-a6,-(sp) ; Save registers for Blitz 2 + lea CUSTOM,a6 ;Store the custom register in A6 + endm + +storeAddressRegistersVBR macro + movem.l a4-a6,-(sp) ; Save registers for Blitz 2 + ;Load vector base into A0 + sub.l a0,a0 + move.l 4.w,a6 + btst #0,297(a6) ; check for 68010 + beq .novbr + lea getvbr(pc),a5 + jsr -30(a6) ; Supervisor() +.novbr: lea CUSTOM,a6 + endM + +restoreAddressRegisters macro + movem.l (sp)+,a4-a6 ; Restore registers for Blitz + rts ;Return to Blitz + endm +;-------------------- + + +; VBR Hack by phx +; https://eab.abime.net/showpost.php?p=1516508&postcount=7 +;----- Get VBR 68010+ --- + mc68010 +getvbr: + movec vbr,a0 + rte + mc68000 +;------------------------ + +; Register CIA-B interrupts with AmigaOS for calling mt_music. +MTInstall: + storeAddressRegisters + jsr _mt_install + restoreAddressRegisters + +MTRemove: + storeAddressRegisters + jsr _mt_remove + restoreAddressRegisters + +;--------------------------------------------------------------------------- +;_mt_init: +; Initialize new module. +; Reset speed to 6, tempo to 125 and start at given song position. +; Master volume is at 64 (maximum). +; --- Init for Blitz ---- +MTInit_bank: + move.l #0,a1 ; Set sample pointer to NULL + move.b d1,d0 ; Set starting position + move.l (a0),a0 ; Set module address + bra MTInit_done +MTInit: + move.l D0,A0 ; a0 = module pointer + move.l D1,A1 ; a1 = sample pointer (NULL means samples are stored within the module) + move.w D2,D0 ; d0 = initial song position +MTInit_done: + storeAddressRegisters + jsr _mt_init + restoreAddressRegisters + +MTEnd: + storeAddressRegisters + jsr _mt_end + restoreAddressRegisters + +MTPlay: + move.b d0,_mt_Enable + rts + +MTIsEnabled: + move.b _mt_Enable,d0 + rts + +MTSetSongEnd: +; Set mt_SongEnd to 0xFF to stop after current song is played + move.b d0,mt_SongEnd(a4) + rts + +;------------------------------------- +MTSoundFX_soundobject: + move.w d1,d2 ; volume + move.w 4(a0),d1 ; period + move.w 6(a0),d0 ; length + move.l (a0),a0 ; sample data + bra MTSoundFX_done +MTSoundFX: + move.l D0,A0 ;a0 = sample pointer + move.w D1,D0 ;d0.w = sample length in words + move.w D2,D1 ;d1.w = sample period + Move.w D3,D2 ;d2.w = sample volume +MTSoundFX_done: + storeAddressRegisters + jsr _mt_soundfx + restoreAddressRegisters + +MTPlayFx: + storeAddressRegisters + move.l D0,A0 ;a0=SfxStructurePointer + jsr _mt_playfx + restoreAddressRegisters + +MTLoopFx: + storeAddressRegisters + move.l D0,A0 ;a0=SfxStructurePointer + jsr _mt_loopfx + restoreAddressRegisters + +MTStopFx: + storeAddressRegisters + jsr _mt_stopfx + restoreAddressRegisters + +MTMusicMask: + storeAddressRegisters + jsr _mt_musicmask + restoreAddressRegisters + +MTMasterVolume: + storeAddressRegisters + jsr _mt_mastervol + restoreAddressRegisters + +MTSampleVolume: + storeAddressRegisters + jsr _mt_samplevol + restoreAddressRegisters + +MTChannelMask: + storeAddressRegisters + jsr _mt_channelmask + restoreAddressRegisters + +MTMusicChannels: + move.b d0,_mt_MusicChannels + rts + +MTE8Trigger: + move.b _mt_E8Trigger,d0 + rts + + include "ptplayer_oscompat.asm" \ No newline at end of file diff --git a/ptplayer_oscompat.asm b/ptplayer_oscompat.asm new file mode 100644 index 0000000..e5e6d5b --- /dev/null +++ b/ptplayer_oscompat.asm @@ -0,0 +1,3974 @@ +;************************************************** +;* ----- Protracker V2.3B Playroutine ----- * +;************************************************** +; +; Version 6.4 +; Written by Frank Wille in 2013, 2016-2024. +; +; I, the copyright holder of this work, hereby release it into the +; public domain. This applies worldwide. +; +; The default version (single section, local base register) should +; work with most assemblers. Tested are: Devpac, vasm, PhxAss, +; Barfly-Asm, SNMA, AsmOne, AsmPro. +; +; The small data model can be enabled by defining the symbol SDATA. In +; this case all functions expect a4 to be initialized with the small +; data base register. Interrupt functions restore a4 from _LinkerDB. +; Small data may work with vasm and PhxAss only. +; +; Exported functions and variables: +; (CUSTOM is the custom-chip register set base address $dff000.) +; +; _mt_install(a6=CUSTOM, a0=VectorBase, d0=PALflag.b) [OSCOMPAT=0] +; Install a CIA-B interrupt for calling mt_music or mt_sfxonly. +; The music module is replayed via mt_music when _mt_Enable is non-zero. +; Otherwise the interrupt handler calls mt_sfxonly to play sound +; effects only. VectorBase is 0 for 68000, otherwise set it to the CPU's +; VBR register. A non-zero PALflag selects PAL-clock for the CIA timers +; (NTSC otherwise). +; +; ok(d0) = _mt_install() [OSCOMPAT=1] +; Register CIA-B interrupts with AmigaOS for calling mt_music or +; mt_sfxonly. Allocate all Paula audio channels via audio.device. +; The music module is replayed via mt_music when _mt_Enable is non-zero. +; Otherwise the interrupt handler calls mt_sfxonly to play sound +; effects only. +; Returns true (1) on success, false (0) otherwise. You must not call +; _mt_remove(), when _mt_install() failed! +; +; _mt_remove(a6=CUSTOM) [OSCOMPAT=0] +; Remove the CIA-B music interrupt, restore the previous handler and +; reset the CIA timer registers to their original values. +; +; _mt_remove() [OSCOMPAT=1] +; Unregister the CIA-B interrupts handlers and deallocate all +; audio channels. +; +; _mt_init(a6=CUSTOM, a0=TrackerModule, a1=Samples|NULL, d0=InitialSongPos.b) +; Initialize a new module. +; Reset speed to 6, tempo to 125 and start at the given song position. +; Master volume is at 64 (maximum). +; When a1 is NULL the samples are assumed to be stored after the patterns. +; +; _mt_end(a6=CUSTOM) +; Stop playing current module and sound effects. +; +; _mt_soundfx(a6=CUSTOM, a0=SamplePointer, +; d0=SampleLength.w, d1=SamplePeriod.w, d2=SampleVolume.w) +; Request playing of an external sound effect on the most unused channel. +; This function is for compatibility with the old API only. +; You may prefer _mt_playfx instead. MINIMAL=0 only. +; +; channelStatus(d0) = _mt_playfx(a6=CUSTOM, a0=SfxStructurePointer) +; Request playing of a prioritized external sound effect, either on a +; fixed channel or on the most unused one. +; Structure layout of SfxStructure: +; void *sfx_ptr (pointer to sample start in Chip RAM, even address) +; WORD sfx_len (sample length in words) +; WORD sfx_per (hardware replay period for sample) +; WORD sfx_vol (volume 0..64, is unaffected by the song's master volume) +; BYTE sfx_cha (0..3 selected replay channel, -1 selects best channel) +; BYTE sfx_pri (priority, must be in the range 1..127) +; When multiple samples are assigned to the same channel the lower +; priority sample will be replaced. When priorities are the same, then +; the older sample is replaced. +; The chosen channel is blocked for music until the effect has +; completely been replayed. +; Returns a pointer to a channel-status structure when the sample +; is scheduled for playing, and NULL when the request was ignored. +; MINIMAL=0 only. +; +; _mt_loopfx(a6=CUSTOM, a0=SfxStructurePointer) +; Request playing of a looped sound effect on a fixed channel, which +; will be blocked for music until the effect is stopped (_mt_stopfx). +; It uses the same sfx-structure as _mt_playfx, but the priority is +; ignored. A looped sound effect has always highest priority and will +; replace a previous effect on the same channel. No automatic channel +; selection possible! +; Also make sure the sample starts with a zero-word, which is used +; for idling when the effect is stopped. This word is included in the +; total length calculation, but excluded when actually playing the loop. +; MINIMAL=0 only. +; +; _mt_stopfx(a6=CUSTOM, d0=Channel.b) [MINIMAL=0] +; Immediately stop a currently playing sound effect on a channel (0..3) +; and make it available for music, or other effects, again. This is the +; only way to stop a looped sound effect (_mt_loopfx) besides stopping +; replay completely (_mt_end). +; +; _mt_musicmask(a6=CUSTOM, d0=ChannelMask.b) [MINIMAL=0] +; Bits set in the ChannelMask define which specific channels are reserved +; for music only. Set bit 0 for channel 0, ..., bit 3 for channel 3. +; When calling _mt_soundfx or _mt_playfx with automatic channel selection +; (sfx_cha=-1) then these masked channels will never be picked. +; The mask defaults to 0. +; +; _mt_mastervol(a6=CUSTOM, d0=MasterVolume.w) [MINIMAL=0] +; Set a master volume from 0 to 64 for all music channels. +; Note that the master volume does not affect the volume of external +; sound effects (which is desired). +; +; _mt_samplevol(d0=SampleNumber.w, d1=Volume.b) [MINIMAL=0] +; Redefine a sample's volume. May also be done while the song is playing. +; Warning: Does not check arguments for valid range! You must have done +; _mt_init before calling this function! +; The new volume is persistent. Even when the song is restarted. +; +; _mt_channelmask(a6=CUSTOM, d0=ChannelMask.b) [MINIMAL=0] +; Bits cleared in the ChannelMask define which specific channels are muted +; for music replay. Clear bit 0 for channel 0, ..., bit 3 for channel 3. +; Additionally a cleared bit prevents any access to the sample pointers +; of this channel. +; The mask defaults to all channels unmuted (bits set) and is reset to +; this state on _mt_init and _mt_end. +; +; _mt_music(a6=CUSTOM) +; The replayer routine. Can be called from your own VBlank interrupt +; handler when VBLANK_MUSIC is set. Is otherwise called automatically +; by Timer-A interrupts after _mt_install. +; +; _mt_dmaon() [NO_TIMERS=1] +; MUST be called ca. 550 ticks after calling _mt_music. Enables Audio +; DMA to play a new note. +; +; _mt_setrep() [NO_TIMERS=1] +; MUST be called ca. 550 ticks after calling _mt_dmaon. Sets the +; repetition pointers and lengths for looped samples. +; +; Byte Variables: +; +; _mt_Enable [VBLANK_MUSIC=0] +; Set this byte to non-zero to play music, zero to pause playing. +; Note that you can still play sound effects, while music is stopped. +; It is set to 0 by _mt_install. +; +; _mt_E8Trigger +; This byte reflects the value of the last E8 command. +; It is reset to 0 after _mt_init. +; +; _mt_MusicChannels [MINIMAL=0] +; This byte defines the number of channels which should be dedicated +; for playing music. So sound effects will never use more +; than 4 - _mt_MusicChannels channels at once. Defaults to 0. +; + +; Set this if you want to build a version which uses AmigaOS to +; register interrupts and reserve audio channels. + ifnd OSCOMPAT +OSCOMPAT equ 1 + endc + +; Optionally you can build a minimal version, which includes only +; the basic player. No sound effects insertion, no master volume, no +; sample volume, etc. Define the symbol MINIMAL=1 for it. + ifnd MINIMAL +MINIMAL equ 0 + endc + +; You may disable sawtooth and rectangle vibratos/tremolos here, which +; will be replaced by sine-waves. They are rarely used and disabling +; them will free a lot of memory for the tables. + ifnd ENABLE_SAWRECT +ENABLE_SAWRECT equ 0 + endc + +; Set this if you can guarantee that the word at $0 is cleared, and if +; you want to use it for idle-looping of samples. + ifnd NULL_IS_CLEARED +NULL_IS_CLEARED equ 0 + endc + +; Setting NO_TIMERS disables the use of both CIA-B timers completely, which +; means that the player doesn't depend on level-6 EXTER interrupts anymore. +; NO_TIMERS=1 automatically includes VBLANK_MUSIC=1 (see below). +; With NO_TIMERS set, your main program has to call all music subroutines +; itself. The typical procedure in a VERTB-based main loop would be: +; 1. call _mt_music +; 2. wait at least 550 ticks, then call _mt_dmaon +; 3. wait at least 550 ticks again, then call _mt_setrep +; The last two steps could for example be performed by Copper interrupts. + ifnd NO_TIMERS +NO_TIMERS equ 0 + else + ifne NO_TIMERS + ifd VBLANK_MUSIC + ifeq VBLANK_MUSIC + fail "NO_TIMERS=1 requires VBLANK_MUSIC=1" + endc + endc ; VBLANK_MUSIC + endc ; NO_TIMERS=1 + endc ; NO_TIMERS + +; This disables only initialization of CIA Timer-A interrupts for music +; replay, which means you cannot set the tempo with the F-command +; anymore and you have to call _mt_music yourself out of your own +; VBlank interrupt handler. Also sound effects will no longer work, +; when no music is playing (_mt_Enable=0). + ifnd VBLANK_MUSIC +VBLANK_MUSIC equ NO_TIMERS + endc + +; Delay in CIA-ticks, which guarantees that at least one Audio-DMA +; took place, even with the lowest periods. +; 496 should be the correct value. But there are some A1200 which +; need at least 550. +DMADELAY equ 576 ; was 496 + + +; exec.library (OSCOMPAT) +_LVOAllocSignal equ -330 +_LVOFreeSignal equ -336 +_LVOOpenDevice equ -444 +_LVOCloseDevice equ -450 +_LVOOpenResource equ -498 + +; cia.resource (OSCOMPAT) +_LVOAddICRVector equ -6 +_LVORemICRVector equ -12 +_LVOAbleICR equ -18 +CIAICRB_TA equ 0 +CIAICRB_TB equ 1 + + +; Custom chip registers +CUSTOM equ $dff000 +INTREQR equ $01e +INTENAR equ $01c +DMACON equ $096 +INTENA equ $09a +INTREQ equ $09c +AUD0LC equ $0a0 +AUD0LEN equ $0a4 +AUD0VOL equ $0a8 +AUD1LC equ $0b0 +AUD1LEN equ $0b4 +AUD1VOL equ $0b8 +AUD2LC equ $0c0 +AUD2LEN equ $0c4 +AUD2VOL equ $0c8 +AUD3LC equ $0d0 +AUD3LEN equ $0d4 +AUD3VOL equ $0d8 + +; Audio channel registers +AUDLC equ 0 +AUDLEN equ 4 +AUDPER equ 6 +AUDVOL equ 8 + +; CIA registers +CIAA equ $bfe001 +CIAB equ $bfd000 +CIAPRA equ $000 +CIATALO equ $400 +CIATAHI equ $500 +CIATBLO equ $600 +CIATBHI equ $700 +CIAICR equ $d00 +CIACRA equ $e00 +CIACRB equ $f00 + + +; Sound effects structure, passed into _mt_playfx + rsreset +sfx_ptr rs.l 1 +sfx_len rs.w 1 +sfx_per rs.w 1 +sfx_vol rs.w 1 +sfx_cha rs.b 1 +sfx_pri rs.b 1 +sfx_sizeof rs.b 0 + + +; Channel Status + rsreset +n_note rs.w 1 +n_cmd rs.b 1 +n_cmdlo rs.b 1 +n_index rs.b 1 +n_sfxpri rs.b 1 +n_reserved1 rs.b 2 +n_start rs.l 1 +n_loopstart rs.l 1 +n_length rs.w 1 +n_replen rs.w 1 +n_period rs.w 1 +n_volume rs.w 1 +n_pertab rs.l 1 +n_dmabit rs.w 1 +n_noteoff rs.w 1 +n_toneportspeed rs.w 1 +n_wantedperiod rs.w 1 +n_pattpos rs.w 1 +n_funk rs.w 1 +n_wavestart rs.l 1 +n_reallength rs.w 1 +n_intbit rs.w 1 +n_sfxptr rs.l 1 +n_sfxlen rs.w 1 +n_sfxper rs.w 1 +n_sfxvol rs.w 1 +n_looped rs.b 1 +n_minusft rs.b 1 +n_vibratoamp rs.b 1 +n_vibratospd rs.b 1 +n_vibratopos rs.b 1 +n_vibratoctrl rs.b 1 +n_tremoloamp rs.b 1 +n_tremolospd rs.b 1 +n_tremolopos rs.b 1 +n_tremoloctrl rs.b 1 +n_gliss rs.b 1 +n_sampleoffset rs.b 1 +n_loopcount rs.b 1 +n_funkoffset rs.b 1 +n_retrigcount rs.b 1 + ifeq MINIMAL +n_freecnt rs.b 1 +n_musiconly rs.b 1 +n_enable rs.b 1 + else +n_reserved2 rs.b 3 + endc +n_sizeof rs.b 0 + + +DISABLE macro + ifeq NO_TIMERS + move.w #$4000,INTENA(a6) + endc + endm + +ENABLE macro + ifeq NO_TIMERS + move.w #$c000,INTENA(a6) + endc + endm + + + ifd SDATA + xref _LinkerDB ; small data base from linker + near a4 + code + endc + + + + ifne OSCOMPAT +;--------------------------------------------------------------------------- + xdef _mt_install +_mt_install: +; Register a CIA-B interrupt with AmigaOS for calling mt_music. +; Reserve all audio channels for our use. +; -> d0.l = status (1 for success, 0 for error) + + move.l a6,-(sp) + + ifnd SDATA + move.l a4,-(sp) + lea mt_data(pc),a4 + endc + + ; open audio.device and allocate all four audio channels + move.l 4.w,a6 + moveq #-1,d0 + jsr _LVOAllocSignal(a6) + lea mt_ioport+20(pc),a0 + move.b d0,-5(a0) ; MP_SIGBIT + bmi .fail + move.l a0,8(a0) ; NewList MP_MSGLIST + addq.l #4,a0 + clr.l (a0) + move.l a0,-(a0) + move.l 276(a6),-(a0) ; MP_SIGTASK = ThisTask + lea mt_audio_name(pc),a0 + lea mt_ioaudio(pc),a1 + moveq #0,d0 + moveq #0,d1 + jsr _LVOOpenDevice(a6) + tst.b d0 + bne .no_device + + ifeq NO_TIMERS + ; attempt to allocate CIA-B timers A and B + lea mt_ciab_name(pc),a1 + jsr _LVOOpenResource(a6) + tst.l d0 + beq .no_rsrc + + move.l d0,a6 + + clr.b mt_Enable(a4) + lea TB_toggle(pc),a0 + clr.b (a0) + + ifeq VBLANK_MUSIC + moveq #CIAICRB_TA,d0 + lea mt_extern_timer_a_is(pc),a1 + jsr _LVOAddICRVector(a6) + tst.l d0 + bne .no_timer_a + endc ; !VBLANK_MUSIC + + moveq #CIAICRB_TB,d0 + lea mt_extern_timer_b_is(pc),a1 + jsr _LVOAddICRVector(a6) + tst.l d0 + bne .no_timer_b + + ; TimerA and TimerB interrupt disable + ifne VBLANK_MUSIC + moveq #$2,d0 ; TimerB only for VBLANK_MUSIC + else + moveq #$3,d0 + endc + jsr _LVOAbleICR(a6) + + lea CIAB,a0 + + ; Stop Timer B + moveq #0,d0 + move.b d0,CIACRB(a0) + + ifeq VBLANK_MUSIC + ; Stop Timer A + move.b d0,CIACRA(a0) + + ; determine if 02 clock for timers is based on PAL or NTSC + move.l 4.w,a1 + cmp.b #50,531(a1) ; PowerSupplyFrequency + beq .1 + move.l #1789773,d0 ; NTSC + bra .2 +.1: move.l #1773447,d0 ; PAL +.2: move.l d0,mt_timerval(a4) + + ; load TimerA in continuous mode for the default tempo of 125 + divu #125,d0 + move.b d0,CIATALO(a0) + lsr.w #8,d0 + move.b d0,CIATAHI(a0) + move.b #$11,CIACRA(a0) ; load timer, start continuous + endc ; !VBLANK_MUSIC + + ; load TimerB with DMADELAY ticks for setting DMA and repeat + move.b #DMADELAY&255,CIATBLO(a0) + move.b #DMADELAY>>8,CIATBHI(a0) + + ; TimerA and TimerB interrupt enable + ifne VBLANK_MUSIC + move.w #$82,d0 ; TimerB only for VBLANK_MUSIC + else + move.w #$83,d0 + endc + jsr _LVOAbleICR(a6) + + endc ; !NO_TIMERS + + ; reset the player + pea .popout(pc) + ifnd SDATA + move.l a4,-(sp) + endc + lea CUSTOM,a6 + bra mt_reset + + ifeq NO_TIMERS +.no_timer_b: + ifeq VBLANK_MUSIC + moveq #CIAICRB_TA,d0 + lea mt_extern_timer_a_is(pc),a1 + jsr _LVORemICRVector(a6) +.no_timer_a: + endc ; !VBLANK_MUSIC + endc ; !NO_TIMERS + + move.l 4.w,a6 +.no_rsrc: + lea mt_ioaudio(pc),a1 + jsr _LVOCloseDevice(a6) +.no_device: + moveq #0,d0 + move.b mt_ioport+15(pc),d0 + jsr _LVOFreeSignal(a6) +.fail: + moveq #0,d0 ; Indicate failure + bra .out +.popout: + moveq #1,d0 ; Indicate success +.out: + ifnd SDATA + move.l (sp)+,a4 + endc + move.l (sp)+,a6 + rts + + +mt_ioport: + dc.l 0,0 + dc.b 4,0 ; MT_MSGPORT + dc.l 0 + dc.b 0,-1 ; PA_SIGNAL + dc.l 0 +.head: dc.l .tail +.tail: dc.l 0 + dc.l .head + +mt_ioaudio: + dc.l 0,0 + dc.b 7,127 ; NT_REPLYMSG, ADALLOC_MAXPREC + dc.l 0,mt_ioport + dc.w .end-mt_ioaudio + dc.l 0,0 + dc.w $20 ; ADCMD_ALLOCATE + dc.b $40,0 ; ADIOF_NOWAIT + dc.w 0 ; ioa_AllocKey + dc.l mt_ch_alloc ; ioa_Data + dc.l 1 ; ioa_Length + dc.w 0,0,0 + ds.b 20 ; ioa_WriteMsg +.end: + + ifeq NO_TIMERS + ifeq VBLANK_MUSIC +mt_extern_timer_a_is: + dc.l 0,0 + dc.b 2 ; NT_INTERRUPT + dc.b 0 ; priority + dc.l mt_timer_int_name + dc.l 0 + dc.l mt_cia_timer_a_code + endc ; !VBLANK_MUSIC + +mt_extern_timer_b_is: + dc.l 0,0 + dc.b 2 ; NT_INTERRUPT + dc.b 0 ; priority + dc.l mt_timer_int_name + dc.l 0 + dc.l mt_cia_timer_b_code + +mt_ciab_name: + dc.b "ciab.resource",0 +mt_timer_int_name: + dc.b "ptplayer timer",0 + endc ; !NO_TIMERS + +mt_audio_name: + dc.b "audio.device",0 +mt_ch_alloc: + dc.b %1111 ; allocate all four channels + even + + +;--------------------------------------------------------------------------- + xdef _mt_remove +_mt_remove: +; Unregister our CIA-B interrupt with AmigaOS. Free all audio channels. + + move.l a6,-(sp) + + ifeq NO_TIMERS + move.l 4.w,a6 + lea mt_ciab_name(pc),a1 + jsr _LVOOpenResource(a6) + tst.l d0 + beq .nociares + + move.l d0,a6 + + ; TimerA and TimerB interrupt disable + ifne VBLANK_MUSIC + moveq #$02,d0 ; TimerB only for VBLANK_MUSIC + else + moveq #$03,d0 + endc + jsr _LVOAbleICR(a6) + + moveq #CIAICRB_TB,d0 + lea mt_extern_timer_b_is(pc),a1 + jsr _LVORemICRVector(a6) + + ifeq VBLANK_MUSIC + moveq #CIAICRB_TA,d0 + lea mt_extern_timer_a_is(pc),a1 + jsr _LVORemICRVector(a6) + endc ; !VBLANK_MUSIC +.nociares: + endc ; !NO_TIMERS + + ; close audio.device, free channels + move.l 4.w,a6 + lea mt_ioaudio(pc),a1 + jsr _LVOCloseDevice(a6) + moveq #0,d0 + move.b mt_ioport+15(pc),d0 + jsr _LVOFreeSignal(a6) + + move.l (sp)+,a6 + rts + + + ifeq NO_TIMERS + ifeq VBLANK_MUSIC +;--------------------------------------------------------------------------- +mt_cia_timer_a_code: +; TimerA interrupt calls mt_music at a selectable tempo (Fxx command), +; which defaults to 50 times per second. + + movem.l d2-d7/a2-a6,-(sp) + lea CUSTOM,a6 + ifd SDATA + lea _LinkerDB,a4 + else + lea mt_data(pc),a4 + endc + + ; do music when enabled + tst.b mt_Enable(a4) + ifeq MINIMAL + beq .2 + else + beq .1 + endc + + bsr mt_music ; music with sfx inserted +.1: movem.l (sp)+,d2-d7/a2-a6 + rts + + ifeq MINIMAL +.2: bsr mt_sfxonly ; no music, only sfx + movem.l (sp)+,d2-d7/a2-a6 + rts + endc + endc ; !VBLANK_MUSIC + + +;--------------------------------------------------------------------------- +mt_cia_timer_b_code: +; Handle one-shot TimerB interrupt. +; TB_toggle-technique suggested by Ross/EAB. + + lea TB_toggle(pc),a0 + not.b (a0) + lea CUSTOM+INTREQ,a0 + beq mt_TimerBsetrep + + ; restart timer for repeat, enable audio DMA after DMADELAY ticks + move.b #$19,CIAB+CIACRB + move.w mt_dmaon(pc),DMACON-INTREQ(a0) + rts + +TB_toggle: + dc.b 0 + even + + +mt_TimerBsetrep: +; Oneshot TimerB interrupt to set repeat samples after another DMADELAY ticks. + + move.l a4,-(sp) + + ifd SDATA + lea _LinkerDB,a4 + else + lea mt_data(pc),a4 + endc + + ; clear possible audio interrupt flags + moveq #0,d0 + or.b mt_dmaon+1(pc),d0 + lsl.w #7,d0 + move.w d0,(a0) + + ; set repeat sample pointers and lengths + ifne MINIMAL + move.l mt_chan1+n_loopstart(a4),AUD0LC-INTREQ(a0) + move.w mt_chan1+n_replen(a4),AUD0LEN-INTREQ(a0) + move.l mt_chan2+n_loopstart(a4),AUD1LC-INTREQ(a0) + move.w mt_chan2+n_replen(a4),AUD1LEN-INTREQ(a0) + move.l mt_chan3+n_loopstart(a4),AUD2LC-INTREQ(a0) + move.w mt_chan3+n_replen(a4),AUD2LEN-INTREQ(a0) + move.l mt_chan4+n_loopstart(a4),AUD3LC-INTREQ(a0) + move.w mt_chan4+n_replen(a4),AUD3LEN-INTREQ(a0) + + else ; !MINIMAL + tst.b mt_chan1+n_enable(a4) + beq .1 + move.l mt_chan1+n_loopstart(a4),AUD0LC-INTREQ(a0) + move.w mt_chan1+n_replen(a4),AUD0LEN-INTREQ(a0) +.1: tst.b mt_chan2+n_enable(a4) + beq .2 + move.l mt_chan2+n_loopstart(a4),AUD1LC-INTREQ(a0) + move.w mt_chan2+n_replen(a4),AUD1LEN-INTREQ(a0) +.2: tst.b mt_chan3+n_enable(a4) + beq .3 + move.l mt_chan3+n_loopstart(a4),AUD2LC-INTREQ(a0) + move.w mt_chan3+n_replen(a4),AUD2LEN-INTREQ(a0) +.3: tst.b mt_chan4+n_enable(a4) + beq .4 + move.l mt_chan4+n_loopstart(a4),AUD3LC-INTREQ(a0) + move.w mt_chan4+n_replen(a4),AUD3LEN-INTREQ(a0) +.4: + endc + + move.l (sp)+,a4 + rts + endc ; !NO_TIMERS + + + else !OSCOMPAT +;--------------------------------------------------------------------------- + xdef _mt_install + xdef _mt_install_cia +_mt_install: +_mt_install_cia: +; Install a CIA-B interrupt for calling mt_music. +; a6 = CUSTOM +; a0 = VectorBase +; d0 = PALflag.b (0 is NTSC) + + ifnd SDATA + move.l a4,-(sp) + lea mt_data(pc),a4 + endc + + clr.b mt_Enable(a4) + + ifeq NO_TIMERS + ; remember level 6 vector and interrupt enable + lea $78(a0),a0 + move.l a0,mt_Lev6Int(a4) + move.w #$2000,d1 + and.w INTENAR(a6),d1 + or.w #$8000,d1 + move.w d1,mt_Lev6Ena(a4) + + ; disable level 6 EXTER interrupts, set player interrupt vector + move.w #$2000,INTENA(a6) + move.l (a0),mt_oldLev6(a4) + lea mt_TimerAInt(pc),a1 + move.l a1,(a0) + + ; reset TimerB toggle + lea TB_toggle(pc),a0 + clr.b (a0) + + ; disable CIA-B interrupts, stop and save all timers + lea CIAB,a0 + moveq #0,d1 + move.b #$7f,CIAICR(a0) + move.b d1,CIACRA(a0) + move.b d1,CIACRB(a0) + lea mt_oldtimers(a4),a1 + move.b CIATALO(a0),(a1)+ + move.b CIATAHI(a0),(a1)+ + move.b CIATBLO(a0),(a1)+ + move.b CIATBHI(a0),(a1) + + ifeq VBLANK_MUSIC + ; determine if 02 clock for timers is based on PAL or NTSC + tst.b d0 + bne .1 + move.l #1789773,d0 ; NTSC + bra .2 +.1: move.l #1773447,d0 ; PAL +.2: move.l d0,mt_timerval(a4) + + ; load TimerA in continuous mode for the default tempo of 125 + divu #125,d0 + move.b d0,CIATALO(a0) + lsr.w #8,d0 + move.b d0,CIATAHI(a0) + move.b #$11,CIACRA(a0) ; load timer, start continuous + endc ; !VBLANK_MUSIC + + ; load TimerB with DMADELAY ticks for setting DMA and repeat + move.b #DMADELAY&255,CIATBLO(a0) + move.b #DMADELAY>>8,CIATBHI(a0) + + ; Ack. pending interrupts, TimerA and TimerB interrupt enable + tst.b CIAICR(a0) + move.w #$2000,INTREQ(a6) + ifne VBLANK_MUSIC + move.b #$82,CIAICR(a0) ; TimerB only for VBLANK_MUSIC + else + move.b #$83,CIAICR(a0) + endc + + ; enable level 6 interrupts + move.w #$a000,INTENA(a6) + endc ; !NO_TIMERS + + bra mt_reset + + +;--------------------------------------------------------------------------- + xdef _mt_remove_cia + xdef _mt_remove +_mt_remove_cia: +_mt_remove: +; Remove CIA-B music interrupt and restore the old vector. +; a6 = CUSTOM + + ifeq NO_TIMERS + ifnd SDATA + move.l a4,-(sp) + lea mt_data(pc),a4 + endc + + ; disable level 6 and CIA-B interrupts + lea CIAB,a0 + move.w #$2000,d0 + move.b #$7f,CIAICR(a0) + move.w d0,INTENA(a6) + tst.b CIAICR(a0) + move.w d0,INTREQ(a6) + + ; restore old timer values + lea mt_oldtimers(a4),a1 + move.b (a1)+,CIATALO(a0) + move.b (a1)+,CIATAHI(a0) + move.b (a1)+,CIATBLO(a0) + move.b (a1),CIATBHI(a0) + move.b #$10,CIACRA(a0) + move.b #$10,CIACRB(a0) + + ; restore original level 6 interrupt vector + move.l mt_Lev6Int(a4),a1 + move.l mt_oldLev6(a4),(a1) + + ; reenable CIA-B ALRM interrupt, which was set by AmigaOS + move.b #$84,CIAICR(a0) + + ; reenable previous level 6 interrupt + move.w mt_Lev6Ena(a4),INTENA(a6) + + ifnd SDATA + move.l (sp)+,a4 + endc + endc ; !NO_TIMERS + rts + + + ifeq NO_TIMERS +;--------------------------------------------------------------------------- +mt_TimerAInt: +; TimerA interrupt calls mt_music at a selectable tempo (Fxx command), +; which defaults to 50 times per second. + + ; check for TB interrupt and clear CIAB interrupt flags + btst #1,CIAB+CIAICR + bne mt_TimerBInt + + ifne VBLANK_MUSIC + move.w #$2000,CUSTOM+INTREQ ; ack interrupt and leave + nop + rte + + else + ; Now it should be a TA interrupt. + ; Other level 6 interrupt sources have to be handled elsewhere. + movem.l d0-d7/a0-a6,-(sp) + lea CUSTOM,a6 + ifd SDATA + lea _LinkerDB,a4 + else + lea mt_data(pc),a4 + endc + + ; clear EXTER interrupt flag + move.w #$2000,INTREQ(a6) + + ; do music when enabled + tst.b mt_Enable(a4) + ifeq MINIMAL + beq .2 + else + beq .1 + endc + + bsr mt_music ; music with sfx inserted +.1: movem.l (sp)+,d0-d7/a0-a6 + nop + rte + + ifeq MINIMAL +.2: bsr mt_sfxonly ; no music, only sfx + movem.l (sp)+,d0-d7/a0-a6 + nop + rte + endc + endc ; !VBLANK_MUSIC + + +;--------------------------------------------------------------------------- +mt_TimerBInt: +; Handle one-shot TimerB interrupt. +; TB_toggle-technique suggested by Ross/EAB. + + move.l a0,-(sp) + lea TB_toggle(pc),a0 + not.b (a0) + lea CUSTOM+INTREQ,a0 + beq mt_TimerBsetrep + + ; restart timer for repeat, enable audio DMA after DMADELAY ticks + move.w #$2000,(a0) ; clear EXTER interrupt flag + move.b #$19,CIAB+CIACRB + move.w mt_dmaon(pc),DMACON-INTREQ(a0) + + move.l (sp)+,a0 + nop + rte + + +mt_TimerBsetrep: +; Oneshot TimerB interrupt to set repeat samples after another DMADELAY ticks. + + ; clear EXTER and possible audio interrupt flags + move.l a4,-(sp) + move.l d0,a4 + moveq #$2000>>7,d0 ; EXTER-flag + or.b mt_dmaon+1(pc),d0 + lsl.w #7,d0 + move.w d0,(a0) + move.l a4,d0 + + ifd SDATA + lea _LinkerDB,a4 + else + lea mt_data(pc),a4 + endc + + ; set repeat sample pointers and lengths + ifne MINIMAL + move.l mt_chan1+n_loopstart(a4),AUD0LC-INTREQ(a0) + move.w mt_chan1+n_replen(a4),AUD0LEN-INTREQ(a0) + move.l mt_chan2+n_loopstart(a4),AUD1LC-INTREQ(a0) + move.w mt_chan2+n_replen(a4),AUD1LEN-INTREQ(a0) + move.l mt_chan3+n_loopstart(a4),AUD2LC-INTREQ(a0) + move.w mt_chan3+n_replen(a4),AUD2LEN-INTREQ(a0) + move.l mt_chan4+n_loopstart(a4),AUD3LC-INTREQ(a0) + move.w mt_chan4+n_replen(a4),AUD3LEN-INTREQ(a0) + + else ; !MINIMAL + tst.b mt_chan1+n_enable(a4) + beq .1 + move.l mt_chan1+n_loopstart(a4),AUD0LC-INTREQ(a0) + move.w mt_chan1+n_replen(a4),AUD0LEN-INTREQ(a0) +.1: tst.b mt_chan2+n_enable(a4) + beq .2 + move.l mt_chan2+n_loopstart(a4),AUD1LC-INTREQ(a0) + move.w mt_chan2+n_replen(a4),AUD1LEN-INTREQ(a0) +.2: tst.b mt_chan3+n_enable(a4) + beq .3 + move.l mt_chan3+n_loopstart(a4),AUD2LC-INTREQ(a0) + move.w mt_chan3+n_replen(a4),AUD2LEN-INTREQ(a0) +.3: tst.b mt_chan4+n_enable(a4) + beq .4 + move.l mt_chan4+n_loopstart(a4),AUD3LC-INTREQ(a0) + move.w mt_chan4+n_replen(a4),AUD3LEN-INTREQ(a0) +.4: + endc + + move.l (sp)+,a4 + move.l (sp)+,a0 + nop + rte + +TB_toggle: + dc.b 0 + even + endc ; !NO_TIMERS + endc ; !OSCOMPAT + + + ifne NO_TIMERS +;--------------------------------------------------------------------------- + xdef _mt_dmaon +_mt_dmaon: +; Enable audio DMA after some delay. +; Called by main program, for example out of a copper-interrupt. + + move.w mt_dmaon(pc),CUSTOM+DMACON + rts + + + xdef _mt_setrep +_mt_setrep: +; Set repeat sample pointers and lengths. +; Called by main program, for example out of a copper-interrupt. + + movem.l a0/a4,-(sp) + + ; reset audio interrupt flags + lea CUSTOM+INTREQ,a0 + move.l d0,a4 + move.w mt_dmaon(pc),d0 + lsl.w #7,d0 + move.w d0,(a0) + move.l a4,d0 + + ifd SDATA + lea _LinkerDB,a4 + else + lea mt_data(pc),a4 + endc + + ; set repeat sample pointers and lengths + ifne MINIMAL + move.l mt_chan1+n_loopstart(a4),AUD0LC-INTREQ(a0) + move.w mt_chan1+n_replen(a4),AUD0LEN-INTREQ(a0) + move.l mt_chan2+n_loopstart(a4),AUD1LC-INTREQ(a0) + move.w mt_chan2+n_replen(a4),AUD1LEN-INTREQ(a0) + move.l mt_chan3+n_loopstart(a4),AUD2LC-INTREQ(a0) + move.w mt_chan3+n_replen(a4),AUD2LEN-INTREQ(a0) + move.l mt_chan4+n_loopstart(a4),AUD3LC-INTREQ(a0) + move.w mt_chan4+n_replen(a4),AUD3LEN-INTREQ(a0) + + else ; !MINIMAL + tst.b mt_chan1+n_enable(a4) + beq .1 + move.l mt_chan1+n_loopstart(a4),AUD0LC-INTREQ(a0) + move.w mt_chan1+n_replen(a4),AUD0LEN-INTREQ(a0) +.1: tst.b mt_chan2+n_enable(a4) + beq .2 + move.l mt_chan2+n_loopstart(a4),AUD1LC-INTREQ(a0) + move.w mt_chan2+n_replen(a4),AUD1LEN-INTREQ(a0) +.2: tst.b mt_chan3+n_enable(a4) + beq .3 + move.l mt_chan3+n_loopstart(a4),AUD2LC-INTREQ(a0) + move.w mt_chan3+n_replen(a4),AUD2LEN-INTREQ(a0) +.3: tst.b mt_chan4+n_enable(a4) + beq .4 + move.l mt_chan4+n_loopstart(a4),AUD3LC-INTREQ(a0) + move.w mt_chan4+n_replen(a4),AUD3LEN-INTREQ(a0) +.4: + endc + + movem.l (sp)+,a0/a4 + rts + endc ; NO_TIMERS + +mt_dmaon: + dc.w $8000 + + +;--------------------------------------------------------------------------- + xdef _mt_init +_mt_init: +; Initialize new module. +; Reset speed to 6, tempo to 125 and start at given song position. +; Master volume is at 64 (maximum). +; a6 = CUSTOM +; a0 = module pointer +; a1 = sample pointer (NULL means samples are stored within the module) +; d0 = initial song position + + ifnd SDATA + move.l a4,-(sp) + lea mt_data(pc),a4 + endc + + move.l a0,mt_mod(a4) + movem.l d2/a2,-(sp) + + ; set initial song position + cmp.b 950(a0),d0 + blo .1 + moveq #0,d0 +.1: move.b d0,mt_SongPos(a4) + + move.l a1,d0 ; sample data location is given? + bne .4 + + ; get number of highest pattern + lea 952(a0),a1 ; song arrangement list + moveq #127,d0 + moveq #0,d2 +.2: move.b (a1)+,d1 + cmp.b d2,d1 + bls .3 + move.b d1,d2 +.3: dbf d0,.2 + addq.b #1,d2 ; number of patterns + + ; now we can calculate the base address of the sample data + moveq #10,d0 + asl.l d0,d2 + lea (a0,d2.l),a1 + add.w #1084,a1 + + ; save start address of each sample and do some fixes for broken mods +.4: lea mt_SampleStarts(a4),a2 + moveq #1,d2 + moveq #31-1,d0 +.5: move.l a1,(a2)+ + moveq #0,d1 + move.w 42(a0),d1 + cmp.w d2,d1 ; length 0 and 1 define unused samples + bls .6 + add.l d1,d1 + add.l d1,a1 + bra .7 +.6: clr.w 42(a0) ; length 1 means zero -> no sample +.7: lea 30(a0),a0 + dbf d0,.5 + + movem.l (sp)+,d2/a2 + + ifeq VBLANK_MUSIC + ; reset CIA timer A to default (125) + move.l mt_timerval(a4),d0 + divu #125,d0 + move.b d0,CIAB+CIATALO + lsr.w #8,d0 + move.b d0,CIAB+CIATAHI + endc + +mt_reset: +; a4 must be initialized with base register + + ; reset speed and counters + move.b #6,mt_Speed(a4) + clr.b mt_Counter(a4) + clr.w mt_PatternPos(a4) + clr.b mt_PattDelTime(a4) + clr.b mt_PattDelTime2(a4) + clr.w mt_PBreakPos(a4) + clr.b mt_PBreakFlag(a4) + clr.b mt_PosJumpFlag(a4) + + ; disable the filter + or.b #2,CIAA+CIAPRA + + ifeq MINIMAL + ; set master volume to 64 + lea MasterVolTab64(pc),a0 + move.l a0,mt_MasterVolTab(a4) + endc + + ; set channel index + clr.b mt_chan1+n_index(a4) + move.b #1,mt_chan2+n_index(a4) + move.b #2,mt_chan3+n_index(a4) + move.b #3,mt_chan4+n_index(a4) + + ; initialize channel DMA and interrupt bits + move.w #$0001,mt_chan1+n_dmabit(a4) + move.w #$0002,mt_chan2+n_dmabit(a4) + move.w #$0004,mt_chan3+n_dmabit(a4) + move.w #$0008,mt_chan4+n_dmabit(a4) + move.w #$0080,mt_chan1+n_intbit(a4) + move.w #$0100,mt_chan2+n_intbit(a4) + move.w #$0200,mt_chan3+n_intbit(a4) + move.w #$0400,mt_chan4+n_intbit(a4) + + clr.b mt_E8Trigger(a4) + ifeq MINIMAL + clr.b mt_SongEnd(a4) + clr.b mt_SilCntValid(a4) + endc + + ifnd SDATA + move.l (sp)+,a4 + endc + + +;--------------------------------------------------------------------------- + xdef _mt_end +_mt_end: +; Stop playing current module. +; a6 = CUSTOM + + DISABLE + ifd SDATA + clr.b mt_Enable(a4) + lea mt_chan1(a4),a0 + bsr resetch + lea mt_chan2(a4),a0 + bsr resetch + lea mt_chan3(a4),a0 + bsr resetch + lea mt_chan4(a4),a0 + bsr resetch + else + lea mt_data(pc),a1 + clr.b mt_Enable(a1) + lea mt_chan1(a1),a0 + bsr resetch + lea mt_chan2(a1),a0 + bsr resetch + lea mt_chan3(a1),a0 + bsr resetch + lea mt_chan4(a1),a0 + bsr resetch + endc + moveq #0,d0 + move.w d0,AUD0VOL(a6) + move.w d0,AUD1VOL(a6) + move.w d0,AUD2VOL(a6) + move.w d0,AUD3VOL(a6) + move.w #$000f,DMACON(a6) + ENABLE + rts + + +resetch: +; a0 = channel status +; All registers are preserved! + + move.w #320,n_period(a0) ; make sure period is not illegal + clr.w n_volume(a0) + clr.w n_sfxlen(a0) + clr.w n_funk(a0) + clr.b n_sfxpri(a0) + clr.b n_looped(a0) + clr.b n_gliss(a0) + ifeq MINIMAL + clr.b n_musiconly(a0) + st n_enable(a0) + endc + rts + + + ifeq MINIMAL +;--------------------------------------------------------------------------- + xdef _mt_soundfx +_mt_soundfx: +; Request playing of an external sound effect on the most unused channel. +; This function is for compatibility with the old API only! +; You should call _mt_playfx instead! +; a6 = CUSTOM +; a0 = sample pointer +; d0.w = sample length in words +; d1.w = sample period +; d2.w = sample volume + + lea -sfx_sizeof(sp),sp + move.l a0,sfx_ptr(sp) + movem.w d0-d2,sfx_len(sp) + move.w #$ff01,sfx_cha(sp) ; any channel, priority=1 + move.l sp,a0 + bsr _mt_playfx + lea sfx_sizeof(sp),sp + rts + + +;--------------------------------------------------------------------------- + xdef _mt_playfx +_mt_playfx: +; Request playing of a prioritized external sound effect, either on a +; fixed channel or on the most unused one. +; A negative channel specification means to use the best one. +; The priority is unsigned and should be greater than zero. +; This channel will be blocked for music until the effect has finished. +; a6 = CUSTOM +; a0 = sfx-structure pointer with the following layout: +; 0: ptr, 4: len.w, 6: period.w, 8: vol.w, 10: channel.b, 11: priority.b +; -> d0 = pointer to channel status or NULL when sample was ignored + + ifd SDATA + movem.l d2-d7/a0-a3/a5,-(sp) + else + movem.l d2-d7/a0-a5,-(sp) + lea mt_data(pc),a4 + endc + + DISABLE + moveq #0,d0 + move.b sfx_cha(a0),d0 + bpl channelsfx ; use fixed channel for effect + + ; Did we already calculate the n_freecnt values for all channels? + tst.b mt_SilCntValid(a4) + bne freecnt_valid + + ; Look at the next 8 pattern steps to find the longest sequence + ; of silence (no new note or instrument). + moveq #8,d2 + move.l #$fffff000,d3 ; mask to ignore effects + + ; reset freecnts for all channels + moveq #0,d0 + move.b d0,mt_chan1+n_freecnt(a4) + move.b d0,mt_chan2+n_freecnt(a4) + move.b d0,mt_chan3+n_freecnt(a4) + move.b d0,mt_chan4+n_freecnt(a4) + moveq #-1,d4 + moveq #-1,d5 + moveq #-1,d6 + moveq #-1,d7 + + ; get pattern pointer + move.l mt_mod(a4),a3 ; a3 mod pointer + lea 952(a3),a5 ; a5 song arrangement list + move.w mt_PatternPos(a4),d1 + move.b mt_SongPos(a4),d0 +.1: move.b (a5,d0.w),d0 + swap d0 + lea 1084(a3),a1 + lsr.l #6,d0 + add.l d0,a1 + lea 1024(a1),a2 ; a2 end of pattern + add.w d1,a1 ; a1 current pattern pos + +.2: moveq #0,d0 + + move.l (a1)+,d1 + and.l d3,d1 + seq d1 + and.b d1,d4 + sub.b d4,mt_chan1+n_freecnt(a4) + add.b d4,d0 + + move.l (a1)+,d1 + and.l d3,d1 + seq d1 + and.b d1,d5 + sub.b d5,mt_chan2+n_freecnt(a4) + add.b d5,d0 + + move.l (a1)+,d1 + and.l d3,d1 + seq d1 + and.b d1,d6 + sub.b d6,mt_chan3+n_freecnt(a4) + add.b d6,d0 + + move.l (a1)+,d1 + and.l d3,d1 + seq d1 + and.b d1,d7 + sub.b d7,mt_chan4+n_freecnt(a4) + add.b d7,d0 + + ; break the loop when no channel has any more free pattern steps + beq .7 + + ; otherwise break after 8 pattern steps + subq.w #1,d2 + beq .7 + + ; End of pattern reached? Then load next pattern pointer. + cmp.l a2,a1 + blo .2 + moveq #0,d1 + moveq #1,d0 + add.b mt_SongPos(a4),d0 + and.w #$007f,d0 + cmp.b 950(a3),d0 ; end of song reached? + blo .1 + moveq #0,d0 + bra .1 + +.7: st mt_SilCntValid(a4) + +freecnt_valid: + sub.l a2,a2 + move.b sfx_pri(a0),d2 + + ; Determine which channels are already allocated for sound + ; effects and check if the limit was reached. In this case only + ; replace sound effect channels by higher priority. + moveq #3,d0 + sub.b mt_MusicChannels(a4),d0 + move.b mt_chan1+n_sfxpri(a4),d4 + or.b mt_chan1+n_musiconly(a4),d4 + sne d1 + add.b d1,d0 + move.b mt_chan2+n_sfxpri(a4),d5 + or.b mt_chan2+n_musiconly(a4),d5 + sne d1 + add.b d1,d0 + move.b mt_chan3+n_sfxpri(a4),d6 + or.b mt_chan3+n_musiconly(a4),d6 + sne d1 + add.b d1,d0 + move.b mt_chan4+n_sfxpri(a4),d7 + or.b mt_chan4+n_musiconly(a4),d7 + sne d1 + add.b d1,d0 + bmi .overwrite ; all channels reserved/playing effects + + ; We will prefer a music channel which had an audio interrupt, + ; because that means the last instrument sample has been played + ; completely, and the channel is now in an idle loop. + ; Also exclude channels which have set a repeat loop. + ; Try not to break them! + moveq #0,d3 + tst.b mt_chan1+n_looped(a4) + bne .1 + or.w #$0080,d3 +.1: tst.b mt_chan2+n_looped(a4) + bne .2 + or.w #$0100,d3 +.2: tst.b mt_chan3+n_looped(a4) + bne .3 + or.w #$0200,d3 +.3: tst.b mt_chan4+n_looped(a4) + bne .4 + or.w #$0400,d3 +.4: move.w INTREQR(a6),d1 + and.w d3,d1 + bne .6 + + ; All channels are busy. Then break the non-looped ones first... + move.w d3,d1 + bne .6 + + ; ..except there are none. Then it doesn't matter. :| + move.w #$0780,d1 + + ; first look for the best unused channel +.6: moveq #0,d3 + btst #7,d1 + seq d0 + or.b d4,d0 + bne .7 + lea mt_chan1+n_freecnt(a4),a1 + cmp.b (a1),d3 + bhi .7 + move.l a1,a2 + move.b (a1),d3 +.7: btst #8,d1 + seq d0 + or.b d5,d0 + bne .8 + lea mt_chan2+n_freecnt(a4),a1 + cmp.b (a1),d3 + bhi .8 + move.l a1,a2 + move.b (a1),d3 +.8: btst #9,d1 + seq d0 + or.b d6,d0 + bne .9 + lea mt_chan3+n_freecnt(a4),a1 + cmp.b (a1),d3 + bhi .9 + move.l a1,a2 + move.b (a1),d3 +.9: btst #10,d1 + seq d0 + or.b d7,d0 + bne .10 + lea mt_chan4+n_freecnt(a4),a1 + cmp.b (a1),d3 + bhi .10 + move.l a1,a2 + bra found_sfx_ch + +.10: move.l a2,d3 + bne found_sfx_ch + +.overwrite: + ; finally try to overwrite a sound effect with lower/equal priority + moveq #0,d3 + tst.b d4 + beq .11 + cmp.b d4,d2 + blo .11 + lea mt_chan1+n_freecnt(a4),a2 + move.b (a2),d3 +.11: tst.b d5 + beq .12 + cmp.b d5,d2 + blo .12 + lea mt_chan2+n_freecnt(a4),a1 + cmp.b (a1),d3 + bhi .12 + move.l a1,a2 + move.b (a1),d3 +.12: tst.b d6 + beq .13 + cmp.b d6,d2 + blo .13 + lea mt_chan3+n_freecnt(a4),a1 + cmp.b (a1),d3 + bhi .13 + move.l a1,a2 + move.b (a1),d3 +.13: tst.b d7 + beq .14 + cmp.b d7,d2 + blo .14 + lea mt_chan4+n_freecnt(a4),a1 + cmp.b (a1),d3 + bhi .14 + move.l a1,a2 + +.14: move.l a2,d3 + beq exit_playfx ; ignore new sfx due to low priority + +found_sfx_ch: + lea -n_freecnt(a2),a2 + bra set_sfx + +channelsfx: +; a0 = sfx structure +; d0 = fixed channel for new sound effect + add.w d0,d0 + lea mt_chan1(a4),a2 + add.w channel_offsets(pc,d0.w),a2 + + ; priority high enough to replace a present effect on this channel? + move.b sfx_pri(a0),d2 + cmp.b n_sfxpri(a2),d2 + bhs set_sfx + sub.l a2,a2 + bra exit_playfx + +set_sfx: +; activate the sound effect on this channel +; a0 = sfx structure +; d2 = sfx priority +; a2 = channel status + move.l (a0)+,n_sfxptr(a2) ; sfx_ptr + move.w (a0)+,n_sfxlen(a2) ; sfx_len + move.w (a0)+,n_sfxper(a2) ; sfx_per + move.w (a0),n_sfxvol(a2) ; sfx_vol + move.b d2,n_sfxpri(a2) + +exit_playfx: + ENABLE + move.l a2,d0 ; ptr to selected channel or NULL + + ifd SDATA + movem.l (sp)+,d2-d7/a0-a3/a5 + else + movem.l (sp)+,d2-d7/a0-a5 + endc + rts + +channel_offsets: + dc.w 0*n_sizeof,1*n_sizeof,2*n_sizeof,3*n_sizeof + + +;--------------------------------------------------------------------------- + xdef _mt_loopfx +_mt_loopfx: +; Request playing of a looped sound effect on a fixed channel, which +; will be blocked for music until the effect is stopped (_mt_stopfx). +; It uses the same sfx-structure as _mt_playfx, but the priority is +; ignored. A looped sound effect has always highest priority and will +; replace a previous effect on the same channel. No automatic channel +; selection possible! +; Also make sure the sample starts with a zero-word, which is used +; for idling when the effect is stopped. This word is included in the +; total length calculation, but excluded when actually playing the loop. +; a6 = CUSTOM +; a0 = sfx-structure pointer with the following layout: +; 0: ptr, 4: len.w, 6: period.w, 8: vol.w, 10: channel.b + + ifnd SDATA + lea mt_data+mt_chan1(pc),a1 + else + lea mt_chan1(a4),a1 + endc + + moveq #3,d0 + and.b sfx_cha(a0),d0 + add.w d0,d0 + add.w channel_offsets(pc,d0.w),a1 + + DISABLE + move.l (a0)+,n_sfxptr(a1) ; sfx_ptr + move.w (a0)+,n_sfxlen(a1) ; sfx_len + move.w (a0)+,n_sfxper(a1) ; sfx_per + move.w (a0),n_sfxvol(a1) ; sfx_vol + st n_sfxpri(a1) ; sfx_pri -1 enables looped mode + ENABLE + rts + + +;--------------------------------------------------------------------------- + xdef _mt_stopfx +_mt_stopfx: +; Immediately stop a currently playing sound effect on a channel. +; a6 = CUSTOM +; d0.b = channel (0..3) + + ifnd SDATA + lea mt_data+mt_chan1(pc),a0 + else + lea mt_chan1(a4),a0 + endc + + and.w #3,d0 + add.w d0,d0 + add.w channel_offsets(pc,d0.w),a0 + + DISABLE + tst.b n_sfxpri(a0) + beq .1 ; no sfx playing anyway + moveq #1,d0 + move.b d0,n_sfxpri(a0) + move.w d0,n_sfxlen(a0) ; idle loop + move.w #108,n_sfxper(a0) ; enter idle as quickly as possible + clr.w n_sfxvol(a0) ; and cut volume + ifne NULL_IS_CLEARED + clr.b n_looped(a0) + clr.l n_sfxptr(a0) ; use $0 for idle-looping + else + tst.b n_looped(a0) + beq .1 + clr.b n_looped(a0) + subq.l #2,n_sfxptr(a0) ; idle loop at sample-start - 2 + endc +.1: ENABLE + + rts + + +;--------------------------------------------------------------------------- + xdef _mt_musicmask +_mt_musicmask: +; Set bits in the mask define which specific channels are reserved +; for music only. +; a6 = CUSTOM +; d0.b = channel-mask (bit 0 for channel 0, ..., bit 3 for channel 3) + + ifnd SDATA + move.l a4,-(sp) + lea mt_data(pc),a4 + endc + + DISABLE + + lsl.b #5,d0 + scs mt_chan4+n_musiconly(a4) + add.b d0,d0 + scs mt_chan3+n_musiconly(a4) + add.b d0,d0 + scs mt_chan2+n_musiconly(a4) + add.b d0,d0 + scs mt_chan1+n_musiconly(a4) + + ENABLE + + ifnd SDATA + move.l (sp)+,a4 + endc + rts + + +;--------------------------------------------------------------------------- + xdef _mt_mastervol +_mt_mastervol: +; Set a master volume from 0 to 64 for all music channels. +; Note that the master volume does not affect the volume of external +; sound effects (which is desired). +; a6 = CUSTOM +; d0.w = master volume + + ifnd SDATA + move.l a4,-(sp) + lea mt_data(pc),a4 + endc + + ; stingray, since each volume table has a size of 65 bytes + ; we simply multiply (optimised of course) by 65 to get the + ; offset to the correct table + lea MasterVolTab0(pc),a0 + add.w d0,a0 + lsl.w #6,d0 + add.w d0,a0 + + ; set new table and adapt all channel volumes immediately + DISABLE + move.l a0,mt_MasterVolTab(a4) + bsr set_all_volumes + ENABLE + + ifnd SDATA + move.l (sp)+,a4 + endc + rts + + +;--------------------------------------------------------------------------- + xdef _mt_channelmask +_mt_channelmask: +; Define which music channels are muted. Doesn't affect sound effects. +; a6 = CUSTOM +; d0.b = channel-mask (bit 0 for channel 0, ..., bit 3 for channel 3) + + ifnd SDATA + move.l a4,-(sp) + lea mt_data(pc),a4 + endc + + DISABLE + + lsl.b #5,d0 + scs mt_chan4+n_enable(a4) + add.b d0,d0 + scs mt_chan3+n_enable(a4) + add.b d0,d0 + scs mt_chan2+n_enable(a4) + add.b d0,d0 + scs mt_chan1+n_enable(a4) + move.l mt_MasterVolTab(a4),a0 + bsr set_all_volumes + + ENABLE + + ifnd SDATA + move.l (sp)+,a4 + endc + rts + + +;--------------------------------------------------------------------------- +set_all_volumes: +; a0 = Master volume table + + tst.b mt_chan1+n_sfxpri(a4) + bne .1 + move.w mt_chan1+n_volume(a4),d0 + move.b (a0,d0.w),d0 + and.b mt_chan1+n_enable(a4),d0 + move.w d0,AUD0VOL(a6) + ifd VOL0_UNLOOPS + bne .1 + clr.b mt_chan1+n_looped(a4) + endc +.1: tst.b mt_chan2+n_sfxpri(a4) + bne .2 + move.w mt_chan2+n_volume(a4),d0 + move.b (a0,d0.w),d0 + and.b mt_chan2+n_enable(a4),d0 + move.w d0,AUD1VOL(a6) + ifd VOL0_UNLOOPS + bne .2 + clr.b mt_chan2+n_looped(a4) + endc +.2: tst.b mt_chan3+n_sfxpri(a4) + bne .3 + move.w mt_chan3+n_volume(a4),d0 + move.b (a0,d0.w),d0 + and.b mt_chan3+n_enable(a4),d0 + move.w d0,AUD2VOL(a6) + ifd VOL0_UNLOOPS + bne .3 + clr.b mt_chan3+n_looped(a4) + endc +.3: tst.b mt_chan4+n_sfxpri(a4) + bne .4 + move.w mt_chan4+n_volume(a4),d0 + move.b (a0,d0.w),d0 + and.b mt_chan4+n_enable(a4),d0 + move.w d0,AUD3VOL(a6) + ifd VOL0_UNLOOPS + bne .4 + clr.b mt_chan4+n_looped(a4) + endc +.4: rts + + +;--------------------------------------------------------------------------- + xdef _mt_samplevol +_mt_samplevol: +; Redefine a sample's volume. May also be done while the song is playing. +; Warning: Does not check arguments for valid range! You must have done +; _mt_init before calling this function! +; The new volume is persistent. Even when the song is restarted. +; d0.w = sample number (0-31) +; d1.b = volume (0-64) + + ifd SDATA + move.l mt_mod(a4),a0 + else + move.l mt_data+mt_mod(pc),a0 + endc + + swap d1 + move.w d0,d1 + add.w d1,d1 + lsl.w #5,d0 + sub.w d1,d0 ; table index: sample number * 30 + swap d1 + move.b d1,12+3(a0,d0.w) ; set sample's volume + rts + endc ; !MINIMAL + + +;--------------------------------------------------------------------------- + xdef _mt_music +_mt_music: +; Used when called by external interrupt handler. +; Saves/restores all registers and sets up A4 when needed. +; a6 = CUSTOM + + ifd SDATA + movem.l d2-d7/a2-a3/a5,-(sp) + bsr mt_music + movem.l (sp)+,d2-d7/a2-a3/a5 + else ; !SDATA + movem.l d2-d7/a2-a5,-(sp) + lea mt_data(pc),a4 + bsr mt_music + movem.l (sp)+,d2-d7/a2-a5 + endc + rts + +mt_music: +; Called from interrupt. +; Play next position when Counter equals Speed. +; Effects are always handled. +; a6 = CUSTOM + + moveq #0,d7 ; d7 is always zero + + lea mt_dmaon+1(pc),a0 + move.b d7,(a0) + + addq.b #1,mt_Counter(a4) + + move.b mt_Counter(a4),d0 + cmp.b mt_Speed(a4),d0 + blo no_new_note + + ; handle a new note + move.b d7,mt_Counter(a4) + tst.b mt_PattDelTime2(a4) + beq get_new_note + + ; we have a pattern delay, check effects then step + lea AUD0LC(a6),a5 + lea mt_chan1(a4),a2 + bsr mt_checkfx + lea AUD1LC(a6),a5 + lea mt_chan2(a4),a2 + bsr mt_checkfx + lea AUD2LC(a6),a5 + lea mt_chan3(a4),a2 + bsr mt_checkfx + lea AUD3LC(a6),a5 + lea mt_chan4(a4),a2 + bsr mt_checkfx + bra settb_step + +no_new_note: + ; no new note, just check effects, don't step to next position + lea AUD0LC(a6),a5 + lea mt_chan1(a4),a2 + bsr mt_checkfx + lea AUD1LC(a6),a5 + lea mt_chan2(a4),a2 + bsr mt_checkfx + lea AUD2LC(a6),a5 + lea mt_chan3(a4),a2 + bsr mt_checkfx + lea AUD3LC(a6),a5 + lea mt_chan4(a4),a2 + bsr mt_checkfx + + ifeq NO_TIMERS + ; set one-shot TimerB interrupt for enabling DMA, when needed + move.b mt_dmaon+1(pc),d0 + beq same_pattern + move.b #$19,CIAB+CIACRB ; load/start timer B, one-shot + endc + bra same_pattern + +get_new_note: + ; determine pointer to current pattern line + move.l mt_mod(a4),a0 + lea 12(a0),a3 ; sample info table + lea 1084(a0),a1 ; pattern data + lea 952(a0),a0 + moveq #0,d0 + move.b mt_SongPos(a4),d0 + move.b (a0,d0.w),d0 ; current pattern number + swap d0 + lsr.l #6,d0 + add.l d0,a1 ; pattern base + add.w mt_PatternPos(a4),a1 ; a1 pattern line + + ; play new note for each channel, apply some effects + lea AUD0LC(a6),a5 + lea mt_chan1(a4),a2 + bsr mt_playvoice + lea AUD1LC(a6),a5 + lea mt_chan2(a4),a2 + bsr mt_playvoice + lea AUD2LC(a6),a5 + lea mt_chan3(a4),a2 + bsr mt_playvoice + lea AUD3LC(a6),a5 + lea mt_chan4(a4),a2 + bsr mt_playvoice + +settb_step: + ifeq NO_TIMERS + ; set one-shot TimerB interrupt for enabling DMA, when needed + move.b mt_dmaon+1(pc),d0 + beq pattern_step + move.b #$19,CIAB+CIACRB ; load/start timer B, one-shot + endc + +pattern_step: + ; next pattern line, handle delay and break + ifeq MINIMAL + clr.b mt_SilCntValid(a4) ; recalculate silence counters + endc + moveq #16,d2 ; offset to next pattern line + + move.b mt_PattDelTime2(a4),d1 + move.b mt_PattDelTime(a4),d0 + beq .1 + move.b d0,d1 + move.b d7,mt_PattDelTime(a4) +.1: tst.b d1 + beq .3 + subq.b #1,d1 + beq .2 + moveq #0,d2 ; do not advance to next line +.2: move.b d1,mt_PattDelTime2(a4) + +.3: add.w mt_PatternPos(a4),d2 ; d2 PatternPos + + ; check for break + bclr d7,mt_PBreakFlag(a4) + beq .4 + move.w mt_PBreakPos(a4),d2 + move.w d7,mt_PBreakPos(a4) + + ; check whether end of pattern is reached +.4: move.w d2,mt_PatternPos(a4) + cmp.w #1024,d2 + blo same_pattern + +song_step: + move.w mt_PBreakPos(a4),mt_PatternPos(a4) + move.w d7,mt_PBreakPos(a4) + move.b d7,mt_PosJumpFlag(a4) + + ; next position in song + moveq #1,d0 + add.b mt_SongPos(a4),d0 + and.w #$007f,d0 + move.l mt_mod(a4),a0 + cmp.b 950(a0),d0 ; end of song reached? + blo .1 + moveq #0,d0 ; restart the song from the beginning + ifeq MINIMAL + addq.b #1,mt_SongEnd(a4) + bne .2 + clr.b mt_Enable(a4) ; stop the song when mt_SongEnd was -1 +.2: and.b #$7f,mt_SongEnd(a4) + endc +.1: move.b d0,mt_SongPos(a4) + +same_pattern: + tst.b mt_PosJumpFlag(a4) + bne song_step + + rts + + + ifeq MINIMAL +;--------------------------------------------------------------------------- +mt_sfxonly: +; Called from interrupt. +; Plays sound effects on free channels. +; a6 = CUSTOM + + moveq #0,d7 ; d7 is always zero + + lea mt_dmaon+1(pc),a0 + move.b d7,(a0) + + lea AUD0LC(a6),a5 + lea mt_chan1(a4),a2 + bsr chan_sfx_only + lea AUD1LC(a6),a5 + lea mt_chan2(a4),a2 + bsr chan_sfx_only + lea AUD2LC(a6),a5 + lea mt_chan3(a4),a2 + bsr chan_sfx_only + lea AUD3LC(a6),a5 + lea mt_chan4(a4),a2 + bsr chan_sfx_only + + ifeq NO_TIMERS + move.b mt_dmaon+1(pc),d0 + beq .1 + move.b #$19,CIAB+CIACRB ; load/start timer B, one-shot + endc + +.1: rts + + +chan_sfx_only: +; Check for new sound samples. Check if previous ones are finished. +; a2 = channel data +; a5 = audio registers + + tst.b n_sfxpri(a2) + beq .1 + + move.w n_sfxlen(a2),d0 + bne start_sfx + tst.b n_looped(a2) + bne .1 + + move.w n_intbit(a2),d0 + and.w INTREQR(a6),d0 + beq .1 + move.w n_dmabit(a2),d0 + and.w mt_dmaon(pc),d0 + bne .1 + + ; last sound effect sample has played, so unblock this channel again + move.b d7,n_sfxpri(a2) + +.1: rts + + +;--------------------------------------------------------------------------- +start_sfx: +; d0 = sfx_len in words +; a2 = channel data +; a5 = audio registers + + ; play new sound effect on this channel + move.w n_dmabit(a2),d1 + move.w d1,DMACON(a6) + + move.l n_sfxptr(a2),a0 + tst.b n_sfxpri(a2) + bpl .1 + + ; looped sound effect + st n_looped(a2) + addq.l #2,a0 ; skip first word, used for idling + subq.w #1,d0 + move.l a0,AUDLC(a5) + move.w d0,AUDLEN(a5) + bra .2 + + ; normal sound effect +.1: move.b d7,n_looped(a2) + move.l a0,AUDLC(a5) + move.w d0,AUDLEN(a5) + moveq #1,d0 ; idles after playing once + ifne NULL_IS_CLEARED + sub.l a0,a0 + endc + + ; save repeat and period for TimerB interrupt +.2: move.l a0,n_loopstart(a2) + move.w d0,n_replen(a2) + move.w n_sfxper(a2),d0 + move.w d0,AUDPER(a5) + move.w d0,n_period(a2) + move.w n_sfxvol(a2),AUDVOL(a5) + + move.w d7,n_sfxlen(a2) ; don't call start_sfx again + + lea mt_dmaon(pc),a0 + or.w d1,(a0) ; DMA-channel to enable on TimerB + rts + endc ; !MINIMAL + + +;--------------------------------------------------------------------------- +mt_checkfx: +; a2 = channel data +; a5 = audio registers + + ifeq MINIMAL + tst.b n_sfxpri(a2) + beq .3 + + move.w n_sfxlen(a2),d0 + beq .2 + bsr start_sfx + + ; channel is blocked, only check some E-commands +.1: move.w #$0fff,d4 + and.w n_cmd(a2),d4 + move.w d4,d0 + clr.b d0 + cmp.w #$0e00,d0 + bne mt_nop + and.w #$00ff,d4 + bra blocked_e_cmds + +.2: tst.b n_looped(a2) + bne .1 + move.w n_intbit(a2),d0 + and.w INTREQR(a6),d0 + beq .1 + move.w n_dmabit(a2),d0 + and.w mt_dmaon(pc),d0 + bne .1 + + ; sound effect sample has played, so unblock this channel again + move.b d7,n_sfxpri(a2) + endc ; !MINIMAL + + ; do channel effects between notes +.3: move.w n_funk(a2),d0 + beq .4 + bsr mt_updatefunk + +.4: move.w #$0fff,d4 + and.w n_cmd(a2),d4 + beq mt_pernop + and.w #$00ff,d4 + + moveq #$0f,d0 + and.b n_cmd(a2),d0 + add.w d0,d0 + move.w fx_tab(pc,d0.w),d0 + jmp fx_tab(pc,d0.w) + +fx_tab: + dc.w mt_arpeggio-fx_tab ; $0 + dc.w mt_portaup-fx_tab + dc.w mt_portadown-fx_tab + dc.w mt_toneporta-fx_tab + dc.w mt_vibrato-fx_tab ; $4 + dc.w mt_tonevolslide-fx_tab + dc.w mt_vibrvolslide-fx_tab + dc.w mt_tremolo-fx_tab + dc.w mt_nop-fx_tab ; $8 + dc.w mt_nop-fx_tab + dc.w mt_volumeslide-fx_tab + dc.w mt_nop-fx_tab + dc.w mt_nop-fx_tab ; $C + dc.w mt_nop-fx_tab + dc.w mt_e_cmds-fx_tab + dc.w mt_nop-fx_tab + + +mt_pernop: +; just set the current period + + move.w n_period(a2),AUDPER(a5) +mt_nop: + rts + + +;--------------------------------------------------------------------------- +mt_playvoice: +; a1 = pattern ptr +; a2 = channel data +; a3 = sample info table +; a5 = audio registers + + move.l (a1)+,d6 ; d6 current note/cmd words + + ifeq MINIMAL + ; channel blocked by external sound effect? + tst.b n_sfxpri(a2) + beq .2 + + move.w n_sfxlen(a2),d0 + beq .1 + bsr start_sfx + bra moreblockedfx + + ; do only some limited commands, while sound effect is in progress +.1: tst.b n_looped(a2) + bne moreblockedfx + move.w n_intbit(a2),d0 + and.w INTREQR(a6),d0 + beq moreblockedfx + move.w n_dmabit(a2),d0 + and.w mt_dmaon(pc),d0 + bne moreblockedfx + + ; sound effect sample has played, so unblock this channel again + move.b d7,n_sfxpri(a2) + endc ; !MINIMAL + +.2: tst.l (a2) ; n_note/cmd: any note or cmd set? + bne .3 + move.w n_period(a2),AUDPER(a5) +.3: move.l d6,(a2) + + moveq #15,d5 + and.b n_cmd(a2),d5 + add.w d5,d5 ; d5 cmd*2 + + moveq #0,d4 + move.b d6,d4 ; d4 cmd argument (in MSW) + swap d4 + move.w #$0ff0,d4 + and.w d6,d4 ; d4 for checking E-cmd (in LSW) + + swap d6 + move.l d6,d0 ; S...S... + clr.b d0 + rol.w #4,d0 + rol.l #4,d0 ; ....00SS + + and.w #$0fff,d6 ; d6 note + + ; get sample start address + add.w d0,d0 ; sample number * 2 + beq set_regs + move.w mult30tab(pc,d0.w),d1 ; d1 sample info table offset + lea mt_SampleStarts(a4),a0 + add.w d0,d0 + move.l -4(a0,d0.w),d2 + + ; read length, volume and repeat from sample info table + lea (a3,d1.w),a0 + move.w (a0)+,d0 ; length + bne .4 + + ifne NULL_IS_CLEARED + moveq #0,d2 ; use $0 for empty samples + else + ; use the first two bytes from the first sample for empty samples + move.l mt_SampleStarts(a4),d2 + endc + addq.w #1,d0 + +.4: move.l d2,n_start(a2) + move.w d0,n_reallength(a2) + + ; determine period table from fine-tune parameter + moveq #0,d3 + move.b (a0)+,d3 + add.w d3,d3 + move.l a0,d1 + lea mt_PerFineTune(pc),a0 + add.w (a0,d3.w),a0 + move.l a0,n_pertab(a2) + move.l d1,a0 + cmp.w #2*8,d3 + shs n_minusft(a2) + + moveq #0,d1 + move.b (a0)+,d1 ; volume + move.w d1,n_volume(a2) + move.w (a0)+,d3 ; repeat offset + beq no_offset + + ; set repeat + add.l d3,d2 + add.l d3,d2 + move.w (a0),d0 + move.w d0,n_replen(a2) + exg d0,d3 ; n_replen to d3 + add.w d3,d0 + bra set_len_start + +mult30tab: + dc.w 0*30,1*30,2*30,3*30,4*30,5*30,6*30,7*30 + dc.w 8*30,9*30,10*30,11*30,12*30,13*30,14*30,15*30 + dc.w 16*30,17*30,18*30,19*30,20*30,21*30,22*30,23*30 + dc.w 24*30,25*30,26*30,27*30,28*30,29*30,30*30,31*30 + +no_offset: + move.w (a0),d3 + ifne NULL_IS_CLEARED + cmp.w #1,d3 + beq .1 + bhi set_replen + else + bne set_replen + endc + ; repeat length zero means idle-looping + addq.w #1,d3 +.1: moveq #0,d2 ; expect two zero bytes at $0 +set_replen: + move.w d3,n_replen(a2) +set_len_start: + move.w d0,n_length(a2) + move.l d2,n_loopstart(a2) + move.l d2,n_wavestart(a2) + + ifeq MINIMAL + move.l mt_MasterVolTab(a4),a0 + move.b (a0,d1.w),d1 + and.b n_enable(a2),d1 + endc + move.w d1,AUDVOL(a5) + + ; remember if sample is looped + ; @@@ FIXME: also need to check if n_loopstart equals n_start + subq.w #1,d3 + sne n_looped(a2) + +set_regs: +; d4 = cmd argument | masked E-cmd +; d5 = cmd*2 +; d6 = cmd.w | note.w + + move.w d4,d3 ; d3 masked E-cmd + swap d4 ; d4 cmd argument into LSW + + tst.w d6 + beq checkmorefx ; no new note + + cmp.w #$0e50,d3 + beq set_finetune + + move.w prefx_tab(pc,d5.w),d0 + jmp prefx_tab(pc,d0.w) + +prefx_tab: + dc.w set_period-prefx_tab,set_period-prefx_tab,set_period-prefx_tab + dc.w set_toneporta-prefx_tab ; $3 + dc.w set_period-prefx_tab + dc.w set_toneporta-prefx_tab ; $5 + dc.w set_period-prefx_tab,set_period-prefx_tab,set_period-prefx_tab + dc.w set_sampleoffset-prefx_tab ; $9 + dc.w set_period-prefx_tab,set_period-prefx_tab,set_period-prefx_tab + dc.w set_period-prefx_tab,set_period-prefx_tab,set_period-prefx_tab + + +mt_sampleoffset: +; cmd 9 x y (xy = offset in 256 bytes) +; d4 = xy + + moveq #0,d0 + move.b d4,d0 + bne .1 + move.b n_sampleoffset(a2),d0 + bra .2 +.1: move.b d0,n_sampleoffset(a2) + +.2: lsl.w #7,d0 + cmp.w n_length(a2),d0 + bhs .3 + sub.w d0,n_length(a2) + add.w d0,d0 + add.l d0,n_start(a2) + rts + +.3: move.w #1,n_length(a2) + rts + + +set_sampleoffset: + bsr mt_sampleoffset + bra set_period + +set_finetune: + lea mt_PerFineTune(pc),a0 + moveq #$0f,d0 + and.b d4,d0 + add.w d0,d0 + add.w (a0,d0.w),a0 + move.l a0,n_pertab(a2) + cmp.w #2*8,d0 + shs n_minusft(a2) + +set_period: +; find nearest period for a note value, then apply finetuning +; d3 = masked E-cmd +; d4 = cmd argument +; d5 = cmd*2 +; d6 = note.w + + lea mt_PeriodTable(pc),a0 + moveq #36-1,d0 + moveq #-2,d1 +.1: addq.w #2,d1 ; table offset + cmp.w (a0)+,d6 + dbhs d0,.1 + + ; apply finetuning, set period and note-offset + move.l n_pertab(a2),a0 + move.w (a0,d1.w),d2 + move.w d2,n_period(a2) + move.w d1,n_noteoff(a2) + + ; check for notedelay + cmp.w #$0ed0,d3 ; notedelay + beq checkmorefx + + ; disable DMA + move.w n_dmabit(a2),d0 + move.w d0,DMACON(a6) + + btst #2,n_vibratoctrl(a2) + bne .2 + move.b d7,n_vibratopos(a2) + +.2: btst #2,n_tremoloctrl(a2) + bne .3 + move.b d7,n_tremolopos(a2) + +.3: move.l n_start(a2),AUDLC(a5) + move.w n_length(a2),AUDLEN(a5) + move.w d2,AUDPER(a5) + lea mt_dmaon(pc),a0 + or.w d0,(a0) + +checkmorefx: +; d4 = cmd argument +; d5 = cmd*2 +; d6 = note.w + + move.w n_funk(a2),d0 + beq .1 + bsr mt_updatefunk + +.1: move.w morefx_tab(pc,d5.w),d0 + jmp morefx_tab(pc,d0.w) + +morefx_tab: + dc.w mt_pernop-morefx_tab,mt_pernop-morefx_tab,mt_pernop-morefx_tab + dc.w mt_pernop-morefx_tab,mt_pernop-morefx_tab,mt_pernop-morefx_tab + dc.w mt_pernop-morefx_tab,mt_pernop-morefx_tab,mt_pernop-morefx_tab + dc.w mt_sampleoffset-morefx_tab ; $9 + dc.w mt_pernop-morefx_tab + dc.w mt_posjump-morefx_tab ; $B + dc.w mt_volchange-morefx_tab + dc.w mt_patternbrk-morefx_tab ; $D + dc.w mt_e_cmds-morefx_tab + dc.w mt_setspeed-morefx_tab + + +set_toneporta: + move.l n_pertab(a2),a0 ; tuned period table + + ; find first period which is less or equal the note in d6 + moveq #36-1,d0 + moveq #-2,d1 +.1: addq.w #2,d1 + cmp.w (a0)+,d6 + dbhs d0,.1 + + tst.b n_minusft(a2) ; negative fine tune? + beq .2 + tst.w d1 + beq .2 + subq.l #2,a0 ; then take previous period + subq.w #2,d1 + +.2: move.w d1,n_noteoff(a2) ; note offset in period table + move.w n_period(a2),d2 + move.w -(a0),d1 + cmp.w d1,d2 + bne .3 + moveq #0,d1 +.3: move.w d1,n_wantedperiod(a2) + + move.w n_funk(a2),d0 + beq .4 + bsr mt_updatefunk + +.4: move.w d2,AUDPER(a5) + rts + + + ifeq MINIMAL +moreblockedfx: +; d6 = note.w | cmd.w + + moveq #0,d4 + move.b d6,d4 ; cmd argument + and.w #$0f00,d6 + lsr.w #7,d6 + move.w blmorefx_tab(pc,d6.w),d0 + jmp blmorefx_tab(pc,d0.w) + +blmorefx_tab: + dc.w mt_nop-blmorefx_tab,mt_nop-blmorefx_tab + dc.w mt_nop-blmorefx_tab,mt_nop-blmorefx_tab + dc.w mt_nop-blmorefx_tab,mt_nop-blmorefx_tab + dc.w mt_nop-blmorefx_tab,mt_nop-blmorefx_tab + dc.w mt_nop-blmorefx_tab,mt_nop-blmorefx_tab + dc.w mt_nop-blmorefx_tab + dc.w mt_posjump-blmorefx_tab ; $B + dc.w mt_nop-blmorefx_tab + dc.w mt_patternbrk-blmorefx_tab ; $D + dc.w blocked_e_cmds-blmorefx_tab + dc.w mt_setspeed-blmorefx_tab ; $F + endc ; !MINIMAL + + +mt_arpeggio: +; cmd 0 x y (x = first arpeggio offset, y = second arpeggio offset) +; d4 = xy + + moveq #0,d0 + move.b mt_Counter(a4),d0 + move.b arptab(pc,d0.w),d0 + beq mt_pernop ; step 0, just use normal period + bmi .1 + + ; step 1, arpeggio by left nibble + lsr.b #4,d4 + bra .2 + + ; step 2, arpeggio by right nibble +.1: and.w #$000f,d4 + + ; offset current note +.2: add.w d4,d4 + add.w n_noteoff(a2),d4 + cmp.w #2*36,d4 + bhs .4 + + ; set period with arpeggio offset from note table + move.l n_pertab(a2),a0 + move.w (a0,d4.w),AUDPER(a5) +.4: rts + +arptab: + dc.b 0,1,-1,0,1,-1,0,1,-1,0,1,-1,0,1,-1,0 + dc.b 1,-1,0,1,-1,0,1,-1,0,1,-1,0,1,-1,0,1 + + +mt_fineportaup: +; cmd E 1 x (subtract x from period) +; d0 = x + + tst.b mt_Counter(a4) + beq do_porta_up + rts + + +mt_portaup: +; cmd 1 x x (subtract xx from period) +; d4 = xx + + move.w d4,d0 + +do_porta_up: + move.w n_period(a2),d1 + sub.w d0,d1 + cmp.w #113,d1 + bge .1 + moveq #113,d1 +.1: move.w d1,n_period(a2) + move.w d1,AUDPER(a5) + rts + + +mt_fineportadn: +; cmd E 2 x (add x to period) +; d0 = x + + tst.b mt_Counter(a4) + beq do_porta_down + rts + + +mt_portadown: +; cmd 2 x x (add xx to period) +; d4 = xx + + move.w d4,d0 + +do_porta_down: + move.w n_period(a2),d1 + add.w d0,d1 + cmp.w #856,d1 + bls .1 + move.w #856,d1 +.1: move.w d1,n_period(a2) + move.w d1,AUDPER(a5) + rts + + +mt_toneporta: +; cmd 3 x y (xy = tone portamento speed) +; d4 = xy + + tst.b d4 + beq mt_toneporta_nc + move.w d4,n_toneportspeed(a2) + move.b d7,n_cmdlo(a2) + +mt_toneporta_nc: + move.w n_wantedperiod(a2),d1 + beq .6 + + move.w n_toneportspeed(a2),d0 + move.w n_period(a2),d2 + cmp.w d1,d2 + blo .2 + + ; tone porta up + sub.w d0,d2 + cmp.w d1,d2 + bgt .3 + move.w d1,d2 + move.w d7,n_wantedperiod(a2) + bra .3 + + ; tone porta down +.2: add.w d0,d2 + cmp.w d1,d2 + blt .3 + move.w d1,d2 + move.w d7,n_wantedperiod(a2) + +.3: move.w d2,n_period(a2) + + tst.b n_gliss(a2) + beq .5 + + ; glissando: find nearest note for new period + move.l n_pertab(a2),a0 + moveq #36-1,d0 + moveq #-2,d1 +.4: addq.w #2,d1 + cmp.w (a0)+,d2 + dbhs d0,.4 + + move.w d1,n_noteoff(a2) ; @@@ needed? + move.w -(a0),d2 + +.5: move.w d2,AUDPER(a5) +.6 rts + + +mt_vibrato: +; cmd 4 x y (x = speed, y = amplitude) +; d4 = xy + + moveq #$0f,d2 + and.b d4,d2 + beq .1 + move.b d2,n_vibratoamp(a2) + bra .2 +.1: move.b n_vibratoamp(a2),d2 + +.2: lsr.b #4,d4 + beq .3 + move.b d4,n_vibratospd(a2) + bra mt_vibrato_nc +.3: move.b n_vibratospd(a2),d4 + +mt_vibrato_nc: + ; calculate vibrato table offset: 64 * amplitude + (pos & 63) + lsl.w #6,d2 + moveq #63,d0 + and.b n_vibratopos(a2),d0 + add.w d0,d2 + + ifne ENABLE_SAWRECT + ; select vibrato waveform + moveq #3,d1 + and.b n_vibratoctrl(a2),d1 + beq .6 + subq.b #1,d1 + beq .5 + + ; ctrl 2 & 3 select a rectangle vibrato + lea mt_VibratoRectTable(pc),a0 + bra .9 + + ; ctrl 1 selects a sawtooth vibrato +.5: lea mt_VibratoSawTable(pc),a0 + bra .9 + endc ; ENABLE_SAWRECT + + ; ctrl 0 selects a sine vibrato +.6: lea mt_VibratoSineTable(pc),a0 + + ; add vibrato-offset to period +.9: move.b (a0,d2.w),d0 + ext.w d0 + add.w n_period(a2),d0 + move.w d0,AUDPER(a5) + + ; increase vibratopos by speed + add.b d4,n_vibratopos(a2) + rts + + +mt_tonevolslide: +; cmd 5 x y (x = volume-up, y = volume-down) +; d4 = xy + + pea mt_volumeslide(pc) + bra mt_toneporta_nc + + +mt_vibrvolslide: +; cmd 6 x y (x = volume-up, y = volume-down) +; d4 = xy + + move.w d4,d3 + move.b n_vibratoamp(a2),d2 + move.b n_vibratospd(a2),d4 + bsr mt_vibrato_nc + + move.w d3,d4 + bra mt_volumeslide + + +mt_tremolo: +; cmd 7 x y (x = speed, y = amplitude) +; d4 = xy + + moveq #$0f,d2 + and.b d4,d2 + beq .1 + move.b d2,n_tremoloamp(a2) + bra .2 +.1: move.b n_tremoloamp(a2),d2 + +.2: lsr.b #4,d4 + beq .3 + move.b d4,n_tremolospd(a2) + bra .4 +.3: move.b n_tremolospd(a2),d4 + + ; calculate tremolo table offset: 64 * amplitude + (pos & 63) +.4: lsl.w #6,d2 + moveq #63,d0 + and.b n_tremolopos(a2),d0 + add.w d0,d2 + + ifne ENABLE_SAWRECT + ; select tremolo waveform + moveq #3,d1 + and.b n_tremoloctrl(a2),d1 + beq .6 + subq.b #1,d1 + beq .5 + + ; ctrl 2 & 3 select a rectangle tremolo + lea mt_VibratoRectTable(pc),a0 + bra .9 + + ; ctrl 1 selects a sawtooth tremolo +.5: lea mt_VibratoSawTable(pc),a0 + bra .9 + endc ; ENABLE_SAWRECT + + ; ctrl 0 selects a sine tremolo +.6: lea mt_VibratoSineTable(pc),a0 + + ; add tremolo-offset to volume +.9: move.w n_volume(a2),d0 + add.b (a0,d2.w),d0 + bpl .10 + moveq #0,d0 +.10: cmp.w #64,d0 + bls .11 + moveq #64,d0 +.11: move.w n_period(a2),AUDPER(a5) + ifeq MINIMAL + move.l mt_MasterVolTab(a4),a0 + move.b (a0,d0.w),d0 + and.b n_enable(a2),d0 + endc + move.w d0,AUDVOL(a5) + ifd VOL0_UNLOOPS + bne .12 + move.b d7,n_looped(a2) + endc + + ; increase tremolopos by speed +.12: add.b d4,n_tremolopos(a2) + rts + + +mt_volumeslide: +; cmd A x y (x = volume-up, y = volume-down) +; d4 = xy + + move.w n_volume(a2),d0 + moveq #$0f,d1 + and.b d4,d1 + lsr.b #4,d4 + beq vol_slide_down + + ; slide up, until 64 + add.b d4,d0 +vol_slide_up: + cmp.b #64,d0 + bls set_pervol + moveq #64,d0 + bra set_pervol + + ; slide down, until 0 +vol_slide_down: + sub.b d1,d0 + bpl set_pervol + moveq #0,d0 + +set_pervol: + move.w n_period(a2),AUDPER(a5) +set_vol: + move.w d0,n_volume(a2) + ifeq MINIMAL + move.l mt_MasterVolTab(a4),a0 + move.b (a0,d0.w),d0 + and.b n_enable(a2),d0 + endc + move.w d0,AUDVOL(a5) + ifd VOL0_UNLOOPS + bne .1 + move.b d7,n_looped(a2) + endc +.1: rts + + +mt_posjump: +; cmd B x y (xy = new song position) +; d4 = xy + + move.b d4,d0 + subq.b #1,d0 + move.b d0,mt_SongPos(a4) + +jump_pos0: + move.w d7,mt_PBreakPos(a4) + st mt_PosJumpFlag(a4) + rts + + +mt_volchange: +; cmd C x y (xy = new volume) +; d4 = xy + + move.w d4,d0 + cmp.w #64,d0 + bls set_vol + moveq #64,d0 + bra set_vol + + +mt_patternbrk: +; cmd D x y (xy = break pos in decimal) +; d4 = xy + + moveq #$0f,d0 + and.w d4,d0 + move.w d4,d1 + lsr.w #4,d1 + add.b mult10tab(pc,d1.w),d0 + cmp.b #63,d0 + bhi jump_pos0 + + lsl.w #4,d0 + move.w d0,mt_PBreakPos(a4) + st mt_PosJumpFlag(a4) + rts + +mult10tab: + dc.b 0,10,20,30,40,50,60,70,80,90,0,0,0,0,0,0 + + +mt_setspeed: +; cmd F x y (xy<$20 new speed, xy>=$20 new tempo) +; d4 = xy + + ifeq VBLANK_MUSIC + cmp.b #$20,d4 + bhs .1 + endc + + move.b d4,mt_Speed(a4) + beq _mt_end + rts + + ifeq VBLANK_MUSIC + ; set tempo (CIA only) +.1: and.w #$00ff,d4 + move.l mt_timerval(a4),d0 + divu d4,d0 + move.b d0,CIAB+CIATALO + lsr.w #8,d0 + move.b d0,CIAB+CIATAHI + rts + endc + + +mt_e_cmds: +; cmd E x y (x=command, y=argument) +; d4 = xy + + moveq #$0f,d0 + and.w d4,d0 ; pass E-cmd argument in d0 + + move.w d4,d1 + lsr.w #4,d1 + add.w d1,d1 + move.w ecmd_tab(pc,d1.w),d1 + jmp ecmd_tab(pc,d1.w) + +ecmd_tab: + dc.w mt_filter-ecmd_tab + dc.w mt_fineportaup-ecmd_tab + dc.w mt_fineportadn-ecmd_tab + dc.w mt_glissctrl-ecmd_tab + dc.w mt_vibratoctrl-ecmd_tab + dc.w mt_finetune-ecmd_tab + dc.w mt_jumploop-ecmd_tab + dc.w mt_tremoctrl-ecmd_tab + dc.w mt_e8-ecmd_tab + dc.w mt_retrignote-ecmd_tab + dc.w mt_volfineup-ecmd_tab + dc.w mt_volfinedn-ecmd_tab + dc.w mt_notecut-ecmd_tab + dc.w mt_notedelay-ecmd_tab + dc.w mt_patterndelay-ecmd_tab + dc.w mt_funk-ecmd_tab + + +blocked_e_cmds: +; cmd E x y (x=command, y=argument) +; d4 = xy + + moveq #$0f,d0 + and.w d4,d0 ; pass E-cmd argument in d0 + + move.w d4,d1 + lsr.w #4,d1 + add.w d1,d1 + move.w blecmd_tab(pc,d1.w),d1 + jmp blecmd_tab(pc,d1.w) + +blecmd_tab: + dc.w mt_filter-blecmd_tab + dc.w mt_rts-blecmd_tab + dc.w mt_rts-blecmd_tab + dc.w mt_glissctrl-blecmd_tab + dc.w mt_vibratoctrl-blecmd_tab + dc.w mt_finetune-blecmd_tab + dc.w mt_jumploop-blecmd_tab + dc.w mt_tremoctrl-blecmd_tab + dc.w mt_e8-blecmd_tab + dc.w mt_rts-blecmd_tab + dc.w mt_rts-blecmd_tab + dc.w mt_rts-blecmd_tab + dc.w mt_rts-blecmd_tab + dc.w mt_rts-blecmd_tab + dc.w mt_patterndelay-blecmd_tab + dc.w mt_rts-blecmd_tab + + +mt_filter: +; cmd E 0 x (x=1 disable, x=0 enable) +; d0 = x + + lsr.b #1,d0 + bcs .1 + bclr #1,CIAA+CIAPRA + rts +.1: bset #1,CIAA+CIAPRA +mt_rts: + rts + + +mt_glissctrl: +; cmd E 3 x (x gliss) +; d0 = x + + move.b d0,n_gliss(a2) + rts + + +mt_vibratoctrl: +; cmd E 4 x (x = vibrato) +; d0 = x + + move.b d0,n_vibratoctrl(a2) + rts + + +mt_finetune: +; cmd E 5 x (x = finetune) +; d0 = x + + lea mt_PerFineTune(pc),a0 + add.w d0,d0 + add.w (a0,d0.w),a0 + move.l a0,n_pertab(a2) + cmp.w #2*8,d0 + shs n_minusft(a2) + rts + + +mt_jumploop: +; cmd E 6 x (x=0 loop start, else loop count) +; d0 = x + + tst.b mt_Counter(a4) + bne .4 + +.1: tst.b d0 + beq .3 ; set start + + ; otherwise we are at the end of the loop + subq.b #1,n_loopcount(a2) + beq .4 ; loop finished + bpl .2 + + ; initialize loop counter + move.b d0,n_loopcount(a2) + + ; jump back to start of loop +.2: move.w n_pattpos(a2),mt_PBreakPos(a4) + st mt_PBreakFlag(a4) + rts + + ; remember start of loop position +.3: move.w mt_PatternPos(a4),n_pattpos(a2) +.4: rts + + +mt_tremoctrl: +; cmd E 7 x (x = tremolo) +; d0 = x + + move.b d0,n_tremoloctrl(a2) + rts + + +mt_e8: +; cmd E 8 x (x = trigger value) +; d0 = x + + move.b d0,mt_E8Trigger(a4) + rts + + +mt_retrignote: +; cmd E 9 x (x = retrigger count) +; d0 = x + + tst.b d0 + beq .1 + + ; set new retrigger count when Counter=0 + tst.b mt_Counter(a4) + bne .2 + move.b d0,n_retrigcount(a2) + + ; avoid double retrigger, when Counter=0 and a note was set + move.w #$0fff,d2 + and.w (a2),d2 + beq do_retrigger +.1: rts + + ; check if retrigger count is reached +.2: subq.b #1,n_retrigcount(a2) + bne .1 + move.b d0,n_retrigcount(a2) ; reset + +do_retrigger: + ; DMA off, set sample pointer and length + move.w n_dmabit(a2),d0 + move.w d0,DMACON(a6) + move.l n_start(a2),AUDLC(a5) + move.w n_length(a2),AUDLEN(a5) + lea mt_dmaon(pc),a0 + or.w d0,(a0) + rts + + +mt_volfineup: +; cmd E A x (x = volume add) +; d0 = x + + tst.b mt_Counter(a4) + beq .1 + rts + +.1: add.w n_volume(a2),d0 + bra vol_slide_up + + +mt_volfinedn: +; cmd E B x (x = volume sub) +; d0 = x + + tst.b mt_Counter(a4) + beq .1 + rts + +.1: move.b d0,d1 + move.w n_volume(a2),d0 + bra vol_slide_down + + +mt_notecut: +; cmd E C x (x = counter to cut at) +; d0 = x + + cmp.b mt_Counter(a4),d0 + bne .1 + move.w d7,n_volume(a2) + move.w d7,AUDVOL(a5) + ifd VOL0_UNLOOPS + move.b d7,n_looped(a2) + endc +.1: rts + + +mt_notedelay: +; cmd E D x (x = counter to retrigger at) +; d0 = x + + cmp.b mt_Counter(a4),d0 + bne .1 + tst.w (a2) ; trigger note when given + bne .2 +.1: rts +.2: move.w n_period(a2),AUDPER(a5) + bra do_retrigger + + +mt_patterndelay: +; cmd E E x (x = delay count) +; d0 = x + + tst.b mt_Counter(a4) + bne .1 + tst.b mt_PattDelTime2(a4) + bne .1 + addq.b #1,d0 + move.b d0,mt_PattDelTime(a4) +.1: rts + + +mt_funk: +; cmd E F x (x = funk speed) +; d0 = x + + tst.b mt_Counter(a4) + bne .1 + move.w d0,n_funk(a2) + bne mt_updatefunk +.1: rts + +mt_updatefunk: +; d0 = funk speed + + move.b mt_FunkTable(pc,d0.w),d0 + add.b d0,n_funkoffset(a2) + bpl .2 + move.b d7,n_funkoffset(a2) + + move.l n_loopstart(a2),d0 + moveq #0,d1 + move.w n_replen(a2),d1 + add.l d1,d1 + add.l d0,d1 + move.l n_wavestart(a2),a0 + addq.l #1,a0 + cmp.l d1,a0 + blo .1 + move.l d0,a0 +.1: move.l a0,n_wavestart(a2) + not.b (a0) + +.2: rts + + +mt_FunkTable: + dc.b 0,5,6,7,8,10,11,13,16,19,22,26,32,43,64,128 + +mt_VibratoSineTable: + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1 + dc.b 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0 + dc.b 0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + dc.b -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0,0,0,0 + dc.b 0,0,0,1,1,1,2,2,2,3,3,3,3,3,3,3 + dc.b 3,3,3,3,3,3,3,3,2,2,2,1,1,1,0,0 + dc.b 0,0,0,-1,-1,-1,-2,-2,-2,-3,-3,-3,-3,-3,-3,-3 + dc.b -3,-3,-3,-3,-3,-3,-3,-3,-2,-2,-2,-1,-1,-1,0,0 + dc.b 0,0,1,1,2,2,3,3,4,4,4,5,5,5,5,5 + dc.b 5,5,5,5,5,5,4,4,4,3,3,2,2,1,1,0 + dc.b 0,0,-1,-1,-2,-2,-3,-3,-4,-4,-4,-5,-5,-5,-5,-5 + dc.b -5,-5,-5,-5,-5,-5,-4,-4,-4,-3,-3,-2,-2,-1,-1,0 + dc.b 0,0,1,2,3,3,4,5,5,6,6,7,7,7,7,7 + dc.b 7,7,7,7,7,7,6,6,5,5,4,3,3,2,1,0 + dc.b 0,0,-1,-2,-3,-3,-4,-5,-5,-6,-6,-7,-7,-7,-7,-7 + dc.b -7,-7,-7,-7,-7,-7,-6,-6,-5,-5,-4,-3,-3,-2,-1,0 + dc.b 0,0,1,2,3,4,5,6,7,7,8,8,9,9,9,9 + dc.b 9,9,9,9,9,8,8,7,7,6,5,4,3,2,1,0 + dc.b 0,0,-1,-2,-3,-4,-5,-6,-7,-7,-8,-8,-9,-9,-9,-9 + dc.b -9,-9,-9,-9,-9,-8,-8,-7,-7,-6,-5,-4,-3,-2,-1,0 + dc.b 0,1,2,3,4,5,6,7,8,9,9,10,11,11,11,11 + dc.b 11,11,11,11,11,10,9,9,8,7,6,5,4,3,2,1 + dc.b 0,-1,-2,-3,-4,-5,-6,-7,-8,-9,-9,-10,-11,-11,-11,-11 + dc.b -11,-11,-11,-11,-11,-10,-9,-9,-8,-7,-6,-5,-4,-3,-2,-1 + dc.b 0,1,2,4,5,6,7,8,9,10,11,12,12,13,13,13 + dc.b 13,13,13,13,12,12,11,10,9,8,7,6,5,4,2,1 + dc.b 0,-1,-2,-4,-5,-6,-7,-8,-9,-10,-11,-12,-12,-13,-13,-13 + dc.b -13,-13,-13,-13,-12,-12,-11,-10,-9,-8,-7,-6,-5,-4,-2,-1 + dc.b 0,1,3,4,6,7,8,10,11,12,13,14,14,15,15,15 + dc.b 15,15,15,15,14,14,13,12,11,10,8,7,6,4,3,1 + dc.b 0,-1,-3,-4,-6,-7,-8,-10,-11,-12,-13,-14,-14,-15,-15,-15 + dc.b -15,-15,-15,-15,-14,-14,-13,-12,-11,-10,-8,-7,-6,-4,-3,-1 + dc.b 0,1,3,5,6,8,9,11,12,13,14,15,16,17,17,17 + dc.b 17,17,17,17,16,15,14,13,12,11,9,8,6,5,3,1 + dc.b 0,-1,-3,-5,-6,-8,-9,-11,-12,-13,-14,-15,-16,-17,-17,-17 + dc.b -17,-17,-17,-17,-16,-15,-14,-13,-12,-11,-9,-8,-6,-5,-3,-1 + dc.b 0,1,3,5,7,9,11,12,14,15,16,17,18,19,19,19 + dc.b 19,19,19,19,18,17,16,15,14,12,11,9,7,5,3,1 + dc.b 0,-1,-3,-5,-7,-9,-11,-12,-14,-15,-16,-17,-18,-19,-19,-19 + dc.b -19,-19,-19,-19,-18,-17,-16,-15,-14,-12,-11,-9,-7,-5,-3,-1 + dc.b 0,2,4,6,8,10,12,13,15,16,18,19,20,20,21,21 + dc.b 21,21,21,20,20,19,18,16,15,13,12,10,8,6,4,2 + dc.b 0,-2,-4,-6,-8,-10,-12,-13,-15,-16,-18,-19,-20,-20,-21,-21 + dc.b -21,-21,-21,-20,-20,-19,-18,-16,-15,-13,-12,-10,-8,-6,-4,-2 + dc.b 0,2,4,6,9,11,13,15,16,18,19,21,22,22,23,23 + dc.b 23,23,23,22,22,21,19,18,16,15,13,11,9,6,4,2 + dc.b 0,-2,-4,-6,-9,-11,-13,-15,-16,-18,-19,-21,-22,-22,-23,-23 + dc.b -23,-23,-23,-22,-22,-21,-19,-18,-16,-15,-13,-11,-9,-6,-4,-2 + dc.b 0,2,4,7,9,12,14,16,18,20,21,22,23,24,25,25 + dc.b 25,25,25,24,23,22,21,20,18,16,14,12,9,7,4,2 + dc.b 0,-2,-4,-7,-9,-12,-14,-16,-18,-20,-21,-22,-23,-24,-25,-25 + dc.b -25,-25,-25,-24,-23,-22,-21,-20,-18,-16,-14,-12,-9,-7,-4,-2 + dc.b 0,2,5,8,10,13,15,17,19,21,23,24,25,26,27,27 + dc.b 27,27,27,26,25,24,23,21,19,17,15,13,10,8,5,2 + dc.b 0,-2,-5,-8,-10,-13,-15,-17,-19,-21,-23,-24,-25,-26,-27,-27 + dc.b -27,-27,-27,-26,-25,-24,-23,-21,-19,-17,-15,-13,-10,-8,-5,-2 + dc.b 0,2,5,8,11,14,16,18,21,23,24,26,27,28,29,29 + dc.b 29,29,29,28,27,26,24,23,21,18,16,14,11,8,5,2 + dc.b 0,-2,-5,-8,-11,-14,-16,-18,-21,-23,-24,-26,-27,-28,-29,-29 + dc.b -29,-29,-29,-28,-27,-26,-24,-23,-21,-18,-16,-14,-11,-8,-5,-2 + + ifne ENABLE_SAWRECT +mt_VibratoSawTable: + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + dc.b -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1 + dc.b 2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 + dc.b -3,-3,-3,-3,-3,-3,-3,-3,-2,-2,-2,-2,-2,-2,-2,-2 + dc.b -1,-1,-1,-1,-1,-1,-1,-1,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,1,1,1,1,1,2,2,2,2,2 + dc.b 3,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5 + dc.b -5,-5,-5,-5,-5,-5,-4,-4,-4,-4,-4,-3,-3,-3,-3,-3 + dc.b -2,-2,-2,-2,-2,-2,-1,-1,-1,-1,-1,0,0,0,0,0 + dc.b 0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3 + dc.b 4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7 + dc.b -7,-7,-7,-7,-6,-6,-6,-6,-5,-5,-5,-5,-4,-4,-4,-4 + dc.b -3,-3,-3,-3,-2,-2,-2,-2,-1,-1,-1,-1,0,0,0,0 + dc.b 0,0,0,0,1,1,1,2,2,2,3,3,3,4,4,4 + dc.b 5,5,5,5,6,6,6,7,7,7,8,8,8,9,9,9 + dc.b -9,-9,-9,-9,-8,-8,-8,-7,-7,-7,-6,-6,-6,-5,-5,-5 + dc.b -4,-4,-4,-4,-3,-3,-3,-2,-2,-2,-1,-1,-1,0,0,0 + dc.b 0,0,0,1,1,1,2,2,3,3,3,4,4,4,5,5 + dc.b 6,6,6,7,7,7,8,8,9,9,9,10,10,10,11,11 + dc.b -11,-11,-11,-10,-10,-10,-9,-9,-8,-8,-8,-7,-7,-7,-6,-6 + dc.b -5,-5,-5,-4,-4,-4,-3,-3,-2,-2,-2,-1,-1,-1,0,0 + dc.b 0,0,0,1,1,2,2,3,3,3,4,4,5,5,6,6 + dc.b 7,7,7,8,8,9,9,10,10,10,11,11,12,12,13,13 + dc.b -13,-13,-13,-12,-12,-11,-11,-10,-10,-10,-9,-9,-8,-8,-7,-7 + dc.b -6,-6,-6,-5,-5,-4,-4,-3,-3,-3,-2,-2,-1,-1,0,0 + dc.b 0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7 + dc.b 8,8,9,9,10,10,11,11,12,12,13,13,14,14,15,15 + dc.b -15,-15,-14,-14,-13,-13,-12,-12,-11,-11,-10,-10,-9,-9,-8,-8 + dc.b -7,-7,-6,-6,-5,-5,-4,-4,-3,-3,-2,-2,-1,-1,0,0 + dc.b 0,0,1,1,2,2,3,3,4,5,5,6,6,7,7,8 + dc.b 9,9,10,10,11,11,12,12,13,14,14,15,15,16,16,17 + dc.b -17,-17,-16,-16,-15,-15,-14,-13,-13,-12,-12,-11,-11,-10,-10,-9 + dc.b -8,-8,-7,-7,-6,-6,-5,-4,-4,-3,-3,-2,-2,-1,-1,0 + dc.b 0,0,1,1,2,3,3,4,5,5,6,6,7,8,8,9 + dc.b 10,10,11,11,12,13,13,14,15,15,16,16,17,18,18,19 + dc.b -19,-19,-18,-18,-17,-16,-16,-15,-14,-14,-13,-13,-12,-11,-11,-10 + dc.b -9,-9,-8,-8,-7,-6,-6,-5,-4,-4,-3,-3,-2,-1,-1,0 + dc.b 0,0,1,2,2,3,4,4,5,6,6,7,8,8,9,10 + dc.b 11,11,12,13,13,14,15,15,16,17,17,18,19,19,20,21 + dc.b -21,-21,-20,-19,-19,-18,-17,-17,-16,-15,-15,-14,-13,-12,-12,-11 + dc.b -10,-10,-9,-8,-8,-7,-6,-6,-5,-4,-4,-3,-2,-1,-1,0 + dc.b 0,0,1,2,3,3,4,5,6,6,7,8,9,9,10,11 + dc.b 12,12,13,14,15,15,16,17,18,18,19,20,21,21,22,23 + dc.b -23,-23,-22,-21,-20,-20,-19,-18,-17,-17,-16,-15,-14,-14,-13,-12 + dc.b -11,-11,-10,-9,-8,-8,-7,-6,-5,-5,-4,-3,-2,-2,-1,0 + dc.b 0,0,1,2,3,4,4,5,6,7,8,8,9,10,11,12 + dc.b 13,13,14,15,16,17,17,18,19,20,21,21,22,23,24,25 + dc.b -25,-25,-24,-23,-22,-21,-21,-20,-19,-18,-17,-16,-16,-15,-14,-13 + dc.b -12,-12,-11,-10,-9,-8,-8,-7,-6,-5,-4,-3,-3,-2,-1,0 + dc.b 0,0,1,2,3,4,5,6,7,7,8,9,10,11,12,13 + dc.b 14,14,15,16,17,18,19,20,21,21,22,23,24,25,26,27 + dc.b -27,-27,-26,-25,-24,-23,-22,-21,-20,-20,-19,-18,-17,-16,-15,-14 + dc.b -13,-13,-12,-11,-10,-9,-8,-7,-6,-6,-5,-4,-3,-2,-1,0 + dc.b 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14 + dc.b 15,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29 + dc.b -29,-28,-28,-27,-26,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15 + dc.b -14,-13,-13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0 + +mt_VibratoRectTable: + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + dc.b 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + dc.b -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + dc.b -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + dc.b 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 + dc.b 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 + dc.b -3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3 + dc.b -3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3 + dc.b 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 + dc.b 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 + dc.b -5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5 + dc.b -5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5 + dc.b 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 + dc.b 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 + dc.b -7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7 + dc.b -7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7,-7 + dc.b 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 + dc.b 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 + dc.b -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 + dc.b -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 + dc.b 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 + dc.b 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11 + dc.b -11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11 + dc.b -11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11,-11 + dc.b 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 + dc.b 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 + dc.b -13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13 + dc.b -13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13,-13 + dc.b 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15 + dc.b 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15 + dc.b -15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15 + dc.b -15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15,-15 + dc.b 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 + dc.b 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 + dc.b -17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17 + dc.b -17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17 + dc.b 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19 + dc.b 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19 + dc.b -19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19 + dc.b -19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19,-19 + dc.b 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21 + dc.b 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21 + dc.b -21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21 + dc.b -21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21,-21 + dc.b 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23 + dc.b 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23 + dc.b -23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23 + dc.b -23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23,-23 + dc.b 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25 + dc.b 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25 + dc.b -25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25 + dc.b -25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25 + dc.b 27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27 + dc.b 27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27 + dc.b -27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27 + dc.b -27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27,-27 + dc.b 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 + dc.b 29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 + dc.b -29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29 + dc.b -29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29 + endc ; ENABLE_SAWRECT + + +mt_PerFineTune: + dc.w mt_Tuning0-mt_PerFineTune,mt_Tuning1-mt_PerFineTune + dc.w mt_Tuning2-mt_PerFineTune,mt_Tuning3-mt_PerFineTune + dc.w mt_Tuning4-mt_PerFineTune,mt_Tuning5-mt_PerFineTune + dc.w mt_Tuning6-mt_PerFineTune,mt_Tuning7-mt_PerFineTune + dc.w mt_TuningM8-mt_PerFineTune,mt_TuningM7-mt_PerFineTune + dc.w mt_TuningM6-mt_PerFineTune,mt_TuningM5-mt_PerFineTune + dc.w mt_TuningM4-mt_PerFineTune,mt_TuningM3-mt_PerFineTune + dc.w mt_TuningM2-mt_PerFineTune,mt_TuningM1-mt_PerFineTune + +mt_PeriodTable: +mt_Tuning0: ; Tuning 0, Normal c-1 - b3 + dc.w 856,808,762,720,678,640,604,570,538,508,480,453 + dc.w 428,404,381,360,339,320,302,285,269,254,240,226 + dc.w 214,202,190,180,170,160,151,143,135,127,120,113 +mt_Tuning1: + dc.w 850,802,757,715,674,637,601,567,535,505,477,450 + dc.w 425,401,379,357,337,318,300,284,268,253,239,225 + dc.w 213,201,189,179,169,159,150,142,134,126,119,113 +mt_Tuning2: + dc.w 844,796,752,709,670,632,597,563,532,502,474,447 + dc.w 422,398,376,355,335,316,298,282,266,251,237,224 + dc.w 211,199,188,177,167,158,149,141,133,125,118,112 +mt_Tuning3: + dc.w 838,791,746,704,665,628,592,559,528,498,470,444 + dc.w 419,395,373,352,332,314,296,280,264,249,235,222 + dc.w 209,198,187,176,166,157,148,140,132,125,118,111 +mt_Tuning4: + dc.w 832,785,741,699,660,623,588,555,524,495,467,441 + dc.w 416,392,370,350,330,312,294,278,262,247,233,220 + dc.w 208,196,185,175,165,156,147,139,131,124,117,110 +mt_Tuning5: + dc.w 826,779,736,694,655,619,584,551,520,491,463,437 + dc.w 413,390,368,347,328,309,292,276,260,245,232,219 + dc.w 206,195,184,174,164,155,146,138,130,123,116,109 +mt_Tuning6: + dc.w 820,774,730,689,651,614,580,547,516,487,460,434 + dc.w 410,387,365,345,325,307,290,274,258,244,230,217 + dc.w 205,193,183,172,163,154,145,137,129,122,115,109 +mt_Tuning7: + dc.w 814,768,725,684,646,610,575,543,513,484,457,431 + dc.w 407,384,363,342,323,305,288,272,256,242,228,216 + dc.w 204,192,181,171,161,152,144,136,128,121,114,108 +mt_TuningM8: + dc.w 907,856,808,762,720,678,640,604,570,538,508,480 + dc.w 453,428,404,381,360,339,320,302,285,269,254,240 + dc.w 226,214,202,190,180,170,160,151,143,135,127,120 +mt_TuningM7: + dc.w 900,850,802,757,715,675,636,601,567,535,505,477 + dc.w 450,425,401,379,357,337,318,300,284,268,253,238 + dc.w 225,212,200,189,179,169,159,150,142,134,126,119 +mt_TuningM6: + dc.w 894,844,796,752,709,670,632,597,563,532,502,474 + dc.w 447,422,398,376,355,335,316,298,282,266,251,237 + dc.w 223,211,199,188,177,167,158,149,141,133,125,118 +mt_TuningM5: + dc.w 887,838,791,746,704,665,628,592,559,528,498,470 + dc.w 444,419,395,373,352,332,314,296,280,264,249,235 + dc.w 222,209,198,187,176,166,157,148,140,132,125,118 +mt_TuningM4: + dc.w 881,832,785,741,699,660,623,588,555,524,494,467 + dc.w 441,416,392,370,350,330,312,294,278,262,247,233 + dc.w 220,208,196,185,175,165,156,147,139,131,123,117 +mt_TuningM3: + dc.w 875,826,779,736,694,655,619,584,551,520,491,463 + dc.w 437,413,390,368,347,328,309,292,276,260,245,232 + dc.w 219,206,195,184,174,164,155,146,138,130,123,116 +mt_TuningM2: + dc.w 868,820,774,730,689,651,614,580,547,516,487,460 + dc.w 434,410,387,365,345,325,307,290,274,258,244,230 + dc.w 217,205,193,183,172,163,154,145,137,129,122,115 +mt_TuningM1: + dc.w 862,814,768,725,684,646,610,575,543,513,484,457 + dc.w 431,407,384,363,342,323,305,288,272,256,242,228 + dc.w 216,203,192,181,171,161,152,144,136,128,121,114 + + ifeq MINIMAL +MasterVolTab0: + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0 +MasterVolTab1: + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 1 +MasterVolTab2: + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + dc.b 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + dc.b 2 +MasterVolTab3: + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1 + dc.b 1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2 + dc.b 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 + dc.b 3 +MasterVolTab4: + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + dc.b 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + dc.b 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 + dc.b 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 + dc.b 4 +MasterVolTab5: + dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1 + dc.b 1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2 + dc.b 2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3 + dc.b 3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4 + dc.b 5 +MasterVolTab6: + dc.b 0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1 + dc.b 1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2 + dc.b 3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4 + dc.b 4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5 + dc.b 6 +MasterVolTab7: + dc.b 0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1 + dc.b 1,1,1,2,2,2,2,2,2,2,2,2,3,3,3,3 + dc.b 3,3,3,3,3,4,4,4,4,4,4,4,4,4,5,5 + dc.b 5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6 + dc.b 7 +MasterVolTab8: + dc.b 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1 + dc.b 2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3 + dc.b 4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5 + dc.b 6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7 + dc.b 8 +MasterVolTab9: + dc.b 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,2 + dc.b 2,2,2,2,2,2,3,3,3,3,3,3,3,4,4,4 + dc.b 4,4,4,4,5,5,5,5,5,5,5,6,6,6,6,6 + dc.b 6,6,7,7,7,7,7,7,7,8,8,8,8,8,8,8 + dc.b 9 +MasterVolTab10: + dc.b 0,0,0,0,0,0,0,1,1,1,1,1,1,2,2,2 + dc.b 2,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4 + dc.b 5,5,5,5,5,5,5,6,6,6,6,6,6,7,7,7 + dc.b 7,7,7,7,8,8,8,8,8,8,9,9,9,9,9,9 + dc.b 10 +MasterVolTab11: + dc.b 0,0,0,0,0,0,1,1,1,1,1,1,2,2,2,2 + dc.b 2,2,3,3,3,3,3,3,4,4,4,4,4,4,5,5 + dc.b 5,5,5,6,6,6,6,6,6,7,7,7,7,7,7,8 + dc.b 8,8,8,8,8,9,9,9,9,9,9,10,10,10,10,10 + dc.b 11 +MasterVolTab12: + dc.b 0,0,0,0,0,0,1,1,1,1,1,2,2,2,2,2 + dc.b 3,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5 + dc.b 6,6,6,6,6,6,7,7,7,7,7,8,8,8,8,8 + dc.b 9,9,9,9,9,9,10,10,10,10,10,11,11,11,11,11 + dc.b 12 +MasterVolTab13: + dc.b 0,0,0,0,0,1,1,1,1,1,2,2,2,2,2,3 + dc.b 3,3,3,3,4,4,4,4,4,5,5,5,5,5,6,6 + dc.b 6,6,6,7,7,7,7,7,8,8,8,8,8,9,9,9 + dc.b 9,9,10,10,10,10,10,11,11,11,11,11,12,12,12,12 + dc.b 13 +MasterVolTab14: + dc.b 0,0,0,0,0,1,1,1,1,1,2,2,2,2,3,3 + dc.b 3,3,3,4,4,4,4,5,5,5,5,5,6,6,6,6 + dc.b 7,7,7,7,7,8,8,8,8,8,9,9,9,9,10,10 + dc.b 10,10,10,11,11,11,11,12,12,12,12,12,13,13,13,13 + dc.b 14 +MasterVolTab15: + dc.b 0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3 + dc.b 3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7 + dc.b 7,7,7,8,8,8,8,9,9,9,9,10,10,10,10,11 + dc.b 11,11,11,11,12,12,12,12,13,13,13,13,14,14,14,14 + dc.b 15 +MasterVolTab16: + dc.b 0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3 + dc.b 4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7 + dc.b 8,8,8,8,9,9,9,9,10,10,10,10,11,11,11,11 + dc.b 12,12,12,12,13,13,13,13,14,14,14,14,15,15,15,15 + dc.b 16 +MasterVolTab17: + dc.b 0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3 + dc.b 4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,8 + dc.b 8,8,9,9,9,9,10,10,10,10,11,11,11,11,12,12 + dc.b 12,13,13,13,13,14,14,14,14,15,15,15,15,16,16,16 + dc.b 17 +MasterVolTab18: + dc.b 0,0,0,0,1,1,1,1,2,2,2,3,3,3,3,4 + dc.b 4,4,5,5,5,5,6,6,6,7,7,7,7,8,8,8 + dc.b 9,9,9,9,10,10,10,10,11,11,11,12,12,12,12,13 + dc.b 13,13,14,14,14,14,15,15,15,16,16,16,16,17,17,17 + dc.b 18 +MasterVolTab19: + dc.b 0,0,0,0,1,1,1,2,2,2,2,3,3,3,4,4 + dc.b 4,5,5,5,5,6,6,6,7,7,7,8,8,8,8,9 + dc.b 9,9,10,10,10,10,11,11,11,12,12,12,13,13,13,13 + dc.b 14,14,14,15,15,15,16,16,16,16,17,17,17,18,18,18 + dc.b 19 +MasterVolTab20: + dc.b 0,0,0,0,1,1,1,2,2,2,3,3,3,4,4,4 + dc.b 5,5,5,5,6,6,6,7,7,7,8,8,8,9,9,9 + dc.b 10,10,10,10,11,11,11,12,12,12,13,13,13,14,14,14 + dc.b 15,15,15,15,16,16,16,17,17,17,18,18,18,19,19,19 + dc.b 20 +MasterVolTab21: + dc.b 0,0,0,0,1,1,1,2,2,2,3,3,3,4,4,4 + dc.b 5,5,5,6,6,6,7,7,7,8,8,8,9,9,9,10 + dc.b 10,10,11,11,11,12,12,12,13,13,13,14,14,14,15,15 + dc.b 15,16,16,16,17,17,17,18,18,18,19,19,19,20,20,20 + dc.b 21 +MasterVolTab22: + dc.b 0,0,0,1,1,1,2,2,2,3,3,3,4,4,4,5 + dc.b 5,5,6,6,6,7,7,7,8,8,8,9,9,9,10,10 + dc.b 11,11,11,12,12,12,13,13,13,14,14,14,15,15,15,16 + dc.b 16,16,17,17,17,18,18,18,19,19,19,20,20,20,21,21 + dc.b 22 +MasterVolTab23: + dc.b 0,0,0,1,1,1,2,2,2,3,3,3,4,4,5,5 + dc.b 5,6,6,6,7,7,7,8,8,8,9,9,10,10,10,11 + dc.b 11,11,12,12,12,13,13,14,14,14,15,15,15,16,16,16 + dc.b 17,17,17,18,18,19,19,19,20,20,20,21,21,21,22,22 + dc.b 23 +MasterVolTab24: + dc.b 0,0,0,1,1,1,2,2,3,3,3,4,4,4,5,5 + dc.b 6,6,6,7,7,7,8,8,9,9,9,10,10,10,11,11 + dc.b 12,12,12,13,13,13,14,14,15,15,15,16,16,16,17,17 + dc.b 18,18,18,19,19,19,20,20,21,21,21,22,22,22,23,23 + dc.b 24 +MasterVolTab25: + dc.b 0,0,0,1,1,1,2,2,3,3,3,4,4,5,5,5 + dc.b 6,6,7,7,7,8,8,8,9,9,10,10,10,11,11,12 + dc.b 12,12,13,13,14,14,14,15,15,16,16,16,17,17,17,18 + dc.b 18,19,19,19,20,20,21,21,21,22,22,23,23,23,24,24 + dc.b 25 +MasterVolTab26: + dc.b 0,0,0,1,1,2,2,2,3,3,4,4,4,5,5,6 + dc.b 6,6,7,7,8,8,8,9,9,10,10,10,11,11,12,12 + dc.b 13,13,13,14,14,15,15,15,16,16,17,17,17,18,18,19 + dc.b 19,19,20,20,21,21,21,22,22,23,23,23,24,24,25,25 + dc.b 26 +MasterVolTab27: + dc.b 0,0,0,1,1,2,2,2,3,3,4,4,5,5,5,6 + dc.b 6,7,7,8,8,8,9,9,10,10,10,11,11,12,12,13 + dc.b 13,13,14,14,15,15,16,16,16,17,17,18,18,18,19,19 + dc.b 20,20,21,21,21,22,22,23,23,24,24,24,25,25,26,26 + dc.b 27 +MasterVolTab28: + dc.b 0,0,0,1,1,2,2,3,3,3,4,4,5,5,6,6 + dc.b 7,7,7,8,8,9,9,10,10,10,11,11,12,12,13,13 + dc.b 14,14,14,15,15,16,16,17,17,17,18,18,19,19,20,20 + dc.b 21,21,21,22,22,23,23,24,24,24,25,25,26,26,27,27 + dc.b 28 +MasterVolTab29: + dc.b 0,0,0,1,1,2,2,3,3,4,4,4,5,5,6,6 + dc.b 7,7,8,8,9,9,9,10,10,11,11,12,12,13,13,14 + dc.b 14,14,15,15,16,16,17,17,18,18,19,19,19,20,20,21 + dc.b 21,22,22,23,23,24,24,24,25,25,26,26,27,27,28,28 + dc.b 29 +MasterVolTab30: + dc.b 0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7 + dc.b 7,7,8,8,9,9,10,10,11,11,12,12,13,13,14,14 + dc.b 15,15,15,16,16,17,17,18,18,19,19,20,20,21,21,22 + dc.b 22,22,23,23,24,24,25,25,26,26,27,27,28,28,29,29 + dc.b 30 +MasterVolTab31: + dc.b 0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7 + dc.b 7,8,8,9,9,10,10,11,11,12,12,13,13,14,14,15 + dc.b 15,15,16,16,17,17,18,18,19,19,20,20,21,21,22,22 + dc.b 23,23,24,24,25,25,26,26,27,27,28,28,29,29,30,30 + dc.b 31 +MasterVolTab32: + dc.b 0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7 + dc.b 8,8,9,9,10,10,11,11,12,12,13,13,14,14,15,15 + dc.b 16,16,17,17,18,18,19,19,20,20,21,21,22,22,23,23 + dc.b 24,24,25,25,26,26,27,27,28,28,29,29,30,30,31,31 + dc.b 32 +MasterVolTab33: + dc.b 0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7 + dc.b 8,8,9,9,10,10,11,11,12,12,13,13,14,14,15,15 + dc.b 16,17,17,18,18,19,19,20,20,21,21,22,22,23,23,24 + dc.b 24,25,25,26,26,27,27,28,28,29,29,30,30,31,31,32 + dc.b 33 +MasterVolTab34: + dc.b 0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7 + dc.b 8,9,9,10,10,11,11,12,12,13,13,14,14,15,15,16 + dc.b 17,17,18,18,19,19,20,20,21,21,22,22,23,23,24,24 + dc.b 25,26,26,27,27,28,28,29,29,30,30,31,31,32,32,33 + dc.b 34 +MasterVolTab35: + dc.b 0,0,1,1,2,2,3,3,4,4,5,6,6,7,7,8 + dc.b 8,9,9,10,10,11,12,12,13,13,14,14,15,15,16,16 + dc.b 17,18,18,19,19,20,20,21,21,22,22,23,24,24,25,25 + dc.b 26,26,27,27,28,28,29,30,30,31,31,32,32,33,33,34 + dc.b 35 +MasterVolTab36: + dc.b 0,0,1,1,2,2,3,3,4,5,5,6,6,7,7,8 + dc.b 9,9,10,10,11,11,12,12,13,14,14,15,15,16,16,17 + dc.b 18,18,19,19,20,20,21,21,22,23,23,24,24,25,25,26 + dc.b 27,27,28,28,29,29,30,30,31,32,32,33,33,34,34,35 + dc.b 36 +MasterVolTab37: + dc.b 0,0,1,1,2,2,3,4,4,5,5,6,6,7,8,8 + dc.b 9,9,10,10,11,12,12,13,13,14,15,15,16,16,17,17 + dc.b 18,19,19,20,20,21,21,22,23,23,24,24,25,26,26,27 + dc.b 27,28,28,29,30,30,31,31,32,32,33,34,34,35,35,36 + dc.b 37 +MasterVolTab38: + dc.b 0,0,1,1,2,2,3,4,4,5,5,6,7,7,8,8 + dc.b 9,10,10,11,11,12,13,13,14,14,15,16,16,17,17,18 + dc.b 19,19,20,20,21,21,22,23,23,24,24,25,26,26,27,27 + dc.b 28,29,29,30,30,31,32,32,33,33,34,35,35,36,36,37 + dc.b 38 +MasterVolTab39: + dc.b 0,0,1,1,2,3,3,4,4,5,6,6,7,7,8,9 + dc.b 9,10,10,11,12,12,13,14,14,15,15,16,17,17,18,18 + dc.b 19,20,20,21,21,22,23,23,24,24,25,26,26,27,28,28 + dc.b 29,29,30,31,31,32,32,33,34,34,35,35,36,37,37,38 + dc.b 39 +MasterVolTab40: + dc.b 0,0,1,1,2,3,3,4,5,5,6,6,7,8,8,9 + dc.b 10,10,11,11,12,13,13,14,15,15,16,16,17,18,18,19 + dc.b 20,20,21,21,22,23,23,24,25,25,26,26,27,28,28,29 + dc.b 30,30,31,31,32,33,33,34,35,35,36,36,37,38,38,39 + dc.b 40 +MasterVolTab41: + dc.b 0,0,1,1,2,3,3,4,5,5,6,7,7,8,8,9 + dc.b 10,10,11,12,12,13,14,14,15,16,16,17,17,18,19,19 + dc.b 20,21,21,22,23,23,24,24,25,26,26,27,28,28,29,30 + dc.b 30,31,32,32,33,33,34,35,35,36,37,37,38,39,39,40 + dc.b 41 +MasterVolTab42: + dc.b 0,0,1,1,2,3,3,4,5,5,6,7,7,8,9,9 + dc.b 10,11,11,12,13,13,14,15,15,16,17,17,18,19,19,20 + dc.b 21,21,22,22,23,24,24,25,26,26,27,28,28,29,30,30 + dc.b 31,32,32,33,34,34,35,36,36,37,38,38,39,40,40,41 + dc.b 42 +MasterVolTab43: + dc.b 0,0,1,2,2,3,4,4,5,6,6,7,8,8,9,10 + dc.b 10,11,12,12,13,14,14,15,16,16,17,18,18,19,20,20 + dc.b 21,22,22,23,24,24,25,26,26,27,28,28,29,30,30,31 + dc.b 32,32,33,34,34,35,36,36,37,38,38,39,40,40,41,42 + dc.b 43 +MasterVolTab44: + dc.b 0,0,1,2,2,3,4,4,5,6,6,7,8,8,9,10 + dc.b 11,11,12,13,13,14,15,15,16,17,17,18,19,19,20,21 + dc.b 22,22,23,24,24,25,26,26,27,28,28,29,30,30,31,32 + dc.b 33,33,34,35,35,36,37,37,38,39,39,40,41,41,42,43 + dc.b 44 +MasterVolTab45: + dc.b 0,0,1,2,2,3,4,4,5,6,7,7,8,9,9,10 + dc.b 11,11,12,13,14,14,15,16,16,17,18,18,19,20,21,21 + dc.b 22,23,23,24,25,26,26,27,28,28,29,30,30,31,32,33 + dc.b 33,34,35,35,36,37,37,38,39,40,40,41,42,42,43,44 + dc.b 45 +MasterVolTab46: + dc.b 0,0,1,2,2,3,4,5,5,6,7,7,8,9,10,10 + dc.b 11,12,12,13,14,15,15,16,17,17,18,19,20,20,21,22 + dc.b 23,23,24,25,25,26,27,28,28,29,30,30,31,32,33,33 + dc.b 34,35,35,36,37,38,38,39,40,40,41,42,43,43,44,45 + dc.b 46 +MasterVolTab47: + dc.b 0,0,1,2,2,3,4,5,5,6,7,8,8,9,10,11 + dc.b 11,12,13,13,14,15,16,16,17,18,19,19,20,21,22,22 + dc.b 23,24,24,25,26,27,27,28,29,30,30,31,32,33,33,34 + dc.b 35,35,36,37,38,38,39,40,41,41,42,43,44,44,45,46 + dc.b 47 +MasterVolTab48: + dc.b 0,0,1,2,3,3,4,5,6,6,7,8,9,9,10,11 + dc.b 12,12,13,14,15,15,16,17,18,18,19,20,21,21,22,23 + dc.b 24,24,25,26,27,27,28,29,30,30,31,32,33,33,34,35 + dc.b 36,36,37,38,39,39,40,41,42,42,43,44,45,45,46,47 + dc.b 48 +MasterVolTab49: + dc.b 0,0,1,2,3,3,4,5,6,6,7,8,9,9,10,11 + dc.b 12,13,13,14,15,16,16,17,18,19,19,20,21,22,22,23 + dc.b 24,25,26,26,27,28,29,29,30,31,32,32,33,34,35,35 + dc.b 36,37,38,39,39,40,41,42,42,43,44,45,45,46,47,48 + dc.b 49 +MasterVolTab50: + dc.b 0,0,1,2,3,3,4,5,6,7,7,8,9,10,10,11 + dc.b 12,13,14,14,15,16,17,17,18,19,20,21,21,22,23,24 + dc.b 25,25,26,27,28,28,29,30,31,32,32,33,34,35,35,36 + dc.b 37,38,39,39,40,41,42,42,43,44,45,46,46,47,48,49 + dc.b 50 +MasterVolTab51: + dc.b 0,0,1,2,3,3,4,5,6,7,7,8,9,10,11,11 + dc.b 12,13,14,15,15,16,17,18,19,19,20,21,22,23,23,24 + dc.b 25,26,27,27,28,29,30,31,31,32,33,34,35,35,36,37 + dc.b 38,39,39,40,41,42,43,43,44,45,46,47,47,48,49,50 + dc.b 51 +MasterVolTab52: + dc.b 0,0,1,2,3,4,4,5,6,7,8,8,9,10,11,12 + dc.b 13,13,14,15,16,17,17,18,19,20,21,21,22,23,24,25 + dc.b 26,26,27,28,29,30,30,31,32,33,34,34,35,36,37,38 + dc.b 39,39,40,41,42,43,43,44,45,46,47,47,48,49,50,51 + dc.b 52 +MasterVolTab53: + dc.b 0,0,1,2,3,4,4,5,6,7,8,9,9,10,11,12 + dc.b 13,14,14,15,16,17,18,19,19,20,21,22,23,24,24,25 + dc.b 26,27,28,28,29,30,31,32,33,33,34,35,36,37,38,38 + dc.b 39,40,41,42,43,43,44,45,46,47,48,48,49,50,51,52 + dc.b 53 +MasterVolTab54: + dc.b 0,0,1,2,3,4,5,5,6,7,8,9,10,10,11,12 + dc.b 13,14,15,16,16,17,18,19,20,21,21,22,23,24,25,26 + dc.b 27,27,28,29,30,31,32,32,33,34,35,36,37,37,38,39 + dc.b 40,41,42,43,43,44,45,46,47,48,48,49,50,51,52,53 + dc.b 54 +MasterVolTab55: + dc.b 0,0,1,2,3,4,5,6,6,7,8,9,10,11,12,12 + dc.b 13,14,15,16,17,18,18,19,20,21,22,23,24,24,25,26 + dc.b 27,28,29,30,30,31,32,33,34,35,36,36,37,38,39,40 + dc.b 41,42,42,43,44,45,46,47,48,48,49,50,51,52,53,54 + dc.b 55 +MasterVolTab56: + dc.b 0,0,1,2,3,4,5,6,7,7,8,9,10,11,12,13 + dc.b 14,14,15,16,17,18,19,20,21,21,22,23,24,25,26,27 + dc.b 28,28,29,30,31,32,33,34,35,35,36,37,38,39,40,41 + dc.b 42,42,43,44,45,46,47,48,49,49,50,51,52,53,54,55 + dc.b 56 +MasterVolTab57: + dc.b 0,0,1,2,3,4,5,6,7,8,8,9,10,11,12,13 + dc.b 14,15,16,16,17,18,19,20,21,22,23,24,24,25,26,27 + dc.b 28,29,30,31,32,32,33,34,35,36,37,38,39,40,40,41 + dc.b 42,43,44,45,46,47,48,48,49,50,51,52,53,54,55,56 + dc.b 57 +MasterVolTab58: + dc.b 0,0,1,2,3,4,5,6,7,8,9,9,10,11,12,13 + dc.b 14,15,16,17,18,19,19,20,21,22,23,24,25,26,27,28 + dc.b 29,29,30,31,32,33,34,35,36,37,38,38,39,40,41,42 + dc.b 43,44,45,46,47,48,48,49,50,51,52,53,54,55,56,57 + dc.b 58 +MasterVolTab59: + dc.b 0,0,1,2,3,4,5,6,7,8,9,10,11,11,12,13 + dc.b 14,15,16,17,18,19,20,21,22,23,23,24,25,26,27,28 + dc.b 29,30,31,32,33,34,35,35,36,37,38,39,40,41,42,43 + dc.b 44,45,46,47,47,48,49,50,51,52,53,54,55,56,57,58 + dc.b 59 +MasterVolTab60: + dc.b 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14 + dc.b 15,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29 + dc.b 30,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44 + dc.b 45,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59 + dc.b 60 +MasterVolTab61: + dc.b 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14 + dc.b 15,16,17,18,19,20,20,21,22,23,24,25,26,27,28,29 + dc.b 30,31,32,33,34,35,36,37,38,39,40,40,41,42,43,44 + dc.b 45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60 + dc.b 61 +MasterVolTab62: + dc.b 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14 + dc.b 15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30 + dc.b 31,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45 + dc.b 46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61 + dc.b 62 +MasterVolTab63: + dc.b 0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14 + dc.b 15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30 + dc.b 31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46 + dc.b 47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62 + dc.b 63 +MasterVolTab64: + dc.b 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 + dc.b 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31 + dc.b 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47 + dc.b 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63 + dc.b 64 + even + endc ; !MINIMAL + + + ifd SDATA + + section __MERGED,bss + +mt_chan1: + ds.b n_sizeof +mt_chan2: + ds.b n_sizeof +mt_chan3: + ds.b n_sizeof +mt_chan4: + ds.b n_sizeof + +mt_SampleStarts: + ds.l 31 + +mt_mod: + ds.l 1 +mt_oldLev6: + ds.l 1 +mt_timerval: + ds.l 1 +mt_oldtimers: + ds.b 4 +mt_Lev6Int: + ds.l 1 +mt_Lev6Ena: + ds.w 1 +mt_PatternPos: + ds.w 1 +mt_PBreakPos: + ds.w 1 +mt_PosJumpFlag: + ds.b 1 +mt_PBreakFlag: + ds.b 1 +mt_Speed: + ds.b 1 +mt_Counter: + ds.b 1 +mt_SongPos: + ds.b 1 +mt_PattDelTime: + ds.b 1 +mt_PattDelTime2: + ds.b 1 + + ifeq MINIMAL +mt_SilCntValid: + ds.b 1 +mt_MasterVolTab: + ds.l 1 + endc + + xdef _mt_Enable +_mt_Enable: +mt_Enable: + ds.b 1 + + xdef _mt_E8Trigger +_mt_E8Trigger: +mt_E8Trigger: + ds.b 1 + + ifeq MINIMAL + xdef _mt_MusicChannels +_mt_MusicChannels: +mt_MusicChannels: + ds.b 1 + + xdef _mt_SongEnd +_mt_SongEnd: +mt_SongEnd: + ds.b 1 + endc ; !MINIMAL + + else ; !SDATA : single section with local base register + + rsreset +mt_chan1 rs.b n_sizeof +mt_chan2 rs.b n_sizeof +mt_chan3 rs.b n_sizeof +mt_chan4 rs.b n_sizeof +mt_SampleStarts rs.l 31 +mt_mod rs.l 1 +mt_oldLev6 rs.l 1 +mt_timerval rs.l 1 +mt_oldtimers rs.b 4 +mt_Lev6Int rs.l 1 +mt_Lev6Ena rs.w 1 +mt_PatternPos rs.w 1 +mt_PBreakPos rs.w 1 +mt_PosJumpFlag rs.b 1 +mt_PBreakFlag rs.b 1 +mt_Speed rs.b 1 +mt_Counter rs.b 1 +mt_SongPos rs.b 1 +mt_PattDelTime rs.b 1 +mt_PattDelTime2 rs.b 1 + ifeq MINIMAL +mt_SilCntValid rs.b 1 +mt_MasterVolTab rs.l 1 + endc +mt_Enable rs.b 1 ; exported as _mt_Enable +mt_E8Trigger rs.b 1 ; exported as _mt_E8Trigger +mt_MusicChannels rs.b 1 ; exported as _mt_MusicChannels +mt_SongEnd rs.b 1 ; exported as _mt_SongEnd + +mt_data: + ds.b mt_Enable + xdef _mt_Enable +_mt_Enable: + ds.b 1 + xdef _mt_E8Trigger +_mt_E8Trigger: + ds.b 1 + ifeq MINIMAL + xdef _mt_MusicChannels +_mt_MusicChannels: + ds.b 1 + xdef _mt_SongEnd +_mt_SongEnd: + ds.b 1 + endc ; !MINIMAL + + endc ; SDATA/!SDATA + + end diff --git a/ptplayer_oscompat.obj b/ptplayer_oscompat.obj new file mode 100644 index 0000000000000000000000000000000000000000..b5e50c121df931efff2a85c6d6b1e69599aca847 GIT binary patch literal 13900 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 ZuN~v Date: Wed, 15 Jan 2025 17:04:54 +0100 Subject: [PATCH 21/23] Changes to be committed: modified: README.md --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0ba3157..dba974e 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,12 @@ # blitz_ptplayer -Port of Frank Wille's Protracker player v6.3 as a Blitz Basic library +Port of Frank Wille's Protracker player v6.4 as a Blitz Basic library -Please take the older version, pre-2023 for now! - -Blitz Basic function/statement entry points added by idrougge +Blitz Basic function/statement entry points added by idrougge, macros by earok Further development by E-Penguin VBR fix by phx -compiled with vasm 1.9d, with the following options: +compiled with vasm 2.0a, with the following options: vasmm68k_mot.exe -devpac -Fhunkexe -kick1hunks -nosym ptplayer.asm -o ptplayer.obj Built wit the following configuration flags: @@ -19,10 +17,13 @@ ENABLE_SAWRECT equ 0 NULL_IS_CLEARED equ 0 +New for ptplayer 6.4 is "OS Compatible" mode; there is a separate build for this with the OSCOMPAT flag = 1. As a result, the MTInstall statement has changed, see below if using this version. Rename the obj to remove "_oscompat" and rebuild the blitzlibs accordingly. + Command reference: LoadBank 0,"mod.song",2 (loads module into bank 0 in chipmem (2)) -MTInstall PAL=True, NTSC=False (installs player in program) +MTInstall PAL=True, NTSC=False (installs player in program) [OSCOMPAT=0, default] +success.l = MTInstall (installs player in program) [OSCOMPAT=1] MTInit Bank#, startpos (inserts module into player) @@ -44,4 +45,4 @@ MTMusicMask bitmask.b (Set bits 0-3 to reserve channels for music only.) MTMusicChannels 0..4 (number of channels dedicated to music) -MTE8Trigger (Value of the last E8 command in case you want to trigger game events from a module) \ No newline at end of file +MTE8Trigger (Value of the last E8 command in case you want to trigger game events from a module) From b5dff1b773ded4549677a1b2f15a693e88cfdf50 Mon Sep 17 00:00:00 2001 From: Daniel Lakey Date: Wed, 15 Jan 2025 17:05:34 +0100 Subject: [PATCH 22/23] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dba974e..75a26ef 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ Port of Frank Wille's Protracker player v6.4 as a Blitz Basic library Blitz Basic function/statement entry points added by idrougge, macros by earok + Further development by E-Penguin VBR fix by phx From 47ebda054981c35e5c9b9308c448f0da88ca2b93 Mon Sep 17 00:00:00 2001 From: Daniel Lakey Date: Wed, 15 Jan 2025 17:12:05 +0100 Subject: [PATCH 23/23] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 75a26ef..e87e913 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ Command reference: LoadBank 0,"mod.song",2 (loads module into bank 0 in chipmem (2)) MTInstall PAL=True, NTSC=False (installs player in program) [OSCOMPAT=0, default] + success.l = MTInstall (installs player in program) [OSCOMPAT=1] MTInit Bank#, startpos (inserts module into player)