From ebd83296003748ca0dcba1a18c55f445ba10c6dd Mon Sep 17 00:00:00 2001 From: Vince Weaver Date: Sat, 28 Dec 2019 22:35:14 -0500 Subject: [PATCH] pt3_lib: update to version 0.2 includes more documentation and Mockingboard slot detection --- pt3_lib/README.pt3_lib | 103 +++++++++++++++++++++-- pt3_lib/hello.bas | 2 +- pt3_lib/pt3_lib.dsk | Bin 143360 -> 143360 bytes pt3_lib/pt3_lib_core.s | 4 +- pt3_lib/pt3_lib_mockingboard_detect.s | 115 ++++++++++++++++++++++++-- pt3_lib/pt3_lib_mockingboard_setup.s | 26 ++++++ pt3_lib/pt3_test.s | 30 +++++-- pt3_lib/zp.inc | 24 +++--- 8 files changed, 266 insertions(+), 38 deletions(-) diff --git a/pt3_lib/README.pt3_lib b/pt3_lib/README.pt3_lib index 3cecaed9..f8027021 100644 --- a/pt3_lib/README.pt3_lib +++ b/pt3_lib/README.pt3_lib @@ -1,17 +1,106 @@ -The PT3_player Library -~~~~~~~~~~~~~~~~~~~~~~ +The PT3_player Library version 0.2 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - by Vince "Deater" Weaver - 7 June 2019 - http://www.deater.net/weave/vmwprod/pt3_player/ + by Vince "Deater" Weaver + http://www.deater.net/weave/vmwprod/pt3_lib/ + + Last Update: 28 December 2019 Plays Vortex Tracker II .pt3 files on the Apple II +Background: +~~~~~~~~~~~ This code is meant as a relatively simple, reasonably optimized version of the PT3 Vortex-Tracker player for use in other programs. -The orignal player code can be found in ../pt3_player/ -That codebase is being *extremely* optimized to the point it's no longer +A much more optimized version can be found in ../pt3_player/ +That codebase has been *extremely* optimized to the point it's no longer very straightforward to reuse the code. +For some more background on this you can watch the talk I gave +at Demosplash 2019 on this topic. + + +What is a PT3 file? +~~~~~~~~~~~~~~~~~~~ + +A PT3 file is a tracker format with a compact file size used on systems +with AY-3-8910 based audio. This is most commonly the ZX-spectrum +and Atari ST machines. + +Originally most PT3 players were in z80 assembly language for use on Z80 +based machines. I have written code that will play the files on modern +systems (using C) and also the included code designed for the 6502-based +Apple II machines with Mockingboard sound cards installed. + +You can find many pt3 files on the internet, or you can use the +VortexTracker tracker to write your own. + + +Using the Code (irq driven): +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +See the "pt3_test.s" example. + +The code is in cc65 6502 assembly language but should be relatively +easy to port to other assemblers. + +To get a pt3 file playing: + + Optionally include "pt3_lib_mockingboard_detect.s" and + call "mockingboard_detect" and "mockingboard_patch" if + you want to auto-detect which slot the mockingboard is in. + Otherwise it will default to Slot#4 + The patch code does a vaguely unsafe find/replace of $C4 + live patch of the slot values, if you want a safer (but much + larger) version you can go into the file and ifdef out + the right code. + + Be sure to either include the pt3 file as a binary, or load + it from disk to a buffer pointed to by PT3_LOC. + Not the beginning of the song needs to be aligned on + a page boundary (this makes the decode code a bit + more simple) + + If you want to make the code more compact but use a lot of + the zero page, you can set PT3_USE_ZERO_PAGE in + "pt3_lib_core.s" This will use zp $80-$FF + but make the pt3 code a bit faster/smaller + + You can set the interrupt speed in pt3_lib_mockingboard_setup.s + Generally files you find online are 50Hz. + For less overhead you can set something like 25Hz but + in that case you'll want to adjust the speed in the + tracker otherwise the songs will play at the wrong speed. + + Vortex tracker by default assumes a system with a 1.77MHz + clock and sets frequencies accordingly. The Mockingboard + runs at 1MHz, so the pt3_lib converts on the fly. + For less overhead you can have the tracker generate + 1MHz music and strip out the 1.77MHz conversion code. + + If you want the music to Loop then set the LOOP value to 1. + + +Using the Code (cycle-counted): +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +I started work on a cycle-counted (deterministic cycle count) pt3 +decoder, but it turned out to be large and complex enough to not be +worth the trouble. + +You can still use pt3 files in cycle-counted demos. See the +../demosplash2019/ directory for an example. What this code does +is decode the pt3 files to memory during non-cycle-counted times, +and then use a deterministic playback function to play back this music. +Each frame of music decodes to 11bytes of register info, which means +at 60Hz you can get roughly 4s of music per 3kB of RAM. + + +Overhead: +~~~~~~~~~ + +It depends exactly on what features you use, but in general it will use +around 3KB of RAM plus the size of the PT3 file (which is often a few K). +Playback overhead depends on the complexity of the sound file but is typically +in the 10% to 15% range when playing back at 50Hz. + +The player also uses 26 zero-page locations. More compact/faster code +can be generated if you're willing to sacrifice 128+ zero page locations. + + diff --git a/pt3_lib/hello.bas b/pt3_lib/hello.bas index 94d3e6b9..744fa7b2 100644 --- a/pt3_lib/hello.bas +++ b/pt3_lib/hello.bas @@ -1,2 +1,2 @@ - 10 PRINT "PT3 LIB TEST V0.1" + 10 PRINT "PT3 LIB TEST V0.2" 100 PRINT CHR$ (4)"BRUN PT3_TEST" diff --git a/pt3_lib/pt3_lib.dsk b/pt3_lib/pt3_lib.dsk index e75e3d529b81bb819f092e29b2a0bd4ea3ef3216..87a591972fad50ce3c978dce5aa32bc23cf0156b 100644 GIT binary patch delta 2789 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 BrWpVL delta 2748 zcmZ`*Yj6|S72cI(S+X(ObHTO%3onBWHW6U+6qtu01}sMd&V!6QZ9&rjwO}+g;hBOW z0p1KlSjI^+wdt_M$Z<#OvLFGrF=oLKnMjTei>HtLsKGR3ifJIrw3DIc;hvQ!$#mK~ zckb>v=R4<~J&$`gFlY+kJVzHXbWVLSL+`C$!O+=f{gW5bZG>KvK~HS2%%x)=(Z%iK zBJ_0nS9DtEkD2p1Y}HeC-V^EUlekeei8LQzTqTUu^+cH@j_~>@Z;0-eKJ3CN+WF@r zBJd+4;(a~@ya*!2o#2kKCK#kuirvSsbqw3jXgIwH^>1IMSu-n=D%W0yqUx_)qDyF@ z{@EpZykB6OnI2}?jRua3spc-OvC$CJMUHdBk>if9hvScxlpaOB=e~ZIQEmq*^}7Vn z@c)5`d15L*RNhc-H$LXrK#Wr*S01vfpjgoAkbDY!eIT3y`8o(ukZ*wEl>300*f4MtQ24ALDs|34(PWKQOvoAN2Q^dn_d(I5 z|H6Q*fN&m*5DlSiG%C%ZqN7VU*^PicmO14ufiv+x=i>l1#*iEJn)2OlmNzVSS;+ z>iZn{#iT&h3!j5kT1>n}gfAj4eI{-b1HLdJifP_e6qaYqCH!3S{2E|F2F^j9Q9cX8 zHF(|)ayJZgX^hK&mA!f{k+0yV-?{<{Bl}N-lNL^c*du=d!WD4ex&Ur0%=MXAl(t|W6by;yn$w9u7B80)O>e&<7u}GkT-GeoX{%L^Ha60OH2KfSlq+&)i zyGnbFhr^m>PZ;yV#Ke#@@2-Pg(OZZxxx*oz#v5eF(FZA+DmVO z>;u=4?6?jOvf^yCaYKvcOIR$1woHu9IU@AI!APLM%IKXV4i6>t`k_w$yboeAj4d{S zR6=+Mkrokt0g;vvexvrDaP?30A38O0ZBJ@=bUq zf!q(zP63lC_kwf8>Y>p|YzF5D?wE8`ub4FNu7O{9J#I)6-Jq<9rzkm^pyxmSfAPaM zQ1jy^B^%}ZY{JhW+RKc@I+G}AakFC7I9sgPl${`SqNeX62wGXV`C=mO=4WX!w=&#@Z6~yniZjKb%66H;t4>lnnOU zY`lhyZYiIr4>gX7eKS*aPLaDfZK*|n$ou9 zw0d6%>K}E`WBnm>8+UBpwEg8o%R8)60r3`+!dz~rqb?nf zoQ*p-bGB@ z3;a?s>6=9`w~9_2;e66;qE5U%n<(R?IRq1HmF&bjL(8u;op@&^vRpeHlHRdlLs>cu zjmlPNVd)o4sfNipEiYeSbDbUOY0zzekL)cnbla&0-|$7N?_jv>HU&?P+0 z6D}G5hfAY%y6+vLwZFF6Zf5PLPuKS>K3~$k_M3y#`rn`V^>2S-`|Iy_mSqlIZg_Xi z49lZGb_PG(?shFG$WHy&-v@4XUkLwB_~dZy-W|X2yz|qlH&<7zT)w1a{+!~O_QD@J zG*)wHbVd`MPn%b-d5c}SX6@=S+;UsH;R^lKQeXNN{hF>tUtf2Jeq(M|fT98cnhKN# z0;LAZ!cewwzSEhXU*XLEI4^I2%Kcbl^&LIh*Y}&FeVclFkyStA&vcf-LeUTE|8@s| LNk`qJ|GDnpu^&gX diff --git a/pt3_lib/pt3_lib_core.s b/pt3_lib/pt3_lib_core.s index fed1de16..b3c95d9c 100644 --- a/pt3_lib/pt3_lib_core.s +++ b/pt3_lib/pt3_lib_core.s @@ -91,7 +91,7 @@ NOTE_TONE_SLIDE_TO_STEP =39 NOTE_STRUCT_SIZE=40 -.ifdef USE_ZERO_PAGE +.ifdef PT3_USE_ZERO_PAGE note_a = $80 note_b = $80+(NOTE_STRUCT_SIZE*1) note_c = $80+(NOTE_STRUCT_SIZE*2) @@ -100,7 +100,7 @@ begin_vars=$80 end_vars=$80+(NOTE_STRUCT_SIZE*3) -.else ; !USE_ZERO_PAGE +.else ; !PT3_USE_ZERO_PAGE begin_vars: note_a: ; reset? diff --git a/pt3_lib/pt3_lib_mockingboard_detect.s b/pt3_lib/pt3_lib_mockingboard_detect.s index 5d406dc0..706d9407 100644 --- a/pt3_lib/pt3_lib_mockingboard_detect.s +++ b/pt3_lib/pt3_lib_mockingboard_detect.s @@ -28,7 +28,7 @@ ; if card was found, X = #$Cn where n is the slot number of the card ; C clear if no Mockingboard found ; other flags clobbered -; zp $85-$87 clobbered +; zp $65-$67 clobbered ; A/Y clobbered ;------------------------------------------------------------------------------ @@ -70,27 +70,124 @@ mb_timer_check_done: +;=================================================================== +; code to patch mockingboard if not in slot#4 +;=================================================================== +; this is the brute force version, we have to patch 39 locations +; see further below if you want to try a smaller, more dangerous, patch + .if 0 mockingboard_patch: lda MB_ADDR_H - sta pt3_irq_smc1+2 + sta pt3_irq_smc1+2 ; 1 - sta pt3_irq_smc2+2 - sta pt3_irq_smc2+5 + sta pt3_irq_smc2+2 ; 2 + sta pt3_irq_smc2+5 ; 3 - sta pt3_irq_smc3+2 - sta pt3_irq_smc3+5 + sta pt3_irq_smc3+2 ; 4 + sta pt3_irq_smc3+5 ; 5 - sta pt3_irq_smc4+2 - sta pt3_irq_smc4+5 + sta pt3_irq_smc4+2 ; 6 + sta pt3_irq_smc4+5 ; 7 + sta pt3_irq_smc5+2 ; 8 + sta pt3_irq_smc5+5 ; 9 + + sta pt3_irq_smc6+2 ; 10 + sta pt3_irq_smc6+5 ; 11 + + sta pt3_irq_smc7+2 ; 12 + sta pt3_irq_smc7+5 ; 13 + + sta mock_init_smc1+2 ; 14 + sta mock_init_smc1+5 ; 15 + + sta mock_init_smc2+2 ; 16 + sta mock_init_smc2+5 ; 17 + + sta reset_ay_smc1+2 ; 18 + sta reset_ay_smc2+2 ; 19 + sta reset_ay_smc3+2 ; 20 + sta reset_ay_smc4+2 ; 21 + + sta write_ay_smc1+2 ; 22 + sta write_ay_smc1+5 ; 23 + + sta write_ay_smc2+2 ; 24 + sta write_ay_smc2+5 ; 25 + + sta write_ay_smc3+2 ; 26 + sta write_ay_smc3+5 ; 27 + + sta write_ay_smc4+2 ; 28 + sta write_ay_smc4+5 ; 29 + + sta write_ay_smc5+2 ; 30 + sta write_ay_smc5+5 ; 31 + + sta write_ay_smc6+2 ; 32 + sta write_ay_smc6+5 ; 33 + + sta setup_irq_smc1+2 ; 34 + sta setup_irq_smc2+2 ; 35 + sta setup_irq_smc3+2 ; 36 + sta setup_irq_smc4+2 ; 37 + sta setup_irq_smc5+2 ; 38 + sta setup_irq_smc6+2 ; 39 rts - .endif +;=================================================================== +; dangerous code to patch mockingboard if not in slot#4 +;=================================================================== +; this code patches any $C4 value to the proper slot# if not slot4 +; this can be dangerous, it might over-write other important values +; that should be $C4 + +; safer ways to do this: +; only do this if 2 bytes after a LDA/STA/LDX/STX +; count total and if not 39 then print error message + +mockingboard_patch: + ; from mockingboard_init $1BBF + ; to done_pt3_irq_handler $1D85 + + ldx MB_ADDR_H + ldy #0 + + lda #mockingboard_init + sta MB_ADDR_H + +mb_patch_loop: + lda (MB_ADDR_L),Y + cmp #$C4 + bne mb_patch_nomatch + + txa + sta (MB_ADDR_L),Y +mb_patch_nomatch: + + inc MB_ADDR_L + lda MB_ADDR_L + bne mb_patch_oflo + inc MB_ADDR_H + +mb_patch_oflo: + lda MB_ADDR_H + cmp #>done_pt3_irq_handler + bne mb_patch_loop + lda MB_ADDR_L + cmp #