- S3MMAXINSTRUMENTS = 128 
- S3MWAVEHEADERBUFFERSIZE = S3MMAXINSTRUMENTS*MOONWAVEHEADERSIZE 
- S3MSAMPLEDATASTART = MODSAMPLEDATASTART 
- S3MMAXCHANNELS = 32 
- S3MMAXPATTERNS = 100 
- S3MPATTERNSTEPCOUNT = 64 
- S3MMAXORDER = 256 
- S3MHEADERADDR = 0xc000 
- S3MMINPERIOD = 4 
-   
-         struct S3MHEADER 
- songname ds 28 
- byte1a ds 1 
- filetype ds 1 
- unused1 ds 2 
- ordernum ds 2 
- instnum ds 2 
- pattnum ds 2 
- flags ds 2 
- createdwith ds 2 
- fileversion ds 2 
- id ds 4 
- globalvol ds 1 
- initialspeed ds 1 
- initialtempo ds 1 
- mastermult ds 1 
- ultraclicks ds 1 
- panningmagic ds 1 
- unused2 ds 8 
- special ds 2 
- chsettings ds S3MMAXCHANNELS 
-         ends 
-   
-         struct S3MINSTRUMENT 
- type ds 1 
- dosname ds 12 
- paraptr ds 3 
- length ds 4 
- loopstart ds 4 
- loopend ds 4 
- volume ds 1 
- disknum ds 1 
- pack ds 1 
- flags ds 1 
- c2spd ds 4 
- tunefactorcoeff ds 2 
- tunefactorshift ds 1 
- unused ds 9 
- name ds 28 
- id ds 4 
-         ends 
-   
-         struct S3MCHANNEL 
- index ds 1 
- instrument ds 1 
- oldinstrument ds 1 
- period ds 2 
- note ds 1 
- volume ds 1 
- tempcommand ds 2 
- vibratocommand ds 1 
- vibratotableposition ds 1 
- tremolocommand ds 1 
- tremolotableposition ds 1 
- wavecontrol ds 1 
- patternloopstart ds 1 
- patternloopcount ds 1 
- pankeyon ds 1 
- tunefactorcoeff ds 2 
- tunefactorshift ds 1 
- tunefactorcoeffoverride ds 2 
- tunefactorshiftoverride ds 1 
- portaspeed ds 1 
- volumeslide ds 1 
-         ends 
-   
-         struct S3MSTEPDATA 
- note ds 1 
- instrument ds 1 
- volume ds 1 
- effectcommand ds 1 
- effectdata ds 1 
- extendedcommand ds 1 
-         ends 
-   
-         struct S3MINFO 
- instparapointers ds 2 
- pattparapointers ds 2 
- choffset ds S3MMAXCHANNELS 
- chpanning ds S3MMAXCHANNELS 
- chnum ds 1 
- chinitsize ds 2 
-         ends 
-   
-         struct S3MPLAYER 
- patterntableindex ds 1 
- patternstepindex ds 1 
- arpeggio ds 1 
- patterndelay ds 1 
- speed ds 1 
- speedstep ds 1 
- channels ds S3MCHANNEL*S3MMAXCHANNELS 
- stepdatabuffer ds S3MSTEPDATA*(S3MMAXCHANNELS+1) 
-         ends 
-   
- s3mpatterntable equ s3mheader+S3MHEADER 
-   
- s3mload 
- ;de = input file name 
- ;out: zf=1 if the file is ready for playing, zf=0 otherwise 
-         ld (s3mloadsamples.filename),de 
-         call memorystreamloadfile 
-         ret nz 
- ;map header to S3MHEADERADDR 
-         ld a,(memorystreampages) 
-         SETPGC000 
-         call opl4init 
-         call s3mloadpatterns 
-         jp nz,memorystreamfree ;sets zf=0 
-         call s3mloadsamples 
-         jp nz,memorystreamfree ;sets zf=0 
- ;init player state 
-         ld hl,s3mplayer 
-         ld de,s3mplayer+1 
-         ld bc,S3MPLAYER-1 
-         ld (hl),0 
-         ldir 
- ;init channels 
-         ld ix,s3minfo.chpanning 
-         ld iy,s3mplayer.channels 
-         ld a,(s3minfo.chnum) 
-         ld b,a 
-         ld c,0 
- .initchloop 
-         ld (iy+S3MCHANNEL.index),c 
-         ld a,(ix) 
-         call s3msetpanning 
-         ld de,S3MCHANNEL 
-         add iy,de 
-         inc ix 
-         inc c 
-         djnz .initchloop 
- ;init player state 
-         ld a,(s3mheader.initialtempo) 
-         call s3msetbpm 
-         xor a 
-         call s3msetnextstep 
-         ld a,(s3mheader.initialspeed) 
-         ld (s3mplayer.speed),a 
-         ld a,1 
-         ld (s3mplayer.speedstep),a 
-         xor a 
-         ld (s3mheader.byte1a),a 
-         ret 
-   
- s3mloadpatterns 
-         call getpanningfunc 
-         ld (.getpanning),hl 
- ;parapointers 
-         ld a,(s3mheader.ordernum) 
-         ld e,a 
-         ld d,0 
-         ld hl,s3mpatterntable 
-         add hl,de 
-         ld (s3minfo.instparapointers),hl 
-         ld a,(s3mheader.instnum) 
-         ld e,a 
-         add hl,de 
-         add hl,de 
-         ld (s3minfo.pattparapointers),hl 
- ;fill channel tables 
-         ld a,(s3mheader.pattnum) 
-         ld e,a 
-         add hl,hl 
-         add hl,hl 
-         ld de,s3minfo.choffset 
-         ld bc,s3minfo.chpanning 
-         ld ix,s3mheader.chsettings 
-         ld iyl,S3MMAXCHANNELS 
-         xor a 
-         ex af,af' 
- .chloop ld a,(ix) 
-         cp 16 
-         jr nc,.choff 
- .getpanning=$+1 
-         call 0 
-         ld (bc),a 
-         inc bc 
-         ex af,af' 
-         ld (de),a 
-         add a,S3MSTEPDATA 
-         ex af,af' 
-         inc de 
- .choff  inc hl 
-         inc ix 
-         dec iyl 
-         jr nz,.chloop 
-         ld a,e 
-         add a,(-s3minfo.choffset)%256 
-         ld (s3minfo.chnum),a 
-         neg 
-         add a,S3MMAXCHANNELS+1 
-         ld b,a 
-         ex af,af' 
-         dec b 
-         jr z,.nofill 
-         ld (de),a 
-         inc de 
-         djnz $-2 
- .nofill cp S3MSTEPDATA*2 
-         jr nc,$+4 
-         ld a,S3MSTEPDATA*2 
-         sub S3MSTEPDATA 
-         ld l,a 
-         ld h,0 
-         ld (s3minfo.chinitsize),hl 
-         xor a 
-         ret 
-   
- getpanningfunc 
-         ld a,(s3mheader.panningmagic) 
-         cp 252 
-         ld hl,.readdefaultpanning 
-         ret z 
-         ld hl,.chsetttingspanning 
-         ret 
- .chsetttingspanning 
-         cp 8 
-         ld a,0x03 
-         ret c 
-         ld a,0x0c 
-         ret 
- .readdefaultpanning 
-         ld a,(hl) 
-         and 15 
-         ret 
-   
- s3mfilestreamseekfast 
- ;dehl = offset 
-         ld (.fileoffsetlo),hl 
-         ld (.fileoffsethi),de 
-         ld hl,(filestreamcurrentaddr) 
-         res 6,h 
-         res 7,h 
-         ld de,(filereadoffset+0) 
-         add hl,de 
-         ld bc,(filereadoffset+2) 
-         jr nc,$+3 
-         inc bc 
-         ex de,hl 
- .fileoffsetlo=$+1 
-         ld hl,0 
-         sub hl,de 
-         ex de,hl 
- .fileoffsethi=$+1 
-         ld hl,0 
-         sbc hl,bc 
-         ld a,h 
-         or l 
-         or d 
-         ld b,e 
-         ld hl,(.fileoffsetlo) 
-         ld de,(.fileoffsethi) 
-         jp nz,s3mfilestreamseek 
-         inc b 
-         dec b 
-         ret z 
-         ld hl,(filestreamcurrentaddr) 
- .loop   bit 6,h 
-         call nz,s3mloadfiledata 
-         inc hl 
-         djnz .loop 
-         ld (filestreamcurrentaddr),hl 
-         ret 
-   
- s3mfilestreamseek 
- ;dehl = offset 
-         push ix 
-         push iy 
-         ld a,(filehandle) 
-         ld b,a 
-         OS_SEEKHANDLE 
-         ld hl,0xffff 
-         ld (filestreamcurrentaddr),hl 
-         pop iy 
-         pop ix 
-         ret 
-   
- s3mloadfiledata 
-         push af,bc,de 
-         exx 
-         ex af,af' 
-         push af,bc,de,hl,ix,iy 
-         ld a,(filehandle) 
-         ld b,a 
-         OS_TELLHANDLE 
-         ld (filereadoffset+0),hl 
-         ld (filereadoffset+2),de 
-         ld de,0x8000 
-         ld hl,0x4000 
-         call readstream_file 
-         pop iy,ix,hl,de,bc,af 
-         exx 
-         ex af,af' 
-         pop de,bc,af 
-         ld hl,0x8000 
-         ret 
-   
- filestreamcurrentaddr ds 2 
- filereadoffset ds 4 
-   
- s3mloadsamples 
- ;output: zf=1 if samples are loaded, zf=0 otherwise 
- .filename=$+1 
-         ld de,0 
-         call openstream_file 
-         or a 
-         ret nz 
-         ld hl,0 
-         ld (filestreamcurrentaddr),hl 
-         dec hl 
-         ld (filereadoffset+0),hl 
-         ld (filereadoffset+2),hl 
-         ld a,(modfilebufferpage) 
-         SETPG8000 
- ;read samples data from file 
-         ld hl,S3MSAMPLEDATASTART%65536 
-         ld a,S3MSAMPLEDATASTART/65536 
-         ld (.sampleaddresslo),hl 
-         ld (.sampleaddresshi),a 
-         ld hl,(s3minfo.instparapointers) 
-         ld iy,s3mwaveheaderbuffer 
-         ld a,(s3mheader.instnum) 
-         ld b,a 
- .mainloop 
-         push bc 
- ;convert instrument parapointer to memory address 
-         ld e,(hl) 
-         inc hl 
-         ld d,(hl) 
-         dec hl 
-         ex de,hl 
-         add hl,hl 
-         add hl,hl 
-         add hl,hl 
-         add hl,hl 
-         ld bc,S3MHEADERADDR 
-         add hl,bc 
-         ex de,hl 
-         ld ix,de 
- ;replace parapointer with pointer 
-         ld (hl),e 
-         inc hl 
-         ld (hl),d 
-         inc hl 
-         push hl 
- ;start filling wavetable entry 
-         bit 0,(ix+S3MINSTRUMENT.flags) 
-         jr z,.noloop 
-         ld bc,(ix+S3MINSTRUMENT.loopstart) 
-         ld de,(ix+S3MINSTRUMENT.loopend) 
-         ld hl,de 
-         dec de 
-         sub hl,bc 
-         jr nz,.hasloop 
- .noloop 
-         ld de,(ix+S3MINSTRUMENT.length) 
-         ld bc,de 
-         dec bc 
- .hasloop 
-         ld hl,0xffff 
-         sub hl,de 
-         ld (iy+ 3),b ;loop hi 
-         ld (iy+ 4),c ;loop lo 
-         ld (iy+ 5),h ;end hi 
-         ld (iy+ 6),l ;end lo 
-         ld (iy+ 7),0x00 ;LFO, VIB 
-         ld (iy+ 8),0xf0 ;AR, D1R 
-         ld (iy+ 9),0xff ;DL, D2R 
-         ld (iy+10),0x0f ;rate correction, RR 
-         ld (iy+11),0x00 ;AM 
- .sampleaddresslo=$+1 
-         ld hl,0 
- .sampleaddresshi=$+1 
-         ld d,0 
-         ld (iy+0),d ;8 bits sample, addr hi 
-         ld (iy+1),h ;addr mi 
-         ld (iy+2),l ;addr lo 
-         ld bc,(ix+S3MINSTRUMENT.length) 
-         ld a,b 
-         or c 
-         jp z,.nextsample 
- ;upload sample 
-         push bc 
-         push de 
-         push hl 
-         ld a,c 
-         dec bc 
-         inc b 
-         ld c,b 
-         ld b,a 
-         push bc 
-         call opl4setmemoryaddress 
-         ld de,0x1102 
-         call opl4writewave 
- ;seek to samples data 
-         ld d,0 
-         ld e,(ix+S3MINSTRUMENT.paraptr+0) 
-         ld l,(ix+S3MINSTRUMENT.paraptr+1) 
-         ld h,(ix+S3MINSTRUMENT.paraptr+2) 
-         add hl,hl : rl de 
-         add hl,hl : rl de 
-         add hl,hl : rl de 
-         add hl,hl : rl de 
-         call s3mfilestreamseekfast 
-         pop bc 
-         ld hl,(filestreamcurrentaddr) 
- ;start uploading 
-         opl4_wait 
-         ld a,6 
-         out (MOON_WREG),a 
- .writeloop 
-         bit 6,h 
-         call nz,s3mloadfiledata 
-         ld d,(hl) 
-         inc hl 
-         opl4_wait 
-         ld a,d 
-         add a,0x80 
-         out (MOON_WDAT),a 
-         djnz .writeloop 
-         dec c 
-         jr nz,.writeloop 
- ;duplicate the last data sample 
-         opl4_wait 
-         ld a,d 
-         add a,0x80 
-         out (MOON_WDAT),a 
-         ld de,0x1002 
-         call opl4writewave 
-         ld (filestreamcurrentaddr),hl 
-         pop hl 
-         pop de 
-         pop bc 
- ;set next write address 
-         xor a 
-         scf ;add +1 account for duping the last data sample 
-         adc hl,bc 
-         ld (.sampleaddresslo),hl 
-         adc a,d 
-         ld (.sampleaddresshi),a 
- ;process c2spd 
-         ld hl,(ix+S3MINSTRUMENT.c2spd) 
-         push ix 
-         push iy 
-         call s3mgettunefactor 
-         pop iy 
-         pop ix 
-         ld (ix+S3MINSTRUMENT.tunefactorcoeff),hl 
-         ld (ix+S3MINSTRUMENT.tunefactorshift),a 
- .nextsample 
-         ld bc,MOONWAVEHEADERSIZE 
-         add iy,bc 
-         pop hl 
-         pop bc 
-         dec b 
-         jp nz,.mainloop 
- ;switch back to memory steam 
-         call closestream_file 
-         ld a,(memorystreamcurrentpage) 
-         SETPG8000 
- ;write headers 
-         ld ix,s3mwaveheaderbuffer 
-         ld hl,MOONSOUNDROMSIZE%65536 
-         ld d,MOONSOUNDROMSIZE/65536 
-         ld bc,S3MWAVEHEADERBUFFERSIZE 
-         call opl4writememory 
-         xor a 
-         ret 
-   
- s3munload 
-         call opl4mute 
-         jp memorystreamfree 
-   
- s3mplay 
-         call s3mwaittimer 
-         ld a,(s3mplayer.speedstep) 
-         dec a 
-         jp nz,.tn 
-         ld a,(s3mplayer.speed) 
-         ld (s3mplayer.speedstep),a 
-         ld a,(s3mplayer.patterndelay) 
-         or a 
-         jr z,.nopatterndelay 
-         dec a 
-         ld (s3mplayer.patterndelay),a 
-         ret 
- .nopatterndelay 
-         call s3mreadstepdata 
-         ld ix,s3mplayer.stepdatabuffer 
-         ld iy,s3mplayer.channels 
-         ld a,(s3minfo.chnum) 
- .t0loop push af 
-         ld (iy+S3MCHANNEL.tunefactorshiftoverride),0xff 
-         call s3msetinstrument 
-         call s3mhandlecommandT0 
-         ld d,(ix+S3MSTEPDATA.effectcommand) 
-         ld e,(ix+S3MSTEPDATA.extendedcommand) 
-         ld hl,0x130d 
-         sub hl,de 
-         call nz,s3mnewnote 
-         ld a,(ix+S3MSTEPDATA.volume) 
-         cp 255 
-         jr z,.novolumecmd 
-         clamp_volume_in_a 
-         ld (iy+S3MCHANNEL.volume),a 
- .novolumecmd 
-         ld a,(iy+S3MCHANNEL.volume) 
-         call s3msetvolume 
-         set 7,(iy+S3MCHANNEL.pankeyon) 
-         call s3mflushpankeyon 
-         ld de,S3MSTEPDATA 
-         add ix,de 
-         ld e,S3MCHANNEL 
-         add iy,de 
-         pop af 
-         dec a 
-         jp nz,.t0loop 
-         ret 
- .tn     ld (s3mplayer.speedstep),a 
-         ld a,(s3mplayer.arpeggio) 
-         inc a 
-         cp 3 
-         jr c,$+3 
-         xor a 
-         ld (s3mplayer.arpeggio),a 
-         ld ix,s3mplayer.stepdatabuffer 
-         ld iy,s3mplayer.channels 
-         ld a,(s3minfo.chnum) 
-         ld b,a 
- .tnloop push bc 
-         call s3mhandlecommandTN 
-         ld de,S3MSTEPDATA 
-         add ix,de 
-         ld e,S3MCHANNEL 
-         add iy,de 
-         pop bc 
-         djnz .tnloop 
-         ret 
-   
- s3msetinstrument 
- ;ix = step data 
- ;iy = channel data 
-         ld a,(ix+S3MSTEPDATA.instrument) 
-         or a 
-         ret z 
-         ld (iy+S3MCHANNEL.instrument),a 
-         dec a 
-         ld hl,(s3minfo.instparapointers) 
-         ld e,a 
-         ld d,0 
-         add hl,de 
-         add hl,de 
-         ld e,(hl) 
-         inc hl 
-         ld d,(hl) 
-         push ix 
-         ld ix,de 
-         ld de,(ix+S3MINSTRUMENT.tunefactorcoeff) 
-         ld (iy+S3MCHANNEL.tunefactorcoeff),de 
-         ld a,(ix+S3MINSTRUMENT.tunefactorshift) 
-         ld (iy+S3MCHANNEL.tunefactorshift),a 
-         ld a,(ix+S3MINSTRUMENT.volume) 
-         ld (iy+S3MCHANNEL.volume),a 
-         ld (iy+S3MCHANNEL.vibratotableposition),0 
-         pop ix 
-         ret 
-   
- s3mhandlecommandT0 
- ;ix = step data 
- ;iy = channel data 
-         ld a,(ix+S3MSTEPDATA.effectcommand) 
-         cp 23 
-         ret nc 
-         ld b,a 
-         add a,a 
-         add a,b 
-         ld (.effectcommandtable),a 
- .effectcommandtable=$+1 
-         jr $ 
-         ret : ds 2          ;  0 - 
-         jp .doeffectA       ;  1 [Set Speed] 
-         ret : ds 2          ;  2 [Pattern Jump] 
-         ret : ds 2          ;  3 [Pattern Break] 
-         jp s3mvolumeslideT0 ;  4 [Volume Slide/Fine Volume Slide up/down] 
-         jp .doeffectE       ;  5 [Porta Down/Fine Porta Down/Xtra Fine Porta] 
-         jp .doeffectF       ;  6 [Porta Up/Fine Porta Up/Extra Fine Porta Down] 
-         ret : ds 2          ;  7 [Porta to note] 
-         jp .doeffectH       ;  8 [Vibrato] 
-         ret : ds 2          ;  9 [Tremor] 
-         jp .doeffectJ       ; 10 [Arpeggio] 
-         jp s3mvolumeslideT0 ; 11 [Vibrato+Volume Slide] 
-         jp s3mvolumeslideT0 ; 12 [Porta+Volume Slide] 
-         ret : ds 2          ; 13 - 
-         ret : ds 2          ; 14 - 
-         ret : ds 2          ; 15 [Sample Offset] 
-         ret : ds 2          ; 16 - 
-         ret : ds 2          ; 17 [Retrig + Volume Slide] 
-         jp .doeffectR       ; 18 [Tremolo] 
-         jp .doeffectS       ; 19 [Extended Command] 
-         jp .doeffectT       ; 20 [Set Tempo] 
-         jp .doeffectU       ; 21 [Fine Vibrato] 
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 22 [Global Volume] 
-         ret 
- .doeffectR 
-         ld b,(ix+S3MSTEPDATA.effectdata) 
-         ld c,(iy+S3MCHANNEL.tremolocommand) 
-         ld a,b 
-         and 0xf0 
-         jr z,.skiptremolohi 
-         xor c 
-         and 0xf0 
-         xor c 
-         ld c,a 
- .skiptremolohi 
-         ld a,b 
-         and 15 
-         jr z,.skiptremololo 
-         xor c 
-         and 15 
-         xor c 
-         ld c,a 
- .skiptremololo 
-         ld (iy+S3MCHANNEL.tremolocommand),c 
-         ret 
- .doeffectJ 
-         xor a 
-         ld (s3mplayer.arpeggio),a 
-         ld hl,(iy+S3MCHANNEL.period) 
-         jp s3msetfrequency 
- .doeffectE 
-         ld a,(ix+S3MSTEPDATA.effectdata) 
-         or a 
-         jr z,$+5 
-         ld (iy+S3MCHANNEL.portaspeed),a 
-         ld a,(iy+S3MCHANNEL.portaspeed) 
-         cp 0xe0 
-         ret c 
-         ld b,a 
-         and 15 
-         ld e,a 
-         ld d,0 
-         bit 4,b 
-         jp z,s3mportadown.slide 
-         ex de,hl 
-         add hl,hl 
-         add hl,hl 
-         ex de,hl 
-         jp s3mportadown.slide 
- .doeffectF 
-         ld a,(ix+S3MSTEPDATA.effectdata) 
-         or a 
-         jr z,$+5 
-         ld (iy+S3MCHANNEL.portaspeed),a 
-         ld a,(iy+S3MCHANNEL.portaspeed) 
-         cp 0xe0 
-         ret c 
-         ld b,a 
-         and 15 
-         ld e,a 
-         ld d,0 
-         bit 4,b 
-         jp z,s3mportaup.slide 
-         ex de,hl 
-         add hl,hl 
-         add hl,hl 
-         ex de,hl 
-         jp s3mportaup.slide 
- .doeffectH 
- .doeffectU 
-         ld a,(ix+S3MSTEPDATA.note) 
-         cp 254 
-         jr nc,.skipnote 
-         ld bc,(iy+S3MCHANNEL.period) 
-         ld (iy+S3MCHANNEL.tempcommand),bc 
- .skipnote 
-         ld b,(ix+S3MSTEPDATA.effectdata) 
-         ld c,(iy+S3MCHANNEL.vibratocommand) 
-         ld a,b 
-         and 0xf0 
-         jr z,.skipvibratohi 
-         xor c 
-         and 0xf0 
-         xor c 
-         ld c,a 
- .skipvibratohi 
-         ld a,b 
-         and 15 
-         jr z,.skipvibratolo 
-         xor c 
-         and 15 
-         xor c 
-         ld c,a 
- .skipvibratolo 
-         ld (iy+S3MCHANNEL.vibratocommand),c 
-         ret 
- .doeffectA 
-         ld a,(ix+S3MSTEPDATA.effectdata) 
-         ld (s3mplayer.speed),a 
-         ret 
- .doeffectT 
-         ld a,(ix+S3MSTEPDATA.effectdata) 
-         jp s3msetbpm 
- .doeffectS 
-         ld a,(ix+S3MSTEPDATA.extendedcommand) 
-         ld b,a 
-         add a,a 
-         add a,b 
-         ld (.exteffcommandtable),a 
- .exteffcommandtable=$+1 
-         jr $ 
-         ret : ds 2    ;  0 [Set Filter on/off] 
-         ret : ds 2    ;  1 [Set Glissando on/off] 
-         jp .doexteff2 ;  2 [Set FineTune] 
-         jp .doexteff3 ;  3 [Set Vibrato Waveform] 
-         jp .doexteff4 ;  4 [Set Tremolo Waveform] 
-         ret : ds 2    ;  5 - 
-         ret : ds 2    ;  6 - 
-         ret : ds 2    ;  7 - 
-         jp .doexteff8 ;  8 [Set Pan Position] 
-         ret : ds 2    ;  9 - 
-         jp .doexteffA ; 10 [Stereo Control] 
-         ret : ds 2    ; 11 [Pattern Loop] 
-         jp .doexteffC ; 12 [Cut Note] 
-         jp .doexteffD ; 13 [Delay Note] 
-         jp .doexteffE ; 14 [Pattern Delay] 
-         ret           ; 15 [Funk Repeat] 
- .doexteff8 
- .doexteffA 
-         ld a,(ix+S3MSTEPDATA.effectdata) 
-         jp s3msetpanning 
- .doexteff2 
-         ld a,(ix+S3MSTEPDATA.effectdata) 
-         ld b,a 
-         add a,a 
-         add a,b 
-         get_array_value b,s3mfinetunefactors 
-         inc hl 
-         ld c,(hl) 
-         ld (iy+S3MCHANNEL.tunefactorcoeffoverride),bc 
-         inc hl 
-         ld a,(hl) 
-         ld (iy+S3MCHANNEL.tunefactorshiftoverride),a 
-         ret 
- .doexteff3 
-         ld a,(iy+S3MCHANNEL.wavecontrol) 
-         ld b,(ix+S3MSTEPDATA.effectdata) 
-         and 0xfc 
-         or b 
-         ld (iy+S3MCHANNEL.wavecontrol),a 
-         ld (iy+S3MCHANNEL.vibratotableposition),0 
-         ret 
- .doexteff4 
-         ld a,(iy+S3MCHANNEL.wavecontrol) 
-         ld b,(ix+S3MSTEPDATA.effectdata) 
-         sla b 
-         sla b 
-         and 0xf3 
-         or b 
-         ld (iy+S3MCHANNEL.wavecontrol),a 
-         ld (iy+S3MCHANNEL.tremolotableposition),0 
-         ret 
- .doexteffC 
-         ld a,(ix+S3MSTEPDATA.effectdata) 
-         or a 
-         jr z,.channeloff 
-         ld (iy+S3MCHANNEL.tempcommand),a 
-         ret 
- .channeloff 
-         ld (iy+S3MCHANNEL.volume),a 
-         jp s3msetvolume 
- .doexteffD 
-         ld a,(ix+S3MSTEPDATA.effectdata) 
-         or a 
-         ret z 
-         ld (iy+S3MCHANNEL.tempcommand),a 
-         ret 
- .doexteffE 
-         ld a,(ix+S3MSTEPDATA.effectdata) 
-         ld (s3mplayer.patterndelay),a 
-         ret 
-   
- s3mhandlecommandTN 
- ;ix = step data 
- ;iy = channel data 
-         ld a,(ix+S3MSTEPDATA.effectcommand) 
-         cp 23 
-         ret nc 
-         ld b,a 
-         add a,a 
-         add a,b 
-         ld (.effectcommandtable),a 
- .effectcommandtable=$+1 
-         jr $ 
-         ret : ds 2       ;  0 - 
-         ret : ds 2       ;  1 [Set Speed] 
-         jp .effectB      ;  2 [Pattern Jump] 
-         jp .effectC      ;  3 [Pattern Break] 
-         jp s3mvolumeslideTN ; 4 [Volume Slide/Fine Volume Slide up/down] 
-         jp s3mportadown  ;  5 [Porta Down/Fine Porta Down/Xtra Fine Porta] 
-         jp s3mportaup    ;  6 [Porta Up/Fine Porta Up/Extra Fine Porta Down] 
-         jp s3mtoneporta  ;  7 [Porta to note] 
-         jp s3mvibrato    ;  8 [Vibrato] 
-         ret : ds 2       ;  9 [Tremor] 
-         jp .doeffectJ    ; 10 [Arpeggio] 
-         jp .doeffectK    ; 11 [Vibrato+Volume Slide] 
-         jp .doeffectL    ; 12 [Porta+Volume Slide] 
-         ret : ds 2       ; 13 - 
-         ret : ds 2       ; 14 - 
-         ret : ds 2       ; 15 [Sample Offset] 
-         ret : ds 2       ; 16 - 
-         ret : ds 2       ; 17 [Retrig + Volume Slide] 
-         jp s3mtremolo    ; 18 [Tremolo] 
-         jp .doeffectS    ; 19 [Extended Command] 
-         ret : ds 2       ; 20 [Set Tempo] 
-         jp s3mfvibrato   ; 21 [Fine Vibrato] 
- ;;;;;;;;;;;;;;;;;;;;;;;;;; 22 [Global Volume] 
-         ret 
- .doeffectK 
-         call s3mvibrato 
-         jp s3mvolumeslideTN 
- .doeffectL 
-         call s3mtoneporta 
-         jp s3mvolumeslideTN 
- .doeffectJ 
-         ld a,(ix+S3MSTEPDATA.effectdata) 
-         or a 
-         ret z 
-         ld hl,(s3mplayer.arpeggio) 
-         dec l 
-         jr z,.datahi 
-         inc l 
-         jr nz,.datalo 
-         ld hl,(iy+S3MCHANNEL.period) 
-         jp s3msetfrequency 
- .datahi rrca 
-         rrca 
-         rrca 
-         rrca 
- .datalo and 15 
-         add a,(iy+S3MCHANNEL.note) 
-         cp 96 
-         jr c,$+4 
-         ld a,95 
-         call s3mnotetoperiod 
-         call s3mtuneperiod 
-         jp s3msetfrequency 
- .effectB 
-         ld a,(s3mplayer.speedstep) 
-         dec a 
-         ret nz 
-         ld a,(ix+S3MSTEPDATA.effectdata) 
-         call s3mskipmarkers 
-         ld (s3mplayer.patterntableindex),a 
-         xor a 
-         jp s3msetnextstep 
- .effectC 
-         ld a,(s3mplayer.speedstep) 
-         dec a 
-         ret nz 
-         ld a,(s3mplayer.patterntableindex) 
-         inc a 
-         call s3mskipmarkers 
-         ld (s3mplayer.patterntableindex),a 
-         ld a,(ix+S3MSTEPDATA.effectdata) 
-         ld b,a 
-         and 0xf0 
-         xor b 
-         ld c,a 
-         xor b 
-         rrca 
-         rrca 
-         rrca 
-         rrca 
-         add a,a 
-         ld b,a 
-         add a,a 
-         add a,a 
-         add a,b 
-         add a,c 
-         jp s3msetnextstep 
- .doeffectS 
-         ld a,(ix+S3MSTEPDATA.extendedcommand) 
-         ld b,a 
-         add a,a 
-         add a,b 
-         ld (.exteffcommandtable),a 
- .exteffcommandtable=$+1 
-         jr $ 
-         ret : ds 2    ;  0 [Set Filter on/off] 
-         ret : ds 2    ;  1 [Set Glissando on/off] 
-         ret : ds 2    ;  2 [Set FineTune] 
-         ret : ds 2    ;  3 [Set Vibrato Waveform] 
-         ret : ds 2    ;  4 [Set Tremolo Waveform] 
-         ret : ds 2    ;  5 - 
-         ret : ds 2    ;  6 - 
-         ret : ds 2    ;  7 - 
-         ret : ds 2    ;  8 [Set Pan Position] 
-         ret : ds 2    ;  9 - 
-         ret : ds 2    ; 10 [Stereo Control] 
-         jp .doexteffB ; 11 [Pattern Loop] 
-         jp .doexteffC ; 12 [Cut Note] 
-         jp .doexteffD ; 13 [Delay Note] 
-         ret : ds 2    ; 14 [Pattern Delay] 
-         ret           ; 15 [Funk Repeat] 
- .doexteffB 
-         ld a,(s3mplayer.speedstep) 
-         dec a 
-         ret nz 
-         ld a,(ix+S3MSTEPDATA.effectdata) 
-         or a 
-         jr nz,.checkloopcount 
-         ld a,(s3mplayer.patternstepindex) 
-         ld (iy+S3MCHANNEL.patternloopstart),a 
-         ret 
- .checkloopcount 
-         ld b,(iy+S3MCHANNEL.patternloopcount) 
-         inc b 
-         cp b 
-         jr c,.restartloop 
-         ld (iy+S3MCHANNEL.patternloopcount),b 
-         ld a,(iy+S3MCHANNEL.patternloopstart) 
-         jp s3msetnextstep 
- .restartloop 
-         ld (iy+S3MCHANNEL.patternloopcount),0 
-         ret 
- .doexteffC 
-         ld a,(iy+S3MCHANNEL.tempcommand) 
-         or a 
-         ret z 
-         dec a 
-         ld (iy+S3MCHANNEL.tempcommand),a 
-         ret nz 
-         ld (iy+S3MCHANNEL.volume),a 
-         jp s3msetvolume 
- .doexteffD 
-         ld a,(iy+S3MCHANNEL.tempcommand) 
-         or a 
-         ret z 
-         dec a 
-         ld (iy+S3MCHANNEL.tempcommand),a 
-         ret nz 
-         jp s3mnewnote 
-   
- s3mnewnote 
- ;ix = step data 
- ;iy = channel data 
-         ld a,(iy+S3MCHANNEL.instrument) 
-         or a 
-         ret z 
-         ld a,(ix+S3MSTEPDATA.effectcommand) 
-         cp 0x07 
-         jr z,.effectG 
-         cp 0x0C 
-         jr z,.effectL 
-         ld a,(ix+S3MSTEPDATA.note) 
-         inc a 
-         ret z 
-         xor a 
-         call s3msetvolume 
-         res 7,(iy+S3MCHANNEL.pankeyon) 
-         call s3mflushpankeyon 
-         ld a,(ix+S3MSTEPDATA.note) 
-         cp 254 
-         jr nz,.validnote 
-         ld (iy+S3MCHANNEL.volume),0 
-         ret 
- .validnote 
-         ld (iy+S3MCHANNEL.note),a 
-         call s3mnotetoperiod 
-         call s3mtuneperiod 
-         ld (iy+S3MCHANNEL.period),hl 
-         call s3msetfrequency 
-         ld a,(iy+S3MCHANNEL.instrument) 
-         cp (iy+S3MCHANNEL.oldinstrument) 
-         ret z 
-         ld (iy+S3MCHANNEL.oldinstrument),a 
-         jp s3msetsamplenumber 
- .effectG 
-         ld a,(ix+S3MSTEPDATA.effectdata) 
-         or a 
-         jr z,.effectL 
-         ld (iy+S3MCHANNEL.portaspeed),a 
- .effectL 
-         ld a,(ix+S3MSTEPDATA.note) 
-         cp 254 
-         ret nc 
-         ld (iy+S3MCHANNEL.note),a 
-         call s3mnotetoperiod 
-         call s3mtuneperiod 
-         ld (iy+S3MCHANNEL.tempcommand),hl 
-         ret 
-   
- s3mskipmarkers 
- ;intput: a = pattern table index 
- ;output: a = pattern table index pointing at non-marker 
-         ld hl,s3mheader.ordernum 
-         ld b,(hl) 
-         ld e,a 
-         ld d,0 
-         ld hl,s3mpatterntable 
-         add hl,de 
- .loop   cp b 
-         jr c,$+6 
-         xor a 
-         ld hl,s3mpatterntable 
-         ld c,a 
-         ld a,(hl) 
-         cp 254 
-         ld a,c 
-         ret c 
-         inc hl 
-         inc a 
-         jp .loop 
-   
- s3mgetpatternpos 
- ;a = pattern table index 
- ;out: dehl = file stream position 
-         ld e,a 
-         ld d,0 
-         ld hl,s3mpatterntable 
-         add hl,de 
-         ld e,(hl) 
-         ld hl,(s3minfo.pattparapointers) 
-         add hl,de 
-         add hl,de 
- ;parapointer 
-         ld a,(hl) 
-         inc hl 
-         ld h,(hl) 
-         ld l,a 
-         xor a 
-         add hl,hl : rla 
-         add hl,hl : rla 
-         add hl,hl : rla 
-         add hl,hl : rla 
-         ld e,a 
- ;skip the first two bytes containing pattern data size 
-         inc l 
-         inc l 
-         ret 
-   
- memorystreamnextpagewrap 
- ;wraps read address rather than resetting to 0x8000 
-         push hl 
-         call memorystreamnextpage 
-         pop hl 
-         res 6,h 
-         set 7,h 
-         ret 
-   
- s3msetnextstep 
- ;a = step index 
- ;output: memory stream at patterntableindex/patternstepindex 
-         dec a ;save decremented patternstepindex to cancel out its increment in T0 
-         ld (s3mplayer.patternstepindex),a 
-         ld a,(s3mplayer.patterntableindex) 
-         call s3mgetpatternpos 
-         call memorystreamseek 
-         ld a,(s3mplayer.patternstepindex) 
-         inc a 
-         ret z 
-         ld b,a 
-         ld hl,(memorystreamcurrentaddr) 
- .steploop 
-         ld a,(hl) 
-         or a 
-         jr z,.nextstep 
- .loop   and 0xe0 
-         ld e,d 
-         add a,a 
-         rl e 
-         add a,a 
-         rl e 
-         rlca 
-         adc a,e 
-         inc a 
-         ld e,a 
-         add hl,de 
-         bit 6,h 
-         call nz,memorystreamnextpagewrap 
-         ld a,(hl) 
-         or a 
-         jp nz,.loop 
- .nextstep 
-         inc hl 
-         djnz .steploop 
-         ld (memorystreamcurrentaddr),hl 
-         ret 
-   
- s3mreadstepdata 
- ;input: memory stream 
- ;output: filled stepdatabuffer, memory stream at next pattern step 
-         ld a,(s3mplayer.patternstepindex) 
-         inc a 
-         cp S3MPATTERNSTEPCOUNT 
-         jr c,.nextpatternstep 
-         ld a,(s3mplayer.patterntableindex) 
-         inc a 
-         call s3mskipmarkers 
-         ld (s3mplayer.patterntableindex),a 
-         call s3mgetpatternpos 
-         call memorystreamseek 
-         xor a 
- .nextpatternstep 
-         ld (s3mplayer.patternstepindex),a 
- ;set defaults 
-         ld ix,s3mplayer.stepdatabuffer 
-         ld (ix+S3MSTEPDATA.note),0xff 
-         ld (ix+S3MSTEPDATA.volume),0xff 
-         ld (ix+S3MSTEPDATA.instrument),0 
-         ld (ix+S3MSTEPDATA.effectcommand),0xff 
-         ld hl,s3mplayer.stepdatabuffer 
-         ld de,s3mplayer.stepdatabuffer+S3MSTEPDATA 
-         ld bc,(s3minfo.chinitsize) 
-         ldir 
- ;read data 
-         ld hl,(memorystreamcurrentaddr) 
-         memory_stream_read_byte a 
-         or a 
-         jr z,.skiploop 
- .loop   ld b,a 
-         and 0x1f 
-         ex de,hl 
-         get_array_value l,s3minfo.choffset 
-         ex de,hl 
-         ld d,0 
-         ld ix,s3mplayer.stepdatabuffer 
-         add ix,de 
-         bit 5,b 
-         jr z,.skipnoteinstrument 
-         memory_stream_read_byte a 
-         cp 254 
-         jr nc,.specialnote 
-         ld c,a 
-         rrca 
-         rrca 
-         and 0x3c 
-         neg 
-         add a,c 
- .specialnote 
-         ld (ix+S3MSTEPDATA.note),a 
-         memory_stream_read_byte a 
-         ld (ix+S3MSTEPDATA.instrument),a 
- .skipnoteinstrument 
-         bit 6,b 
-         jr z,.skipvolume 
-         memory_stream_read_byte a 
-         ld (ix+S3MSTEPDATA.volume),a 
- .skipvolume 
-         bit 7,b 
-         jr z,.skipeffect 
-         memory_stream_read_byte a 
-         memory_stream_read_byte c 
-         ld (ix+S3MSTEPDATA.effectcommand),a 
-         cp 19 
-         ld a,c 
-         jr nz,.notextended 
-         srl c 
-         srl c 
-         srl c 
-         srl c 
-         ld (ix+S3MSTEPDATA.extendedcommand),c 
-         and 15 
- .notextended 
-         ld (ix+S3MSTEPDATA.effectdata),a 
- .skipeffect 
-         memory_stream_read_byte a 
-         or a 
-         jr nz,.loop 
- .skiploop 
-         ld (memorystreamcurrentaddr),hl 
-         ret 
-   
- s3mnotetoperiod 
- ;a = note 
- ;out: hl = period 
-         add a,a 
-         get_array_value a,st3periods 
-         inc hl 
-         ld h,(hl) 
-         ld l,a 
-         ret 
-   
- s3mtuneperiod 
- ;iy = channel data 
- ;hl = period 
- ;out: hl = period 
-         ld bc,(iy+S3MCHANNEL.tunefactorcoeffoverride) 
-         ld a,(iy+S3MCHANNEL.tunefactorshiftoverride) 
-         or a 
-         jp p,.hasoverride 
-         ld bc,(iy+S3MCHANNEL.tunefactorcoeff) 
-         ld a,(iy+S3MCHANNEL.tunefactorshift) 
-         or a 
- .hasoverride 
-         push af 
-         ex de,hl 
-         call uintmul16 
-         pop af 
-         jr z,.noshift 
-         ld b,a 
- .shiftloop 
-         add hl,hl 
-         rl de 
-         djnz .shiftloop 
- .noshift 
-         ld hl,-S3MMINPERIOD 
-         add hl,de 
-         ex de,hl 
-         ret c 
-         ld hl,S3MMINPERIOD 
-         ret 
-   
- s3mgettunefactor 
- ;hl = c2spd 
- ;output: hl = factor, a = shift 
- ;poor man's floating point: period*8363/c2spd = (period*factor)>>(16-shift) 
-         ld de,0 
-         exx 
-         ld de,8363 
-         ld hl,0 
-         call uintdiv32 
-         ld a,d 
-         or e 
-         ret z 
-         ex de,hl 
-         push hl 
-         ld b,16 
-         add hl,hl 
-         dec b 
-         jr nc,$-2 
-         ld a,h 
-         or l 
-         jr z,$+3 
-         inc b 
-         inc b 
-         pop hl 
-         ex de,hl 
-         ld a,b 
- .shiftloop 
-         srl de 
-         rr hl 
-         djnz .shiftloop 
-         ret 
-   
- s3msetbpm equ modsetbpm 
- s3mwaittimer equ modwaittimer 
-   
- s3msetfrequency 
- ;iy = channel data 
- ;hl = period 
-         ld a,(modperiodlookuppage) 
-         SETPGC000 
- ;hl=(hl/4)*2 
-         ld a,l 
-         srl h 
-         rra 
-         and %11111110 
-         ld l,a 
- ;two-bytes lookup 
-         ld de,0xc000-2 
-         add hl,de 
-         ld d,(hl) 
-         inc hl 
-         ld l,(hl) 
-         ld a,(memorystreampages) 
-         SETPGC000 
-         ld a,(iy+S3MCHANNEL.index) 
-         add 0x38 
-         ld e,a 
-         call opl4writewave 
-         ld d,l 
-         ld a,e 
-         sub 0x18 
-         ld e,a 
-         jp opl4writewave 
-   
- s3mportaup 
- ;iy = channel data 
-         ld a,(iy+S3MCHANNEL.portaspeed) 
-         cp 0xe0 
-         ret nc 
-         rlca 
-         rlca 
-         ld d,a 
-         and 0xfc 
-         ld e,a 
-         xor d 
-         ld d,a 
- .slide  ld hl,(iy+S3MCHANNEL.period) 
-         sub hl,de 
-         jr c,.clamp 
-         ex de,hl 
-         ld hl,-S3MMINPERIOD 
-         add hl,de 
-         ex de,hl 
-         jr c,$+5 
- .clamp  ld hl,S3MMINPERIOD 
-         ld (iy+S3MCHANNEL.period),hl 
-         jp s3msetfrequency 
-   
- s3mportadown 
- ;iy = channel data 
-         ld a,(iy+S3MCHANNEL.portaspeed) 
-         cp 0xe0 
-         ret nc 
-         rlca 
-         rlca 
-         ld d,a 
-         and 0xfc 
-         ld e,a 
-         xor d 
-         ld d,a 
- .slide  ld hl,(iy+S3MCHANNEL.period) 
-         add hl,de 
-         ex de,hl 
-         ld hl,-32000 
-         add hl,de 
-         ex de,hl 
-         jr nc,$+5 
-         ld hl,31999 
-         ld (iy+S3MCHANNEL.period),hl 
-         jp s3msetfrequency 
-   
- s3mtoneporta 
- ;iy = channel data 
-         ld hl,(iy+S3MCHANNEL.period) 
-         ld bc,(iy+S3MCHANNEL.tempcommand) 
-         ld de,hl 
-         sub hl,bc 
-         ex de,hl 
-         jp z,s3msetfrequency ;period == tempcommand 
-         ld e,(iy+S3MCHANNEL.portaspeed) 
-         ld d,0 
-         jr c,.pospitchbend ;period < tempcommand 
-         sla de 
-         sla de 
-         sbc hl,de 
-         jr nc,$+5 
-         ld hl,0 
-         ld de,hl 
-         sub hl,bc 
-         ex de,hl 
-         jr nc,.finalize ;period >= tempcommand 
-         ld hl,bc 
-         jr .finalize 
- .pospitchbend 
-         sla de 
-         sla de 
-         add hl,de 
-         ld de,hl 
-         sub hl,bc 
-         ex de,hl 
-         jr c,$+4 ;period < tempcommand 
-         ld hl,bc 
- .finalize 
-         ld (iy+S3MCHANNEL.period),hl 
-         jp s3msetfrequency 
-   
-         macro s3m_vibrato is_fine 
-         ld a,(iy+S3MCHANNEL.vibratotableposition) 
-         rrca 
-         rrca 
-         and 0x1f 
-         get_array_value e,modvibratotable 
-         ld hl,0 
-         ld a,(iy+S3MCHANNEL.vibratocommand) 
-         and 15 
-         call nz,mul8x8 
-         xor a 
-         add hl,hl : rla 
-         if !is_fine 
-         add hl,hl : rla 
-         add hl,hl : rla 
-         endif 
-         ld e,h 
-         ld d,a 
-         ld hl,(iy+S3MCHANNEL.period) 
-         bit 7,(iy+S3MCHANNEL.vibratotableposition) 
-         jr z,.periodup 
-         sub hl,de 
-         jr .finalize 
- .periodup 
-         add hl,de 
- .finalize 
-         ld (iy+S3MCHANNEL.tempcommand),hl 
-         call s3msetfrequency 
-         ld a,(iy+S3MCHANNEL.vibratocommand) 
-         rrca 
-         rrca 
-         and 0x3c 
-         add a,(iy+S3MCHANNEL.vibratotableposition) 
-         ld (iy+S3MCHANNEL.vibratotableposition),a 
-         endm 
-   
- s3mfvibrato 
- ;iy = channel data 
-         s3m_vibrato 1 
-         ret 
-   
- s3mvibrato 
- ;iy = channel data 
-         s3m_vibrato 0 
-         ret 
-   
- s3msetvolume 
- ;iy = channel data 
- ;a = volume 
-         get_array_value d,modvolumetable 
-         sll d 
-         ld a,(iy+S3MCHANNEL.index) 
-         add a,0x50 
-         ld e,a 
-         jp opl4writewave 
-   
- s3msetsamplenumber 
- ;iy = channel data 
- ;a = sample number 
-         add a,0x7f 
-         ld d,a 
-         ld a,(iy+S3MCHANNEL.index) 
-         add 0x08 
-         ld e,a 
-         call opl4writewave 
- ;wait for the header to load 
-         in a,(MOON_STAT) 
-         and 3 
-         jr nz,$-4 
-         ret 
-   
- s3mflushpankeyon 
- ;iy = channel data 
-         ld d,(iy+S3MCHANNEL.pankeyon) 
-         ld a,(iy+S3MCHANNEL.index) 
-         add a,0x68 
-         ld e,a 
-         jp opl4writewave 
-   
- s3mvolumeslideT0 
- ;ix = step data 
- ;iy = channel data 
-         ld a,(ix+S3MSTEPDATA.effectdata) 
-         or a 
-         jr z,$+5 
-         ld (iy+S3MCHANNEL.volumeslide),a 
-         ld a,(iy+S3MCHANNEL.volumeslide) 
-         cp 0xf0 
-         jr c,.checkslideup 
-         and 15 
-         ld b,a 
-         ld a,(iy+S3MCHANNEL.volume) 
-         sub b 
-         jr nc,$+3 
-         xor a 
-         ld (iy+S3MCHANNEL.volume),a 
-         jp s3msetvolume 
- .checkslideup 
-         rrca 
-         rrca 
-         rrca 
-         rrca 
-         cp 0xf0 
-         ret c 
-         and 15 
-         add a,(iy+S3MCHANNEL.volume) 
-         clamp_volume_in_a 
-         ld (iy+S3MCHANNEL.volume),a 
-         jp s3msetvolume 
-   
- s3mvolumeslideTN 
- ;iy = channel data 
-         ld a,(iy+S3MCHANNEL.volumeslide) 
-         cp 0x10 
-         jr nc,.checkslideup 
-         ld b,a 
-         ld a,(iy+S3MCHANNEL.volume) 
-         sub b 
-         jr nc,$+3 
-         xor a 
-         ld (iy+S3MCHANNEL.volume),a 
-         jp s3msetvolume 
- .checkslideup 
-         rrca 
-         rrca 
-         rrca 
-         rrca 
-         cp 0x10 
-         ret nc 
-         add a,(iy+S3MCHANNEL.volume) 
-         clamp_volume_in_a 
-         ld (iy+S3MCHANNEL.volume),a 
-         jp s3msetvolume 
-   
- s3msetpanning 
- ;iy = channel data 
- ;a = panning 
-         get_array_value l,modpantable 
-         ld a,(iy+S3MCHANNEL.pankeyon) 
-         and 0x80 
-         or l 
-         ld (iy+S3MCHANNEL.pankeyon),a 
-         ret 
-   
- s3mtremolo 
- ;iy = channel data 
-         ld a,(iy+S3MCHANNEL.tremolotableposition) 
-         rrca 
-         rrca 
-         and 0x1f 
-         get_array_value e,modvibratotable 
-         ld hl,0 
-         ld a,(iy+S3MCHANNEL.tremolocommand) 
-         and 15 
-         call nz,mul8x8 
-         ld a,h 
-         sla l 
-         rla 
-         sla l 
-         rla 
-         bit 7,(iy+S3MCHANNEL.tremolotableposition) 
-         jr z,.volumeup 
-         ld b,a 
-         ld a,(iy+S3MCHANNEL.volume) 
-         sub b 
-         jr nc,.finalize 
-         xor a 
-         jr .finalize 
- .volumeup 
-         add a,(iy+S3MCHANNEL.volume) 
-         clamp_volume_in_a 
- .finalize 
-         call s3msetvolume 
-         ld a,(iy+S3MCHANNEL.tremolocommand) 
-         rrca 
-         rrca 
-         and 0x3c 
-         add a,(iy+S3MCHANNEL.tremolotableposition) 
-         ld (iy+S3MCHANNEL.tremolotableposition),a 
-         ret 
-   
- s3mfinetunefactors 
-         db 0x87,0x96,0x01 ;7895 
-         db 0x86,0xcd,0x01 ;7941 
-         db 0x86,0x0f,0x01 ;7985 
-         db 0x85,0x0b,0x01 ;8046 
-         db 0x84,0x0a,0x01 ;8107 
-         db 0x83,0x0a,0x01 ;8169 
-         db 0x82,0x09,0x01 ;8232 
-         db 0x81,0x48,0x01 ;8280 
-         db 0x80,0x00,0x01 ;8363 (No finetune) 
-         db 0xfe,0x7a,0x00 ;8413 
-         db 0xfc,0xf9,0x00 ;8463 
-         db 0xfb,0x04,0x00 ;8529 
-         db 0xf9,0x7f,0x00 ;8581 
-         db 0xf7,0x7a,0x00 ;8651 
-         db 0xf5,0x6f,0x00 ;8723 
-         db 0xf4,0x7b,0x00 ;8757 
-   
- st3periods 
-         ;    C     C#    D     D#    E     F     F#    G     G#    A     A#    B 
-         dw 27392,25856,24384,23040,21696,20480,19328,18240,17216,16256,15360,14496 ;0 
-         dw 13696,12928,12192,11520,10848,10240, 9664, 9120, 8608, 8128, 7680, 7248 ;1 
-         dw  6848, 6464, 6096, 5760, 5424, 5120, 4832, 4560, 4304, 4064, 3840, 3624 ;2 
-         dw  3424, 3232, 3048, 2880, 2712, 2560, 2416, 2280, 2152, 2032, 1920, 1812 ;3 
-         dw  1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016,  960,  906 ;4 
-         dw   856,  808,  762,  720,  678,  640,  604,  570,  538,  508,  480,  453 ;5 
-         dw   428,  404,  381,  360,  339,  320,  302,  285,  269,  254,  240,  226 ;6 
-         dw   214,  202,  190,  180,  170,  160,  151,  143,  135,  127,  120,  113 ;7 
-         dw   107,  101,   95,   90,   85,   80,   75,   71,   67,   63,   60,   56 ;8 
- st3periodtablesize=($-st3periods)/2 
-