diff --git a/out/iatheory_scrapbook.pdf b/out/iatheory_scrapbook.pdf index 60024b7b..5d9a3248 100644 Binary files a/out/iatheory_scrapbook.pdf and b/out/iatheory_scrapbook.pdf differ diff --git a/src/bonusphase.tex b/src/bonusphase.tex index d8738570..4cb21b81 100644 --- a/src/bonusphase.tex +++ b/src/bonusphase.tex @@ -1,9 +1,40 @@ \chapter{Congoatulations Hotshot} \label{sec:bonus} - \lstset{style=6502Style} -This decidedly ropey mini-game has a few interesting features. +If you are a good boy, and transfer enough energy to the planets' cores, then you too can +get to play Iridis Alpha's decidedly ropey mini-game. + +\begin{lstlisting}[escapechar=\%, caption=\icode{bonusPhaseEarned} is set once \icode{currCoreEnergyLevel} reaches 14.] +EnterBonusPhaseInterruptHandler%\index{EnterBonusPhaseInterruptHandler}% +UpdateCoreEnergyLevel + LDX currCoreEnergyLevel + .. +UpdateCoreGraphic + DEC SCREEN_RAM + LINE23_COL13,X + LDA SCREEN_RAM + LINE23_COL13,X + CMP #$7F + BNE ReturnFromCoreEnergyLevel + LDA #$80 + STA SCREEN_RAM + LINE23_COL13,X + INX + ; Has currCoreEnergyLevel reached 14 yet? + CPX #14 + ; If so, you get to play the bonus phase. + BEQ MaybeEarnedBonusPhase + + ... +MaybeEarnedBonusPhase + LDA lowerPlanetActivated + BEQ EarnedBonusPhase + DEX + JMP FinalizeLevelAndReturn + +EarnedBonusPhase + INC bonusPhaseEarned +ReturnFromCoreEnergyLevel + RTS +\end{lstlisting} \section{Entry Sequence} \begin{figure}[H] @@ -12,6 +43,16 @@ \section{Entry Sequence} \caption{The entry sequence\index{sequence}.} \end{figure} +\begin{lstlisting}[escapechar=\%, caption=Good boys pass this test.] +MaybeGoToBonusPhase + LDA bonusPhaseEarned + BEQ ResumeMainGameAgain + SEI + JSR StoreStatusBarDetail + JSR ClearScreen3 + JSR DisplayEnterBonusRoundScreen +\end{lstlisting} + The entry sequence\index{sequence} is an animated cascade of colored bars that appears to roll down from the top of the screen\index{screen}. You might assume we achieve this effect by simply drawing\index{drawing} a series of colored text-based lines in a tight loop. Not the case: @@ -302,7 +343,7 @@ \section{Generating Maps} with a map for the level we are simply referencing each of these rows by their index in the array. Here is the data structure\index{structure} defining each of the 32 rows: -\begin{lstlisting}[basicstyle=\tiny,escapechar=\%] +\begin{lstlisting}[basicstyle=\tiny\ttfamily,escapechar=\%] bonusPhaseMapRowDefinitions%\index{bonusPhaseMapRowDefinitions}% .BYTE $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; 00 .BYTE $0D,$0D,$0E,$0E,$0E,$0E,$0E,$00,$00,$0B,$0B,$00,$00,$0D,$0D,$0D,$0D,$0D,$0E,$0E @@ -341,7 +382,7 @@ \section{Generating Maps} The one at index \icode{\$15} (21 in decimal\index{decimal}) is this guy: -\begin{lstlisting}[basicstyle=\tiny,escapechar=\%] +\begin{lstlisting}[basicstyle=\tiny\ttfamily,escapechar=\%] bonusPhaseMapRowDefinitions%\index{bonusPhaseMapRowDefinitions}% .BYTE $00,$10,$0B,$11,$00,$00,$10,$0B,$11,$00,$00,$10,$0B,$11,$00,$00,$10,$0B,$11,$00 \end{lstlisting} @@ -534,7 +575,7 @@ \section{Generating Maps} \begin{minipage}[b]{0.45\linewidth} \centering -\begin{lstlisting}[basicstyle=\tiny,escapechar=\%] +\begin{lstlisting}[basicstyle=\tiny\ttfamily,escapechar=\%] BonusPhaseFillTopLineAfterScrollUp%\index{BonusPhaseFillTopLineAfterScrollUp}% LDX offsetForScrollUp%\index{offsetForScrollUp}% LDY bonusPhaseMapDefinition%\index{bonusPhaseMapDefinition}%,X @@ -575,7 +616,7 @@ \section{Generating Maps} \hspace{0.5cm} \begin{minipage}[b]{0.45\linewidth} \centering -\begin{lstlisting}[basicstyle=\tiny,escapechar=\%] +\begin{lstlisting}[basicstyle=\tiny\ttfamily,escapechar=\%] BonusPhaseFillBottomLineAfterScrollDown%\index{BonusPhaseFillBottomLineAfterScrollDown}% LDX offsetForScrollDown%\index{offsetForScrollDown}% LDY bonusPhaseMapDefinition%\index{bonusPhaseMapDefinition}%,X @@ -670,7 +711,7 @@ \subsection{Choosing a Map} The trick is that these segments aren't themselves generated procedurally\index{procedurally}, we defined them in advance. We did this in \icode{bonusMapSegmentArray\index{bonusMapSegmentArray}} -given below. We've highlighted the segments used in our map in red: +given below. \begin{lstlisting}[escapechar=\%] bonusMapSegmentArray%\index{bonusMapSegmentArray}% @@ -709,7 +750,7 @@ \subsection{Choosing a Map} \end{lstlisting} By way of example this is what the last segment in the list above looks like when -rendered as a section of our map: +rendered as a section of our map (remember to read from back to front): \begin{figure}[H] { @@ -760,7 +801,13 @@ \subsection{Choosing a Map} \section{Some Very Ugly Sprites\index{Sprites}} -For some reason the gilby sprite in the bonus phase is made impossibly ugly. +There is no escaping the obvious: the gilby sprite in the bonus phase is impossibly ugly. It's such a contrast with the +elegance of the main game's gilby sprite that for me it spoils the effect of the mini-game completely. + +There is of course +a simple technical reason why the gilby sprite design is completely unsuitable for the vertical orientation used in the +bonus phase. Multi-coloured sprites cannot be defined in terms of single pixels - the building blocks are always two pixels +wide so it is by definition not possible to create an object oriented on the Y axis that does not look unappealingly blocky. \begin{figure}[H] { @@ -844,7 +891,8 @@ \section{Some Very Ugly Sprites\index{Sprites}} \end{figure} \section{IBalls} - +The main adversary in the bonus phase, these at least have the merit of being visually appealing. The blinking animation is +particularly pleasing, if a little eerie! \begin{figure}[H] { \setlength{\tabcolsep}{1.0pt} diff --git a/src/book.tex b/src/book.tex index b52915e9..6663ead0 100644 --- a/src/book.tex +++ b/src/book.tex @@ -526,7 +526,6 @@ \subfile{src/torus} % draft \subfile{src/mif} \subfile{src/dna} % wip -\subfile{src/torus-animation} % draft \subfile{src/bugs} %draft \appendix \chapter*{Appendices} diff --git a/src/bugs.tex b/src/bugs.tex index b0219402..27b59109 100644 --- a/src/bugs.tex +++ b/src/bugs.tex @@ -13,23 +13,15 @@ \section{The Byte that Broke} We must apologise to some of the earliest purchasers of IA, as well... it seems that some data was corrupted during the production phase of the data duplication, and the first few hundred copies of the game used to bug -out if you lost at the Bonus Phase All the buggy copies known of have +out if you lost at the Bonus Phase. All the buggy copies known of have been replaced, and all current tapes are fine, but if you find you have a buggy version you should send it back to those awfully nice Hewsons -people and they ll give you an unglitchified one, +people and they ll give you an unglitchified one. \end{definition} -What happened is that the master tape had dropped the very last byte in the second +What happened is that the mastering process had dropped the very last byte in the second section of game data on the tape. -\begin{figure}[H] - { - \begin{adjustbox}{width=10cm,center} - \surface{bugs/spool-sections-glitches.png} - \end{adjustbox} - }\caption[]{The problematic section of data highlighted in red.} -\end{figure} - \begin{figure}[H] { \setlength{\tabcolsep}{3.0pt} @@ -53,6 +45,15 @@ \section{The Byte that Broke} }\caption{Chunk \icode{BF00} is missing its last byte.} \end{figure} +\begin{figure}[H] + { + \begin{adjustbox}{width=10cm,center} + \surface{bugs/spool-sections-glitches.png} + \end{adjustbox} + }\caption[]{The problematic section of data highlighted in red.} +\end{figure} + + \begin{lstlisting}[caption=The data segment as it should be\, with \icode{\$A2} at \icode{\$BFFF},escapechar=\%] $BFFF A2 07 LDX #$07 $C001 A9 08 LDA #$08 diff --git a/src/dna.tex b/src/dna.tex index d5d4a4d6..210c8ce1 100644 --- a/src/dna.tex +++ b/src/dna.tex @@ -1,20 +1,17 @@ \chapter{A Pause Mode for your Pause Mode} \label{sec:dna} \lstset{style=6502Style} + +Any pause mode must surely be in need of a pause mode. Titled 'DNA' this little entertainment is +a cousin of Minter's previous work on Psychedelia for the C64 and Colorspace for the Atari 800 +in 1984 and 1985. It isn't accessed directly from the game but instead is invoked by pressing the +asterisk key while playing 'Made in France'. + \begin{figure}[H] \centering \includegraphics[width=10cm]{src/dna/dnascreenshot.png}% \caption{DNA: A pause mode within a pause mode.} \end{figure} -\begin{lstlisting}[caption=Defining the text for the DNA screen\index{screen},escapechar=\%] -titleTextLine1%\index{titleTextLine1}% .TEXT " % % % DNA % % % " -titleTextLine2%\index{titleTextLine2}% .TEXT " CONCEIVED%\index{CONCEIVED}% AND EXECUTED%\index{EXECUTED}% B" -titleTextLine3%\index{titleTextLine3}% .TEXT "Y Y A K " -titleTextLine4%\index{titleTextLine4}% .TEXT " SPACE: CANCEL SCREEN TEX" -titleTextLine5%\index{titleTextLine5}% .TEXT "TF5 AND F7 CHANGE COLOURS" -titleTextLine6%\index{titleTextLine6}% .TEXT " LISTEN TO TALKING%\index{TALKING}% HEADS." -titleTextLine7%\index{titleTextLine7}% .TEXT ".BE NICE TO HAIRY ANIMALS%\index{ANIMALS}% " -\end{lstlisting} \begin{figure}[H] @@ -31,10 +28,15 @@ \chapter{A Pause Mode for your Pause Mode} \end{figure} \clearpage -Any pause mode must surely be in need of a pause mode. Titled 'DNA' this little entertainment is -a cousin of Minter's previous work on Psychedelia for the C64 and Colorspace for the Atari 800 -in 1984 and 1985. It isn't accessed directly from the game but instead is invoked by pressing the -asterisk key while playing 'Made in France'. +\begin{lstlisting}[caption=Defining the text for the DNA screen\index{screen},escapechar=\%] +titleTextLine1%\index{titleTextLine1}% .TEXT " % % % DNA % % % " +titleTextLine2%\index{titleTextLine2}% .TEXT " CONCEIVED%\index{CONCEIVED}% AND EXECUTED%\index{EXECUTED}% B" +titleTextLine3%\index{titleTextLine3}% .TEXT "Y Y A K " +titleTextLine4%\index{titleTextLine4}% .TEXT " SPACE: CANCEL SCREEN TEX" +titleTextLine5%\index{titleTextLine5}% .TEXT "TF5 AND F7 CHANGE COLOURS" +titleTextLine6%\index{titleTextLine6}% .TEXT " LISTEN TO TALKING%\index{TALKING}% HEADS." +titleTextLine7%\index{titleTextLine7}% .TEXT ".BE NICE TO HAIRY ANIMALS%\index{ANIMALS}% " +\end{lstlisting} Minter first shared it as a tiny 11K demo in a UK Compunet forum in the summer of 1986. It followed shortly after 'Torus', an oscillator-based demo, shared at the same time and which we @@ -102,16 +104,14 @@ \chapter{A Pause Mode for your Pause Mode} \begin{minipage}[b]{0.75\linewidth} \centering -\begin{lstlisting}[caption=\icode{dnaCurrentPhase\index{dnaCurrentPhase}} is set by the 'Q' key.,escapechar=\%] +\begin{lstlisting}[caption=\icode{dnaCurrentPhase\index{dnaCurrentPhase}} is +set by the 'Q' key. ,basicstyle=\scriptsize\ttfamily,escapechar=\%] LDX dnaCurrentSpritesPosArrayIndex%\index{dnaCurrentSpritesPosArrayIndex}% .. - ; Add in the phase to our index to the X position - ; of the sprite on the right hand chain. If the - ; result is greater than the number of values - ; in the array ($27) subtract it out again. - ; This means the 'Phase' setting acts as an - ; offset into the X Position array picking up - ; previous values of X Pos from the left hand chain. + ; Add in the phase to our index to the X position of the + ; sprite on the right hand chain. If the result is greater + ; than the number of values in the array ($27) subtract it + ; out again. TXA .. ADC dnaCurrentPhase%\index{dnaCurrentPhase}% @@ -134,8 +134,11 @@ \chapter{A Pause Mode for your Pause Mode} \begin{minipage}[b]{0.25\linewidth} \centering \includegraphics[width=3cm]{dna/dnaphase.png}% - \vspace{5cm} + \vspace{2cm} \end{minipage} +In essence, the 'Phase' setting acts as an offset into the X +Position array picking up previous values of X Pos from the left hand +chain. In the table below we've selected a few frames from the first second or two showing how the array fills up and how values from the array are selected for the X position of the left and right chains. Notice how the 'Phase' setting of 5 effectively means @@ -155,11 +158,11 @@ \chapter{A Pause Mode for your Pause Mode} } & \makecell[l]{ \lstinputlisting[linewidth=4.5cm,framexleftmargin=-0.1cm,framexrightmargin=-0.22cm, - basicstyle=\tiny,escapechar=!,backgroundcolor=\color{white},frame=single,framerule=0pt]{dna/initial/frame46.tex} + basicstyle=\tiny\ttfamily,escapechar=!,backgroundcolor=\color{white},frame=single,framerule=0pt]{dna/initial/frame46.tex} } & \makecell[l]{ \lstinputlisting[linewidth=4.5cm,framexleftmargin=-0.1cm,framexrightmargin=-0.22cm, - basicstyle=\tiny,escapechar=!,backgroundcolor=\color{white},frame=single,framerule=0pt]{dna/initial/frame46r.tex} + basicstyle=\tiny\ttfamily,escapechar=!,backgroundcolor=\color{white},frame=single,framerule=0pt]{dna/initial/frame46r.tex} } \\ \addlinespace \makecell[l]{ @@ -167,11 +170,11 @@ \chapter{A Pause Mode for your Pause Mode} } & \makecell[l]{ \lstinputlisting[linewidth=4.5cm,framexleftmargin=-0.1cm,framexrightmargin=-0.22cm, - basicstyle=\tiny,escapechar=!,backgroundcolor=\color{white},frame=single,framerule=0pt]{dna/initial/frame58.tex} + basicstyle=\tiny\ttfamily,escapechar=!,backgroundcolor=\color{white},frame=single,framerule=0pt]{dna/initial/frame58.tex} } & \makecell[l]{ \lstinputlisting[linewidth=4.5cm,framexleftmargin=-0.1cm,framexrightmargin=-0.22cm, - basicstyle=\tiny,escapechar=!,backgroundcolor=\color{white},frame=single,framerule=0pt]{dna/initial/frame58r.tex} + basicstyle=\tiny\ttfamily,escapechar=!,backgroundcolor=\color{white},frame=single,framerule=0pt]{dna/initial/frame58r.tex} } \\ \addlinespace \makecell[l]{ @@ -179,11 +182,11 @@ \chapter{A Pause Mode for your Pause Mode} } & \makecell[l]{ \lstinputlisting[linewidth=4.5cm,framexleftmargin=-0.1cm,framexrightmargin=-0.22cm, - basicstyle=\tiny,escapechar=!,backgroundcolor=\color{white},frame=single,framerule=0pt]{dna/initial/frame67.tex} + basicstyle=\tiny\ttfamily,escapechar=!,backgroundcolor=\color{white},frame=single,framerule=0pt]{dna/initial/frame67.tex} } & \makecell[l]{ \lstinputlisting[linewidth=4.5cm,framexleftmargin=-0.1cm,framexrightmargin=-0.22cm, - basicstyle=\tiny,escapechar=!,backgroundcolor=\color{white},frame=single,framerule=0pt]{dna/initial/frame67r.tex} + basicstyle=\tiny\ttfamily,escapechar=!,backgroundcolor=\color{white},frame=single,framerule=0pt]{dna/initial/frame67r.tex} } \\ \addlinespace \bottomrule @@ -303,17 +306,11 @@ \chapter{A Pause Mode for your Pause Mode} \end{lstlisting} When we compare the routines responsible for calculating the X Pos for Wave 2 and Wave 1 side by side on the next -page we can see many similarities between the two. They are both maintaining and updating an index into -\icode{newXPosOffsetsArray\index{newXPosOffsetsArray}} and using that to come up with a new X position. They both have to deal towards the end -of the routine\index{routine} with the need to ensure the index does not exceed \icode{\$40} and subtract \icode{\$40} from it if -it does. The output of \icode{CalculateValueOfNewXPosForWave2} is essentially the value stored in \icode{notionalNewXPosForWave2\index{notionalNewXPosForWave2}} -and this is what is used as the input above, in conjunction with \icode{newXPosForWave1\index{newXPosForWave1}}, to come up with the final -value for the X position for Wave 1. It is this value that gets added to the head of the working array \icode{dnaXPosDataHeadArray} -by \icode{DNA\_PropagatePreviousXPosToTheRight}. +page we can see many similarities between the two. -\begin{minipage}[b]{0.55\linewidth} +\begin{minipage}[b]{0.50\linewidth} \centering -\begin{lstlisting}[escapechar=\%,basicstyle=\tiny, caption=Calculate a notional X Pos value for Wave 2. This routine\index{routine} is called by the one on the +\begin{lstlisting}[escapechar=\%,basicstyle=\tiny\ttfamily, caption=Calculate a notional X Pos value for Wave 2. This routine\index{routine} is called by the one on the right\, its return value is \icode{notionalNewXPosForWave2\index{notionalNewXPosForWave2}}. ] CalculateValueOfNewXPosForWave2 LDA dnaWave2Enabled%\index{dnaWave2Enabled}% @@ -367,9 +364,9 @@ \chapter{A Pause Mode for your Pause Mode} \end{lstlisting} \end{minipage} \hspace{0.5cm} -\begin{minipage}[b]{0.55\linewidth} +\begin{minipage}[b]{0.50\linewidth} \centering - \begin{lstlisting}[escapechar=\%,basicstyle=\tiny, caption=Calculate the new X Pos value for the head sprites\index{sprites}\, as well as propagate the + \begin{lstlisting}[escapechar=\%,basicstyle=\tiny\ttfamily, caption=Calculate the new X Pos value for the head sprites\index{sprites}\, as well as propagate the values in \icode{dnaXPosDataHeadArray} to the right when required.] CalculateValueOfNewXPosForWave1 DEC actualSpeed%\index{actualSpeed}% @@ -435,6 +432,14 @@ \chapter{A Pause Mode for your Pause Mode} \end{minipage} +They are both maintaining and updating an index into +\icode{newXPosOffsetsArray\index{newXPosOffsetsArray}} and using that to come up with a new X position. They both have to deal towards the end +of the routine\index{routine} with the need to ensure the index does not exceed \icode{\$40} and subtract \icode{\$40} from it if +it does. The output of \icode{CalculateValueOfNewXPosForWave2} is essentially the value stored in \icode{notionalNewXPosForWave2\index{notionalNewXPosForWave2}} +and this is what is used as the input above, in conjunction with \icode{newXPosForWave1\index{newXPosForWave1}}, to come up with the final +value for the X position for Wave 1. It is this value that gets added to the head of the working array \icode{dnaXPosDataHeadArray} +by \icode{DNA\_PropagatePreviousXPosToTheRight}. + diff --git a/src/level_data.tex b/src/level_data.tex index b051fe8e..6bf9414e 100644 --- a/src/level_data.tex +++ b/src/level_data.tex @@ -93,7 +93,7 @@ \chapter{Enemies and their Discontents} \section{Something Simple - Byte 0: The Enemy's Color} To get an understanding of how the level data is used we can start with the very first byte of the data used for the first wave in the very first level. This is the wave of flying saucers you will already be -familiar with if you have played the game (:)): +familiar with if you have played the game: \begin{figure}[H] { \setlength{\tabcolsep}{3.0pt} @@ -122,8 +122,8 @@ \section{Something Simple - Byte 0: The Enemy's Color} \begin{lstlisting}[escapechar=\%] planet1Level1Data%\index{planet1Level1Data}% - ; Byte 0 (Index $00): An index into colorsForAttackShips%\index{colorsForAttackShips}% that applies a - ; color value for the ship sprite. + ; Byte 0 (Index $00): An index into colorsForAttackShips%\index{colorsForAttackShips}% + ; that applies a color value for the ship sprite. .BYTE $06 \end{lstlisting} @@ -139,9 +139,6 @@ \section{Something Simple - Byte 0: The Enemy's Color} to the register\index{register} \icode{\$D027} that determines the color of the sprite: \begin{lstlisting}[escapechar=\%] -;------------------------------------------------------- -; DrawUpperPlanetAttackShips%\index{DrawUpperPlanetAttackShips}% Routine -;------------------------------------------------------- DrawUpperPlanetAttackShips%\index{DrawUpperPlanetAttackShips}% LDX #$0C LDY #$06 @@ -208,7 +205,7 @@ \section{Bytes 1-4: Sprite Animation} \begin{subfigure}{0.3\textwidth} \input{sprites/CUMMING_COCK3} \end{subfigure} - }\caption[position=top]{Confirmation that the game developer was a young male. The sprites\index{sprites} used to animate attack wave 17 in the Mushroom Planet.} + }\caption[position=top]{Confirmation that the game developer was a young caucasian male. The sprites\index{sprites} used to animate attack wave 17 in the Mushroom Planet.} \end{figure} Looking again at the table in the previous page we can see the first 7 bytes are concerned with the appearance and basic behaviour of the @@ -776,7 +773,7 @@ \section{Bytes 6-8: Alternate Enemy Waves} -\begin{table}[H] +\begin{figure}[H] { \setlength{\tabcolsep}{3.0pt} \setlength\cmidrulewidth{\heavyrulewidth} % Make cmidrule = @@ -803,7 +800,7 @@ \section{Bytes 6-8: Alternate Enemy Waves} \end{adjustbox} }\caption{Actual use of Bytes 6, 7, and 8. Note that the value in Byte 6 doesn't matter, as long as it's non-zero.} -\end{table} +\end{figure} \section{Bytes 22-23: The Bytes of Death} @@ -834,11 +831,11 @@ \section{Bytes 22-23: The Bytes of Death} \begin{lstlisting}[escapechar=\%] lickerShipWaveData%\index{lickerShipWaveData}% = $1118 .. - ; Byte 22 (Index $16): Stickiness%\index{Stickiness}% factor, does the enemy stick to the player - ; sapping their energy%\index{energy}% if they're near them? + ; Byte 22 (Index $16): Stickiness%\index{Stickiness}% factor, does the enemy stick to + ; the player sapping their energy%\index{energy}% if they're near them? .BYTE $01 - ; Byte 23 (Index $17): Does the enemy gravitate quickly toward the player when its - ; been shot? (Typical lickership%\index{lickership}% behaviour) + ; Byte 23 (Index $17): Does the enemy gravitate quickly toward the + ; player when its been shot? (Typical lickership%\index{lickership}% behaviour) .BYTE $01 .. \end{lstlisting} @@ -849,7 +846,8 @@ \section{Bytes 22-23: The Bytes of Death} \begin{lstlisting}[escapechar=\%] MaybeQuicklyGravitatesToGilby%\index{MaybeQuicklyGravitatesToGilby}% - ; Byte 23: Does the enemy gravitate quickly towards the gilby when it is shot? + ; Byte 23: Does the enemy gravitate quickly towards the gilby + ; when it is shot? LDY #$17 LDA (currentShipWaveDataLoPtr%\index{currentShipWaveDataLoPtr}%),Y BEQ MaybeStickyAttackShipBehaviour%\index{MaybeStickyAttackShipBehaviour}% @@ -947,7 +945,8 @@ \section{Byte 35: Energy Multiplier} LDA SCREEN_RAM%\index{SCREEN\_RAM}% + LINE22_COL3%\index{LINE22\_COL3}%,X CMP #$7F BNE b547B - ; Note the reference to the index $80 of the first tile in the set above. + ; Note the reference to the index $80 of the first tile + ; in the set above. LDA #$80 STA SCREEN_RAM%\index{SCREEN\_RAM}% + LINE22_COL3%\index{LINE22\_COL3}%,X INX @@ -1063,11 +1062,13 @@ \section{Bytes 9-11: A Clever Plan} BEQ MaybeQuicklyGravitatesToGilby%\index{MaybeQuicklyGravitatesToGilby}% UnusedRoutine%\index{UnusedRoutine}% - ; As above, this section is never reached because Byte 10 is never set. + ; As above, this section is never reached because Byte 10 + ; is never set. DEC someKindOfRateLimitingForAttackWaves%\index{someKindOfRateLimitingForAttackWaves}%,X BNE MaybeQuicklyGravitatesToGilby%\index{MaybeQuicklyGravitatesToGilby}% - ; Store Bytes 10 and 11 in newMovementLoPtr%\index{newMovementLoPtr}% and newMovementHiPtr%\index{newMovementHiPtr}% respectively. + ; Store Bytes 10 and 11 in newMovementLoPtr%\index{newMovementLoPtr}% and + ; newMovementHiPtr%\index{newMovementHiPtr}% respectively. STA newMovementHiPtr%\index{newMovementHiPtr}% DEY ; Byte 9: Y is now $09. @@ -1177,30 +1178,30 @@ \section{Bytes 15-17: Sprite Switching} switch to: \begin{lstlisting}[escapechar=\%] SwitchToAlternatingWaveData%\index{SwitchToAlternatingWaveData}% - LDY #16 + LDY #16 ;------------------------------------------------------- ; UpdateWaveDataPointersForCurrentEnemy%\index{UpdateWaveDataPointersForCurrentEnemy}% ;------------------------------------------------------- UpdateWaveDataPointersForCurrentEnemy%\index{UpdateWaveDataPointersForCurrentEnemy}% - ; Byte 16 Y has been set to 16 above, so we're pulling in the pointer%\index{pointer}% - ; to the second tranche of wave data for this level. - ; Or Y has been set by the caller. - LDA (currentShipWaveDataLoPtr%\index{currentShipWaveDataLoPtr}%),Y - PHA - INY - ; Byte 17 - LDA (currentShipWaveDataLoPtr%\index{currentShipWaveDataLoPtr}%),Y + ; Byte 16 Y has been set to 16 above, so we're pulling in the pointer%\index{pointer}% + ; to the second tranche of wave data for this level. + ; Or Y has been set by the caller. + LDA (currentShipWaveDataLoPtr%\index{currentShipWaveDataLoPtr}%),Y + PHA + INY + ; Byte 17 + LDA (currentShipWaveDataLoPtr%\index{currentShipWaveDataLoPtr}%),Y - ; If we have a nullPtr%\index{nullPtr}% then there's no wave data to get - ; so the enemy ship can be cleared out and we can return. - BEQ ClearDeadShipFromLevelData%\index{ClearDeadShipFromLevelData}% + ; If we have a nullPtr%\index{nullPtr}% then there's no wave data to get + ; so the enemy ship can be cleared out and we can return. + BEQ ClearDeadShipFromLevelData%\index{ClearDeadShipFromLevelData}% - STA activeShipsWaveDataHiPtrArray%\index{activeShipsWaveDataHiPtrArray}%,X - STA currentShipWaveDataHiPtr%\index{currentShipWaveDataHiPtr}% - PLA - STA currentShipWaveDataLoPtr%\index{currentShipWaveDataLoPtr}% - STA activeShipsWaveDataLoPtrArray%\index{activeShipsWaveDataLoPtrArray}%,X + STA activeShipsWaveDataHiPtrArray%\index{activeShipsWaveDataHiPtrArray}%,X + STA currentShipWaveDataHiPtr%\index{currentShipWaveDataHiPtr}% + PLA + STA currentShipWaveDataLoPtr%\index{currentShipWaveDataLoPtr}% + STA activeShipsWaveDataLoPtrArray%\index{activeShipsWaveDataLoPtrArray}%,X \end{lstlisting} In the case of Level 1 this second set of wave data is at \icode{planet1Level1Data2ndStage\index{planet1Level1Data2ndStage}}. This data is more @@ -1276,7 +1277,7 @@ \section{Bytes 28-29: Data to Load When Hit by a Bullet} { \setlength{\tabcolsep}{3.0pt} \setlength\cmidrulewidth{\heavyrulewidth} % Make cmidrule = - \begin{adjustbox}{width=13cm} + \begin{adjustbox}{width=10cm,center} \begin{tabular}{lll} \toprule diff --git a/src/music.tex b/src/music.tex index 1377487e..1f6f304c 100644 --- a/src/music.tex +++ b/src/music.tex @@ -295,22 +295,26 @@ \section{Some Basics} dynamically selected one: +\begin{lstlisting}[escapechar=\%] +titleMusicHiBytes%\index{titleMusicHiBytes}% + ; C C# D D# E F F# G G# A A# B + .BYTE $08,$08,$09,$09,$0A,$0B,$0B,$0C,$0D,$0E,$0E,$0F ; 4 + .BYTE $10,$11,$12,$13,$15,$16,$17,$19,$1A,$1C,$1D,$1F ; 5 + .BYTE $21,$23,$25,$27,$2A,$2C,$2F,$32,$35,$38,$3B,$3F ; 6 + .BYTE $43,$47,$4B,$4F,$54,$59,$5E,$64,$6A,$70,$77,$7E ; 7 + .BYTE $86,$8E,$96,$9F,$A8,$B3,$BD,$C8,$D4,$E1,$EE,$FD ; 8 +\end{lstlisting} +\clearpage \begin{lstlisting}[escapechar=\%,caption=The lookup table for all of the notes used in the theme music. The two lowest available octaves\index{octaves} are not used by the game. To see this for yourself\, compare the first entry in \icode{titleMusicHiBytes\index{titleMusicHiBytes}}/\icode{titleMusicLowBytes\index{titleMusicLowBytes}} (\$08 and \$61\, -giving \$0861) with the entry highlighted in red in the previous table.,basicstyle=\tiny] - ; C C# D D# E F F# G G# A A# B -titleMusicHiBytes%\index{titleMusicHiBytes}% .BYTE $08,$08,$09,$09,$0A,$0B,$0B,$0C,$0D,$0E,$0E,$0F ; 4 - .BYTE $10,$11,$12,$13,$15,$16,$17,$19,$1A,$1C,$1D,$1F ; 5 - .BYTE $21,$23,$25,$27,$2A,$2C,$2F,$32,$35,$38,$3B,$3F ; 6 - .BYTE $43,$47,$4B,$4F,$54,$59,$5E,$64,$6A,$70,$77,$7E ; 7 - .BYTE $86,$8E,$96,$9F,$A8,$B3,$BD,$C8,$D4,$E1,$EE,$FD ; 8 - - ; C C# D D# E F F# G G# A A# B -titleMusicLowBytes%\index{titleMusicLowBytes}% .BYTE $61,$E1,$68,$F7,$8F,$30,$DA,$8F,$4E,$18,$EF,$D2 ; 4 - .BYTE $C3,$C3,$D1,$EF,$1F,$60,$B5,$1E,$9C,$31,$DF,$A5 ; 5 - .BYTE $87,$86,$A2,$DF,$3E,$C1,$6B,$3C,$39,$63,$BE,$4B ; 6 - .BYTE $0F,$0C,$45,$BF,$7D,$83,$D6,$79,$73,$C7,$7C,$97 ; 7 - .BYTE $1E,$18,$8B,$7E,$FA,$06,$AC,$F3,$E6,$8F,$F8,$2E ; 8 +giving \$0861) with the entry highlighted in red in the previous table.] +titleMusicLowBytes%\index{titleMusicLowBytes}% + ; C C# D D# E F F# G G# A A# B + .BYTE $61,$E1,$68,$F7,$8F,$30,$DA,$8F,$4E,$18,$EF,$D2 ; 4 + .BYTE $C3,$C3,$D1,$EF,$1F,$60,$B5,$1E,$9C,$31,$DF,$A5 ; 5 + .BYTE $87,$86,$A2,$DF,$3E,$C1,$6B,$3C,$39,$63,$BE,$4B ; 6 + .BYTE $0F,$0C,$45,$BF,$7D,$83,$D6,$79,$73,$C7,$7C,$97 ; 7 + .BYTE $1E,$18,$8B,$7E,$FA,$06,$AC,$F3,$E6,$8F,$F8,$2E ; 8 \end{lstlisting} So now that we know where the notes are and how to make them go beep we just have to figure out the order that \icode{PlayTitleScreenMusic\index{PlayTitleScreenMusic}} @@ -321,8 +325,8 @@ \section{Some Basics} \begin{figure}[H] { - \begin{adjustbox}{width=11cm,center} - \includegraphics[width=11cm]{music/title_no_1_page_1001.png}% + \begin{adjustbox}{width=13cm,center} + \includegraphics{music/title_no_1_page_1001.png}% \end{adjustbox} }\caption[]{The first title tune in Iridis Alpha.} \end{figure} @@ -367,7 +371,7 @@ \subsection{Structure} Here it is deciding whether or not to play new note on Voice 1: -\begin{lstlisting}[caption=\icode{MaybePlayVoice1\index{MaybePlayVoice1}}\, part of \icode{PlayTitleScreenMusic\index{PlayTitleScreenMusic}}.,basicstyle=\tiny,escapechar=\%] +\begin{lstlisting}[caption=\icode{MaybePlayVoice1\index{MaybePlayVoice1}}\, part of \icode{PlayTitleScreenMusic\index{PlayTitleScreenMusic}}.,escapechar=\%] MaybePlayVoice1%\index{MaybePlayVoice1}% DEC voice1NoteDuration%\index{voice1NoteDuration}% BNE MaybePlayVoice2%\index{MaybePlayVoice2}% @@ -447,7 +451,7 @@ \subsection{Structure} AND #$03 STA voice3IndexToMusicNoteArray%\index{voice3IndexToMusicNoteArray}% \end{lstlisting} - +\clearpage \begin{definition}[Extracting the Title Music] \setlength{\intextsep}{0pt}% \setlength{\columnsep}{3pt}% @@ -477,16 +481,16 @@ \subsection{Structure} We end up with \icode{IridisAlphaTitleMusicAll.txt} full of lines like: -\begin{lstlisting}[basicstyle=\tiny,escapechar=\%] +\begin{lstlisting}[basicstyle=\footnotesize\ttfamily,escapechar=\%] TRACE: 1 C:$d400-$d415 (Trace store) #1 (Trace store d400) 279 052 -.C:1598 8D 00 D4 STA $D400 - A:61 X:00 Y:00 SP:e8 ..-..I.. 96135469 +.C:1598 8D 00 D4 STA $D400 - A:61 X:00 Y:00 SP:e8 96135469 #1 (Trace store d401) 279 060 -.C:159e 8D 01 D4 STA $D401 - A:08 X:00 Y:00 SP:e8 ..-..I.. 96135477 +.C:159e 8D 01 D4 STA $D401 - A:08 X:00 Y:00 SP:e8 96135477 #1 (Trace store d40b) 280 059 \end{lstlisting} -This examples gives us the value in \icode{A} written to each register\index{register} for Voice 1. For example, \icode{\$61} +This snppiet gives us the value in \icode{A} written to each register\index{register} for Voice 1. For example, \icode{\$61} has been written to \icode{\$D400} and \icode{\$08} has been written to \icode{\$D401}. We can now write a short Python \href{https://github.com/mwenge/iatheory/tree/main/notebooks}{notebook} that @@ -497,10 +501,16 @@ \subsection{Structure} With the sequence\index{sequence} of notes in three arrays, each representing one of the 3 voices, it is a simple matter to transform this into ABC format, a music notation frequently used for traditional music. +\end{definition} +\clearpage +\begin{definition}[Extracting the Title Music cont.] +\setlength{\intextsep}{0pt}% +\setlength{\columnsep}{3pt}% +\small -\lstinputlisting[caption=Title Tune No 1 in ABC format, basicstyle=\tiny]{music/title_no_1_page_1.abc} +\lstinputlisting[caption=Title Tune No 1 in ABC format, basicstyle=\tiny\ttfamily]{music/title_no_1_page_1.abc} -We can then use the tool `abcm2ps` to transform this into an SVG image file giving the music in + We can then use the tool \icode{abcm2ps} to transform this into an SVG image file giving the music in standard Western notation. \end{definition} @@ -558,8 +568,8 @@ \subsection{Phrasing} }\caption{The value in \icode{titleMusicNoteArray\index{titleMusicNoteArray}} is an index into \icode{titleMusicHiBytes\index{titleMusicHiBytes}/titleMusicLowBytes\index{titleMusicLowBytes}}.} \end{figure} - Playing the 4 note phrase we've stored in this array is done here: +\clearpage \begin{lstlisting}[escapechar=\%] ; Play the note currently pointed to by @@ -631,7 +641,7 @@ \subsection{Phrasing} \begin{figure}[H] { - \begin{adjustbox}{width=11cm,center} + \begin{adjustbox}{width=13cm,center} \includegraphics[width=11cm]{music/16BarStructure_Tune14.png}% \end{adjustbox} }\caption[]{A full 16 bar passage showing the nested structure\index{structure} of Voices 1 and 2} diff --git a/src/torus.tex b/src/torus.tex index 3c2279e5..bf5f7573 100644 --- a/src/torus.tex +++ b/src/torus.tex @@ -2,27 +2,60 @@ \chapter{Another 16\textsuperscript{4} Tunes} \label{sec:torusmusic} It's possible to dig into the making of the title music and how Jeff Minter -arrived the music configuration he did thanks to a number of tiny demo programs that +arrived at the music configuration he did thanks to a number of tiny demo programs that survive from the period when he was developing Iridis Alpha. -Jeff distributed these on Compunet in the summer of 1986. +Jeff distributed these little toys on `Compunet` in the summer of 1986. `Compunet' was a +predecessor to the modern internet that enabled C64 users with the equipment and +determination to dial up a local computer server over telephone landlines. Once dialled in, +they could participate in message boards and exchange and download files such as the little +\icode{prg} demos that Jeff Minter created here, +\href{https://github.com/mwenge/iridisalpha/tree/master/demos/torus}{\textcolor{blue}{\icode{torus.prg}}} +and +\href{https://github.com/mwenge/iridisalpha/tree/master/demos/torus2}{\textcolor{blue}{\icode{torus2.prg}}}. -It turns out he was inspired by an article in 'Byte' magazine from June 1986 that -described how to make 'Fractal Music'. This article outline a version of the +It turns out that Minter was heavily inspired by an article in 'Byte' magazine from June 1986. +This article, 'Musical Fractals' by Charles Dodge and Curtis Bahn outlined a version of the algorithm that Jeff ultimately adopted. The 'self-similarity' we encountered in the way the Iridis Alpha theme tunes are constructed, a four-note structure\index{structure} repeated across different time intervals on each of the three voices, finds its -roots in this article. - +roots in this article. The basic concept of layering we encountered in the previous chapter is illustrated using a tree diagram. The musical phrase played +at the top-most level is spread out in time in the levels above: \begin{figure}[H] -{ - \begin{adjustbox}{width=11cm,center} - \includegraphics[width=11cm]{torus/fractal.jpg}% + { + \begin{adjustbox}{width=9cm,center} + \frame{\includegraphics[width=11cm]{torus/fractal_illustration.png}}% \end{adjustbox} -}\caption[]{} + }\caption[]{The layering of the same notes across different time intervals, from \href{https://archive.org/details/byte-magazine-1986-06/page/n191/mode/2up}{\textcolor{blue}{'Musical Fractals'}}} \end{figure} +The listings given in the article were in \icode{BASIC} and inscrutable to anything but the most minute attention: +\begin{lstlisting}[escapechar=\%,caption=A snippet from \icode{VARIATN.BAS}\, a progam for determinate fractal and Brownian variation.] +200 REM FRACTAL ROUTINE +205 PRINT"COMPUTING FRACTAL" +210 FOR Am1 TO PN +220 AP(A)=P(A)+RC:AD(A)=D(A) +230 FOR 131 TO PN +240 BC=BC+1: IF R -1 THEN GOSUB 700 +245 BP(BC).AP(A)+P(B)+RC:BD(BC).D(B)*D(A) +250 FOR 0.1 TO PN +260 CC.CC+1:IF R.1 THEN GOSUB 700 +270 CP(CC).1111,(BC)+P(C)+RC:CD(CC).D(C)*BEI(BC):DT.DT+GD(CG) +280 NEXT C: NEXT B: NEXT A +\end{lstlisting} + + +Certainly, Minter must have experimented with them in order to arrive at his own version of fractal music. But it seems much more likely +that what caught his eye were patterns such as this one, which in a way make the general idea obvious and intuitive to the casual +reader: +\begin{figure}[H] + { + \begin{adjustbox}{width=12cm,center} + \frame{\includegraphics[width=11cm]{torus/fractal_notes.png}}% + \end{adjustbox} + }\caption[]{A pattern that looks familiar from our notation of Iridis Alpha's title music, from \href{https://archive.org/details/byte-magazine-1986-06/page/n191/mode/2up}{\textcolor{blue}{'Musical Fractals'}}} +\end{figure} \section{Taurus:Torus} \begin{figure}[H] @@ -30,58 +63,470 @@ \section{Taurus:Torus} \begin{adjustbox}{width=11cm,center} \includegraphics[width=11cm]{torus/torus.png}% \end{adjustbox} -}\caption[]{} +}\caption[]{The splash screen for the Taurus:Torus demo.} \end{figure} -This first demo, released in July 1986(?), has a version of Iridis' music-generating +This first demo, released in July 1986, has a version of Iridis' music-generating algorithm that is nearly fully formed. However, the music it produces is quite different. In fact, it is nearer to a tool for listening to and selecting music than anything else. -The four seed values in \icode{titleMusicNoteArray\index{titleMusicNoteArray}} that are used to seed all subequently +The four seed values in \icode{titleMusicNoteArray\index{titleMusicNoteArray}} that are used to seed all subsequently generated tunes (\icode{00 07 0C 07} in Iridis Alpha) can be selected and changed by the user. They're called 'Oscillators' and each can be any value between 0 and 16, i.e. any of \icode{0 1 2 3 4 5 6 7 8 9 A B C D E F}. - +If we compare the title screen music routine from Iridis Alpha and the routine we find in \icode{Taurus:Torus} we can see that we +have a fractal-like self-similarity between the two going on. There are in fact only two points of difference, see if you can +spot them by glancing through the listings side by side: \lstset{style=6502Style} -\lstinputlisting[caption=The music routine\index{routine} in Torus:Taurus side-by-side with Iridis Alpha.,basicstyle=\tiny]{torus/sidebyside.asm} +\lstinputlisting[caption=The music routine\index{routine} in Torus:Taurus side-by-side with Iridis Alpha.,basicstyle=\tiny\ttfamily]{torus/sidebyside.asm} -When it runs the demo cycles through procedural\index{procedural} configurations of \icode{titleMusicNoteArray\index{titleMusicNoteArray}} of 64 notes each. +The Iridis Alpha routine has an extra step. At the very start it has a trapdoor for bailing out. This allows it control the duration +of the notes selected for playing: + +\begin{lstlisting}[] +PlayTitleScreenMusic + DEC baseNoteDuration + BEQ MaybeStartNewTune + RTS + +MaybeStartNewTune + LDA previousBaseNoteDuration + STA baseNoteDuration +\end{lstlisting} + +The second difference lies in the placement of \icode{JSR SelectNewNotesToPlay}. The Torus demo selects new procedurally generated notes at every visit, +whereas the Iridis Alpha version only selects new notes at the beginning of a new 16 bar structure. +\begin{lstlisting}[] + ; We'll only select a new tune when + ; we've reached the beginning of a + ; new 16 bar structure. + INX + TXA + AND #$03 + STA notesPlayedSinceLastKeyChange + BNE MaybePlayVoice1 + + JSR SelectNewNotesToPlay +\end{lstlisting} + +When it runs, the demo cycles through procedural\index{procedural} configurations of \icode{titleMusicNoteArray\index{titleMusicNoteArray}} of 64 notes each. In other words, exactly the kind of fractal structure\index{structure} we observed in Iridis Alpha proper. The examples below -give a flavour of the music it generats: +give a flavour of the music it generates: \begin{figure}[H] { \begin{adjustbox}{width=14cm,center} \includegraphics[width=14cm]{torus/title_no_1_page_1001.png}}% \end{adjustbox} -}\caption[]{Bars 2 and 4 are always repeated} +}\caption[]{A pattern familiar from our discussion of the main title's music.} \end{figure} +As before, the key to understanding the principle in operation here is that the four notes played by the entirety of 'Voice 1' are played in the first +four bars of 'Voice 2', and the first single bar of 'Voice 3'. The notes from each voice seed the notes selected for the other voices. Just as in our +discussion of the title tunes, the initial parameters chosen have a chaotic effect on the notes that are eventually played resulting in radically different +tunes from just small adjustments in initial values. \begin{figure}[H] { \begin{adjustbox}{width=14cm,center} - \includegraphics[width=14cm]{torus/title_no_2_page_1001.png}}% + \includegraphics[width=14cm]{torus/title_no_4_page_1001.png}}% \end{adjustbox} -}\caption[]{Bars 2 and 4 are always repeated} +}\caption[]{Tune 4 from Taurus:Torus} \end{figure} -Not all of the tunes are 64-note based. It does generate some that are truncated. + +\clearpage +\section{A Graphical Interlude: Oscillator in 4 Parts} +Before we move on from Torus:Taurus, let's take a brief graphical diversion. +This demo was also the laboratory where the elegant animation\index{animation} used when awarding a bonus was developed. +\begin{figure}[H] +{ + \setlength{\tabcolsep}{3.0pt} + \setlength\cmidrulewidth{\heavyrulewidth} % Make cmidrule = + \begin{adjustbox}{width=12cm,center} + \begin{subfigure}{0.3\textwidth} + \includegraphics[width=4cm]{torus/bonusbounty.png}% + \end{subfigure} + \begin{subfigure}{0.3\textwidth} + \includegraphics[width=4cm]{torus/torus.png}% + \end{subfigure} + \end{adjustbox} +}\caption[]{The Torus oscillator animation\index{animation} and Iridis' bonus animation\index{animation}.} +\end{figure} \begin{figure}[H] { \setlength{\tabcolsep}{3.0pt} \setlength\cmidrulewidth{\heavyrulewidth} % Make cmidrule = \centering - \def\MULTICOLORONE{green} - \def\MULTICOLORTWO{red} + \def\MULTICOLORONE{red} + \def\MULTICOLORTWO{white} \def\SPRITECOLOR{blue} + \begin{subfigure}{0.5\textwidth} + \input{sprites/LAND_GILBY1} + \end{subfigure} + \def\MULTICOLORONE{yellow} + \def\MULTICOLORTWO{red} + \def\SPRITECOLOR{c64_purple} + \begin{subfigure}{0.5\textwidth} \input{sprites/BULLHEAD} - }\caption[position=top]{The 'Torus' sprite.} + \end{subfigure} +}\caption[position=top]{The sprites used in the Iridis Alpha bonus phase and the Torus demo.} \end{figure} +The code handling each is identical and was only very lightly modified for the final game. +Starting with a side by side comparison of the two main routines we can see that the demo +code survived pretty much intact in Iridis Alpha. + + +\begin{minipage}[b]{0.45\linewidth} +\centering +\begin{lstlisting}[caption=Animation in Torus Demo,basicstyle=\tiny\ttfamily] +RunMainInterruptHandler + LDY #$00 + LDA #$F0 + STA $D012 ;Raster Pos + DEC counterBetweeXPosUpdates + BNE MaybeUpdateYPos + +UpdateXPos + LDA initialCounterBetweenXPosUpdates + STA counterBetweeXPosUpdates + + LDA incrementForXPos + CLC + ADC indexForXPosInSpritePosArray + STA indexForXPosInSpritePosArray + +MaybeUpdateYPos + DEC counterBetweenYPosUpdates + BNE MaybeUpdateXPosOffset + + LDA initialCounterBetweenYPosUpdates + STA counterBetweenYPosUpdates + + LDA indexForYPosInSpritePosArray + CLC + ADC incrementForYPos + STA indexForYPosInSpritePosArray + +MaybeUpdateXPosOffset + DEC cyclesBetweenXPosOffsetUpdates + BNE MaybeUpdateYPosOffset + + LDA oscillator3Value + STA cyclesBetweenXPosOffsetUpdates + INC indexForXPosOffetsetInSpritePosArray + +MaybeUpdateYPosOffset + DEC cyclesBetweenYPosOffsetUpdates + BNE StoreInitialIndexValues + + LDA oscillator4Value + STA cyclesBetweenYPosOffsetUpdates + INC indexForYPosOffsetInSpritePosArray + +StoreInitialIndexValues + ; Store the initial values for our indices + ; on the stack. + LDA indexForXPosInSpritePosArray + PHA + LDA indexForYPosInSpritePosArray + PHA + LDA indexForXPosOffetsetInSpritePosArray + PHA + LDA indexForYPosOffsetInSpritePosArray + PHA +\end{lstlisting} +\end{minipage} +\hspace{0.5cm} +\begin{minipage}[b]{0.45\linewidth} +\centering +\begin{lstlisting}[basicstyle=\tiny\ttfamily,caption=... and Iridis Alpha.,escapechar=\%] +AnimateGilbiesForNewBonus%\index{AnimateGilbiesForNewBonus}% + LDY #$00 + LDA #$F0 + STA $D012 ;Raster Position + DEC counterBetweeXPosUpdates%\index{counterBetweeXPosUpdates}% + BNE MaybeUpdateYPos%\index{MaybeUpdateYPos}% + +UpdateXPos + LDA initialCounterBetweenXPosUpdates%\index{initialCounterBetweenXPosUpdates}% + STA counterBetweeXPosUpdates%\index{counterBetweeXPosUpdates}% + + LDA incrementForXPos%\index{incrementForXPos}% + CLC + ADC indexForXPosInSpritePosArray%\index{indexForXPosInSpritePosArray}% + STA indexForXPosInSpritePosArray%\index{indexForXPosInSpritePosArray}% + +MaybeUpdateYPos%\index{MaybeUpdateYPos}% + DEC counterBetweenYPosUpdates%\index{counterBetweenYPosUpdates}% + BNE MaybeResetOsc3WorkingValue%\index{MaybeResetOsc3WorkingValue}% + + LDA initialCounterBetweenYPosUpdates%\index{initialCounterBetweenYPosUpdates}% + STA counterBetweenYPosUpdates%\index{counterBetweenYPosUpdates}% + + LDA indexForYPosInSpritePosArray%\index{indexForYPosInSpritePosArray}% + CLC + ADC incrementForYPos%\index{incrementForYPos}% + STA indexForYPosInSpritePosArray%\index{indexForYPosInSpritePosArray}% + +MaybeResetOsc3WorkingValue%\index{MaybeResetOsc3WorkingValue}% + DEC oscillator3WorkingValue%\index{oscillator3WorkingValue}% + BNE MaybeResetOsc4WorkingValue%\index{MaybeResetOsc4WorkingValue}% + + LDA oscillator3Value%\index{oscillator3Value}% + STA oscillator3WorkingValue%\index{oscillator3WorkingValue}% + INC indexForXPosOffetsetInSpritePosArray%\index{indexForXPosOffetsetInSpritePosArray}% + +MaybeResetOsc4WorkingValue%\index{MaybeResetOsc4WorkingValue}% + DEC oscillator4WorkingValue%\index{oscillator4WorkingValue}% + BNE InitializeSpriteAnimation%\index{InitializeSpriteAnimation}% + + LDA oscillator4Value%\index{oscillator4Value}% + STA oscillator4WorkingValue%\index{oscillator4WorkingValue}% + INC inxedForYPosOffsetInSpritePosArray%\index{inxedForYPosOffsetInSpritePosArray}% + +InitializeSpriteAnimation%\index{InitializeSpriteAnimation}% + ; Store the initial values for our indices + ; on the stack. + LDA indexForXPosInSpritePosArray%\index{indexForXPosInSpritePosArray}% + PHA + LDA indexForYPosInSpritePosArray%\index{indexForYPosInSpritePosArray}% + PHA + LDA indexForXPosOffetsetInSpritePosArray%\index{indexForXPosOffetsetInSpritePosArray}% + PHA + LDA inxedForYPosOffsetInSpritePosArray%\index{inxedForYPosOffsetInSpritePosArray}% + PHA +\end{lstlisting} +\end{minipage} + +To start getting a handle on how the oscillation animation\index{animation} works, lets plot the first 24 animations that the Torus +demo uses when left to its own devices. We get a variety of different trajectories, some relatively simple, some +quite convoluted. + +\clearpage +\begin{figure}[p] + \centering + \foreach \l in {0, ..., 23} + { + \begin{subfigure}{0.3\textwidth} + \begin{tikzpicture} + \begin{axis}[ + xmin = 0, xmax = 150, + ymin = 0, ymax = 256, + xtick distance = 50, + ytick distance = 50, + grid = both, + minor tick num = 10, + major grid style = {lightgray}, + minor grid style = {lightgray!25}, + width = \textwidth, + height = 0.75\textwidth, + legend cell align = {left}, + legend pos = north west, + font = \tiny\ttfamily + ] + \addplot[blue, ultra thin, mark = *,mark size=0.4pt] table [x = {x}, y = {y}] {src/torus/Oscillation\l.dat}; + \end{axis} + \end{tikzpicture} + \end{subfigure} + }% +\caption{The first 24 oscillation patterns generated by the Torus demo.} +\end{figure} +\clearpage + +When we look into the code we find this petting zoo of animations is principally driven by a simple sequence\index{sequence} +of bytes stored in \icode{spritePositionArray}. + +\CopyPartialFile{../iridisalpha/demos/torus/src/torus.asm}{tmp.asm}{243}{251}% +\lstinputlisting[]{tmp.asm} + +We can get a sense of how this rising and falling sequence\index{sequence} of values can be used to plot a course across the screen\index{screen} +if we treat each as an x and y value on a graph of cartesian co-ordinates. In the twenty four instances below we +start by treating the value as providing both the x and y position. In each subsequent one we skip an increasing +number of positions ahead in the sequence\index{sequence} to get the y value, producing a variety of elliptical orbits around +the screen\index{screen}. + +\begin{figure}[H] + \centering + \foreach \l in {0, ..., 11} + { + \begin{subfigure}{0.3\textwidth} + \begin{tikzpicture} + \begin{axis}[ + xmin = 0, xmax = 256, + ymin = 0, ymax = 256, + xtick distance = 50, + ytick distance = 50, + grid = both, + minor tick num = 10, + major grid style = {lightgray}, + minor grid style = {lightgray!25}, + width = \textwidth, + height = 0.75\textwidth, + legend cell align = {left}, + legend pos = north west, + font = \tiny\ttfamily + ] + \addplot[blue, ultra thin, mark = *,mark size=0.4pt] table [x = {x}, y = {y}] {src/torus/OscillationRaw-\l.dat}; + \end{axis} + \end{tikzpicture} + \end{subfigure} + }% +\caption{Using the x/y offset in \icode{spritePositionArray} where y is the value after x in the array.} +\end{figure} + +To get beyond simple ellipsoids we need to do more than pick a different value in the array for our x and y offsets. +Here we experiment with something a little more involved. We update the x and y positions at different intervals +and when skipping ahead in \icode{spritePositionArray} for a new value for x and y we use a pre-selected, random +number of bytes to skip past. + +\begin{figure}[H] + \centering + \foreach \l in {0, ..., 11} + { + \begin{subfigure}{0.3\textwidth} + \includegraphics[width=4cm]{torus/OscillationTest\l.png}% + \end{subfigure} + }% +\caption{Testing different values of x and y} +\end{figure} + +This is starting to look more like the actual results we observed and it is where the 4 values selectable by the player using keys z, x, c, and v in the Torus demo come in. In addition +to controlling the music generation procedure, as we've already seen, they also determine the way the values in +\icode{spritePositionArray} are selected for position the sprite in each new frame. This is based on letting them +determine the frequency\index{frequency} with which the position of the x and y values of each sprite is changed and how far to skip +ahead in \icode{spritePositionArray} when selecting a new value from it for the x and y position. + + +\begin{figure}[H] + { + \setlength{\tabcolsep}{3.0pt} + \setlength\cmidrulewidth{\heavyrulewidth} % Make cmidrule = + \begin{adjustbox}{width=12cm,center} + + \begin{tabular}{rllllllll} + \toprule + Key & Name & Purpose & Code &\\ + \midrule +Z & Oscillator 1 & \makecell[l]{ +- Intervals between updating X position.\\ +- The amount to increment the index into\\ +\icode{spritePositionArray}\\ +when getting the next X position.\\ +} & \makecell[l]{ +\CopyPartialFile{../iridisalpha/demos/torus/src/torus.asm}{tmp.asm}{735}{751}% +\lstinputlisting[linewidth=5cm,basicstyle=\tiny\ttfamily]{tmp.asm} +} \\ + \midrule +X & Oscillator 2 & \makecell[l]{ +- Intervals between updating Y position.\\ +- The amount to increment the index into \\ +\icode{spritePositionArray}\\ +when getting the next Y position.\\ +} & \makecell[l]{ +\CopyPartialFile{../iridisalpha/demos/torus/src/torus.asm}{tmp.asm}{753}{768}% +\lstinputlisting[linewidth=5cm,basicstyle=\tiny\ttfamily]{tmp.asm} +} \\ + \midrule +C & Oscillator 3 & \makecell[l]{ +- How often to increase the index that seeks \\ +ahead to get a value\\ +from \icode{spritePositionArray}for adding \\ +to the next X position.\\ +} & \makecell[l]{ +\CopyPartialFile{../iridisalpha/demos/torus/src/torus.asm}{tmp.asm}{770}{780}% +\lstinputlisting[linewidth=5cm,basicstyle=\tiny\ttfamily]{tmp.asm} +} \\ + \midrule +V & Oscillator 4 & \makecell[l]{ +- How often to increase the index that seeks \\ +ahead to get a value\\ +from \icode{spritePositionArray}for adding \\ +to the next Y position.\\ +} & \makecell[l]{ +\CopyPartialFile{../iridisalpha/demos/torus/src/torus.asm}{tmp.asm}{782}{791}% +\lstinputlisting[linewidth=5cm,basicstyle=\tiny\ttfamily]{tmp.asm} +} \\ + \addlinespace + \bottomrule + \end{tabular} + \end{adjustbox} + }\caption{The purpose of each of the oscillator values.} +\end{figure} + +In \icode{RunMainInterruptHandler} we can see how each of these values +set by the player is used to maintain an accounting of the different sprite positions for each of the 8 sprites\index{sprites}: + +\CopyPartialFile{../iridisalpha/demos/torus/src/torus.asm}{tmp.asm}{361}{403}% +\lstinputlisting[]{tmp.asm} + +Before animating each of the 8 sprites\index{sprites} we use the values set by the player to prepare the variables +that will be applied to positioning each sprite. For example the value selected with the Z key has been +used to set \icode{initialCounterBetweenXPosUpdates\index{initialCounterBetweenXPosUpdates}} and \icode{incrementForXPos\index{incrementForXPos}}. In the first lines above +in \icode{UpdateXPos} we use them to set up \icode{indexForXPosInSpritePositionArray\index{indexForXPosInSpritePositionArray}}. THis is then used +in \icode{SpriteAnimationLoop\index{SpriteAnimationLoop}} to selec the X position of the current sprite: + + +\CopyPartialFile{../iridisalpha/demos/torus/src/torus.asm}{tmp.asm}{419}{424}% +\lstinputlisting[]{tmp.asm} + +You can follow the same lineage between the setting of each value in our table above with the rest of the +\icode{SpriteAnimationLoop\index{SpriteAnimationLoop}} routine\index{routine}. + +\clearpage +\begin{definition}[A Testing Hack] +\setlength{\intextsep}{0pt}% +\setlength{\columnsep}{3pt}% +\small +It's not easy to create a bonus animation and test it unless you plan to play through the game each time until you earn a bonus. For that + reason it makes sense to have some way of calling up the bonus routine quick-and-dirty-like. + In the CheckKeyboardInGame\index{CheckKeyboardInGame} routine, we find the following: +\begin{lstlisting}[basicstyle=\tiny\ttfamily,escapechar=\%] + ; We can award ourselves a bonus bounty by + ; pressing Y at any time, as long as '1C' is the + ; first character%\index{character}% in the hiscore table. Not sure + ; what this hack is for, testing? +CheckYPressed%\index{CheckYPressed}% + CMP #KEY_Y ; Y Pressed + BNE ReturnFromKeyboardCheck%\index{ReturnFromKeyboardCheck}% + LDA canAwardBonus%\index{canAwardBonus}% + CMP #$1C + BNE ReturnFromKeyboardCheck%\index{ReturnFromKeyboardCheck}% + INC bonusAwarded%\index{bonusAwarded}% + RTS +\end{lstlisting} + +In the above the `canAwardBonus\index{canAwardBonus}` byte is the first letter in the name of the player with the top score in the Hi-Score table. By default this is 'YAK': + +\begin{lstlisting}[basicstyle=\tiny\ttfamily,escapechar=\%] +;------------------------------------------------------- +; The high score table. +;------------------------------------------------------- +hiScoreTablePtr%\index{hiScoreTablePtr}% .TEXT "0068000" +canAwardBonus%\index{canAwardBonus}% .TEXT "YAK " + .FILL 10, $00 + .TEXT "0065535RATT" + .FILL 10, $00 +\end{lstlisting} +But if we change 'Y' to \icode{\$1C} like so, we can activate the hack: + +\begin{lstlisting}[basicstyle=\tiny\ttfamily,escapechar=\%] +hiScoreTablePtr%\index{hiScoreTablePtr}% .TEXT "0068000" +canAwardBonus%\index{canAwardBonus}% .TEXT $1C,"AK " +\end{lstlisting} + +Note that \icode{\$1C} is charset\index{charset} code for a bull's head symbol in Iridis Alpha, so it is also possible to enter this as the initial of a high scorer name if we get a score that puts us to the top of the table: + +\CopyPartialFile{../iridisalpha/src/graphics/charset.asm}{tmp.asm}{287}{296}% +\lstinputlisting[basicstyle=\tiny\ttfamily]{tmp.asm} + + +I'm guessing this was used for testing the animation\index{animation} routine\index{routine} and left in as an Easter egg. +\end{definition} \section{Taurus/Torus Two} \begin{figure}[H] @@ -89,11 +534,14 @@ \section{Taurus/Torus Two} \begin{adjustbox}{width=11cm,center} \includegraphics[width=11cm]{torus/torus2.png}% \end{adjustbox} -}\caption[]{} +}\caption[]{Now even freakier you say.} \end{figure} +In this second iteration of the Taurus:Torus demo, the theme tune for the main game has crystallized. The routines are now sufficiently identical +that they produce the same output. The initial settings produced the Iridis Alpha theme tune as it was in the game's final release. + \lstset{style=6502Style} -\lstinputlisting[caption=The music routine\index{routine} in Taurus:Torus II side-by-side with Iridis Alpha.,basicstyle=\tiny]{torus/sidebyside2.asm} +\lstinputlisting[caption=The music routine\index{routine} in Taurus:Torus II side-by-side with Iridis Alpha.,basicstyle=\tiny\ttfamily]{torus/sidebyside2.asm} \begin{figure}[H] @@ -101,7 +549,7 @@ \section{Taurus/Torus Two} \begin{adjustbox}{width=14cm,center} \includegraphics[width=14cm]{torus/torus2_title_no_1_page_1001.png}% \end{adjustbox} -}\caption[]{} +}\caption[]{The music produced by Taurus/Torus Two. This is note for note identical to the theme tune in the final game below.} \end{figure} \begin{figure}[H] @@ -109,6 +557,6 @@ \section{Taurus/Torus Two} \begin{adjustbox}{width=14cm,center} \includegraphics[width=14cm]{music/title_no_1_page_1001.png}% \end{adjustbox} -}\caption[]{} +} \end{figure} diff --git a/src/torus/sidebyside.asm b/src/torus/sidebyside.asm index 9305b71d..954675fb 100644 --- a/src/torus/sidebyside.asm +++ b/src/torus/sidebyside.asm @@ -13,23 +13,26 @@ PlayTitleScreenMusic PlayTitleScreenM DEC numberOfNotesToPlayInTune DEC numberOfNotesToPlayInTune BNE MaybePlayVoice1 BNE MaybePlayVoice1 - + + ; Select new notes every time we enter + ; the routine. JSR SelectNewNotesToPlay ; Set up a new tune. ; Set up a new tune. LDA #$C0 LDA #$C0 STA numberOfNotesToPlayInTune STA numberOfNotesToPlayInTune - ; This is what will eventually time us out of - ; the title music and enter attract mode. + ; This is what will eventually time us out + ; of the music and enter attract mode. INC f7PressedOrTimedOutToAttractMode LDX notesPlayedSinceLastKeyChange LDX notesPlayedSinceLastKeyChange LDA titleMusicNoteArray,X LDA titleMusicNoteArray,X STA offsetForNextVoice1Note STA offsetForNextVoice1Note - ; We'll only select a new tune when we've reached the ; We'll only select a new tune when we've reached - ; beginning of a new 16 bar structure. ; the beginning of a new 16 bar structure. + ; We'll only select a new tune when + ; we've reached the beginning of a + ; new 16 bar structure. INX INX TXA TXA AND #$03 AND #$03 @@ -70,8 +73,8 @@ PlayTitleScreenMusic PlayTitleScreenM CLC CLC ADC offsetForNextVoice2Note ADC offsetForNextVoice2Note - ; Use this new value to change the key of the next four ; Use this new value to change the key of the next four - ; notes played by voice 3. ; notes played by voice 3. + ; Use this new value to change the key of ; Use this new value to change the key of + ; the next four notes played by voice 3. ; the next four notes played by voice 3. STA offsetForNextVoice3Note STA offsetForNextVoice3Note TAY TAY @@ -89,7 +92,8 @@ PlayTitleScreenMusic PlayTitleScreenM STA voice3NoteDuration STA voice3NoteDuration ; Play the note currently pointed to by ; Play the note currently pointed to by - ; voice3IndexToMusicNoteArray in titleMusicNoteArray. ; voice3IndexToMusicNoteArray in titleMusicNoteArray. + ; voice3IndexToMusicNoteArray in ; voice3IndexToMusicNoteArray in + ; titleMusicNoteArray. ; titleMusicNoteArray. LDX voice3IndexToMusicNoteArray LDX voice3IndexToMusicNoteArray LDA titleMusicNoteArray,X LDA titleMusicNoteArray,X CLC CLC @@ -97,12 +101,12 @@ PlayTitleScreenMusic PlayTitleScreenM TAY TAY JSR PlayVoice3 JSR PlayNoteVoice3 - ; Move voice3IndexToMusicNoteArray to the next ; Move voice3IndexToMusicNoteArray to the next - ; position in titleMusicNoteArray. ; position in titleMusicNoteArray. + ; Move voice3IndexToMusicNoteArray to the ; Move voice3IndexToMusicNoteArray to the + ; next position in titleMusicNoteArray. ; next position in titleMusicNoteArray. INX INX TXA TXA - ; Since it's only 4 bytes long ensure we wrap ; Since it's only 4 bytes long ensure we wrap - ; back to 0 if it's greater than 3. ; back to 0 if it's greater than 3. + ; Since it's only 4 bytes long ensure we ; Since it's only 4 bytes long ensure we + ; wrap back to 0 if it's greater than 3. ; wrap back to 0 if it's greater than 3. AND #$03 AND #$03 STA voice3IndexToMusicNoteArray STA voice3IndexToMusicNoteArray diff --git a/src/torus/sidebyside2.asm b/src/torus/sidebyside2.asm index 09f4c668..b1584c33 100644 --- a/src/torus/sidebyside2.asm +++ b/src/torus/sidebyside2.asm @@ -1,118 +1,111 @@ -;------------------------------------------- ;----------------------------------------------- -; PlayTitleScreenMusic ; PlayTitleScreenMusic -; (TORUS:TAURUS II) ; (IRIDIS ALPHA) -;------------------------------------------- ;----------------------------------------------- -PlayTitleScreenMusic PlayTitleScreenMusic - LDA UnusedValue1 DEC baseNoteDuration - STA UnusedValue2 BEQ MaybeStartNewTune - RTS -MaybeStartNewTune MaybeStartNewTune - LDA previousBaseNoteDuration - STA baseNoteDuration +;------------------------------------------- ;----------------------------------------------- +; PlayTitleScreenMusic ; PlayTitleScreenMusic +; (TORUS:TAURUS II) ; (IRIDIS ALPHA) +;------------------------------------------- ;----------------------------------------------- +PlayTitleScreenMusic PlayTitleScreenMusic + LDA UnusedValue1 DEC baseNoteDuration + STA UnusedValue2 BEQ MaybeStartNewTune + RTS +MaybeStartNewTune MaybeStartNewTune + LDA previousBaseNoteDuration + STA baseNoteDuration - DEC numberOfNotesToPlayInTune DEC numberOfNotesToPlayInTune - BNE MaybePlayVoice1 BNE MaybePlayVoice1 - - ; Set up a new tune. ; Set up a new tune. - LDA #$C0 LDA #$C0 ; 193 - STA numberOfNotesToPlayInTune STA numberOfNotesToPlayInTune - - ; This is what will eventually time us out of playing - ; the title music and enter attract mode. - INC f7PressedOrTimedOutToAttractMode - - LDX notesPlayedSinceLastKeyChange LDX notesPlayedSinceLastKeyChange - LDA titleMusicNoteArray,X LDA titleMusicNoteArray,X - STA offsetForNextVoice2Note STA offsetForNextVoice1Note - - ; We'll only select a new tune when we've reached the ; We'll only select a new tune when we've reached the - ; beginning of a new 16 bar structure. ; beginning of a new 16 bar structure. - INX INX - TXA TXA - AND #$03 AND #$03 - STA notesPlayedSinceLastKeyChange STA notesPlayedSinceLastKeyChange - BNE MaybePlayVoice1 BNE MaybePlayVoice1 - - JSR SelectNewNotesToPlay JSR SelectNewNotesToPlay + DEC numberOfNotesToPlayInTune DEC numberOfNotesToPlayInTune + BNE MaybePlayVoice1 BNE MaybePlayVoice1 + + ; Set up a new tune. ; Set up a new tune. + LDA #$C0 LDA #$C0 ; 193 + STA numberOfNotesToPlayInTune STA numberOfNotesToPlayInTune + + ; This is what will eventually time us out of + ; playing the title music and enter attract mode. + INC f7PressedOrTimedOutToAttractMode -MaybePlayVoice1 MaybePlayVoice1 - DEC voice1NoteDuration DEC voice1NoteDuration - BNE MaybePlayVoice2 BNE MaybePlayVoice2 - - LDA #$30 LDA #$30 - STA voice1NoteDuration STA voice1NoteDuration - - LDX voice1IndexToMusicNoteArray LDX voice1IndexToMusicNoteArray - LDA titleMusicNoteArray,X LDA titleMusicNoteArray,X - CLC CLC - ADC offsetForNextVoice2Note ADC offsetForNextVoice1Note - TAY TAY - STY offsetForNextVoice3Note STY offsetForNextVoice2Note - - JSR PlayNoteVoice1 JSR PlayNoteVoice1 - - INX INX - TXA TXA - AND #$03 AND #$03 - STA voice1IndexToMusicNoteArray STA voice1IndexToMusicNoteArray + LDX notesPlayedSinceLastKeyChange LDX notesPlayedSinceLastKeyChange + LDA titleMusicNoteArray,X LDA titleMusicNoteArray,X + STA offsetForNextVoice2Note STA offsetForNextVoice1Note + + ; We'll only select a new tune when we've reached ; We'll only select a new tune when we've reached + ; the beginning of a new 16 bar structure. ; the beginning of a new 16 bar structure. + INX INX + TXA TXA + AND #$03 AND #$03 + STA notesPlayedSinceLastKeyChange STA notesPlayedSinceLastKeyChange + BNE MaybePlayVoice1 BNE MaybePlayVoice1 + + JSR SelectNewNotesToPlay JSR SelectNewNotesToPlay -MaybePlayVoice2 MaybePlayVoice2 - DEC voice2NoteDuration DEC voice2NoteDuration - BNE MaybePlayVoice3 BNE MaybePlayVoice3 - - LDA #$0C LDA #$0C - STA voice2NoteDuration STA voice2NoteDuration - LDX voice2IndexToMusicNoteArray LDX voice2IndexToMusicNoteArray - LDA titleMusicNoteArray,X LDA titleMusicNoteArray,X - CLC CLC - ADC offsetForNextVoice3Note ADC offsetForNextVoice2Note - - ; Use this new value to change the key of the next four ; Use this new value to change the key of the next four - ; notes played by voice 1. ; notes played by voice 3. - STA offsetForNextVoice1Note STA offsetForNextVoice3Note - - TAY - JSR PlayNoteVoice2 - INX - TXA - AND #$03 - STA voice2IndexToMusicNoteArray - +MaybePlayVoice1 MaybePlayVoice1 + DEC voice1NoteDuration DEC voice1NoteDuration + BNE MaybePlayVoice2 BNE MaybePlayVoice2 + + LDA #$30 LDA #$30 + STA voice1NoteDuration STA voice1NoteDuration + + LDX voice1IndexToMusicNoteArray LDX voice1IndexToMusicNoteArray + LDA titleMusicNoteArray,X LDA titleMusicNoteArray,X + CLC CLC + ADC offsetForNextVoice2Note ADC offsetForNextVoice1Note + TAY TAY + STY offsetForNextVoice3Note STY offsetForNextVoice2Note + + JSR PlayNoteVoice1 JSR PlayNoteVoice1 + + INX INX + TXA TXA + AND #$03 AND #$03 + STA voice1IndexToMusicNoteArray STA voice1IndexToMusicNoteArray - TAY - JSR PlayNoteVoice2 - INX - TXA - AND #$03 - STA voice2IndexToMusicNoteArray +MaybePlayVoice2 MaybePlayVoice2 + DEC voice2NoteDuration DEC voice2NoteDuration + BNE MaybePlayVoice3 BNE MaybePlayVoice3 + + LDA #$0C LDA #$0C + STA voice2NoteDuration STA voice2NoteDuration + LDX voice2IndexToMusicNoteArray LDX voice2IndexToMusicNoteArray + LDA titleMusicNoteArray,X LDA titleMusicNoteArray,X + CLC CLC + ADC offsetForNextVoice3Note ADC offsetForNextVoice2Note + + ; Use this new value to change the key of the next ; Use this new value to change the key of the next + ; four notes played by voice 1. ; four notes played by voice 3. + STA offsetForNextVoice1Note STA offsetForNextVoice3Note + + TAY TAY + JSR PlayNoteVoice2 JSR PlayNoteVoice2 + INX INX + TXA TXA + AND #$03 AND #$03 + STA voice2IndexToMusicNoteArray STA voice2IndexToMusicNoteArray -MaybePlayVoice3 MaybePlayVoice3 - DEC voice3NoteDuration DEC voice3NoteDuration - BNE ReturnFromTitleMusic BNE ReturnFromTitleScreenMusic - - LDA #$03 LDA #$03 - STA voice3NoteDuration STA voice3NoteDuration - - ; Play the note currently pointed to by ; Play the note currently pointed to by - ; voice3IndexToMusicNoteArray in titleMusicNoteArray. ; voice3IndexToMusicNoteArray in titleMusicNoteArray. - LDX voice3IndexToMusicNoteArray LDX voice3IndexToMusicNoteArray - LDA titleMusicNoteArray,X LDA titleMusicNoteArray,X - CLC CLC - ADC offsetForNextVoice1Note ADC offsetForNextVoice3Note - TAY TAY - JSR PlayNoteVoice3 JSR PlayNoteVoice3 - - ; Move voice3IndexToMusicNoteArray to the next ; Move voice3IndexToMusicNoteArray to the next - ; position in titleMusicNoteArray. ; position in titleMusicNoteArray. - INX INX - TXA TXA - ; Since it's only 4 bytes long ensure we wrap ; Since it's only 4 bytes long ensure we wrap - ; back to 0 if it's greater than 3. ; back to 0 if it's greater than 3. - AND #$03 AND #$03 - STA voice3IndexToMusicNoteArray STA voice3IndexToMusicNoteArray - -ReturnFromTitleMusic ReturnFromTitleScreenMusic - RTS RTS +MaybePlayVoice3 MaybePlayVoice3 + DEC voice3NoteDuration DEC voice3NoteDuration + BNE ReturnFromTitleMusic BNE ReturnFromTitleScreenMusic + + LDA #$03 LDA #$03 + STA voice3NoteDuration STA voice3NoteDuration + + ; Play the note currently pointed to by ; Play the note currently pointed to by + ; voice3IndexToMusicNoteArray in ; voice3IndexToMusicNoteArray in + ; titleMusicNoteArray. ; titleMusicNoteArray. + LDX voice3IndexToMusicNoteArray LDX voice3IndexToMusicNoteArray + LDA titleMusicNoteArray,X LDA titleMusicNoteArray,X + CLC CLC + ADC offsetForNextVoice1Note ADC offsetForNextVoice3Note + TAY TAY + JSR PlayNoteVoice3 JSR PlayNoteVoice3 + + ; Move voice3IndexToMusicNoteArray to the next ; Move voice3IndexToMusicNoteArray to the next + ; position in titleMusicNoteArray. ; position in titleMusicNoteArray. + INX INX + TXA TXA + ; Since it's only 4 bytes long ensure we wrap ; Since it's only 4 bytes long ensure we wrap + ; back to 0 if it's greater than 3. ; back to 0 if it's greater than 3. + AND #$03 AND #$03 + STA voice3IndexToMusicNoteArray STA voice3IndexToMusicNoteArray + +ReturnFromTitleMusic ReturnFromTitleScreenMusic + RTS RTS