back to project page

Apple II Programmers Aid 1.6.$D717~$D7FF - Music.BIN Disassembly

                   ; ==============================================================================
                   ; 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

Symbol Table

DONE$D762
DOWN$D734
ENTRY$D717
LOOKUP$D74E
NOTES$D796
PATH1$D728
PATH2$D72C
PATH3$D732
PATH4$D742
PATH5$D746
PATH6$D74C
PLAY$D71A
PLAY2$D71F
PLAY3$D739
REST$D782
REST2$D787
REST3$D790
REST4$D794
SHIFT$D75B