65816 Assembly programming for the SNES and Super Famicom

The Super Nintendo (AKA Super Famicom)  was the successor to the NES... relatively late to the 16 bit game, the SNES made the unusual choice to not use the 68000 CPU for its processor, favoring the slower and less efficient 65186... this is rumored to be because during development backwards compatibility with the NES was planned...

The 65186 is a true 16 bit CPU, but it has a 6502 compatibility mode, in which it works exactly like a 6502, and we can perform basic SNES programming without even using a 16 bit command!
Cpu 3.58 mhz 65816 
Ram 128k
Vram 64k
Tiles 1024
Sprites 128 onscreen (32 per line)
Resolution 512x448
Colors 256 (16 per tile) from 32768 
Sound chip 64k DSP 8 channel  SPC700
The UK SNES PSU outputs 9V AC the Japanese Super Famicom PSU outputs 10V DC - CENTER NEGATIVE BE CAREFUL! adapters that output AC are very rare and will kill most systems - and Center negative was common for 80's Japanese systems, but is very rare these days - YOU HAVE BEEN WARNED!


ChibiAkumas Tutorials


Lesson A1 - Extra commands in the 65c02/65816

Lesson H8 - Hello World on the SNES / Super Famicom

Lesson H1 - Hello World on the Super Nintendo (65816 version)

Lesson S8 - Bitmap Drawing on the SNES / Super Famicom

Lesson S17 - Joypad Reading on the SNES / Super Famicom

Lesson S1 - Moving a bitmap on the SNES (65816 version)

Lesson S28 -  Tile Bitmap Clipping on the SNES

Lesson S29 - Hardware Sprite Clipping on the SNES
   
Lesson P7 - Bitmap Functions on the SNES / Super Famicom

Lesson P15 - Joystick Reading on the NES / Famicom and SNES

Lesson P22 - Palette Definitions on the SNES / Super Famicom

Lesson P27 - Sound on the SNES / Super Famicom: the SPC700

Lesson P35 - Hardware Sprites on the SNES / Super Famicom

Lesson P41 - Multiple layers on the SNES

Lesson P42 - Color maths on the Super Nintendo

Lesson P52 - Sound on the SuperNintendo (ChibiSound Pro)

Lesson P64 - Multiplatform Software tilemap on the SNES


Useful Document
SNES Documentation v2.30: Written by Yoshi
nocash SNES hardware specifications

Memory Map

The 65816 has a 16 bit address bus, it generally works with a 16 bit address, and a 8 bit Data Bank register (DB)

The ZeroPage and Stack pointer are relocatable, but default to the 6502 normal of $0000-$01FF

Our minimal 32k cartridge will appear in rom from $8000, and execution will start there, the CPU will default in 8 bit mode.
Bank 16 bit address Purpose
$00-$3F $0000-$1FFF RAM

$2000-$5FFF Hardware Regs

$6000-$7FFF Expand (???)

$8000-$FFFF Cartridge Rom
$70 $0000-$7FFF Battery Backed Up Ram
$7E $0000-$1FFF Scratchpad RAM (same as bank $00 to $3F)

$2000-$FFFF RAM
$7F $0000-$FFFF RAM

VRAM

VRAM addresses are re-configurable, Here's a sample map which we'll be working around in these tutorials ... Note the addresses are in WORDS (2 bytes in each address)... so the 64k memory is accessed by addresses $0000-$7FFF

 Address  Use
$0000  BG1 Tilemap
$1000  Tile Patterns
$4000  Sprite Patterns
$7FFF  Last byte of ram 

Cartridge Header
Address Bytes Category Purpose Example
$FFC0 21 Rom Cartridge title (Space Padded) Test Rom9012345678901
$FFD6 1 Rom ROM/RAM information on cart. $00
$FFD7 1 Rom ROM size. $01
$FFD8 1 Rom RAM size. $00
$FFD9 1 Rom Developer ID code. $00
$FFDB 1 Rom Version number. $00
$FFDC 2 Rom Checksum complement. $????
$FFDE 2 Rom Checksum. $????
$FFE0 2 65816 Mode
$0000
$FFE2 2 65816 Mode
$0000
$FFE4 2 65816 Mode COP Vector $0000
$FFE6 2 65816 Mode Brk Vector $0000
$FFE8 2 65816 Mode Abort Vector (Unused) $0000
$FFEA 2 65816 Mode NMI Vector (V-blank) $0000
$FFEC 2 65816 Mode Reset Vector (Unused) $0000
$FFEE 2 65816 Mode IRQ Vector (H/V/External) $0000
$FFF0 2 6502 Mode
$0000
$FFF2 2 6502 Mode
$0000
$FFF4 2 6502 Mode COP Vector $0000
$FFF6 2 6502 Mode BRK Vector (unused) $0000
$FFF8 2 6502 Mode Abort Vector (Unused) $0000
$FFFA 2 6502 Mode NMI Vector (V-blank) $0000
$FFFC 2 6502 Mode Reset Vector (6502 Mode) $8000
$FFFE 2 6502 Mode IRQ/BRK Vector $0000

Tile Definitions
Tile definitions use 4 bitplanes for 16 colors, and tile definitions are 8x8 - so 32 bytes ... Data is transferred in Words, and rather strangely we send bitplane 1+2 of lines, one at a time... then we do the same for bitplanes 3 and 4

Byte 1 Byte 2
First 16 bytes 00111100
01111111
01100011
01100011
01111111
01100011
01100011
00000000
00222200
02222222
02200022
02200022
02222222
02200022
02200022
00000000
Second 16 bytes 00333300
03333333
03300033
03300033
03333333
03300033
03300033
00000000
00444400
04444444
04400044
04400044
04444444
04400044
04400044
00000000

Tilemap Data
The Tilemap will typically start from address $0000, each entry contains two bytes

 F   E   D   C   B   A   9   8    
 7   6   5   4   3   2   1   0    

V H L P P P T T
T T T T T T T T
V=vflip H=hflip L=layer (in front of sprites) P=palette T=tile number

Palette Definitions 


Palettes are defined by something called 'CGRAM'
there are 256 palette entries...
Because we can only select 8 palettes for Tiles/Sprites, assuming our sprites and tiles are 16 colors the first 128 colors will be used by Tiles, and the second 128 will be used by Sprites

To define a palette entry we need to select a palette entry number by writing it's number as a byte to $2121

We have to write two bytes to $2122... we write them in REVERSE order, so the low byte is written first!

Address Name Purpose Bits Details
$2121 CGADD Colour # (or pallete) selection xxxxxxxx x=color (0-255)
$2122 CGDATA Colour data -bbbbbgg gggrrrrr Color Data BGR


H byte (sent second)
L byte (sent first)
 F  E  D  C  B  A  9  8
 7  6  5  4  3  2  1  0
- B B B B B G G
G G G R R R R R
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

Sprite Definitions - Overview
Sprites use as special bank of 512 bytes of 'OAM' memory for their definitions... they also use standard VRAM for the pattern data.
In theory the Pattern data can be relocated... but in practice it's best to just assume it's at $4000 (address in 16 bit words)
Sprites can be various sizes - a 'default size' is set for all sprites... and certain selected sprites can be double size...
this is, however a bit tricky... lets say you have the default size as 8x8... and one double size 16,16 sprite
If we point this sprite 'double size' 16x16 sprite to pattern  'Tile 0',  the 4 8x8 chunks will be made up of tile numbers:
1 2
16 17

Lets look at this example of a 16x16 sprite in AkuSprite Editor... Akusprite editor is designed for 8x8 sprites, but we can export a 16x16 one in the following way

If we want to export this quickly, so we can use it as a single doublesize sprite, one option is to tick the 'FixedSize' tickbox, and set the size to 128,16

This will export the sprite correctly - of course there will be a lot of unused space in the exported file... so we would want to combine all our 16x16 together into a single image

Generally it would be easier to build up a 16x16 sprite from 4 8x8 sprites
Sprite Definitions - Ports Used
Address Name Purpose Bits Details
$2101 OBSEL OAM size (Sprite) SSSNNBBB S=size (See below) N=Bame addr B=Base addr
$2102 OAMADDL/L OAM address LLLLLLLL a=oam address L
$2103 OAMADDL/H OAM address R000000H R= priority Rotation / H=oam address MSB
$2104 OAMDATA OAM data ????????
$212C TM Main screen designation ---S4321 S=sprites 4-1=enable Bgx
$2138 OAMDATAREAD Read data from OAM


Sprite Sizes

Sprite size is set for all sprites, but each sprite can either be normal or large defined by a single bit from address $0100 onwards

SSS
Size (Normal / Large)
0   %000 
8x8 / 16x16
1   %001 8x8 / 32x32
2   %010 8x8 / 64x64
3   %011 16x16 / 32x32
4   %100 16x16 / 64x64
5   %101 32x32 / 64x64
6   %110 16x32 / 32x64 (Undocumented)
7   %111 16x32 / 32x32 (Undocumented)

Sprite Definitions - OAM Data

The SNES has 128 hardware sprites.

Selecting a HL address is done by setting registers $2102 (L) and $2103 (H)

Each address below $0100 holds Two Bytes (The first table)...each address $0100 or above holds just one!... All data is written via the $2104 
Note, Sprites use Palettes from 128... so the color palette used is the value in CCC +128
Sprite data should only be written to Vram during Vsync.
Address Byte 1 Byte 2 Meaning SprNum
$0000 XXXXXXXX YYYYYYYY X=Xpos (bits 0-7) Y=Ypos 0
$0001 TTTTTTTT YXPPCCCT T=Tile Y=yflip X=xflip P=priority compared to BG C=palette +128 0
$0002 XXXXXXXX YYYYYYYY X=Xpos (bits 0-7) Y=Ypos 1
$0003 TTTTTTTT YXPPCCCT T=Tile Y=yflip X=xflip P=priority compared to BG C=palette +128 1
$00FE XXXXXXXX YYYYYYYY X=Xpos (bits 0-7) Y=Ypos 127
$00FF TTTTTTTT YXPPCCCT T=Tile Y=yflip X=xflip P=priority compared to BG C=palette +128
127
$0100 SXSXSXSX (no 2nd byte) S=LargeSize (1=Large size) sprite X=Xpos (bit 8) 0-3
$0101 SXSXSXSX (no 2nd byte) S=LargeSize (1=Large size) sprite X=Xpos (bit 8) 4-6

$011F SXSXSXSX (no 2nd byte) S=LargeSize (1=Large size) sprite X=Xpos (bit 8) 124-127

SNES Joypad

Presumably because of the planned backwards compatibility, the SNES actually uses the same ports in the same way as the NES! However, because the SNES has more buttons, we can do 16 reads rather than 8 to get the extra buttons.

    Mode          Port      Purpose     7          6          5          4          3          2          1          0     
Write $4016 Strobe (reset) - - - - - - - Strobe
Read $4016 Joypad 1/3 - - - - - Mic Pad3 Pad1
Read $4017 Joypad 2/4 - - - - - - Pad4 Pad2

  F     E     D     C     B     A     9     8    
7 6 5   4     3   2   1     0  
- - - - R L X A
Right Left Down Up Start Select Y B

The SNES firmware also reads in these ports automatically, and stores these in ram - we can use these versions if we prefer.

Address Name Purpose Bits
$4218 JOY1L   Joypad #1 status (set during interrupt)   AXLR----
$4219 JOY1H Joypad #1 status (if $4200 is set) BYSTUDLR
$421A JOY2L Joypad #2 status AXLR----
$421B JOY2H Joypad #2 status BYSTUDLR
$421C JOY3L Joypad #3 status AXLR----
$421D JOY3H Joypad #3 status BYSTUDLR
$421E JOY4L Joypad #4 status AXLR----
$421F JOY4H Joypad #4 status BYSTUDLR


Ports

rw Address Name Purpose Bits Details
w $2100 INIDISP Screen display x000bbbb x=screen disable (1=disable) bbbb=brightness (15=max)
w $2101 OBSEL OAM size (Sprite) sssnnbbb sss^size nn=name addr bb=base addr
w 2 $2102 OAMADDL/H OAM address aaaaaaaa r000000m a=oam address r=priority m=addr MSB
wd $2104 OAMDATA OAM data ???????? ????????
w $2105 BGMODE Screen mode abcdefff abcd=tile sizes (8x8 or 16x16) e=pri fff=mode def
w $2106 MOSAIC Screen pixelation xxxxabcd xxxxx=pixel size abcd=affect background layer
w $2107 BG1SC BG1 Tilemap VRAM location xxxxxxab xxx=address (* $800 bytes / $400 words)
ab SC size 00=32x32 01=64x32 10=32x64 11=64x64
w $2108 BG2SC BG2 Tilemap VRAM location xxxxxxab xxx=address (* $800 bytes / $400 words)
ab SC size 00=32x32 01=64x32 10=32x64 11=64x64
w $2109 BG3SC BG3 Tilemap VRAM location xxxxxxab xxx=address (* $800 bytes / $400 words)
SC size 00=32x32 01=64x32 10=32x64 11=64x64
w $210A BG4SC BG4 Tilemap VRAM location xxxxxxab xxx=address (* $800 bytes / $400 words)
SC size 00=32x32 01=64x32 10=32x64 11=64x64
w $210B BG12NBA BG1 & BG2 VRAM location aaaabbbb aaaa=base addr for BG1 (* $2000 bytes / $1000 words)
bbbb=base addr for BG2
(* $2000 bytes / $1000 words)
w $210C BG34NBA BG3 & BG4 VRAM location ccccdddd cccc=base addr for BG3 (* $2000 bytes / $1000 words)
dddd=base addr for BG4
(* $2000 bytes / $1000 words)
wd $210D BG1HOFS BG1 horizontal scroll mmmmmaaa aaaaaaaa aaa=horiz offset, mmm=Mode 7 option
wd $210E BG1VOFS BG1 vertical scroll
Write L then H byte of offset to this address
wd $210F BG2HOFS BG2 horizontal scroll
Write L then H byte of offset to this address
wd $2110 BG2VOFS BG3 vertical scroll
Write L then H byte of offset to this address
wd $2111 BG3HOFS BG3 horizontal scroll - Same as $210D. Write L then H byte of offset to this address
wd $2112 BG3VOFS BG3 vertical scroll
Write L then H byte of offset to this address
wd $2113 BG4HOFS BG4 horizontal scroll
Write L then H byte of offset to this address
wd $2114 BG4VOFS BG4 vertical scroll
Write L then H byte of offset to this address
w $2115 VMAIN Video port contro l i000abcd I 0=inc on $2118 or $2139 1=$2119 or $213A� abcd=move size
w 2 $2116-$2117 VMADDL/H Video port address LLLLLLLL HHHHHHHH Memory address (in bytepairs)$0000-$7FFF
w 2 $2118-$2119 VMDATAL/H Video port data LLLLLLLL HHHHHHHH Byte Data
w $211A M7SEL MODE7 settings ab0000yx ab=out of area function x=xflip y=yflip
w $211B M7A COS rotate angle / X Expnsn

w $211C M7B SIN rotate angle / X Expnsn

w $211D M7C SIN rotate angle / Y Expnsn

w $211E M7D COS rotate angle / Y Expnsn

wd $211F M7X Center position X (13-bit data only)

wd $2120 M7Y Center position Y (13-bit data only)

w $2121 CGADD Colour # (or pallete) selection xxxxxxxx x=color (0-255)
wd $2122 CGDATA Colour data -bbbbbgg gggrrrrr Color Data BGR
w $2123 W12SEL Window mask settings abcdefgh
w $2124 W34SEL Window mask settings abcdefgh
w $2125 WOBJSEL Window mask settings abcdefgh
w $2126 WH0 Window 1 left position aaaaaaaa a=position
w $2127 WH1 Window 1 right position

w $2128 WH2 Window 2 left position - Same as $2126.
w $2129 WH3 Window 2 right position

w $212A WBGLOG Mask logic cfg for Win 1 & 2 per scr aabbccdd aa..dd=BG 4321
w $212B WOBJLOG Mask logic for Colour Win & OBJ Win 0000aabb a=color params b=obj window params
w $212C TM Main screen designation ---S4321 S=sprites 4-1=enable Bgx
w $212D TD Sub-screen designation *** Same as $212C
w $212E TMW Window mask main screen desig *** Same as $212C
w $212F TSW Window mask sub screen desig *** Same as $212C
w $2130 CGWSEL Fixed color addition or screen addition abcd00ef ab=main cd=sub ef=color data
w $2131 CGADSUB Addition/subtraction for screens mrgsabcd & OBJs [CGADSUB]
w $2132 COLDATA Fixed colour data for fixed colour +/- bgrdddddd Blue Green Red dddd=constant
w $2133 SETINI Screen mode/video select sn00pvshi
r $2134 MPYL Multiplication result (low)

r $2135 MPYM Multiplication result (middle)

r $2136 MPYH Multiplication result (high)

r $2137 SLHV Software latch for H/V counter aaaaaaaa a=result
r $2138 OAMDATAREAD Read data from OAM

r 2 $2139 VMDATAL/HREAD Read data from VRAM

r $213B CGDATAREAD Read data from CG-RAM (colour)

r d $213C OPHCT Horizontal scanline location

r d $213D OPVCT Vertical scanline location

r $213E STAT77 PPU status flag & version number trm0vvvv
r $213F STAT78 PPU status flag & version number fl0mvvvv
rw $2140 APUI00 APUI00 � Sound Port 0

rw $2141 APUI01 APUI01 � Sound Port 1

rw $2142 APUI02 APUI02 � Sound Port 2

rw $2143 APUI03 APUI03 � Sound Port 3

rw $2180 WMDATA Read/write WRAM

rw $2181 WMADDL WRAM data (low byte)

rw $2182 WMADDM WRAM data (middle byte)

rw $2183 WMADDH WRAM data (high byte)

rw $4016 JOYWR/JOYA Joypad Output / JOY 1 Read (bit 0 x12)

r $4017 JOYB JOY 2 Read (bit 0 x12)

w $4200 NMITIMEN Interrupt Enable and Joypad Request v0yx000j V=Vblank NMI / YX=HorizVert interrupt / J=Joypad Request
w $4201 WRIO Programmable I/O port (out-port)

w $4202 WRMPYA Multiplicand 'A'

w $4203 WRMPYB Multiplier 'B�

w 2 $4204 WRDIVL/H Dividend C

w $4205 WRDIVB Divisor B

w 2 $4207 HTIMEL/H Video H IRQ beam pos/pointer 0000000x xxxxxxxx x: Beam position.
w 2 $4209 VTIMEL/H Video V IRQ beam pos/pointer 0000000y yyyyyyyy y: Beam position.
w $420B MDMAEN DMA enable 76543210
w $420C HDMAEN HDMA enable .

w $420D MEMSEL Cycle speed 0000000x 0=2.68 1=3.58
r $4210 RDNMI NMI x000vvvv x=disable NMI v=version
rw $4211 TIMEUP Video IRQ i0000000 i=irq enabled
rw $4212 HVBJOY Status xy00000a x=vblank state y=hblank state a=joypad ready
r $4213 RDIO Programmable I/O port (in-port)

r 2 $4214 RDDIVL/H Quotient of divide result

r 2 $4216 RDMPYL/H Multiplication or divide result

r $4218 JOY1L Joypad #1 status (set during interrupt) AXLR----
r $4219 JOY1H Joypad #1 status (if $4200 is set) BYSTUDLR
r $421A JOY2L Joypad #2 status AXLR----
r $421B JOY2H Joypad #2 status BYSTUDLR
r $421C JOY3L Joypad #3 status AXLR----
r $421D JOY3H Joypad #3 status BYSTUDLR
r $421E JOY4L Joypad #4 status AXLR----
r $421F JOY4H Joypad #4 status BYSTUDLR
w $43x0 DMAPX DMA Control vh0cbaaa
w $43x1 BBADX DMA Destination LLLLLLLL H=$21
w 2 $43x2 A1TXL/H Source address

w $43x4 A1BX Source bank address

w 2 $43x5 DASXL/H DMA transfer size & HDMA address

w $43xA NTRLX Number of lines for HDMA transfer cxxxxxxx C=continue (0=yes) x=lines to transfer

WD=Write Double byte
2=Write 2 byte word


Sound on the SNES via the SPC700
To make sound on the SNES we have to use it:s SPC700 processor! what's the SPC700? well it's an 8 bit dedicated sound CPU with 64k of isolated memory - meaning that it can't be directly accessed from the main CPU!
The SPC700 is a great sound processor, used by the Super Nintendo and many other systems like... er.. the super nintendo!
Ok, so nothing else uses it, and it's a total pain!  while it's sound ability is good, its a real hassle to program... it uses it's own special instruction set, and its bytecode matches no other CPU, and data has to be transferred to it using a special procedure because we can't access it's memory directly.

The CPU is 8 bit and Little endian, so $1234 is stored in ram as $34,$12... it's registers are the same as the 6502, but it has some 16 bit commands that use YA as a 16-bit pair like the Z80

 it has a ZeroPage, but the ZeroPage is referred to as the DirectPage, as it can be at $00xx or $01xx... $01xx is also used as the Stack

Most data transfer commands are done with MOVe commands (like the 68000), but like the z80, the destination is on the left... for example:
mov a, #$00     ;Set A to $00

I'm not planning to go into greater detail than I need on this CPU, so please see the tutorials below:
Best SP700 reference
Also a good tutorial

Sound samples must also be held in SPC700 ram...

Pointers to Sound samples are stored in a single block..  this block must be byte aliened, 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
    align 8
SFXBank:                                    ;We're going to load this into $0300
    dw SFXBank_Sound1-SFXBank+$300            ;Sample 0 main
    dw SFXBank_Sound1-SFXBank+$300            ;Sample 0 Loop
    align 4
SFXBank_Sound1:
    ;     SSSSFFLE S= bitshift (0-12) FF=Filter L=Loop E=End
    db  %11000111,$FF,$F0,$F0,$F0,$F0,$F0,$F0,$F0
    ;              01  23  45  67  89  AB  CD  EF
   

The SPC700 Memory Map

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

The SPC700 Registers

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) N=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 (0=off)
4D EON Echo enable CCCCCCCC Channel (0=off)
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

SPC700 and VASM

Vasm doesn't support the SPC700, but we can simulate the commands with macros, for example, we can set an immediate value with the macro to the right         macro s_mov_a_ii,aval        ;Set A=immediate value
            db $E8
            db \aval
        endm
If we want to do a relative jump, we can calculate the relative offset with Destination-(*+1) s_bne_r SoundCallPause-(*+1)
If we want to include our SPC700 code in our main rom, we'll need to adjust call addresses for the changing location, to do this we can use the formula [DestinationLabel]- [StartOfProgramInMainRom]+ [DestinationOfProgramInSPC700Ram] s_call_addr SoundCallResume-SoundProgram+SndPrgMemLoc