| 
   
 
 Learn Multi platform
          6502 Assembly Programming... For
          Monsters! Platform
            Specific Lessons 
      
      Platform Specific Series - Now we
      know the basics, lets look at the details of the platforms we're covering!
       
 
      
        
        
        Color
              Palette
 
          
            
              | The Nes palette is slightly odd
                compared to RGB systems, you can see how the HEX values map to
                colors in the chart to the right-> 
 
 
 Each tile has 4 colors*, and you can have 4 separate palettes
                for tiles... and a separate 4 for sprites... this makes a total
                of 32 color definitions for tiles and sprites combined
 
 * Though the background colors are common to all sprites.
 | 
 
                  
                    Check
out
                  GameTechWiki for better palette info!
                      | $00 | $01 | $02 | $03 | $04 | $05 | $06 | $07 | $08 | $09 | $0A | $0B | $0C | $0D | $0E | $0F |  
                      | $10 | $11 | $12 | $13 | $14 | $15 | $16 | $17 | $18 | $19 | $1A | $1B | $1C | $1D | $1E | $1F |  
                      | $20 | $21 | $22 | $23 | $24 | $25 | $26 | $27 | $28 | $29 | $2A | $2B | $2C | $2D | $2E | $2F |  
                      | $30 | $31 | $32 | $33 | $34 | $35 | $36 | $37 | $38 | $39 | $3A | $3B | $3C | $3D | $3E | $3F |  |  
              | Our Tutorials use a common format
                on all platforms, where each channel is defined by one nibble in
                $-GRB format... 
 Because this doesn't easily map to the Nes palette, we'll
                convert it using a Lookup table of 3x3x3 size, so RGB values
                will go from 0-2
 |  |  
          
            
              | The NES
                  palette is a bit rigid, and doesn't really map to RGB very
                  well, but we've done what we can! 
 If you don't like the mappings here, you can change the Lookup
                  Table, or write your own native code.
 
 |  |  Vram Layout
 
 
          
            
              | While Pattern data is defined by
                the Name Table, the Attribute Table defines the Color 
 By default the NES has too little VRAM to use Name/Attrib Table
                2 & 3
 
 Specify a memory address by writing the byte pair to $2006...
                HIGH BYTE FIRST... (Big Endian)
 
 NOTE: Writing
                  to VRAM outside of VBLANK will cause problems... also
                note, selecting
                  an address resets PPUSCROLL
 
 EG, lets point to $3F00... and write $11 to the first palette
                entry!
 
 lda #$3F      ;High
                  byte
 sta $2006    ;Send
                  to vram select
 lda #$00      ;Low
                  byte
 sta $2006   
                  ;Send to vram select
 
 lda #$11       ;New value
 sta $2007   
                  ;Send to write data
 | 
                  
                    
                      | Vram From | Vram To | Purpose |  
                      | $0000 | $0FFF | Pattern
Table
                          0 |  
                      | $1000 | $1FFF | Pattern
Table
                          1 |  
                      | $2000 | $23BF | NameTable
0
                          (32x30) |  
                      | $23C0 | $23FF | Attribute
Table
                          0 |  
                      | $2400 | $27BF | NameTable
1
                          (32x30) |  
                      | $27C0 | $27FF | Attribute
Table
                          1 |  
                      | $2800 | $2BBF | NameTable
2
                          (32x30) * |  
                      | $2BC0 | $2BFF | Attribute
Table
                          2 * |  
                      | $2C00 | $2FBF | NameTable
3
                          (32x30) * |  
                      | $2FC0 | $2FFF | Attribute
Table
                          3 * |  
                      | $3000 | $3EFF | Copy
of
                          $2000-$2EFF |  
                      | $3F00 | $3F1F | Palette
                          definitions |  
                      | $3F20 | $3FFF | Copies
of
                          $3F00-$3F1F |  * Extra Ram Only
 
 |  
              | Each palette on the NES has 3
                colors -Background Color 0 is a common background color, and
                Color 0 in sprites transparent | 
                   
                  
                    
                      | Address | Category |  
                      | $3F00 | Common
                          Background Color |  
                      | $3F01 | Background
                          Palette 0 |  
                      | $3F05 | Background
                          Palette 1 |  
                      | $3F09 | Background
                          Palette 2 |  
                      | $3F0D | Background
                          Palette 3 |  
                      | $3F11 | Sprite
                          Palette 0 |  
                      | $3F15 | Sprite
                          Palette 1 |  
                      | $3F19 | Sprite
                          Palette 2 |  
                      | $3F1D | Sprite
                          Palette 3 |  |  
              | The NES Name Table defines the Tilemap's patterns... one byte
                per 8x8 tile defines the number of the tile... the name table is
                32x30 tiles in size, so spans from $2000-$23BF 
 Note: Name Table 2,3 ($2800-$3000) are not available on an
                standard NES , they will only be available if your cartridge has
                Extra Ram!
 
 Color's are defined  by the 'Attribute table'...
                effectively, each square block of 2x2 tiles (16x16 pixels) have
                to use the same color palette... and each block of 4x4 tiles
                (32x32 pixels) are defined by a single byte (2 bits per block -
                for 4 possible palettes)
 
 Lets take the example to the right, with different 16 tiles
                making up a grid of 32x32 pixels- each areas palette would be
                defined by a single byte in the way below:
 
 
                  
                    
                      | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |  
                      | D | D | C | C | B | B | A | A |  |  |  
          
            
              |  | Unfortunately on the
                NES, although our tiles are 8x8 pixel, our palette is defined at
                a 16x16 level 
 You'll notice objects in NES games (Such as ? blocks in mario)
                are usally 16x16 to ensure this isn't a problem.
 |  Our
              Code
 
            
              
                | We're going to use a 3x3x3
                  table of color conversions to convert our one nibble per
                  channel $-GRB definition to something valid for the NES 
 Offset=(G*9)+(R*3)+B
 
 to calculate the color of the equivalent GRB color for the NES
 |  |  
                | When we call our SetPalette
                  function, we set A to the palette entry, and z_h and z_l as
                  the word defining the new color (in $-GRB format) 
 We then want to calculate the palette entry, using the formula
                  we just mentioned...
 
 We're going to use two functions
 
 One is 'PalcolConv' - which will take a high nibble, and
                  convert it to a value 0-2....
 The other is PalConvR - which will take the low nibble and do
                  the same....
 
 Once we've calculated the offset, we store it into Y
 |  |  
                | We're going to need to load in
                  the address of the lookup table into z_hl |  |  
                | We need to work out the correct
                  address to write to within the GTIA ($D016-D01A on the Atari
                  800 or $C016-C01A on the 5200) 
 Color 0's address is $D01A
 Color 1's address is $D016
 Color 2's address is $D017
 Color 3's address is $D018
 
 Once we've calculated the palette address to write to, we just
                  output the new color we got from the lookup table to the
                  destination address
 |  |  
                | Our palette conversion routines
                  use the following RGB conversions to convert a nibble to a
                  value 0-2 
 0-4...0
 5-9...1
 10-15...2
 |  |  
          
            
              | Two ports are used to define the color on the SNES, the first
                takes a single byte and selects the color we want to change, 
 the second takes two bytes, and defines the new RGB color for
                that entry.
 | 
                  
                    
                      | Address | Name | Purpose | Bits | Details |  
                      | $2121 | CGADD | Color
                          # (or pallete) selection | xxxxxxxx | x=color
                          (0-255) |  
                      | $2122 | CGDATA | Color
                          data | -bbbbbgg
gggrrrrr | Color
                          Data BGR |  |  
              | The 256 colors make up 16
                palettes... the first 8 are used by Background patterns... the
                second 8 are used by Sprites 
 
 | 
                   
                  
                    
                      | Color Number | Type | Palette Number |  
                      | 0 | Background | 0 |  
                      | 16 | Background | 1 |  
                      | 32 | Background | 2 |  
                      | 48 | Background | 3 |  
                      | 64 | Background | 4 |  
                      | 80 | Background | 5 |  
                      | 96 | Background | 6 |  
                      | 112 | Background | 7 |  
                      | 128 | Sprite | 0 |  
                      | 144 | Sprite | 1 |  
                      | 160 | Sprite | 2 |  
                      | 176 | Sprite | 3 |  
                      | 192 | Sprite | 4 |  
                      | 208 | Sprite | 5 |  
                      | 224 | Sprite | 6 |  
                      | 240 | Sprite | 7 |  
                      | 255 | � | � |  |  
              | In these tutorials, we a common format using one nibble per
                color channel in the format $-GRB... 
 The SNES uses 5 bits per channel in the format %-BBBBBGG
                GGGRRRRR
 
 We will need to convert our format to the correct format for the
                SNES!
 | 
                  
                    
                      | 
 | F | E | D | B | C | A | 9 | 8 | 
 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |  
                      | Our
                          Format | - | - | - | - | G | G | G | G | 
 | R | R | R | R | B | B | B | B |  
                      | SNES
                          Format | - | B | B | B | B | B | G | G | 
 | G | G | G | R | R | R | R | R |  |  
 
          
            
              | The 255 onscreen
                  colors of the SNES are split between the Backgrounds and
                  Sprites, but the code will look at today will set background
                  and sprite colors at the same time. 
 This will make things easier, and as we've only got 16 colors
                  on most systems, we won't miss the extra colors in these
                  tutorials!
 |  |  
 Our
              Code
 
          
            
              | When we call our SetPalette function, we set A to the palette
                entry, and z_h and z_l as the word defining the new color (in
                $-GRB format) 
 We define the Color we're going to change by storing A in port
                $2121
 
 We're going to use z_bc as a 'buildup' for the SNES format, so
                we need to clear z_c
 |  |  
              | We're going to first shift the Green component... 
 We only have 4 bits in our definition, but the SNES uses 5,
                we'll shift two of the bits to the left into z_c, and store the
                top two bits into z_b
 
 |  |  
              | We're going to shift the Red and
                Blue bits as well, 
 the correct format for the SNES is now in z_b and z_c
 |  |  
              | To set the color we write the Low
                byte from z_b to $2122, then we write the High byte from z_c
                also to $2122 
 Once we've done the main color, we add 128 - and set the
                equivalent sprite color to the same color.
 |  |  
      
 
 
        
          
            
              |  | In these tutorials we will write a driver
                  called 'ChibiSound' - it takes a single byte in the
                  accumulator, in the format %NVPPPPPP where N=Noise, V=Volume
                  and P=Pitch. 
 This allows our multiplatform games to make sound effects in a
                  similar way on all the systems.
 
 |  
  Pokey Ports  Sound on the Atari is handled by the POKEY
        chip, it's address varies depending on if we're using an atari 800 or
        5200. There are 4 sound channels, each takes a Frequency from 0-255, a Volume
        0-15 (0=lowest), and a 'Noise' nibble...
 
 The noise setting will define the sound
        type, a setting of %1010 is a square wave, a setting of %0000 is
        distortion 
 
        
          
            
              | Group | Name | Description | Address
                    A80 | Address
                    A52 | Bits | Meaning |  
              | POKEY | AUDF1 | Audio
                  frequency 1 control | $D200 | $E800 | FFFFFFFF | F=Frequency |  
              | POKEY | AUDC1 | Audio
                  channel 1 control | $D201 | $E801 | NNNNVVVV | N=Noise
                  V=Volume |  
              | POKEY | AUDF2 | Audio
                  frequency 2 control | $D202 | $E802 | FFFFFFFF | F=Frequency |  
              | POKEY | AUDC2 | Audio
                  channel 2 control | $D203 | $E803 | NNNNVVVV | N=Noise
                  V=Volume |  
              | POKEY | AUDF3 | Audio
                  frequency 3 control | $D204 | $E804 | FFFFFFFF | F=Frequency |  
              | POKEY | AUDC3 | Audio
                  channel 3 control | $D205 | $E805 | NNNNVVVV | N=Noise
                  V=Volume |  
              | POKEY | AUDF4 | Audio
                  frequency 4 control | $D206 | $E806 | FFFFFFFF | F=Frequency |  
              | POKEY | AUDC4 | Audio
                  channel 4 control | $D207 | $E807 | NNNNVVVV | N=Noise
                  V=Volume |  
              | POKEY | AUDCTL | general
                  audio control | $D208 | $E808 | N1234HHS | N=Noise
                  bit depth 1234=Channel Clocks HH=highpass filters S=main
                  clockspeed |  
 Writing Chibi Sound! 
        
          
            
              | Lets write ChibiSound... 
 First we need to check if the value we've been passed is zero -
                if it is, then we'll use this as the volume and write it to reg
                1 of the POKEY...
 
 Since the POKEY is at a different place on the different Atari's
                we use a symbol definition which will point to $D200 or $E800,
                and add the value of the register we want to change, so $D201
                would become POKEY+$01
 |  
 
  |  
              | First we need to decide what kind of sound we want to make, if
                the top bit is 1, then we want to make a noise! 
 We make a noise by setting the top nibble of POKEY+1 to %0000
                .... we make a square wave with %1010
 
 The same register also handles the volume with the bottom
                nibble, we have only one volume bit in the Chibisound byte, so
                we set the lowest 3 bits to 1...
 
 We store the result in z_as for after we've worked out the
                volume.
 
 Finally we write Zero to POKEY+8... this sets up all the pokey
                settings for our sound to play
 |  |  
              | OK, lets set the pitch of the sound... We have 6 pitch bits, but we rotate them to the left, so we're
                setting a value 0-252
 We write this to POKEY+0 to set Channel 1's pitch/Frequency
 |  |  
              | Finally let's get the Volume... we take the one volume bit and
                shift it to the top of the low nibble... 
 We then OR in the noise on/off we stored in z_as, and write it
                to POKEY+1
 (this is where we jumped to if A was 0 at the start)
 |  |  
 
 
        
          
            
              |  | The Sound
                  on the Atari Lynx is odd...while it's very capable, compared
                  to other systems it's very hard to understand!...Fortunately
                  if we ignore the documentation, and just use some 'basic code'
                  we can get some stuff working easily! |  Sound on the Atari Lynx The sound ports are memory mapped, there are
      4 channels in total. 
      Each Channel links to a timer source,
        Channel 0 links to 'Timer 7'... Channel 1 links to 0, Channel 2 links to
        1, Channel 3 links to 2, 
 
        
          
            
              | From | To | Name | Description | Bits | Meaning |  
              | FD1C | FD1F | TIMER7 | Timer
                  Channel 7 and mag1b | 
 | 
 |  
              | FD20 | FD20 | VOL | Audio
                  Channel 0 � 2�s compliment Volume control | -VVVVVVV | V=Volume
                  (0-127) |  
              | FD21 | FD21 | FEEDBACK | Audio
                  Channel 0 � Shift register feedback enable | FFFFFFFF | Effective
                  sound of instrument ($10=Square Tone / $90=Noise) 
 |  
              | FD22 | FD22 | RAW | Audio
                  Channel 0 � Audio Output Value (Raw Data) | 
 | Constantly
                  changes |  
              | FD23 | FD23 | SHIFTL | Audio
                  Channel 0 � Lower 8 bits of shift register | SSSSSSSS | Shift
                  Regsiter L |  
              | FD24 | FD24 | FREQ | Audio
                  Channel 0 � Audio Timer Backup Value | TTTTTTTT | T=Timer
                  (effectively Frequency) |  
              | FD25 | FD25 | CONTROL | Audio
                  Channel 0 � Audio Control Bits | FTIRCKKK | F=Feedback
                  bit 7 , reset Timer done, enable Integrate, enable Reload
                  enable Count, clocK select |  
              | FD26 | FD26 | COUNT | Audio
                  Channel 0 � Audio Counter | 
 | Constantly
                  changes |  
              | FD27 | FD27 | OTHER | Audio
                  Channel 0 �Other Audio Bits | SSSS-CBB | S=Shift
                  Register H, C=Clock state B=Borrow |  
              | FD28 | FD28 | VOL | Audio
                  Channel 1 � 2�s compliment Volume control | -VVVVVVV | V=Volume
                  (0-127) |  
              | FD29 | FD29 | FEEDBACK | Audio
                  Channel 1 � Shift register feedback enable | FFFFFFFF | Effective
                  pitch of instrument |  
              | FD2A | FD2A | RAW | Audio
                  Channel 1 � Audio Output Value (Raw Data) | 
 | Constantly
                  changes |  
              | FD2B | FD2B | SHIFTL | Audio
                  Channel 1 � Lower 8 bits of shift register | SSSSSSSS | Shift
                  Regsiter L |  
              | FD2C | FD2C | FREQ | Audio
                  Channel 1 � Audio Timer Backup Value | TTTTTTTT | T=Timer
                  (effectively Frequency) |  
              | FD2D | FD2D | CONTROL | Audio
                  Channel 1 � Audio Control Bits | FTIRCKKK | F=Feedback
                  bit 7 , reset Timer done, enable Integrate, enable Reload
                  enable Count, clocK select |  
              | FD2E | FD2E | COUNT | Audio
                  Channel 1 � Audio Counter | 
 | Constantly
                  changes |  
              | FD2F | FD2F | OTHER | Audio
                  Channel 1 �Other Audio Bits | SSSS-CBB | S=Shift
                  Register H, C=Clock state B=Borrow |  
              | FD30 | FD30 | VOL | Audio
                  Channel 2 � 2�s compliment Volume control | -VVVVVVV | V=Volume
                  (0-127) |  
              | FD31 | FD31 | FEEDBACK | Audio
                  Channel 2 � Shift register feedback enable | FFFFFFFF | Effective
                  pitch of instrument |  
              | FD32 | FD32 | RAW | Audio
                  Channel 2 � Audio Output Value (Raw Data) | 
 | Constantly
                  changes |  
              | FD33 | FD33 | SHIFTL | Audio
                  Channel 2 � Lower 8 bits of shift register | SSSSSSSS | Shift
                  Regsiter L |  
              | FD34 | FD34 | FREQ | Audio
                  Channel 2 � Audio Timer Backup Value | TTTTTTTT | T=Timer
                  (effectively Frequency) |  
              | FD35 | FD35 | CONTROL | Audio
                  Channel 2 � Audio Control Bits | FTIRCKKK | F=Feedback
                  bit 7 , reset Timer done, enable Integrate, enable Reload
                  enable Count, clocK select |  
              | FD36 | FD36 | COUNT | Audio
                  Channel 2 � Audio Counter | 
 | Constantly
                  changes |  
              | FD37 | FD37 | OTHER | Audio
                  Channel 2 �Other Audio Bits | SSSS-CBB | S=Shift
                  Register H, C=Clock state B=Borrow |  
              | FD38 | FD38 | VOL | Audio
                  Channel 3 � 2�s compliment Volume control | -VVVVVVV | V=Volume
                  (0-127) |  
              | FD39 | FD39 | FEEDBACK | Audio
                  Channel 3 � Shift register feedback enable | FFFFFFFF | Effective
                  pitch of instrument |  
              | FD3A | FD3A | RAW | Audio
                  Channel 3 � Audio Output Value (Raw Data) | 
 | Constantly
                  changes |  
              | FD3B | FD3B | SHIFTL | Audio
                  Channel 3 � Lower 8 bits of shift register | SSSSSSSS | Shift
                  Regsiter L |  
              | FD3C | FD3C | FREQ | Audio
                  Channel 3 � Audio Timer Backup Value | TTTTTTTT | T=Timer
                  (effectively Frequency) |  
              | FD3D | FD3D | CONTROL | Audio
                  Channel 3 � Audio Control Bits | FTIRCKKK | F=Feedback
                  bit 7 , reset Timer done, enable Integrate, enable Reload
                  enable Count, clocK select |  
              | FD3E | FD3E | COUNT | Audio
                  Channel 3 � Audio Counter | 
 | Constantly
                  changes |  
              | FD3F | FD3F | OTHER | Audio
                  Channel 3 �Other Audio Bits | SSSS-CBB | S=Shift
                  Register H, C=Clock state B=Borrow |  
              | FD40 | FD40 | ATTENREG0 | LLLLRRRR
                  � Audio Attenuation | 
 | 
 |  
              | FD41 | FD41 | ATTENREG1 | LLLLRRRR
                  � Audio Attenuation | 
 | 
 |  
              | FD42 | FD42 | ATTENREG2 | LLLLRRRR
                  � Audio Attenuation | 
 | 
 |  
              | FD43 | FD43 | ATTENREG3 | LLLLRRRR
                  � Audio Attenuation | 
 | 
 |  
              | FD44 | FD44 | MPAN | Stereo
                  attenuation selection | 
 | 
 |  
              | FD50 | FD50 | MSTEREO | Stereo
                  disable | LLLLRRRR | 0=all on
                  255=all off |  
 Sfx with Chibisound!
 
        
          
            Writing Chibi Sound!
              | These tutorials use a sound 'driver' called ChibiSound, Chibisound uses a single byte parameter in the Accumulator,
                  and provides 64 different pitches, in low or high volume with
                  either tones or noise.
 Sending a value of 0 mutes sound, all other values make a
                  tone...
 
 Chibisound is intended to make it simple to have SFX in
                  multiplatform games and was used in Grime Z80 and Grime 6502
 
 | 
                  
                    
                      | 7 
 | 6 
 | 5 
 | 4 
 | 3 
 | 2 
 | 1 
 | 0 
 | 
 | 
 |  
                      | T | V | P | P | P | P | P | P | 
 | T=Tone
                          V=Volume P=Pitch |  |  
        
          
            
              | Lets start writing ChibiSound! First we need to check if the Accumulator is zero, if it is,
                then we need to turn off sound.
 
 If it is, then we just need to set the volume to zero, by
                writing 0 to $FD20 (Channel 0 Volume)
 |  
 
  |  
              | Otherwise We need to set a volume - the Lynx needs a volume of
                0-127 (7 bits)... 
 We only have one bit for our volume in the accumulator, so we
                set the other six bits to 1,
 We write the result to $FD20 (Channel 0 Volume)
 |  |  
              | Next we need to set the pitch... we have 6 bits in our
                definition, and we write these to $FD24 |  |  
              | Our next stage is to select the 'instrument' depending on bit
                7 of the accumulator, 
 If we want to make noise, we need to use the value $90.... if we
                want a tone, then we'll use $10
 
 Either way we write this to port $FD21 for channel 0
 |  |  
              | We're ready, but we need to reset a few other settings to
                prepare the sound chip. ($FD21,$FD23,$FD27,$FD50) 
 We don't need any special settings for these to make our basic
                sounds
 |  |  
              | We need to reset the 'audio output' of the channel - this is
                the current value the sound channel is playing - we reset it to
                $80 to make it silent |  |  
              | Finally we need to set the Audio control bits 
 Our sound will now play!
 |  |  
 
        
          
            
              |   | Lesson
                  P25 - Sound on the PC Engine
                  (TurboGrafx-16) The PC Engine Has 6 digital sound channels, unlike many other
                systems, they use digital sound.
 
 We need to define 'wave data' to make our square wave sounds for
                chibisound... lets find out how!
 
 |  |  
 | 
 |  |  
 
        Sfx with Chibisound!
 
          
            
              
                | These tutorials use a sound 'driver' called
                    ChibiSound, Chibisound uses a single byte parameter in the Accumulator,
                    and provides 64 different pitches, in low or high volume
                    with either tones or noise.
 Sending a value of 0 mutes sound, all other values make a
                    tone...
 
 Chibisound is intended to make it simple to have SFX in
                    multiplatform games and was used in Grime Z80 and Grime 6502
 
 | 
                    
                      
                        | 7 
 | 6 
 | 5 
 | 4 
 | 3 
 | 2 
 | 1 
 | 0 
 | 
 | 
 |  
                        | T | V | P | P | P | P | P | P | 
 | T=Tone
                            V=Volume P=Pitch |  |  
 Sound Ports on the PC Engine
 The PC engine has sound 10 registers
        controlling 6 channels - all 6 are digital (they use 32 bytes of 5 bit
        wave data), but only 5 and 6 can make noise 
 Before we write our wave data we need to
        set Bit 7 of Reg 4 to 0... then we write 32 bytes to $0806 (only 5 bits
        per sample!) 
 
        
          
            
              | Reg | Address | Meaning | Channels | 7 
 | 6 
 | 5 
 | 4 
 | 3 
 | 2 
 | 1 
 | 0 
 | Bit
                    Meaning |  
              | 0 | $0800 | Channel
                  Select | All | - | - | - | - | - | C | C | C | Channel
                  Select |  
              | 1 | $0801 | Main
                  Amplitude Level | All | L | L | L | L | R | R | R | R | L/R
                  Volume |  
              | 2 | $0802 | Frequency
                  L | 0-5 | L | L | L | L | L | L | L | L | Frequency
                  botttom 8 bits |  
              | 3 | $0803 | Frequency
                  H | 0-5 | - | - | - | - | H | H | H | H | Frequency
                  top 4 bits |  
              | 4 | $0804 | Channel
                  On/Write | 0-5 | E | D | - | V | V | V | V | V | Enable
                  (play)/write data... Direct digitaldata� channel Volume |  
              | 5 | $0805 | LR Volume | 0-5 | L | L | L | L | R | R | R | R | L/R
                  Volume |  
              | 6 | $0806 | Waveform
                  Data | 0-5 | - | - | - | W | W | W | W | W | Wave
                  data (write 32 times) |  
              | 7 | $0807 | Noise
                  Enable | 4-5 | E | - | - | N | N | N | N | N | Enable
                  noise� Noise freq (Chn 4/5 only) |  
              | 8 | $0808 | LFO Freq | All | F | F | F | F | F | F | F | F | LFO
                  Frequency |  
              | 9 | $0809 | LFO
                  Control | All | T | - | - | - | - | - | C | C | LFO
                  Trigger� Control |  
 Writing Chibi Sound! 
        
          
            
              | Starting ChibiSound, we first want to select a channel... we
                need a channel that can make noise, so we'll use Channel 5 
 |   |  
              | We want to write data to the wave sample, so we need to set
                bit 7 of $0804 to zero, To do this quickly, we'll use the HuC6280 command STZ to set
                $0804 to zero - this also turns OFF the channel
 
 Now we want to write the wave data for a square wave, because we
                can only use 5 bit samples, we need to write 4 bytes of
                %00011111 and then 4 of %000000000,
 We repeat this 8 times to make the 32 bit sample
 
 |   |  
              | If A=0 then we want to mute the sound, we actually turned the
                channel off with our last command, so we can just return 
 |   |  
              | We now need to turn on the channel, and set a proper volume,
                Chibisound only has one bit for volume, we shift this into bit 4
                of the volume setting (setting all the other volume bits to
                1)... 
 We also need to set the top bit to turn the channel on.
 
 We then also set the main and LR volume to max
 
 |   |  
              | OK, we now need to set the pitch of the sound... 
 We have 6 bits, but we split them up, so we can best use them
                for the PC Engine's 12 bit frequency definition.
 
 |   |  
              | Finally, we check our noise bit (bit 7), 
 If the noise bit is 0, we need to turn off the noise (by writing
                zero)...
 
 If it's not, then we need to convert our 6 bit frequency for the
                5 bit noise setting, and turn noise on.
 
 We're finally done!
 |  |  
          
            
              | Making a
                  beep is a bit of a pain on the PC-Engine, but it's 6 digital
                  sound channels give it powerful sound potential, but it's
                  easier to understand than the Lynx, and far easier than the
                  horrible complexity of the SNES (which is comparable in other
                  technical capabilities) 
 
 |  |  
 
 Sound Ports on the Nes/Famicom
 
 The NES and Famicom use a set of memory
      mapped registers to configure the 5 different sound channels, we just
      write byte data to the ports to configure the ports (though we cannot read
      back) 
      
        
          
            
              | Address | Purpose | Bits | Details |  
              | 4000h | APU
                    Channel 1 (Rectangle) Volume/Decay (W) | CCLEVVVV | Volume,
                    Envelope, Length counter,duty Cycle |  
              | 4001h | APU
                    Channel 1 (Rectangle) Sweep (W) | EUUUDSSS | Sweep,
                    Direction,Update rate, Enabled |  
              | 4002h | APU
                    Channel 1 (Rectangle) Frequency (W) | LLLLLLLL | frequency
                    L byte |  
              | 4003h | APU
                    Channel 1 (Rectangle) Length (W) | CCCCCHHH | frequency
                    H byte, length Counter load register |  
              | 4004h | APU
                    Channel 2 (Rectangle) Volume/Decay (W) | CCLEVVVV | Volume,
                    Envelope, Length counter,duty Cycle |  
              | 4005h | APU
                    Channel 2 (Rectangle) Sweep (W) | EUUUDSSS | Sweep,
                    Direction,Update rate, Enabled |  
              | 4006h | APU
                    Channel 2 (Rectangle) Frequency (W) | LLLLLLLL | frequency
                    L byte |  
              | 4007h | APU
                    Channel 2 (Rectangle) Length (W) | CCCCCHHH | frequency
                    H byte, length Counter load register |  
              | 4008h | APU
                    Channel 3 (Triangle) Linear Counter (W) | CLLLLLLL | Clock
                    disable start, Linear count load reg |  
              | 4009h | APU
                    Channel 3 (Triangle) N/A (-) | -------- | unused |  
              | 400Ah | APU
                    Channel 3 (Triangle) Frequency (W) | LLLLLLLL | frequency
                    L byte |  
              | 400Bh | APU
                    Channel 3 (Triangle) Length (W) | CCCCCHHH | frequency
                    H byte, length Counter load register |  
              | 400Ch | APU
                    Channel 4 (Noise) Volume/Decay (W) | CCLEVVVV | Volume,
                    Envelope, Length counter,duty Cycle |  
              | 400Dh | APU
                    Channel 4 (Noise) N/A (-) | -------- | unused |  
              | 400Eh | APU
                    Channel 4 (Noise) Frequency (W) | LLLLLLLL | frequency
                    L byte |  
              | 400Fh | APU
                    Channel 4 (Noise) Length (W) | CCCCCHHH | frequency
                    H byte, length Counter load register |  
              | 4010h | APU
                    Channel 5 (DMC) Play mode and DMA frequency (W) | IL�FFFF | Irq
                    enable, Loop, Frequency |  
              | 4011h | APU
                    Channel 5 (DMC) Delta counter load register (W) | -DDDDDDD | Delta
                    Counter or 7bit PCM Data |  
              | 4012h | APU
                    Channel 5 (DMC) Address load register (W) | AAAAAAAA | Address
                    = $C000+(A*$40) |  
              | 4013h | APU
                    Channel 5 (DMC) Length register (W) | LLLLLLLLL | Length
                    = (L*$10)+1 Bytes |  
              | 4015h | DMC/IRQ/length
                    counter status/Sound channel enable register (RW) | DF-54321 | Dmc
                    irq status/ Frame irq status Channel 12345 on |  
 Writing Chibi Sound! 
        
          
            
              | Lets Write the Chibisound driver... 
 First we need to calculate the volume, we only have one bit of
                volume definition passed to chibisound, so we'll move it to bit
                4, and set the other 3 volume bits to zero,
 
 We also need to set bits 4 and 5, to disable the Clock and
                Envelope of the channel, however where we need to write this
                depends on if we're making a tone or noise, so we'll store it
                into X for later
 |  |  
              | Now we've popped A, we have the flags updated, so we'll check
                if A is zero, if it is we need to silence the channels 
 We do this by writing zero to $4015... which will turn off all
                the channels!
 |  
 
  |  
              | We're going to test bit 7 of the accumulator - if it's 1, then
                we need to play a noise sample |  |  
              | If we're not making a noise, then we need to store the
                volume we calculated to $4000 
 Now we need to put our 6 bits of pitch in the Accumulator into
                $4003 and $4002...
 
 We flip the nibbles, and rotate the bits around into the best
                positions for the sound.
 |  |  
              | Now we need to turn channel 0 on so we can hear our noise...
                we do this by setting bit 0 to one, and writing to $4015 |  |  
              | If we ARE making a noise, we need to store the volume
                to $400C instead 
 We now need to load the noise frequency into $400E, however we
                can only really use 4 of the bits of our 6 bit frequency, as we
                need to move them to the 4 least significant bits to get a noise
                type that matches our other systems.
 |  |  
              | Next we need to set the bottom 3 bits of $400F to zero - the
                top 5 bits are the counter (but we disabled this to our write to
                $400C) 
 we also need to turn on bit 4 of $4015 to turn on the sound
                channel, but we can load A once, and use the same value for both
                purposes
 We then jump to the 'ChibiSound_Silent' label, to write to $4015
                and return
 |  |  
          
            
              | In this way we
                  can at least manage the basics of sound on the Nes, of course
                  the NES has more capabilities - like triangle waves, and the
                  potential for digital sound... but if you want to use those,
                  you'll have to figure them out yourself! |  |  
 
        
          
            
              |   | Lesson
                  P27 - Sound on the
                  SNES / Super Famicom: the SPC700 The SNES has a dedicated sound processor - which is a pain!,
                unfortunately it's a custom chip which uses a totally different
                instruction set - which makes it a total F ing pain!
 
 Lets learn how we can 'tame' the SPC700
 
 |  |  SNS_V1_SPC700_Driver.asm
 
 | 
 |  |  
 SPC700 Assembly 
        
          
            
              | The SNES SPC700 sound chip uses it's own assembly language,
                it's syntax is similar to z80, but it's registers are more like
                the 6502! 
 
 We're going to write a simple
                  program in these tutorials, but we're not going to cover the
                  whole instruction set... please see the SFC
                    development wiki  which already covers this in far more
                  detail than I ever can.
 In these tutorials we use VASM
                  for assembly, but VASM doesn't support the SPC700 instruction
                  set - if we were writing a complex program we could use a different
                    assembler , but as we're only doing the basics, we'll
                  define macros to simulate the SPC700 commands we need. |  |  
        
          
            
              |  | If you want
                  to make something more complex, you'll probably want to use a
                  real SPC700 assembler, then again, it would probably be best
                  to find some existing music playing software for the SPC700
                  and use that instead! 
 |  
 Communicating with the
                SPC700
 The SPC700 has it's own memory with a zero
        page (direct page) - 4 bytes of this memory 'link' to 4 bytes on the
        regular 65816 processor - these are our way of instructing (and
        transferring data) to the SPC700 
        
          
            
              | 65816
                    side 
 | SPC700 side 
 |  
              | $2140 | $00F4 |  
              | $2141 | $00F5 |  
              | $2142 | $00F6 |  
              | $2143 | $00F7 |  
 The important thing to remember with the
        SPC700 ports is R and W are separateThe 65816 can write $66 to $2140, but that doesn't mean that value
          will be read back by the 65816!... $2140 will only contain $66 if
        the SPC700 writes $66 value to its port $00F4 ($00F4/$2140 are a pair)
 That's how wait loops may write a value to the port, and wait until the
        same value is read back as a sync timer
 
        The SPC700 Memory Map The SPC700 has 64k of ram, there are two
          registers at $00F2 and $00F3 to access the sound registers (well look
          at them later) and 4 registers to talk to the 65816 
          
            
              
                | Start 
 | End | Purpose |  
                | 0000 | 00EF 
 | Zero
                    Page / Direct Page |  
                | 00F0 | 00F0 | Unused |  
                | 00F1 | 00F1 | Control
                    Port (Timers & reset) |  
                | 00F2 | 00F2 | Sound
                    Register Select |  
                | 00F3 | 00F3 | Sound
                    Register Value |  
                | 00F4 | 00F4 | Link
                    to 65816 address $2140 |  
                | 00F5 | 00F5 | Link
                    to 65816 address $2141 |  
                | 00F6 | 00F6 | Link
                    to 65816 address $2142 |  
                | 00F7 | 00F7 | Link
                    to 65816 address $2143 |  
                | 0100 | 01FF | Stack |  
                | 0200 | FFBF | RAM |  
                | FFC0 | FFFF | ROM |  
 Waiting for the
                  initialization of the firmware. When the snes turns on, We need to wait
          for the SPC700 to be ready, we need to check for 2 values 1. Wait  for $2140 to return $AA 2. Wait  for $2141 to return $BB 
 Sending data to the
                SPC700 When the system boots the firmware of the
        SPC700 at $FFC0 this will start and wait for data from the 65816.. to
        send data to the SPC700 ram we use the following procedure. 1. Store the Destination address (in SPC700
        ram) into $2142/3 2. Store a nonzero value in $2141 3. Write $CC to $2140 to tell the system
        we're ready 4. Wait for $2140 to return $CC 5. Write the first byte to #2141 6. Write the bytenumber (0) to $2140 7. Wait for $2140 to return the bytenumber
        (0) 8. Write the next byte to $2141 Executing the data we transferred9. Increase the bytenumber in $2140 10. Wait for $2140 to return the
          bytenumber 11. repeat stage 8 
 1. Write $0 to $2141 2. Write the address (in SPC700 ram) to call
      into $2142/3 3. Read the value in $2140, add 2, and write
      the value back to $2140 4. Wait for $2140 to return the same value 
 Coding the transfer
              routine 
      
        
          
            | First we need to wait for the SPC700 to be ready, 
 It should return $AA and $BB
 |  |  
            | We write the destination address (in SPC700 ram) to $2142/3 
 Now we write 1 to $3141, and $CC to $2140
 
 We now wait for $2140 to return $CC...
 
 REMEMBER: Just because we wrote $CC to $2140 doesn't mean
              we'll read it back - not until the SPC700 writes $CC there!
 |  |  
            | We need to write the first byte from HL to #2141 
 We also init the byte count by writing 0 to $2140 and waiting for
              the SPC700 to return it.
 |  |  
            | We now repeat the procedure for all the bytes we want to send, 
 Writing each to #2141, then updating the bytecount in $2140, and
              waiting for $2140 to update.
 
 We're using X as our loopcounter, and we repeat until it
              reaches zero - so this function is limited to 256 bytes
 |  |  
            | Our program is copied, but now we need to call our
              program,  We do this by writing the call address to $2142/3 
 We add 2 to whatever is in $2140, and write it back, and then wait
              for the SPC700 to return the same value from $2140
 
 The address we passed in $2142/3 will be now running on the
              SPC700!
 |  |  
        
          
            |  | We can transfer more data
              later, but we'll need to get the SPC700 to call the firmware at
              $FFC0 somehow... 
 The code we'll look at in moment can do this - and we use it to
              write our program code in one transfer, then the samples as a
              second.
 |  
 Coding for the SPC700 We want to be able to control the sound
      chip's register from the 65816 easilly, to do this we're going create a
      program for the SPC700, which will write data from the accessible ports
      straight to the registers... here's how we'll use the ports 
      
        
          
            | Port
                SPC700 / 65816 | Purpose |  
            | $00F4 / $2140 | if command 3... Reg num |  
            | $00F5 / $2141 | if command 3... Reg val |  
            | $00F6 / $2142 | Command... 3=SetReg / 2=restart ROM data transfer |  
            | $00F7 / $2143 | Sync... Read the value in this port, and write it back to the
              SPC700 to do a command.. then wait until the read data changes to
              know the command was processed |  
 
      
        
          
            | We're going to define two memory addresses... SPDest is the address in SPC700 ram that our program will end up
              ($0300)
 SoundKeyAddr is a byte in SPC700 ram we'll use to hold a temp byte
              for the pause routine. ($F000)
 |  |  
            | The main loop is simple, we call to SoundCallPause to wait for a
              'sync' on $00F7 Then we read in the command from $00F6, depending on what it is,
              we process it.
 
 Then we need to clear the command with SCallResume, and finally we
              jump back to the main loop
 |  |  
            | Note, Because this code will be copied to a different processor,
              we need to recalculate destination addresses for CALL (absolute)
              and BRAnch (relative) 
 for a CALL (absolute), we calculate the Destination
                Label minus the start of the code (on
              the 65816) plus the destination address of
              the code (on the SPC700)
 
 for a BRAnch (relative), we calculate the Destination
                Label minus the current address of the
                running code (*+1)
 | Fixing addresses for Branches and Calls: 
  |  
            | When we get a Command 3 - we need to write data to the sound
              registers... 
 The Sound Register number is read from $00F4, and written to the
              selection port at $00F2
 The new Sound Register value is read from $00F5, and written to
              the selection port at $00F3
 
 The sound register has now been updated... we call SCallResume to
              tell the 65816 side that we've done what was asked.
 Finally we return to the main loop
 |  |  
            | If we get Command 2, then the 65816 wants to send more data (for
              example sound samples) We announce the command was processed, then jump into the firmware
              at $FFC0 - which will take things from there!
 |  |  
            | the SoundCallPause waits until the Sync at $00F7 is sent... this
              happens when the 65816 writes the sent value back to
              $00F7/$2143... the temp value at 'SoundKeyAddr' ($F000) is used to store the
              expected value
 |  |  
            | Finally we need a 'finishing' command -this updates the 'Sync'
              value, and writes it back, this is what we execute when we've
              finished a command... 
 Our program is done!
 |  |  
 Using our code to control
              SPC700 registers from the 65816 
      
        
          
            | When we want to set a registers, we can load X with the new
              Value, and A with the Register number then we call call ProcessSoundCOmmandX to do the work!
 |  |  
            | We write the new value to $2141, and the register to $2140 
 now we send 'Command 3' to $2142 to start the routine we just
              wrote.
 
 Next we update the Sync byte - telling the SPC700 to get to work,
 
 Finally we pause until the sync byte changes - when it does the
              SPC700 has finished the job!
 |  |  
 
      
        
          
            |  | This isn't
                really a smart way to use the SPC700 - really we'd want a music
                player on there, so we could leave it playing music by itself
                without bothering the main CPU, But in this example, we want something 'simple' so we can
                control the registers and learn how they work.
 
 |  
      
      
 SPC700 Sound Registers The driver we wrote last time is handling
        setting the registers, but we still need to know what the actual
        registers do, so we know what to change, here's the register list in
        full! 
        
          
            
              | Address | Register | Description | Bits | Meaning |  
              | c0 | VOL
                  (L) | Left
                  Volume | -VVVVVVV | Volume |  
              | c1 | VOL
                  (R) | Right
                  Volume | -VVVVVVV | Volume |  
              | c2 | P
                  (L) | Pitch
                  L | PPPPPPPP | Pitch |  
              | c3 | P
                  (H) | Pitch
                  H | --PPPPPP | Pitch |  
              | c4 | SRCN | Source
                  number (references the source directory) | SSSSSSSS | Source |  
              | c5 | ADSR
                  (1) | If
                  bit7 is set, ADSR is enabled. If cleared GAIN is used. | EDDDAAAA | Enable,
                  Dr, Ar |  
              | c6 | ADSR
                  (2) | These
                  two registers control the ADSR envelope. | LLLRRRRR | sL,sR |  
              | c7 | GAIN | This
                  register provides function for software envelopes. | GGGGGGGG | G=Envelope
                  bits |  
              | c8 | -ENVX | (auto
                  updated) Readable current Envelope Value | 0VVVVVVV | Value |  
              | c9 | -OUTX | (auto
                  updated) Readable current Waveform Value | SVVVVVVV | Signed
                  Value |  
              | 0C | MVOL
                  (L) | Main
                  Volume Left | -VVVVVVV | Volume |  
              | 1C | MVOL
                  (R) | Main
                  Volume Right | -VVVVVVV | Volume |  
              | 2C | EVOL
                  (L) | Echo
                  Volume Left | -VVVVVVV | Volume |  
              | 3C | EVOL
                  (R) | Echo
                  Volume Right | -VVVVVVV | Volume |  
              | 4C | KON | Key
                  On | CCCCCCCC | Channel |  
              | 5C | KOF | Key
                  Off | CCCCCCCC | Channel |  
              | 6C | FLG | DSP
                  Flags. (used for MUTE,ECHO,RESET,NOISE CLOCK) | RMENNNNN | Reset
                  (0=off) Mute (0=off) Echo (1=0ff) Noise clock 
 |  
              | 7C | -ENDX | (auto
                  updated) read to see if channel done | CCCCCCCC | Channel |  
              | 0D | EFB | Echo
                  Feedback | SFFFFFFF | Signed
                  Feedback |  
              | 2D | PMON | Pitch
                  modulation | CCCCCCC- | Channel
                  (1-7) |  
              | 3D | NON | Noise
                  enable | CCCCCCCC | Channel |  
              | 4D | EON | Echo
                  enable | CCCCCCCC | Channel |  
              | 5D | DIR | Offset
                  of source directory (DIR*100h = memory offset) | OOOOOOOO | Offset
                  $oo00 |  
              | 6D | ESA | Echo
                  buffer start offset (ESA*100h = memory offset) | OOOOOOOO | Offset
                  $oo00 |  
              | 7D | EDL | Echo
                  delay, 4-bits, higher values require more memory. | ----EEEE | Echo
                  delay |  
              | fF | COEF | 8-tap
                  FIR Filter coefficients | SCCCCCCC | Signed
                  Coeficcient |  
              | c = Channel 0-7� f
                  = filter coefficient 0-7 | 
 | 
 |  
 
        Sfx with Chibisound!
 
          
            
              
                | These tutorials use a sound 'driver' called
                    ChibiSound, Chibisound uses a single byte parameter in the Accumulator,
                    and provides 64 different pitches, in low or high volume
                    with either tones or noise.
 Sending a value of 0 mutes sound, all other values make a
                    tone...
 
 Chibisound is intended to make it simple to have SFX in
                    multiplatform games and was used in Grime Z80 and Grime 6502
 
 | 
                    
                      
                        | 7 
 | 6 
 | 5 
 | 4 
 | 3 
 | 2 
 | 1 
 | 0 
 | 
 | 
 |  
                        | T | V | P | P | P | P | P | P | 
 | T=Tone
                            V=Volume P=Pitch |  |  
 Sound Samples 
 
          
            
              
                | Sound samples must also be held in SPC700 ram... 
 Pointers to Sound samples are stored in a single block..
                   this block must be byte alined, and has 256 sound
                  definitions - each of which contains 2 pointers... one for the
                  start of the sample, and one for the loop
 
 For example, take the example definition, this will be loaded
                  into SPC700 ram at $300... we would tell the sound chip to use
                  $03 as the memory position using the "DIR" sound register $5D
 
 The sound samples themselves are made up of 9 byte chunks, the
                  first byte is a header, and the other 16 nibbles are the sound
                  data... the final sample in the sound should have the End bit
                  set in the header, and the Loop bit if you wish
 |  |  
            
              
                | The sound
                    sample format is pretty complex, it's effectively a kind of
                    ADPCM, you'll probably want to find a converter and convert
                    some WAV samples into the correct format for the SNES. 
 The sample we use here is just a simple test so we can here
                    the SPC700 actually do something!
 
 |  |  
        Writing ChibiSound 
          
            
              
                | Exclusively on the Snes we're going to have an INIT routine,
                  this is because we need to transfer the program to the SPC700
                  to handle that side of things, 
 At the same time, we'll set up the default volume, pointer to
                  our sound sample, and a few other values.
 
 Now they're set up We won't need to set these values again!
 |  |  
                | The init routine to transfer the data to the SPC700 has two
                  parts, 
 The first copies the program code to $0200
 
 The Second Copies the sound sample(s) to $0300 - this is the
                  square wave we'll use for beeps!
 |  |  
                | The First thing ChibiSound does is check if the sound needs
                  to be turned off.. 
 If it does we use 'Keys off' to turn off Channel 0... this
                  tells the SPC700 to stop channel 0
 |  |  
                | Before we start, we need to reset some settings... we set
                  'Keys On' to zero so no keys are pressed, and the 'Noise
                  Clock' bits to 1 |  |  
                | We're going to check if the noise needs turning on (Bit 7 of
                  the accumulator when chibisound is called) 
 If we need noise, then we turn noise on for Channel 0, we also
                  need to set the noise clock (pitch of the noise)... we have 6
                  bits of frequency in the accumulator, but we can only use 5.
 
 If the noise is off, then we turn 'noise on' to zero.
 
 |  |  
                | Ok, we're going to set the pitch of the tone, the SNES uses
                  14 tone bits (in registers $02,$03 for Channel 0) 
 We only have 6 bits - so the bottom 8 (in $02) will be all
                  zero, and we'll set out 6 bits in reg 03.
 
 |   |  
                | Ok, on to volume - the SNES uses 7 bits - we have just one
                  volume bit, so we'll just set the other six to 1 to make a 'central' channel, we set both Left (reg $00) and
                  Right ($01) to the same volume
 |  |  
                | Our sound is pretty much ready,  but there's a few last
                  setting to set... 
 First we want to set the source sample (Reg $04) - we're using
                  Sample 0 in our sound bank.
 
 Next we want to turn of 'ADSR' (Reg $05 - envelopes) we just
                  want a simple tone - so we set this to 0
 
 Finally we want to start the tone of Channel 0 - so we set Bit
                  0 of 'Keys down' (Reg $4C) to 1
 
 Phew! after all that, we're done!
 |  |  
 
        
          
            
              |  | We've made a beep on the SNES - and it's
                  only taken about 200 lines of code! 
 Of course, the SNES sound chip is really designed for making
                  music, using digital sound samples of instruments, but if you
                  want to try to do that, you're on your own!
 There are music players available for the snes - so you don't
                  really need to write one, but for these tutorials, we always
                  try to do things ourselves!
 
 |  
 
 
        
 VIC 20 Sound portsThere are 3 sound channels for the 3 different frequencies, one for
          random noise, and a volume setting...
 As well as volume The top 4 bits of the $900E also handles color
 
          
            
              | Address | Meaning | Bits | Details |  
              | $900A | Frequency
                  for oscillator 1 (Bass) | OFFFFFFF | O=On
                  F=Frequency 
 |  
              | $900B | Frequency
                  for oscillator 2 (medium) | OFFFFFFF | O=On
                  F=Frequency |  
              | $900C | Frequency
                  for oscillator 3 (high freq) | OFFFFFFF | O=On
                  F=Frequency |  
              | $900D | Frequency
                  of noise source | OFFFFFFF | O=On
                  F=Frequency |  
              | $900E | Volume of
                  all sound / Auxiliary color information | CCCCVVVV | V=Volume
                  C=Aux color |  
 
 
        Sfx with Chibisound!
 
          
            
              
                | These tutorials use a sound 'driver' called
                    ChibiSound, Chibisound uses a single byte parameter in the Accumulator,
                    and provides 64 different pitches, in low or high volume
                    with either tones or noise.
 Sending a value of 0 mutes sound, all other values make a
                    tone...
 
 Chibisound is intended to make it simple to have SFX in
                    multiplatform games and was used in Grime Z80 and Grime 6502
 
 | 
                    
                      
                        | 7 
 | 6 
 | 5 
 | 4 
 | 3 
 | 2 
 | 1 
 | 0 
 | 
 | 
 |  
                        | T | V | P | P | P | P | P | P | 
 | T=Tone
                            V=Volume P=Pitch |  |  
 Writing Chibisound 
        
          
            
              | When Chibisound starts, we have our byte in the
                  Accumulator, we need to back it up into the stack during each
                  stage to keep it intact, 
 First we're going to work out the pitch, but we don't know
                  where we want to store it until we decide whether we make
                  Noise or Tone sounds.
 
 We calculate the tone from the 6 bits, and store it in Y - we
                  store a zero in X to silence the other channel we don't need
 
 |  |  
              | When we get the Accumulator back to the stack, the flags will
                be updated, if A=0 we now jump to the volume routine to silence the sound
                chip - we'll see this later
 |  |  
              | Depending on our Noise bit, we either make a Tone using port
                $900D, or a noise with $900D... we need to write a zero to the
                one we're not using. 
 To do this we use the calculated pitch value in Y, and the zero
                in X to do the job, but we'll flip them around if the noise bit
                is set
 |  |  
              | Our last task is to set the volume, We only have one volume
                bit in the Accumulator so set the other 3 volume bits to 1. 
 We need to load this into $900E to set the volume, but the top
                nibble of this register handles the Aux color,
 
 First we load this, and keep it in zeropage entry z_as, then we
                OR in our new volume, and store the result back to $900E
 
 Note this code also handles the 'Mute' jump we wrote earlier.
 |  |  
 
        
          
            
              | We've used 2
                  of the 4 channels, but the other 2 are basically the same,
                  they just handle different sound pitches. 
 We can change the pitch of the function by using $900A or
                  $900C instead of $9000B in our code.
 
 |  |  
 
 
 
        SID 6581 sound chipThe SID chip uses memory addresses $D400-$D41C
 
          
            
              | Address | Description | Bits | Meaning |  
              | $D400 | Voice #1
                  frequency L | LLLLLLLL | 
 |  
              | $D401 | Voice #1
                  frequency H | HHHHHHHH | Higher
                  values=higher pitch |  
              | $D402 | Voice #1
                  pulse width L | LLLLLLLL | 
 |  
              | $D403 | Voice #1
                  pulse width H | ----HHHH | 
 |  
              | $D404 | Voice #1
                  control register | NPST-RSG | Noise /
                  Pulse / Sawtooth / Triangle / - test / Ring mod / Sync / Gate
                  (Turn on) 
 |  
              | $D405 | Voice #1
                  Attack and Decay length | AAAADDDD | Atack /
                  Decay |  
              | $D406 | Voice #1
                  Sustain volume and Release length. | SSSSRRRR | Sustain /
                  Release |  
              | $D407 | Voice #2
                  frequency L | LLLLLLLL | 
 |  
              | $D408 | Voice #2
                  frequency H | HHHHHHHH | Higher
                  values=higher pitch |  
              | $D409 | Voice #2
                  pulse width L | LLLLLLLL | 
 |  
              | $D40A | Voice #2
                  pulse width H | ----HHHH | 
 |  
              | $D40B | Voice #2
                  control register | NPST-RSG | Noise /
                  Pulse / Sawtooth / Triangle / - test / Ring mod / Sync / Gate (Turn on) |  
              | $D40C | Voice #2
                  Attack and Decay length | AAAADDDD | Atack /
                  Decay |  
              | $D40D | Voice #2
                  Sustain volume and Release length. | SSSSRRRR | Sustain /
                  Release |  
              | $D40E | Voice #3
                  frequency L | LLLLLLLL | 
 |  
              | $D40F | Voice #3
                  frequency H | HHHHHHHH | Higher
                  values=higher pitch |  
              | $D410 | Voice #3
                  pulse width L | LLLLLLLL | 
 |  
              | $D411 | Voice #3
                  pulse width H | ----HHHH | 
 |  
              | $D412 | Voice #3
                  control register. | NPST-RSG | Noise /
                  Pulse / Sawtooth / Triangle / - test / Ring mod / Sync / Gate (Turn on) |  
              | $D413 | Voice #3
                  Attack and Decay length. | AAAADDDD | Atack /
                  Decay |  
              | $D414 | Voice #3
                  Sustain volume and Release length. | SSSSRRRR | Sustain /
                  Release |  
              | $D415 | Filter
                  cut off frequency L | -----LLL | 
 |  
              | $D416 | Filter
                  cut off frequency H | HHHHHHHH | 
 |  
              | $D417 | Filter
                  control | RRRREVVV | R=Resonance
                  / External / V= Voice 3-1 |  
              | $D418 | Volume
                  and filter modes | MHBLVVVV | Mute3 /
                  Highpass / Bandpass / Lowpass / Volume (0=silent) |  
              | $D41B | Voice #3
                  waveform output. (Read only) | DDDDDDDD | 
 |  
              | $D41C | Voice #3
                  ADSR output. (Read only) | DDDDDDDD | 
 |  
        Sfx with Chibisound!
 
          
            
              
                | These tutorials use a sound 'driver' called
                    ChibiSound, Chibisound uses a single byte parameter in the Accumulator,
                    and provides 64 different pitches, in low or high volume
                    with either tones or noise.
 Sending a value of 0 mutes sound, all other values make a
                    tone...
 
 Chibisound is intended to make it simple to have SFX in
                    multiplatform games and was used in Grime Z80 and Grime 6502
 
 | 
                    
                      
                        | 7 
 | 6 
 | 5 
 | 4 
 | 3 
 | 2 
 | 1 
 | 0 
 | 
 | 
 |  
                        | T | V | P | P | P | P | P | P | 
 | T=Tone
                            V=Volume P=Pitch |  |  
        
          
            
              | When Chibisound starts, we need to back up the Accumulator at
                each stage to keep it's value for the next calculation - we do
                this by pushing it onto the stack. 
 We're going to use Voice #1 for all our sounds, and first we'll
                set the pitch with our 6 bits, we write them to address $D401 to
                set the pitch
 |  |  
              | Now we'll pull the Accumulator off the stack, this will also
                update the flags, we'll use this to check if the Accumulator is
                zero, if it is, we jump to the label 'ChibiSouns_Silent - which
                will set the volume to zero via $D418 |  
 
  |  
              | Next we're going to check the Noise bit... if it's set, we
                need to set bit 7 of $D404.... if not we need to make a tone, by
                setting Bit 6 
 Either way, we also need to set Bit 0 (the 'Gate') this turns
                the sound on
 |  |  
              | We need to set some of the other registers to make our tone,
                We need to set $D402 and $D405 to zero,  but we also want
                to set $D400,$D403 and $D406 to 255... 
 We start by setting X to zero, and writing it to $D402/5... then
                we DEcrement X, and write 255 to $D400/3/6
 This sets up the tone we want.
 |  |  
              | Finally, we need to set the volume, the SID uses a 4 bit
                volume, but we only have 1 bit in the Accumulator, we set the
                other bits to 1, and write the value to $D418 
 This is also where the 'Silent' jump we made earlier ends up.
 |  |  
        
          
            
              |  | We've only used one
                Voice in this example, but we can use the others in the same
                way... we can also change the value we write to $D404, #%01000000 will be a Pulse, #%00100000
                is Sawtooth, and #%00010000 is a Triangle sound.
 
 Attack and Decay can also be changed to alter the way the sound
                changes over time.
 |  |  | 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Buy my Assembly programming book
 on Amazon in Print or Kindle!
 
 
  
 
  
 
  
 Available worldwide!
 Search 'ChibiAkumas' on
 your local Amazon website!
 Click here for more info!
 
 
 
 
 
 
 
 
 
 
   
 
 
 
 
 
 
 
 
   
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Buy my Assembly programming book
 on Amazon in Print or Kindle!
 
 
  
 
  
 
  
 Available worldwide!
 Search 'ChibiAkumas' on
 your local Amazon website!
 Click here for more info!
 
 
 
 
 
 
 
 
 
 
   
 
 
 
 
 
 
 
 
   
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Buy my Assembly programming book
 on Amazon in Print or Kindle!
 
 
  
 
  
 
  
 Available worldwide!
 Search 'ChibiAkumas' on
 your local Amazon website!
 Click here for more info!
 
 
 
 
 
 
 
 
 
 
   
 
 
 
 
 
 
 
 
   
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 |