; ============================================================================== ; Apple II [$D0 ROM] (341-0016) - Programmer's Aid #1 [1978] ; ------------------------------------------------------------------------------ ; Part 6 [$D717~$D7FF]: Music Subroutine by Gary J. Shannon; ; Copyright (c) 1978 by Apple Computer Inc. All Rights Reserved ; ------------------------------------------------------------------------------ ; Instructions are in the Programmer's Aid #1 Installation and Operating Manual ; ============================================================================== ; Analyzed (via McFadden's SourceGen) by James Davis [Last Updated: 2020/06/04] ; ============================================================================== ; ; Equates: Zero Page Work Areas; & Parameter Passing Areas ; DOWNTIME EQU $00 {addr/1} ;Speaker Negative Pulse Width UPTIME EQU $01 {addr/1} ;Speaker Positive Pulse Width DURATION EQU $02 {addr/2} ;Musical Note Time-Duration Counter TIMBRE EQU $02FD {addr/1} ;Musical Note Timbre Value (Poke 765) TIME EQU $02FE {addr/1} ;Musical Note Time Value (Poke 766) PITCH EQU $02FF {addr/1} ;Musical Note Pitch Value (Poke 767) SPEAKER EQU $C030 {addr/1} ;Speaker Data Output Toggle Switch ORG $D717 ; ; ============================================================================== ; Music Subroutine by Gary J. Shannon [Do Pokes, then Call -10473 from BASIC] ; ============================================================================== ; DURATION EQU $02 ;Renamed to match Instructions; Original Name: LENGTH ; TIMBRE EQU $02FD ;Renamed to match Instructions; Original Name: VOICE ; TIME EQU $02FE ;Renamed to match Instructions; Original Name: LONG ; PITCH EQU $02FF ;Renamed to match Instructions; Original Name: NOTE ; ------------------------------------------------------------------------------ ; D717: 4C 4E D7 ENTRY JMP LOOKUP ;Get Pulse Widths (Duty Cycle Data) ; ------------------------------------------------------------------------------ ; Play One Note: Musical Note Cycles are divided into UPTIME & DOWNTIME halves; ; Musical Note Duty Cycle Data is from NOTES: UPTIME & DOWNTIME; ; Musical Note Time-Duration Count is kept in DURATION ; ------------------------------------------------------------------------------ ; ; ----------------------------------- ;UPTIME Half-Cycle: D71A: A4 01 PLAY LDY UPTIME ;Get Positive Pulse Width D71C: AD 30 C0 LDA SPEAKER ;Toggle Speaker Data Output D71F: E6 02 PLAY2 INC DURATION ;Advance Note Time-Duration Counter, Low D721: D0 05 BNE PATH1 ;Branch if Duration (Low) is Not Expired D723: E6 03 INC DURATION+1 ;Advance Note Time-Duration Counter, High D725: D0 05 BNE PATH2 ;Branch if Duration (High) is Not Expired D727: 60 RTS ;Return to Caller; Time-Duration Expired ; ;Do Time Adjustments: D728: EA PATH1 NOP ;Delay (2 Machine Cycles) D729: 4C 2C D7 JMP PATH2 ;Delay (3 Machine Cycles) More D72C: 88 PATH2 DEY ;Reduce Pulse Counter (Width) D72D: F0 05 BEQ DOWN ;Toggle if Pulse Count (Width) is Expired D72F: 4C 32 D7 JMP PATH3 ;Continue UPTIME if Count is Not Expired ; ;Jump Delays (3 Machine Cycles) More D732: D0 EB PATH3 BNE PLAY2 ;UPTIME: Same # of Cyles; Always Taken ; ----------------------------------- ;DOWNTIME Half-Cycle: D734: A4 00 DOWN LDY DOWNTIME ;Get Negative Pulse Width D736: AD 30 C0 LDA SPEAKER ;Toggle Speaker Data Output D739: E6 02 PLAY3 INC DURATION ;Advance Note Time-Duration Counter, Low D73B: D0 05 BNE PATH4 ;Branch if Duration (Low) is Not Expired D73D: E6 03 INC DURATION+1 ;Advance Note Time-Duration Counter, High D73F: D0 05 BNE PATH5 ;Branch if Duration (High) is Not Expired D741: 60 RTS ;Return to Caller; Time-Duration Expired ; ;Do Time Adjustments: D742: EA PATH4 NOP ;Delay (2 Machine Cycles) D743: 4C 46 D7 JMP PATH5 ;Delay (3 Machine Cycles) More D746: 88 PATH5 DEY ;Reduce Pulse Counter (Width) D747: F0 D1 BEQ PLAY ;Toggle if Pulse Count (Width) is Expired D749: 4C 4C D7 JMP PATH6 ;Continue DOWNTIME if Count is Not Expired ; ;Jump Delays (3 Machine Cycles) More D74C: D0 EB PATH6 BNE PLAY3 ;DOWNTIME: Same # of Cyles; Always Taken ; ------------------------------------------------------------------------------ ; Note Table Lookup Subroutine: Gets a Note's UPTIME & DOWNTIME from its PITCH & ; Sets its DURATION in accordance with its TIMBRE ; ------------------------------------------------------------------------------ ; DURATION EQU $02 ;Musical Note Time-Duration Counter ; TIMBRE EQU $02FD ;Musical Note Timbre Value (Poke 765) ; TIME EQU $02FE ;Musical Note Time Value (Poke 766) ; PITCH EQU $02FF ;Musical Note Pitch Value (Poke 767) ; ------------------------------------------------------------------------------ D74E: AD FF 02 LOOKUP LDA PITCH ;Get Musical Note Pitch Value (User Poked) D751: 0A ASL A ;Double Musical Note Pitch Value (1 of 2) D752: A8 TAY ;Set Indexed Addressing Pointer D753: B9 96 D7 LDA NOTES,Y ;Get Note UPTIME (Positive Pulse Width)= D756: 85 00 STA DOWNTIME ;Set Note DOWNTIME (Negative Pulse Width)= ; ----------------------------------- ;Shift Time According to Note's TIMBRE: D758: AD FD 02 LDA TIMBRE ;Get Note's Timbre Value (User Poked) D75B: 4A SHIFT LSR A ;Halve Note Timbre Value (Duty Cycle) D75C: F0 04 BEQ DONE ;Exit if Halving Results in a Zero Value D75E: 46 00 LSR DOWNTIME ;Else, Halve Negative Pulse Width, too D760: D0 F9 BNE SHIFT ;Loop if Halving Result is Not Zero yet ; ----------------------------------- ;Compute New UPTIME/DOWNTIME Pulse Widths: D762: B9 96 D7 DONE LDA NOTES,Y ;Get Original Note's Positive Pulse Width ; ;Compute Difference: D765: 38 SEC ;Prep to Subtract w/o Borrow [A-Data-!C] D766: E5 00 SBC DOWNTIME ;Subtract Shifted Negative Pulse Width D768: 85 01 STA UPTIME ;*** Set New Positive Pulse Width *** D76A: C8 INY ;Advance Indexed Addressing Pointer D76B: B9 96 D7 LDA NOTES,Y ;Get Original Note's Negative Pulse Width ; ;Add Difference [Add w/ Carry: A+Data+C]: D76E: 65 00 ADC DOWNTIME ;Add Shifted Negative Pulse Width D770: 85 00 STA DOWNTIME ;*** Set New Negative Pulse Width *** ; ----------------------------------- ;Compute Compliment of Duration Count: D772: A9 00 LDA #0 ;Prepare to Subtract from Zero D774: 38 SEC ;Prep to Subtract w/o Borrow [A-Data-!C] D775: ED FE 02 SBC TIME ;Subtract Musical Note Time Value D778: 85 03 STA DURATION+1 ;Set Note Time-Duration Counter, High D77A: A9 00 LDA #0 ;Clear Accumulator D77C: 85 02 STA DURATION ;Set Note Time-Duration Counter, Low ; ----------------------------------- ;Prepare to Play Musical Note: D77E: A5 01 LDA UPTIME ;Get Positive Pulse Width D780: D0 98 BNE PLAY ;If Note is Not a Rest Note, Play it ... ; ; ------------------------------------------------------------------------------ ; Rest Note Subroutine: Plays Note #0 Silently, with same/regular Note Durations ; ------------------------------------------------------------------------------ ; ; Rest Note Iterations: Do UPTIME 1st, DOWNTIME 2nd; == Same # of UP/DOWN Cyles ; ; ;Do Time Adjustments: D782: EA REST NOP ;Delay (2 Machine Cycles) D783: EA NOP ;Delay (2 Machine Cycles) More D784: 4C 87 D7 JMP REST2 ;Delay (3 Machine Cycles) More D787: E6 02 REST2 INC DURATION ;Advance Note Time-Duration Counter, Low D789: D0 05 BNE REST3 ;Branch if Duration (Low) is Not Expired D78B: E6 03 INC DURATION+1 ;Advance Note Time-Duration Counter, High D78D: D0 05 BNE REST4 ;Branch if Duration (High) is Not Expired D78F: 60 RTS ;Return to Caller; Time-Duration Expired D790: EA REST3 NOP ;Delay (2 Machine Cycles) D791: 4C 94 D7 JMP REST4 ;Delay (3 Machine Cycles) More D794: D0 EC REST4 BNE REST ;Do DOWNTIME: Same Cyle; Always Taken ; ------------------------------------------------------------------------------ ; Notes Table: Values are in UP/DOWN Pairs; e.g., 1st Note is a Rest [#0 =(0,0)] ; ------------------------------------------------------------------------------ ; D796: 00 00 NOTES HEX 0000 ;Note #00: A Rest (A Silence, Not a Pitch) ; Chromatic Octave: "Contra" [Below Bass Clef] - [Low End of Partial Octave] D798: F6 F6 HEX F6F6 ;Note #01: Contra F or (uppercase) FF D79A: E8 E8 HEX E8E8 ;Note #02: Contra F# (F-sharp or G-flat) D79C: DB DB HEX DBDB ;Note #03: Contra G or (uppercase) GG D79E: CF CF HEX CFCF ;Note #04: Contra G# (G-sharp or A-flat) D7A0: C3 C3 HEX C3C3 ;Note #05: Contra A or (uppercase) AA D7A2: B8 B8 HEX B8B8 ;Note #06: Contra A# (A-sharp or B-flat) D7A4: AE AE HEX AEAE ;Note #07: Contra B or (uppercase) BB ; Chromatic Octave: "Great" [Bottom of & below Bass Clef] D7A6: A4 A4 HEX A4A4 ;Note #08: Great C or (uppercase) C D7A8: 9B 9B HEX 9B9B ;Note #09: Great C# (C-sharp or D-flat) D7AA: 92 92 HEX 9292 ;Note #10: Great D or (uppercase) D D7AC: 8A 8A HEX 8A8A ;Note #11: Great D# (D-sharp or E-flat) D7AE: 82 82 HEX 8282 ;Note #12: Great E or (uppercase) E D7B0: 7B 7B HEX 7B7B ;Note #13: Great F or (uppercase) F D7B2: 74 74 HEX 7474 ;Note #14: Great F# (F-sharp or G-flat) D7B4: 6D 6E HEX 6D6E ;Note #15: Great G or (uppercase) G D7B6: 67 68 HEX 6768 ;Note #16: Great G# (G-sharp or A-flat) D7B8: 61 62 HEX 6162 ;Note #17: Great A or (uppercase) A D7BA: 5C 5C HEX 5C5C ;Note #18: Great A# (A-sharp or B-flat) D7BC: 57 57 HEX 5757 ;Note #19: Great B or (uppercase) B ; Chromatic Octave: "Small" [Top of Bass Clef] (letters may be lowercase) D7BE: 52 52 HEX 5252 ;Note #20: Small C or (lowercase) c D7C0: 4D 4E HEX 4D4E ;Note #21: Small C# (C-sharp or D-flat) D7C2: 49 49 HEX 4949 ;Note #22: Small D or (lowercase) d D7C4: 45 45 HEX 4545 ;Note #23: Small D# (D-sharp or E-flat) D7C6: 41 41 HEX 4141 ;Note #24: Small E or (lowercase) e D7C8: 3D 3E HEX 3D3E ;Note #25: Small F or (lowercase) f D7CA: 3A 3A HEX 3A3A ;Note #26: Small F# (F-sharp or G-flat) D7CC: 36 37 HEX 3637 ;Note #27: Small G or (lowercase) g D7CE: 33 34 HEX 3334 ;Note #28: Small G# (G-sharp or A-flat) D7D0: 30 31 HEX 3031 ;Note #29: Small A or (lowercase) a D7D2: 2E 2E HEX 2E2E ;Note #30: Small A# (A-sharp or B-flat) D7D4: 2B 2C HEX 2B2C ;Note #31: Small B or (lowercase) b ; Chromatic Octave: "One-Line" [Most of Treble Clef] (letters may be lowercase) D7D6: 29 29 HEX 2929 ;Note #32: One-line C, C-one, or C^1 ; ;^[Middle C is half-way between the Clefs] D7D8: 26 27 HEX 2627 ;Note #33: One-line C# (C-sharp or D-flat) D7DA: 24 25 HEX 2425 ;Note #34: One-line D, D-one, or D^1 D7DC: 22 23 HEX 2223 ;Note #35: One-line D# (D-sharp or E-flat) D7DE: 20 21 HEX 2021 ;Note #36: One-line E, E-one, or E^1 D7E0: 1E 1F HEX 1E1F ;Note #37: One-line F, F-one, or F^1 D7E2: 1D 1D HEX 1D1D ;Note #38: One-line F# (F-sharp or G-flat) D7E4: 1B 1C HEX 1B1C ;Note #39: One-line G, G-one, or G^1 D7E6: 1A 1A HEX 1A1A ;Note #40: One-line G# (G-sharp or A-flat) D7E8: 18 19 HEX 1819 ;Note #41: One-line A, A-one, or A^1 D7EA: 17 17 HEX 1717 ;Note #42: One-line A# (A-sharp or B-flat) D7EC: 15 16 HEX 1516 ;Note #43: One-line B, B-one, or B^1 ; Chromatic Octave: "Two-Line" [Top of & above Treble Clef] (may be lowercase) D7EE: 14 15 HEX 1415 ;Note #43: Two-line C, C-two, or C^2 D7F0: 13 14 HEX 1314 ;Note #33: Two-line C# (C-sharp or D-flat) D7F2: 12 12 HEX 1212 ;Note #46: Two-line D, D-two, or D^2 D7F4: 11 11 HEX 1111 ;Note #47: Two-line D# (D-sharp or E-flat) D7F6: 10 10 HEX 1010 ;Note #48: Two-line E, E-two, or E^2 D7F8: 0F 10 HEX 0F10 ;Note #49: Two-line F, F-two, or F^2 D7FA: 0E 0F HEX 0E0F ;Note #50: Two-line F# (F-sharp or G-flat) ; ----------------------------------- ;^[High End of Partial Octave] D7FC: FF FF HEX FFFF ;Junk Bytes D7FE: FF FF HEX FFFF ;Junk Bytes