P*-------------------------------
*
* playlite.s
*
* This is the lite version of the music player --
* won't fill up your rasters and won't slow you
* down.  Fields are not available, and Shadow SID
* is not copied over, although it is still available
* and used to store information (instruments, frequencies,
* etc.).  This saves a lot on indexing and such, and
* makes for a much more streamlined player (and makes
* the full player more streamlined as well).
*
* The full variable tables are present however.  This
* makes life easier on the compiler, and also opens
* the possibility of e.g. stereo SID files.
*
* Strategy:
*   Execute global macros
*   Decrement duration
*   If zero, then read in and process new data.  Routines to
*       - Activate macros
*       - Load variables
*       - Load instruments
*       - Load duration/frequency values
*   Then process macros.  If active, then jump into it.
*   Repeat for the other two voices!
*
*  Note that values are always loaded into Shadow SID; if
*  Shadow SID is not active, then values are also copied
*  into real SID.  Thus the original SID values are still
*  accessible by macros.
*
* Almost there!
*
* SLJ 11/25/96
*
         ORG $0FE0

         DA MACBEGIN
         DA FIELDEND
         DS ^

*
* Constants
*
SID      = $D400
NUMINST  = 14             ;14 Instruments total (arbitrary)

* Player directives
* See PROCDATA

HOLD     = $F0            ;Special note (hold current note)

*
* My little kernal jump table
*
KERNAL   
* JMP MYINIT ;Be sure to comment this out :)
         JMP MAINPLAY
         JMP INIT
         JMP SETIRQ
         JMP LOADINST     ;Load instrument
         JMP GETP         ;Get current voice pointer
         JMP SETP         ;Set current voice pointer
         JMP V1GETP       ;Get voice 1 pointer
         JMP V2GETP
         JMP V3GETP
         JMP V1SETP       ;Set voice 1 pointer
         JMP V2SETP
         JMP V3SETP
RETURN                    ;This is what macros call to exit.
         PLA              ;Get the exit address
         STA MACLO,Y
         PLA
         STA MACHI,Y
         PLA              ;Active macros
         RTS              ;That's it!



         TXT 'lite v1.0 slj'
:SNEAKY  DS KERNAL+64-:SNEAKY ;Fill up to $1000 + 64
         ERR *-1/$1040    ;Break if past $1000 + 64

*
* Variables, tables, etc.
*

*
* These are the variables and such that must stay fixed
* so that the compiler and editor don't screw everything
* up.
*

FREQLO   DS 192           ;Low byte, frequencies
* $1100
FREQHI   DS 192           ;High byte freq, page boundary

MARKERS  DS 64            ;Table of 32 marker locations
* $1200
MACADR   DS 48            ;Macro starting addresses
                          ;stored seqentially, i.e.
                          ;lo hi lo hi ... thus may
                          ;be jumped to via JMP()
                          ;MUST start on page boundary
GLOBVAR  DS 16            ;Global variables
SHADOW   DS 25            ;Shadow SID
DURTAB   DS 24            ;Table of initial durations
INSTAB                    ;Instrument table
         DS 14*9          ;126 bytes
TEMPY    DS 1             ;Temporary storage
LOCOFF   DS 1             ;(local) ariable offset
                          ;8*current voice
COFFSET  DS 1             ;SID offset (0 7 14) of current voice
CURVOICE DS 1             ;Current voice
CURFIELD DS 1             ;Current field
LASTNOTE DS 3             ;Last note read in for voice

V1FIELD  DS 1             ;Voice 1 field
V2FIELD  DS 1             ;Voice 2 field
V3FIELD  DS 1             ;field associated with Voice 3
DURATION DS 6             ;Duration for current field
* $1300
TIMERUPT DA $42C6         ;Interrupt timer setting (1/60th sec.)
STOPME   DS 3             ;Stop this voice
CURINST  DS 6             ;Current instrument for this field
FIELDS                    ;Pointers to start of fields.
         DA FIELD1
         DA FIELD2
         DA FIELD3
         DS 6
MACBYTE1 DS 7             ;Active macros, lowest byte
MACBYTE2 DS 7             ;1 for each field
MACBYTE3 DS 7             ;MUST be sequential!
LOCALVAR DS 32            ;3+1 sets of 8 local variables

* Missing things:
TIMES24                   ;x*24 -- macro addr access
         DFB 0,24,48,72

MACLO    
         DS 24            ;Global macro entry points
                          ;Treated as field #0
         DS 72            ;Macro entry/exit points low byte
                          ;24 macros * 3 fields = 72
MACHI    
         DS 24            ;Global entry/exit
         DS 72            ;Macro entry/exit addresses

FOFFSET  DS 3             ;Offset into fields

REPLO    DS 3             ;Repeats, lo address
REPHI    DS 3             ;Hi address
REPTIMES DS 3             ;Number of times to repeat
BITP                      ;Table of 2^(x mod 8)
         DFB 1,2,4,8,16,32,64,128
         DFB 1,2,4,8,16,32,64,128
         DFB 1,2,4,8,16,32,64,128
DIV8T7                    ;x/8 * 7 (for MACBYTE indexing)
         DFB 0,0,0,0,0,0,0,0
         DFB 7,7,7,7,7,7,7,7
         DFB 14,14,14,14,14,14,14,14

SETIRQ   SEI              ;Otherwise, set up simple IRQ player
         JSR INIT
         LDA #<IRQ
         STA $0314
         LDA #>IRQ
         STA $0315
         LDA TIMERUPT
         STA $DC04
         LDA TIMERUPT+1
         STA $DC05
         CLI
         RTS
IRQ                       ;Simple IRQ player.
* INC $D020
         JSR MAINPLAY
* DEC $D020
IRQBLAH  JMP $EA31

*
* (Merlin) Macros
*

*
* The (music) macro handler.  Shift bits until an active
* macro is found, and go from there!  If a macro active byte
* is zero, then it may be skipped completely.
*
* Parameters:
*    ]1 -- Current voice (0 1 2 or 3)
*          (0 = global macros)
*    The correct index into macro address tables is
*          computed as 24*]1
*    X is set to the current field
*
* On exit, Z is zet.
*
NEWMAC   MAC
         LDA MACBYTE1+]1  ;Active macros -- 3 bytes
         LDY #24*]1-1     ;Start one lower

L1       INY
         LSR              ;Check
         BCC CONT1        ;Carry set -> call macro
CALL1    JSR INITMAC      ;Enters macros at initial point
CONT1    BNE L1           ;Last line of INITMAC is PLA

         LDY #]1*24+7     ;Advance to next macro chunk
                          ;(the INY will advance Y to Y+8)
         LDA MACBYTE2+]1
L2       INY
         LSR
         BCC CONT2
         JSR INITMAC
CONT2    BNE L2

         LDY #]1*24+15
         LDA MACBYTE3+]1
L3       INY
         LSR
         BCC CONT3
         JSR INITMAC
CONT3    BNE L3
         <<<              ;That's it!

OLDMAC   MAC              ;Identical to above, with CALLMAC
         LDA MACBYTE1+]1  ;Active macros -- 3 bytes
         LDY #]1*24-1

L1       INY
         LSR              ;Check
         BCC CONT1        ;Carry set -> call macro
CALL1    JSR CALLMAC      ;Enters macros at initial point
CONT1    BNE L1           ;Last line of INITMAC is PLA

         LDY #]1*24+7     ;Advance to next macro chunk
                          ;(the INY will advance Y to Y+8)
         LDA MACBYTE2+]1
L2       INY
         LSR
         BCC CONT2
         JSR CALLMAC
CONT2    BNE L2

         LDY #]1*24+15
         LDA MACBYTE3+]1
L3       INY
         LSR
         BCC CONT3
         JSR CALLMAC
CONT3    BNE L3
         <<<              ;That's it!

* Subroutine to read in new data from the current field.
* The field pointers are stored inside the routine,
* so each voice stores its own pointers.  To switch
* fields within a voice, these pointers must be
* swapped out and the new pointers swapped in.
*
* Parameters:
*   ]1 -- Current offset into the field data.
*   ]2 -- Current voice # (index for current note/dur)
*         (1 2 or 3)
*   ]3 -- SID offset for current voice (0 7 or 14)
*
* Due to space considerations (i.e. the lite version
* probably shouldn't take up 3k :), this is now
* a genuine procedure.  As a result, the two
* variables ]2 and ]3 must be set up elsewhere,
* in curfield and coffset.  Also required is
* LOCOFF, which tells the current offset into
* the local variables.

* Player instructions

SETVOL   = $80            ;Set volume
ACTGLOB  = $81            ;Activate global macro
DACTGLOB = $82            ;Deactivate global macro
ACTLOC   = $83            ;Activate local macro
DACTLOC  = $84            ;Deactivate local macro
LINST    = $85            ;Load instrument
REPEAT   = $86            ;Repeat loop begin
ENDREP   = $87            ;Go to last REPEAT and repeat!
STOP     = $88            ;Halts the field at that point
GATEON   = $89            ;Turn gate bit on
GATEOFF  = $8A            ;Turn gate bit off
RESTART  = $8B            ;Restart player (JMP INIT)
JUMP     = $8C            ;Jump to marker
JSUB     = $8D            ;Subroutine to marker
RSUB     = $8E            ;Return from subroutine
HICODE   = $8F            ;Always last instruction

LOADGLOB = %11000000      ;Load global variable (mask)
LOADLOC  = %11100000      ;Load local variable

* Table of handler routine addresses for above

TABLE1   
         DA SETV
         DA ACTG
         DA DACTG
         DA ACTL
         DA DACTL
         DA LOADINST
         DA REP
         DA ENDR
         DA STO
         DA GON
         DA GOFF
         DA PLAINIT
         DA JMPMARK
         DA JSRMARK
         DA RTSMARK

STACK    DS 24            ;Stack for JSR, 4-deep
STACKP   DS 3             ;Stack index for each field

PROCDATA 
IHANDLE  
         CMP #LOADGLOB    ;Load variable has bit 6 set
         BCC NOLOAD
         CMP #LOADLOC     ;Less than => global load
         BCC GLOBAL       ;Load local is probably the most
                          ;frequent directive.
         AND #%00011111   ;Strip out the variable number
         CLC
         ADC LOCOFF       ;Local variable offset
         TAY              ;for current voice
         TXA
         STA LOCALVAR,Y   ;voice*8th set of entries
         RTS

GLOBAL   AND #%00011111
         TAY
         TXA
         STA GLOBVAR,Y
         RTS

NOLOAD   
         CMP #HICODE      ;Restart on anything above
         BCS PLAINIT
         ASL              ;Double it
         TAY
         LDA TABLE1,Y     ;Get address of routine
         STA :JMP+1
         LDA TABLE1+1,Y   ;hi byte
         STA :JMP+2
:JMP     JMP SETV
PLAINIT  PLA
         PLA

*
* Initialization routine
*
* Reset field pointers, macros
* inactive, fields for voice 1 2 and 3
*
INIT     
         LDX #01          ;Start all durations at 1
         STX DURATION
         STX DURATION+1
         STX DURATION+2
         DEX
         STX STOPME
         STX STOPME+1
         STX STOPME+2

         TXA
         LDX #3           ;Reset macros
:L2      STA MACBYTE1,X
         STA MACBYTE2,X
         STA MACBYTE3,X
         DEX
         BPL :L2

         STA SID+4        ;Clear gate bits
         STA SID+$0B
         STA SID+$12

         STA STACKP
         LDA #8
         STA STACKP+1
         LDA #16
         STA STACKP+2

         DO 0             ;Not necessary for lite routine
         LDX #1
         STX V1FIELD
         INX
         STX V2FIELD
         INX
         STX V3FIELD
         FIN              ;-----------------------

         LDA FIELDS       ;Initialize the field pointers
         LDX FIELDS+1     ;voice 1
         JSR V1SETP

         LDA FIELDS+2     ;Voice 2 = field 2
         LDX FIELDS+3
         JSR V2SETP

         LDA FIELDS+4
         LDX FIELDS+5
         JSR V3SETP

         JMP VOICE1       ;Continue into player, skipping
                          ;global macros.


SETV                      ;Set volume
         LDA SHADOW+24
         AND #$F0
         STA SHADOW+24
         TXA
         ORA SHADOW+24
         STA SHADOW+24
         STA SID+24
         RTS

REP                       ;Repeat begin
         LDY CURFIELD
         TXA
         STA REPTIMES-1,Y ;Number of times to repeat
         JSR GETP         ;Get current pointer
* GETP leaves Y unchanged
* LDY CURFIELD ;returns (A X) = (lo hi)
         STA REPLO-1,Y    ;Low byte
         TXA
         STA REPHI-1,Y    ;Hi byte
SNEAKY   RTS

ENDR                      ;Repeat
         LDX CURFIELD
         DEC REPTIMES-1,X
         BEQ SNEAKY
         LDY CURFIELD
         LDA REPLO-1,Y    ;Restore index
         LDX REPHI-1,Y
         JMP SETP         ;Set current voice to (A X 0)

ACTG                      ;Activate global macro
         LDA BITP,X       ;Get the bit!
         LDY DIV8T7,X     ;x/8*7, gives MACBYTE1 2 or 3
         ORA MACBYTE1,Y   ;automatically
         STA MACBYTE1,Y   ;Global=voice 0
         TXA              ;Reset entry address
         ASL
         TAY
         LDA MACADR,Y
         STA MACLO,X
         LDA MACADR+1,Y
         STA MACHI,X
         RTS

DACTG                     ;Deactivate
         LDA BITP,X
         EOR #$FF
         LDY DIV8T7,X
         AND MACBYTE1,Y
         STA MACBYTE1,Y
         RTS

ACTL                      ;Activate local macro
         LDA DIV8T7,X     ;Macro byte offset
         CLC
         ADC CURFIELD     ;offset further by current voice
         TAY
         LDA BITP,X
         ORA MACBYTE1,Y
         STA MACBYTE1,Y
         RTS

DACTL    
         LDA DIV8T7,X
         CLC
         ADC CURFIELD
         TAY
         LDA BITP,X
         EOR #$FF
         AND MACBYTE1,Y
         STA MACBYTE1,Y
         RTS

STO                       ;Stop voice
         LDX CURFIELD
         STA STOPME-1,X   ;And that's the end of that!
         RTS

GON                       ;Turn gate on
         LDX COFFSET
         LDA SHADOW+4,X
         ORA #$01
         STA SHADOW+4,X
         STA SID+4,X
         RTS

GOFF                      ;Turn gate off
         LDX COFFSET
         LDA SHADOW+4,X
         AND #$FE
         STA SHADOW+4,X
         STA SID+4,X
         RTS

JMPMARK                   ;X contains marker # index
         LDA MARKERS,X
         PHA
         LDA MARKERS+1,X
         TAX
         PLA
         JMP SETP

JSRMARK  
         LDY CURFIELD     ;1 2 or 3
         LDA STACKP-1,Y
         CLC
         ADC #2
         STA STACKP-1,Y
         TAY
         LDA MARKERS,X
         PHA
         LDA MARKERS+1,X
         PHA
         JSR GETP
         STA STACK-1,Y    ;lo
         TXA
         STA STACK-2,Y    ;hi
         PLA              ;Destination hi
         TAX
         PLA
         JMP SETP

RTSMARK  
         LDY CURFIELD
         LDA STACKP-1,Y
         SEC
         SBC #2
         STA STACKP-1,Y
         TAY
         LDA STACK,Y
         TAX              ;High byte
         LDA STACK+1,Y
         JMP SETP


*
* Load instrument routine
*
* On entry: X contains the instrument to be loaded
*
* On exit: Y is preserved
*
TIMES9   DFB 0,9,18,27,36,45,54,63,72,81,90,99,108,117

LOADINST 
         STY TEMPY
         LDY CURVOICE     ;1 2 or 3
         TXA
         STA CURINST-1,Y
         LDA TIMES9,X
         TAX
         LDY COFFSET      ;Current voice offset
         LDA INSTAB,X     ;Pulse lo
         STA SHADOW+2,Y
         STA SID+2,Y
         LDA INSTAB+1,X   ;Pulse hi
         STA SHADOW+3,Y
         STA SID+3,Y
         LDA INSTAB+2,X   ;Control register
         STA SHADOW+4,Y
         STA SID+4,Y
         LDA INSTAB+3,X   ;Attack/decay
         STA SHADOW+5,Y
         STA SID+5,Y
         LDA INSTAB+4,X   ;Sustain/release
         STA SHADOW+6,Y
         STA SID+6,Y

         LDY CURVOICE     ;1 2 or 3
         LDA INSTAB+8,X   ;Filter mode, 0 means no filter
         BEQ :NOFILTER    ;Zero means no filter
         LDA SHADOW+$18   ;Volume, filter mode
         AND #$0F
         ORA INSTAB+8,X
         STA SHADOW+$18
         STA SID+$18
         LDA SHADOW+$17   ;Next up: resonance
         AND #$0F         ;Other voices may be filtered
         ORA BITP-1,Y
         ORA INSTAB+7,X   ;Grab resonance
         STA SHADOW+$17
         STA SID+$17
         LDA INSTAB+6,X   ;Cutoff hi
         STA SHADOW+$16
         STA SID+$16
         LDA INSTAB+5,X   ;Cutoff lo
         STA SHADOW+$15
         STA SID+$15
         LDY TEMPY
         RTS
:NOFILTER                 ;Don't filter this voice
         LDA BITP-1,Y
         EOR #$FF         ;Set bit to 0
         AND SHADOW+$17
         STA SHADOW+$17
         STA SID+$17
         LDY TEMPY
         RTS

*
* These routines will either fetch or set the current
* data (field) pointers for voice1, voice2, and voice3.
* Data is passed in/out via (A X) = (lo hi)
*
* The index is added into the pointer.
*
GETP                      ;Figure out which one to call
         LDA CURVOICE
         CMP #2
         BEQ V2GETP
         BPL V3GETP

V1GETP   
         LDX V1POINT+5
         LDA FOFFSET
         CLC
         ADC V1POINT+4
         BCC :RTS
         INX
:RTS     RTS
V2GETP   
         LDX V2POINT+5
         LDA FOFFSET+1
         CLC
         ADC V2POINT+4
         BCC :RTS
         INX
:RTS     RTS
V3GETP   
         LDX V3POINT+5
         LDA FOFFSET+2
         CLC
         ADC V3POINT+4
         BCC :RTS
         INX
:RTS     RTS

SETP                      ;Again figure which to use
         PHA
         LDA CURVOICE
         CMP #2
         BEQ V2SETPLA     ;Special branch
         BPL V3SETPLA
V1SETPLA PLA
V1SETP   
         STA V1POINT+4
         STX V1POINT+5
         STA V1POINT+8
         STX V1POINT+9
         LDA #00
         STA FOFFSET
         RTS

V2SETPLA PLA
V2SETP   
         STA V2POINT+4
         STX V2POINT+5
         STA V2POINT+8
         STX V2POINT+9
         LDA #00
         STA FOFFSET+1
         RTS

V3SETPLA PLA
V3SETP   
         STA V3POINT+4
         STX V3POINT+5
         STA V3POINT+8
         STX V3POINT+9
         LDA #00
         STA FOFFSET+2
         RTS

*
* Main interrupt routine:
*   If Shadow SID active, then copy it over.
*   DEC duration
*   If zero then read in new data
*   Process macros
*   Repeat for other voices
*
MAINPLAY 
* LDA COPYSHAD ;Zero means active
* BNE GLOBMAC
* LDX #24
*:L1 LDA SHADOW,X
* STA SID,X
* DEX
* BPL :L1

GLOBMAC                   ;Take care of global macros
         LDX #00          ;1-6 are normal fields, 0 global
         STX CURFIELD
         STX LOCOFF       ;Local variable offset
         >>> OLDMAC,0

VOICE1   
         LDA #08
         STA LOCOFF
         LDX #00
         STX COFFSET      ;SID offset
         INX
         STX CURVOICE     ;Current voice

* LDX V1FIELD ;Current field voice 1
         STX CURFIELD     ;Current field
* LDY TIMES24,X ;x*24 gives the field offset
                          ;(because there are 24 macros!)
                          ;for macro addresses.
         LDA STOPME
         BNE JV2
         DEC DURATION-1,X
         BEQ V1LOOP
NOUPD81  >>> OLDMAC,1     ;Otherwise deal with macros.
JV2      JMP VOICE2

V1POINT  
V1LOOP   LDY FOFFSET      ;v1offset, v2offset, etc.
                          ;An offset into the data; less
                          ;self-modifying code this way
                          ;(i.e. more saved cycles)
:FP1     LDX $1000,Y      ;First byte: note or data
         INY
:FP2     LDA $1000,Y      ;Second byte: inst or duration
         INY
         STY FOFFSET
         BNE :CONT1
         INC :FP1+2       ;Increment the high bytes
         INC :FP2+2       ;Simple!
:CONT1   
         TAY              ;Y is now index for LNOTE
         BPL :LNOTE       ;High bit set means instruction
         JSR PROCDATA
         LDA STOPME
         BEQ V1LOOP
         JMP VOICE2

:HOLD    LDX CURFIELD
         JMP NOUPD81

:LNOTE                    ;Otherwise, load frequency and
                          ;duration data
                          ;X is note number i.e. freq index
                          ;A is duration index
                          ;Store in SID as well as Shadow SID
         LDA DURTAB,Y
         STA DURATION     ;New duration
         CPX #HOLD        ;Slur current note?
         BEQ :HOLD        ;If so, then do it!
         STX LASTNOTE     ;Last note read for v1, etc.
         LDA FREQLO,X
         STA SHADOW
         STA SID
         LDA FREQHI,X
         STA SHADOW+1
         STA SID+1
         LDX CURFIELD
         >>> NEWMAC,1     ;Restart macros


VOICE2   
         LDX CURVOICE
         INX
         STX CURVOICE
         STX CURFIELD
         LDA STOPME+1
         BNE JV3
         LDA #16
         STA LOCOFF
         LDA #07
         STA COFFSET
* LDX V2FIELD
* STX CURFIELD
* LDA TIMES24,X
* TAY
         DEC DURATION-1,X
         BEQ V2LOOP
NOUPD82  >>> OLDMAC,2     ;Otherwise deal with macros.
JV3      JMP VOICE3

V2POINT  
V2LOOP   LDY FOFFSET+1    ;v1offset, v2offset, etc.
                          ;An offset into the data; less
                          ;self-modifying code this way
                          ;(i.e. more saved cycles)
:FP1     LDX $1000,Y      ;First byte: inst or duration
         INY
:FP2     LDA $1000,Y      ;Second byte: note or data
         INY
         STY FOFFSET+1
         BNE :CONT1
         INC :FP1+2       ;Increment the high bytes
         INC :FP2+2       ;Simple!
:CONT1   
         TAY
         BPL :LNOTE       ;High bit set means instruction
         JSR PROCDATA
         LDA STOPME+1
         BEQ V2LOOP
         JMP VOICE3

:HOLD    LDX CURFIELD
         JMP NOUPD82

:LNOTE                    ;Otherwise, load frequency and
                          ;duration data
                          ;X is note number i.e. freq index
                          ;A is duration index
                          ;Store in SID as well as Shadow SID
         LDA DURTAB,Y
         STA DURATION+1   ;New duration
         CPX #HOLD        ;Slur current note?
         BEQ :HOLD        ;If so, then do it!
         STX LASTNOTE+1   ;Last note read for v1, etc.
         LDA FREQLO,X
         STA SHADOW+7
         STA SID+7
         LDA FREQHI,X
         STA SHADOW+8
         STA SID+8
         LDX CURFIELD
         >>> NEWMAC,2     ;Restart macros

VOICE3   
         LDA STOPME+2
         BNE DONERTS
         LDX CURVOICE
         INX
         STX CURVOICE
         STX CURFIELD
         LDA #24
         STA LOCOFF
         LDA #14
         STA COFFSET
* LDX V3FIELD
* STX CURFIELD
* LDA TIMES24,X
* TAY
         DEC DURATION-1,X
         BEQ V3LOOP
NOUPD83  >>> OLDMAC,3     ;Otherwise deal with macros.
DONERTS  RTS

V3POINT  
V3LOOP   LDY FOFFSET+2    ;v1offset, v2offset, etc.
                          ;An offset into the data; less
                          ;self-modifying code this way
                          ;(i.e. more saved cycles)
:FP1     LDX $1000,Y      ;First byte: inst or duration
         INY
:FP2     LDA $1000,Y      ;Second byte: note or data
         INY
         STY FOFFSET+2
         BNE :CONT1
         INC :FP1+2       ;Increment the high bytes
         INC :FP2+2       ;Simple!
:CONT1   
         TAY
         BPL :LNOTE       ;High bit set means instruction
         JSR PROCDATA
         LDA STOPME+2
         BEQ V3LOOP
         RTS

:HOLD    LDX CURFIELD
         JMP NOUPD83

:LNOTE                    ;Otherwise, load frequency and
                          ;duration data
                          ;X is note number i.e. freq index
                          ;A is duration index
                          ;Store in SID as well as Shadow SID
         LDA DURTAB,Y
         STA DURATION+2   ;New duration
         CPX #HOLD        ;Slur current note?
         BEQ :HOLD        ;If so, then do it!
         STX LASTNOTE+2   ;Last note read for v1, etc.
         LDA FREQLO,X
         STA SHADOW+14
         STA SID+14
         LDA FREQHI,X
         STA SHADOW+15
         STA SID+15
         LDX CURFIELD
         >>> NEWMAC,3     ;Restart macros
         RTS              ;All done!

*
* The macro caller.  There are two entry points,
* INITMAC and CALLMAC.  INITMAC will enter a macro
* at the beginning.  CALLMAC will enter a macro
* where it last left off.
*
* On entry, Y is assume to contain the correct field
* offset position in the macro address lookup table.
* (i.e. tells which macro we are on, and in which field)
*
* In all cases A and Y are preserved.  The last line is
* PLA, thus the zero flag is set appropriately on exit.
*
INITMAC  
         PHA
         LDX CURFIELD
         TYA              ;We need to strip out the field
         SEC              ;offset, i.e. subtract 24*n
         SBC TIMES24,X
         ASL              ;Multiply by two because of how
                          ;initial address table is set up.
         STA :ADDR+1      ;Store the correct location
:ADDR    JMP (MACADR)     ;MACADR = Initial address, (lo,hi)

CALLMAC  
         PHA
         LDA MACHI,Y      ;MACLO etc. contain last exit
         PHA              ;locations.
         LDA MACLO,Y
         PHA
         RTS              ;Neat, huh?

MACBEGIN 

*
* Now some simple song data -- just a scale.
*
FIELD1   
         DFB RESTART,RESTART
FIELD2   
         DFB STOP,STOP
FIELD3   
         DFB STOP,STOP
FIELDEND 
