Moonsound OPL4 programming

By Juan Luis

Resident (46)

Juan Luis's picture

10-02-2019, 04:15

Hi,
I'm trying to develop a minimalist program in assembler for playing a few notes with MoonSound and I couldn't hear any note until now. I reset Wave, FM1, FM2 LSI registers 00h and 01h, enable OPL3 and OPL4, set ASDR values, set frequency, etc, but I can't hear anything.

Do anyone know where I can find an assembler example for this?

Login or register to post comments

By Pencioner

Paladin (933)

Pencioner's picture

10-02-2019, 20:15

google for "vgmplay for MSX", it is open source

By yzi

Champion (441)

yzi's picture

10-02-2019, 23:19

If you just want the sample playing part of it, try looking at my SootSound project

https://sourceforge.net/p/sootsound/

MOD/XM style music player
https://sourceforge.net/p/sootsound/code/ci/master/tree/ltmp...

MoonSound routines
https://sourceforge.net/p/sootsound/code/ci/master/tree/moon...

By Juan Luis

Resident (46)

Juan Luis's picture

10-02-2019, 23:24

Thank you very much Pencioner and yzi. I'm trying to compile vgmplay but COM.asm includes a file named Macros.asm that it's not present in the VGMPlay project. Anyway, I'm interested in the source code and I believe I have all the information I need.

Thanks for the links.

By Giangiacomo Zaffini 2

Master (158)

Giangiacomo Zaffini 2's picture

10-02-2019, 23:46

BouKiCHi has a GitHub public repo for his project for Moonsound, a MML-based music driver:
GitHub/BouKiCHi/moondrv
It's assembler of choice is pasmo if I can remember well.

By Pencioner

Paladin (933)

Pencioner's picture

11-02-2019, 01:52

Juan Luis wrote:

COM.asm includes a file named Macros.asm that it's not present in the VGMPlay project

it includes two libs as subrepos, neonlib and gunzip - you might have to use mercurial submodules initiating (i actually don't remember how i did it, i use git on my work, no mercurial, and never was familiar with it - so just googled for solution)

By Juan Luis

Resident (46)

Juan Luis's picture

25-02-2019, 23:50

After Reading the source code, I could see that sootsound uses wavetable to generate music instead of FM. I am trying to synthesize FM music. I believe that I followed all required steps:

- Initialization
- Set Moonsound in music mode
- Configure 2 Operator FM channels
- Configure parameters for both operators (tremolo, vibrato, sustained, KSR, ADSR, etc.)

but when I started to produce the sound with a F-Number and a Block setting Key-On, nothing happens. I can't hear any sound. Is there something wrong in my code? I'm a Little bit frustrated.

include "msx-bios.inc"
include "msx-dos.inc"
include "keyboard.inc"

MOONSOUND_WAVE_REG_PORT     		  equ 0x7E
MOONSOUND_WAVE_DATA_PORT    		  equ 0x7F

MOONSOUND_STATUS_REG_PORT   		  equ 0xC4
MOONSOUND_FM_BANK1_REG_PORT 		  equ 0xC4
MOONSOUND_FM_DATA1_PORT      		  equ 0xC5
MOONSOUND_FM_BANK2_REG_PORT 		  equ 0xC6
MOONSOUND_FM_DATA2_PORT      		  equ 0xC7

MOONSOUND_REG_LSI_TEST1     		  equ 0x00

MOONSOUND_REG_LSI_TEST2 			  equ 0x01

MOONSOUND_REG_MEMORY        		  equ 0x02
MOONSOUND_MEMORY_ACCESS_MASK          equ 00000001b
MOONSOUND_MEMORY_TYPE_MASK            equ 00000010b
MOONSOUND_WAVE_TABLE_HEADER_MASK 	  equ 00011100b
MOONSOUND_DEVICE_ID_MASK         	  equ 11100000b

MOONSOUND_MEMORY_HIGH				  equ 0x03
MOONSOUND_MEMORY_MID				  equ 0x04
MOONSOUND_MEMORY_LOW				  equ 0x05 ; Autoincremented
MOONSOUND_DATA_REGISTER     		  equ 0x06

MOONSOUND_WAVE_TABLE_NUMBER_LOW_BASE  equ 0x08 		; 0x08 - 0x1F
MOONSOUND_WAVE_TABLE_NUMBER_LOW_MASK  equ 11111111b

MOONSOUND_WAVE_WVNUMHI_FNUM_BASE 	  equ 0x20 		; 0x20 - 0x37
MOONSOUND_WAVE_TABLE_NUMBER_HIGH_MASK equ 00000001b
MOONSOUND_WAVE_F_NUMBER_LOW_MASK      equ 11111110b

MOONSOUND_WAVE_FNUM_OCT_PSRVB_BASE    equ 0x38		; 0x38 - 0x4F
MOONSOUND_WAVE_F_NUMBER_HIGH_MASK     equ 00000111b
MOONSOUND_WAVE_OCTAVE_MASK			  equ 11110000b
MOONSOUND_WAVE_PSEUDO_REVERB_MASK     equ 00001000b

MOONSOUND_WAVE_TOTLVL_LVLDIR_BASE     equ 0x50		; 0x50 - 0x67
MOONSOUND_WAVE_TOTAL_LEVEL_MASK       equ 11111110b
MOONSOUND_WAVE_LEVEL_DIRECT_MASK	  equ 00000001b

MOONSOUND_WAVE_KEYON_DAMP_LFORST_CH_PANPOT equ 0x68	; 0x68 - 0x7F
MOONSOUND_WAVE_KEYON_MASK             equ 10000000b
MOONSOUND_WAVE_DAMP_MASK			  equ 01000000b
MOONSOUND_WAVE_LFORST_MASK			  equ 00100000b
MOONSOUND_WAVE_CHANNEL_MASK           equ 00010000b
MOONSOUND_WAVE_PANPOT_MASK            equ 00001111b

MOONSOUND_WAVE_LFO_VIB_AR_BASE		  equ 0x80		; 0x80 - 0x97
MOONSOUND_WAVE_LFO_MASK				  equ 00111000b
MOONSOUND_WAVE_VIB_MASK				  equ 00000111b

MOONSOUND_WAVE_AR_D1R_BASE            equ 0x98		; 0x98 - 0xAF
MOONSOUND_WAVE_ATTACK_RATE_MASK		  equ 11110000b
MOONSOUND_WAVE_DECAY1_RATE_MASK		  equ 00001111b

MOONSOUND_WAVE_DL_D2R_BASE			  equ 0xB0		; 0xB0 - 0xC7
MOONSOUND_WAVE_DECAY_LEVEL_MASK       equ 11110000b
MOONSOUND_WAVE_DECAY2_RATE_MASK       equ 00001111b

MOONSOUND_WAVE_RR_RC_BASE			  equ 0xC8		; 0xC8 - 0xDF
MOONSOUND_WAVE_RELEASE_RATE_MASK      equ 00001111b
MOONSOUND_WAVE_RATE_CORRECTION_MASK   equ 11110000b

MOONSOUND_WAVE_AM_BASE				  equ 0xE0		; 0xE0 - 0xF7
MOONSOUND_WAVE_AM_MASK                equ 00000111b ; AM = Tremolo depth

MOONSOUND_WAVE_MIX_CONTROL_FM_BASE       equ 0xF8
MOONSOUND_WAVE_MIX_CONTROL_FM_RIGHT_MASK equ 00111000b
MOONSOUND_WAVE_MIX_CONTROL_FM_LEFT_MASK  equ 00000111b

MOONSOUND_WAVE_MIX_CONTROL_PCM_BASE       equ 0xF9
MOONSOUND_WAVE_MIX_CONTROL_PCM_RIGHT_MASK equ 00111000b
MOONSOUND_WAVE_MIX_CONTROL_PCM_LEFT_MASK  equ 00000111b

;
; External memory header structure
;
;	DATA BIT: 0 0 		8 bits
;			  0 1      12 bits
;			  1 0      16 bits
;             1 1      Prohibited
;
;			+-------+-------+-------+-------+-------+-------+-------+--------+
; Address   |   D7	|	D6	|	D5	|	D4	|	D3	|	D2	|	D1	|	D0   |
;			+-------+-------+-------+-------+-------+-------+-------+--------+
;           |   DATA BIT    |	              Start Address                  |
;   0000H   |    1  |    0  |  S21  |  S20  |  S19  |  S18  |  S17  |  S16   |
;			+-------+-------+-------+-------+-------+-------+-------+--------+
;   0001H   |  S15  |  S14  |  S13  |  S12  |  S11  |  S10  |   S9  |   S8   |
;			+-------+-------+-------+-------+-------+-------+-------+--------+
;   0002H   |   S7  |   S6  |   S5  |   S4  |   S3  |   S2  |   S1  |   S0   |
;			+-------+-------+-------+-------+-------+-------+-------+--------+
;           |                        Loop Address                            |
;   0003H   |  L15  |  L14  |  L13  |  L12  |  L11  |  L10  |   L9  |   L8   |
;			+-------+-------+-------+-------+-------+-------+-------+--------+
;   0004H   |   L7  |   L6  |   L5  |   L4  |   L3  |   L2  |   L1  |   L0   |
;			+-------+-------+-------+-------+-------+-------+-------+--------+
;           |                         End Address                            |
;   0005H   |  E15  |  E14  |  E13  |  E12  |  E11  |  E10  |   E9  |   E8   |
;			+-------+-------+-------+-------+-------+-------+-------+--------+
;   0006H   |   E7  |   E6  |   E5  |   E4  |   E3  |   E2  |   E1  |   E0   |
;			+-------+-------+-------+-------+-------+-------+-------+--------+
;           |               |	       LFO          |          VIB           |
;   0007H   |    Not used   |   S2  |   S1  |   S0  |   V2  |   V1  |   V0   |
;			+-------+-------+-------+-------+-------+-------+-------+--------+
;           |          Attack Rate          |        Decay 1 Rate            |
;   0008H   |   3   |   2   |   1   |   0   |   3   |   2   |   1   |   0    |
;			+-------+-------+-------+-------+-------+-------+-------+--------+
;           |          Decay level          |        Decay 2 Rate            |
;   0009H   |   3   |   2   |   1   |   0   |   3   |   2   |   1   |   0    |
;			+-------+-------+-------+-------+-------+-------+-------+--------+
;           |        Rate correction        |        Release Rate            |
;   000AH   |   3   |   2   |   1   |   0   |   3   |   2   |   1   |   0    |
;			+-------+-------+-------+-------+-------+-------+-------+--------+
;           |                                       |   AM (Tremolo Depth)   |
;   000BH   |                Not used               |   2   |   1   |   0    |  (Total 12 bytes)
;			+-------+-------+-------+-------+-------+-------+-------+--------+
;						.
;						.
;						. Wave number 1 header (12 bytes)
;						.
;						.
;						. Wave number 2 header (12 bytes)
;						.
;						.
;						.
;						.
;						.
;						. Wave number 383 header (12 bytes)
;						.
;						.
;			+-------+-------+-------+-------+-------+-------+-------+--------+
;			|                       16-bit data format                       |
;           |  D15  |  D14  |  D13  |  D12  |  D11  |  D10  |   D9  |   D8   |
;			+-------+-------+-------+-------+-------+-------+-------+--------+
;			|                       16-bit data format                       |
;           |   D7  |   D6  |   D5  |   D4  |   D3  |   D2  |   D1  |   D0   |
;			+-------+-------+-------+-------+-------+-------+-------+--------+
;			|                       12-bit data format                       |
;           |  D11  |  D10  |   D9  |   D8  |  D7   |  D6   |   D5  |   D4   |   EVEN
;			+-------+-------+-------+-------+-------+-------+-------+--------+
;			|                       12-bit data format                       |
;           |   D3  |   D2  |   D1  |   D0  |  D3   |  D2   |   D1  |   D0   |   EVEN / ODD
;			+-------+-------+-------+-------+-------+-------+-------+--------+
;			|                       12-bit data format                       |
;           |  D11  |  D10  |   D9  |   D8  |  D7   |  D6   |   D5  |   D4   |   ODD
;			+-------+-------+-------+-------+-------+-------+-------+--------+
;			|                        8-bit data format                       |
;           |   D7  |   D6  |   D5  |   D4  |   D3  |   D2  |   D1  |   D0   |
;			+-------+-------+-------+-------+-------+-------+-------+--------+
;

org 0x0100	; MSX-DOS Application

main:
	call moonsound_detect
	or a
	jr z,main_end
	
	; Moonsound initialization
	;
	; Input:
	; Reg B = four_op_mask
	; Reg C = deep_tremolo 1 bit (True or False)
	; Reg D = deep_vibrato 1 bit (True or False)
	ld b,0
	ld c,0
	ld d,0
	call moonsound_init
	
	call set_moonsound_music_mode
	
	ld a,0	; Channel 1
	ld b,1	; (Channel B) Right = True
	ld c,1	; (Channel A) Left  = True
	ld d,0	; (CNT)       Additive synthesis = False
	ld e,0	;             Feedback = 0 [0..7]
	call set_moonsound_fm_channel
	
	;
	; Mode 2 Operators. Operator 1 has slot no. 1 and Operator 2 has slot no. 4
	;
	; set FM cell patch
	;
	;	Input:
	;		A - Cell
	;		B - Tremolo		True/False
	;		C - Vibrato		True/False
	;		D - Sustained 	True/False
	;		E - KSR			True/False
	;		H - Freq. Multiplier [0..15]
	;		L - Key scale level [0..3]
	;		B'- Output level / Total level [0..63]
	;		C'- Attack Rate [0..15]
	;		D'- Decay Rate  [0..15]
	;		H'- Sustain level [0..15]
	;		L'- Release rate [0..15]
	;		A'- Wave select [0..7]
	ld a,1 	; Slot/Cell 1
	ld b,0	; Tremolo=false
	ld c,0	; Vibrato=false
	ld d,1	; Sustained=true
	ld e,1	; KSR=true
	ld h,1	; Freq. Multiplier=x1
	ld l,3	; KSL=3
	exx
	ld b,32	; OutputLevel or TotalLevel=32 (intermediate level)
	ld c,7	; Attack Rate = 7 (intermediate value)
	ld d,7	; Decay Rate = 7 (intermediate value)
	ld h,7	; Sustain level = 7 (intermediate value)
	ld l,7	; Release rate = 7 (intermediate value)
	exx
	ex af,af'
	ld a,0	; Wave Select = 0 Sinusoide (default)
	ex af,af'
	call set_moonsound_cell_patch

	ld a,4 	; Slot/Cell 4
	ld b,0	; Tremolo=false
	ld c,0	; Vibrato=false
	ld d,1	; Sustained=true
	ld e,1	; KSR=true
	ld h,1	; Freq. Multiplier=x1
	ld l,3	; KSL=3
	exx
	ld b,32	; OutputLevel or TotalLevel=32 (intermediate level)
	ld c,7	; Attack Rate = 7 (intermediate value)
	ld d,7	; Decay Rate = 7 (intermediate value)
	ld h,7	; Sustain level = 7 (intermediate value)
	ld l,7	; Release rate = 7 (intermediate value)
	exx
	ex af,af'
	ld a,0	; Wave Select = 0 Sinus wave (default)
	ex af,af'
	call set_moonsound_cell_patch
	
;
; start FM channel
;
;	Input:
;		A	- Channel
;		BC	- f_number (B = f_number >> 8, C = f_number & 0xFF)
;		D	- Block
	ld a,0
	ld bc,345
	ld d,3
	call start_fm_moonsound_channel
	
; Press ESC to exit
test_esc:
	ld b,7
	call read_row_keyboard_status
	bit ESC_KEY,a
	jr nz,test_esc
	
main_end:
	ret
	
;
; Moonsound detect
;
;	Input: None
;
;	Output: A != 0 if moonsound is detected
;           A  = 0 otherwise
moonsound_detect PROC
local error_msg
	in a,(MOONSOUND_STATUS_REG_PORT)
	inc a
	ret nz	; if a = 0xFF after read port then we'll print a message error
	ld c,DOS_STRING_OUTPUT
	ld de,error_msg
	call DOS
	ret
	
error_msg db "Moonsound not found",13,10,"$"
ENDP

;
; Moonsound initialization
;
; Input:
; Reg B = four_op_mask
; Reg C = deep_tremolo 1 bit (True or False)
; Reg D = deep_vibrato 1 bit (True or False)
;
moonsound_init PROC
local tremolo_disabled, vibrato_disabled

	push bc
	ld a,0x01
	ld b,0x20
	call write_moonsound_fm1_register	; ms_fm1_write(0x01, 0x20);
	
	ld a,0x05
	ld b,0x03
	call write_moonsound_fm2_register	; ms_fm2_write(0x05, 0x03);
	
	pop bc
	ld a,b
	and 0x3F	; four_op_mask & 0x3F
	ld b,a
	ld a,0x04
	call write_moonsound_fm2_register	; ms_fm2_write(0x04, four_op_mask & 0x03
	
	ld a,c
	or a
	jr z,tremolo_disabled
	ld c,1 << 7	; (deep_tremolo << 7)

tremolo_disabled:
	ld a,d
	or a
	jr z,vibrato_disabled
	ld d,1 << 6	; (deep_vibrato << 6)
	
vibrato_disabled:
	ld a,c
	or d	; (deep_tremolo << 7) | (deep_vibrato << 6)
	ld b,a
	ld a,0xBD
	call write_moonsound_fm1_register	; ms_fm1_write(0xBD, (deep_tremolo << 7) | (deep_vibrato << 6));
	
	ld a,0x02
	ld b,0x11
	call write_moonsound_wav_register	; ms_wave_write(0x02, 0x11);
	ret
ENDP

;
; Detect RAM
;
; Input: None
;
; Output: A Number of 64KBytes banks detected
;
moonsound_detect_ram PROC
local outer_loop, inner_loop, inner_loop_continue, end_proc

	ld a,0x02
	ld b,0x11
	call write_moonsound_wav_register	; set custom sample headers are not relevant.
										; CPU READ/WRITE mode enabled
	
	ld b,0
	
outer_loop:
	ld c,b	; c = copy of b for counter
	
	; Bank b write process
	ld a,b
	add a,0x20
	ld b,a
	ld a,MOONSOUND_MEMORY_HIGH
	call write_moonsound_wav_register; ms_wave_write(0x03, b + 0x20);
	
	ld a,MOONSOUND_MEMORY_MID
	ld b,0
	call write_moonsound_wav_register; ms_wave_write(0x04, 0);
	
	ld a,MOONSOUND_MEMORY_LOW
	ld b,0
	call write_moonsound_wav_register; ms_wave_write(0x05, 0);
	
	ld a,c
	add a,123
	ld b,a
	ld a,MOONSOUND_DATA_REGISTER
	call write_moonsound_wav_register; ms_wave_write(0x06, b + 123);
	
	; Bank b read check process
	ld a,c
	add a,0x20
	ld b,a
	ld a,MOONSOUND_MEMORY_HIGH
	call write_moonsound_wav_register; ms_wave_write(0x03, b + 0x20);
	
	ld a,MOONSOUND_MEMORY_MID
	ld b,0
	call write_moonsound_wav_register; ms_wave_write(0x04, 0);
	
	ld a,MOONSOUND_MEMORY_LOW
	ld b,0
	call write_moonsound_wav_register; ms_wave_write(0x05, 0);

	ld a,MOONSOUND_DATA_REGISTER
	call read_moonsound_wav_register
	sub 123
	cp c	; ms_wave_read(6) != c + 123
	jr z,inner_loop
	
	; Returning number of iteration in Reg. A
	ld a,c
	ret		
	
inner_loop:
	ld d,b	; for (d = b; d >= 0; d--)
	
	ld a,d
	add a,0x20
	ld b,a
	ld a,MOONSOUND_MEMORY_HIGH
	call write_moonsound_wav_register; ms_wave_write(0x03, b + 0x20);
	
	ld a,MOONSOUND_MEMORY_MID
	ld b,0
	call write_moonsound_wav_register; ms_wave_write(0x04, 0);
	
	ld a,MOONSOUND_MEMORY_LOW
	ld b,0
	call write_moonsound_wav_register; ms_wave_write(0x05, 0);
	
	ld a,MOONSOUND_DATA_REGISTER
	call read_moonsound_wav_register
	sub 123
	cp d	; ms_wave_read(6) != d + 123
	jr nz,inner_loop_continue

inner_loop_continue:
	ld b,d
	dec b
	jp p,inner_loop

	ld b,c
	inc b
	cp 32
	jr nz,outer_loop
	
end_proc:
	ld a,b
	ret
ENDP

;
; set FM cell patch
;
;	Input:
;		A - Cell
;		B - Tremolo		True/False
;		C - Vibrato		True/False
;		D - Sustained 	True/False
;		E - KSR			True/False
;		H - Freq. Multiplier [0..15]
;		L - Key scale level [0..3]
;		B'- Output level / Total level [0..63]
;		C'- Attack Rate [0..15]
;		D'- Decay Rate  [0..15]
;		H'- Sustain level [0..15]
;		L'- Release rate [0..15]
;		A'- Wave select [0..7]
;
;	Remarks:
;		The size of this routine can be reduced.
set_moonsound_cell_patch PROC
local cellNumberLessOrEqual18, cellNumberGreaterThan18, continue
	push af	; Saving cell number
	sla b
	sla b
	sla b
	sla b
	sla b
	sla b
	sla b	; Tremolo << 7
	
	sla c
	sla c
	sla c
	sla c
	sla c
	sla c	; Vibrato << 6
	
	sla d
	sla d
	sla d
	sla d
	sla d	; Sustained << 5
	
	sla e
	sla e
	sla e
	sla e	; KSR << 4
	
	ld a,h
	and 0x0F	; Freq. Multiplier & 0x0F
	
	or b
	or c
	or d
	or e	
	ld b,a	; B = (Tremolo << 7)|(Vibrato<<6)|(Sustained<<5)|(KSR<<4)|(Freq.Multiplier & 0x0F)
			; Reg. C, D, E y H libres
	
	sla l
	sla l
	sla l
	sla l
	sla l
	sla l	; Key Scale Level << 6
	
	exx
	ld a,b	; Output Level / Total Level
	exx
	or l	
	ld c,a	; C = (Key Scale Level << 6) | (Output Level & 0x3F)
			; Reg. D, E, H, L y B' libres
	
	exx
	ld a,d
	and 0x0F; Decay Rate & 0x0F
	sla c
	sla c
	sla c
	sla c
	or c
	exx
	ld d,a	; D = (Attack Rate << 4) | (Decay Rate & 0x0F)
			; Reg. E, H, L, B', C' y D' libres
	exx
	ld a,l
	and 0x0F
	sla h
	sla h
	sla h
	sla h
	or h
	exx
	ld e,a 	; E = (Sustain Level << 4) | (Release Rate & 0x0F)
			; Reg. H, L, B', C', D', H' y L' libres
			
	ex af,af'
	and 0x07

	ld h,a	; H = wave select & 0x07

	pop af
	cp 18
	jr nc,cellNumberGreaterThan18
	
	; Resumen
	; B = (Tremolo << 7)|(Vibrato<<6)|(Sustained<<5)|(KSR<<4)|(Freq.Multiplier & 0x0F)
	; C = (Key Scale Level << 6) | (Output Level & 0x3F)
	; D = (Attack Rate << 4) | (Decay Rate & 0x0F)
	; E = (Sustain Level << 4) | (Release Rate & 0x0F)
	; L   free
	; AF' free
	; BC' free
	; DE' free
	; HL' free
cellNumberLessOrEqual18:
	dec a
	ld l,a
	
	add a,0x20
	; Reg B already loaded
	call write_moonsound_fm1_register
	
	ld a,l
	add a,0x40
	ld b,c
	call write_moonsound_fm1_register
	
	ld a,l
	add a,0x60
	ld b,d
	call write_moonsound_fm1_register
	
	ld a,l
	add a,0x80
	ld b,e
	call write_moonsound_fm1_register
	
	ld a,l
	add a,0xE0
	ld b,h
	jp write_moonsound_fm1_register
	;ret	; Implicit
	
cellNumberGreaterThan18:
	sub 19
	ld l,a
	
	add a,0x20
	; Reg B already loaded
	call write_moonsound_fm2_register
	
	ld a,l
	add a,0x40
	ld b,c
	call write_moonsound_fm2_register
	
	ld a,l
	add a,0x60
	ld b,d
	call write_moonsound_fm2_register
	
	ld a,l
	add a,0x80
	ld b,e
	call write_moonsound_fm2_register
	
	ld a,l
	add a,0xE0
	ld b,h
	jp write_moonsound_fm2_register
	;ret	; Implicit
ENDP

;
; set FM channel parameters
;	
;	Input:
;		A - Channel
;		B - Right
;		C - Left
;		D - Additive synthesis
;		E - Feedback
;
;	Output:
;		All registers destroyed except D, HL
set_moonsound_fm_channel PROC
local channelNumberLessOrEqual8, channelNumberGreaterThan8

	ex af,af'	; Saving Reg. A content in shadow register
	
	sla b
	sla b
	sla b
	sla b
	sla b	; right << 5
	
	sla c
	sla c
	sla c
	sla c	; left << 4

	ld a,e
	and 0x07
	sla a
	ld e,a	; (feedback & 0x07) << 1
	
	ld a,d
	and 0x01; additive_synthesis & 0x01	
	
	or b
	or c
	or e

	ld b,a	; B = (right << 5) | (left << 4) | ((feedback & 0x07) << 1) | (additive_synthesis & 0x01)
	
	ex af,af'	; Recovering Reg. A from shadow register
	cp 8
	jr nc,channelNumberGreaterThan8
	
channelNumberLessOrEqual8:
	add a,0xC0	; Yamaha 278B OPL4 FM Algorithm Register
	; Reg. B already contains value
	jp write_moonsound_fm1_register	; Implicit call
	;ret	
	
channelNumberGreaterThan8:
	add a,0xC0 - 9
	; Reg. B already contains value
	jp write_moonsound_fm2_register	; Implicit call
	;ret							; Implicit ret
ENDP

;
; start FM channel
;
;	Input:
;		A	- Channel
;		BC	- f_number (B = f_number >> 8, C = f_number & 0xFF)
;		D	- Block
;
;	Output:
;		All registers destroyed
start_fm_moonsound_channel PROC
local channelNumberLessOrEqual8, channelNumberGreaterThan8
	ld h,b		; Saving B register
	ld l,a		; Saving A register
	cp 8
	jr nc,channelNumberGreaterThan8
	
channelNumberLessOrEqual8:
	add a,0xA0
	ld b,c		; B = f_number & 0xFF
	call write_moonsound_fm1_register

	ld a,d		
	and 0x07
	add a,a
	add a,a		; A = (block & 0x07) << 2
	or h
	or 0x20		; A = 0x20 | ((block & 0x07) << 2) | (f_number >> 8)
				; 0x20 is Key On
	ld b,a		; B = 0x20 | ((block & 0x07) << 2) | (f_number >> 8)
	
	ld a,l
	add a,0xB0	
	jp write_moonsound_fm1_register	; Implicit call
	;ret							; Implicit ret
	
channelNumberGreaterThan8:
	add a,0xA0 - 9	; Yamaha 278B OPL4 FM Algorithm Register
	ld b,c		; B = f_number & 0xFF
	call write_moonsound_fm2_register

	ld a,d
	and 0x07
	add a,a
	add a,a		; A = (block & 0x07) << 2
	or h
	or 0x20		; A = 0x20 | ((block & 0x07) << 2) | (f_number >> 8)
	ld b,a		; B = 0x20 | ((block & 0x07) << 2) | (f_number >> 8)
	
	ld a,l
	add a,0xB0 - 9
	jp write_moonsound_fm2_register	; Implicit call
	;ret							; Implicit ret
ENDP

;
; stop FM channel
;
;	Input:
;		A	- Channel
;
;	Output:
;		Reg. A, B destroyed
stop_fm_moonsound_channel PROC
local channelNumberLessOrEqual8, channelNumberGreaterThan8

	cp 8
	jr nc,channelNumberGreaterThan8

channelNumberLessOrEqual8:
	add a,0xB0
	ld b,0
	jp write_moonsound_fm1_register	; Implicit call
	; ret							; Implicit ret
	
channelNumberGreaterThan8:
	add a,0xB0 - 9
	ld b,0
	jp write_moonsound_fm2_register	; Implicit call
	; ret							; Implicit ret
ENDP

;
; Select wave
;
;	Input:
;		A 	- Channel
;		BC 	- Number of wave (B = wave >> 8, C = wave & 0xFF)
;
;	Output:
;		Reg. A, B, L destroyed
select_moonsound_wave PROC
	ld l,a
	add a,0x20
	call write_moonsound_wav_register
	ld a,l
	add a,0x08
	ld b,c
	jp write_moonsound_wav_register	; Implicit call
	;ret							; Implicit ret
ENDP

;
; Moonsound configure envelope of the channel ADSR
;
;	Input:
;		A - channel number
;		B - lfo rate		[0..7]
;		C - vibrato depth	[0..7]
;		D - tremolo depth	[0..7]
;		E - attack rate		[0..15]
;		H - decay1 rate		[0..15]
;		L - decay level		[0..15]
;		H'- decay2 rate		[0..15]
;		L'- release rate	[0..15]	
;
;	Output:
;		All registers destroyed
configure_moonsound_channel PROC
	ex af,af'
	ld a,b
	and 0x07
	add a,a
	add a,a
	add a,a
	ld b,a		; B = (lfo_rate & 0x07) << 3
	
	ld a,c
	and 0x07	; A = vibrato_depth & 0x07
	or b		
	ld b,a		; B = ((lfo_rate & 0x07) << 3) | (vibrato_depth & 0x07)
	
	ex af,af'
	ld c,a		; El registro C ya no lo necesitamos. Guardamos una copia de A en él.
	add a,0x80
	call write_moonsound_wav_register
				; ms_wave_write(0x80 + channel, ((lfo_rate & 0x07) << 3) | (vibrato_depth & 0x07))
				
	ld a,e
	and 0x0F
	add a,a
	add a,a
	add a,a
	add a,a
	ld e,a		; E = (attack_rate & 0x0F) << 4
	ld a,h
	and 0x0F
	or e		
	ld b,a		; B = ((attack_rate & 0x0F) << 4) | (decay1_rate & 0x0F)
	ld a,c
	add a,0x98
	call write_moonsound_wav_register
				; ms_wave_write(0x98 + channel, ((attack_rate & 0x0F) << 4) | (decay1_rate & 0x0F))
				
	ld a,l
	and 0x0F
	add a,a
	add a,a
	add a,a
	add a,a
	ld l,a		; L = (decay_level & 0x0F) << 4
	exx
	ld a,h 		; A = H'
	exx
	and 0x0F
	or l
	ld b,a
	ld a,c
	add a,0xB0
	call write_moonsound_wav_register
				; ms_wave_write(0xB0 + channel, ((decay_level & 0x0F) << 4) | (decay2_rate & 0x0F))
				
	exx
	ld a,l		; A = L'
	exx
	and 0x0F
	or 0xF0
	ld b,a		; B = (release_rate & 0x0F) | 0xF0
	ld a,c
	add a,0xC8
	call write_moonsound_wav_register
				; ms_wave_write(0xC8 + channel, (release_rate & 0x0F) | 0xF0)
	ld a,d
	and 0x07
	ld b,a		; B = tremolo_depth & 0x07
	ld a,c
	add a,0xE0
	jp write_moonsound_wav_register	; Implicit call
				; ms_wave_write(0xE0 + channel, tremolo_depth & 0x07)
	;ret
ENDP

;
; Configure no envelope
;
;	Input:
;		A - channel
;
;	Output:
;		Reg. A, L destroyed
configure_moonsound_channel_noenvelope PROC
	ld l,a	; Saving copy of A
	
	add a,0x80
	ld b,0
	call write_moonsound_wav_register
	
	ld a,l
	add a,0x98
	ld b,0xF0
	call write_moonsound_wav_register
	
	ld a,l
	add a,0xB0
	ld b,0xFF
	call write_moonsound_wav_register
	
	ld a,l
	add a,0xC8
	;ld b,0xFF		; Not needed
	call write_moonsound_wav_register
	
	ld a,l
	add a,0xE0
	ld b,0
	jr write_moonsound_wav_register	; Implicit call
	; ret
ENDP

;
; Start Wave Channel
;
;	Input:
;		A  - Channel number
;		BC - f number
;		D  - octave (signed char)
;		E  - volume
;		H  - pan
;
;	Output:
;		A, B, D, H, L destroyed
start_wave_moonsound_channel PROC
	ld l,a	; Saving copy of reg. A
	ld a,h
	ex af,af'	; salvando en Reg. A shadow el valor de pan
	ld a,d
	and 0x0F
	add a,a
	add a,a
	add a,a
	add a,a
	ld d,a	; D = (octave & 0x0F) << 4
	
	ld a,c
	add a,a
	ld h,a	; EEYYY!!! estamos machacando el valor de pan (pero está controlado), H = f_number << 1

	ld a,b
	;and 00000011b	; Perhaps not needed when reg. B is in range
	add a,a
	sla c
	rla				; A = f_number bits 9, 8 and 7
	or d			; A = (octave & 0x0F) << 4 | (fnumber >> 7) & 0x07
	ld b,a
	ld a,l
	add a,0x38
	call write_moonsound_wav_register
					; ms_wave_write(0x38 + channel, ((octave & 0x0F) << 4) |
					;								((fnumber >> 7) & 0x07)
	
	ld a,l
	add a,0x20
	call read_moonsound_wav_register
	and 0x01
	or h
	ld b,a			; B = (ms_wave_read(0x20 + channel) & 0x01) | (f_number << 1)
	ld a,l
	add a,0x20
	call write_moonsound_wav_register
					; ms_wave_write(0x20 + channel, (ms_wave_read(0x20 + channel) & 0x01) |
					;								(fnumber << 1) & 0xFE
	ld a,e
	or 0x01			; 0x01: LEVEL DIRECT (immediate volume change)
	ld b,a
	ld a,l
	add a,0x50
	call write_moonsound_wav_register
					; ms_wave_write(0x50 + channel, volume | 0x01)

	ex af,af'		; A = pan
	or 0x80 | 0x20
	ld b,a			; B = 0x80 | 0x20 | pan
	ld a,l
	add a,0x68
	; ms_write(0x68 + channel, 0x80 | 0x20 | pan)
	jr write_moonsound_wav_register	; Implicit call
	;ret							; Implicit ret
ENDP

;
; Stop Wave Channel
;
;	Input:
;		A  - Channel number
;
;	Output:
;		Reg. A destroyed
stop_wave_moonsound_channel PROC
	add a,0x68
	ld b,0x40
	jr write_moonsound_wav_register	; Implicit call
	;ret							; Implicit ret
ENDP

;
; Wave wait for ready after wave select
;
;	Input:
;		None
;
;	Output:
;		None
wave_wait_for_ready_after_channel_selection PROC
	in a,(MOONSOUND_STATUS_REG_PORT)	
	and 0x02
	jr z,wave_wait_for_ready_after_channel_selection
	ret
ENDP

;
; set Moonsound memory in CPU Mode
;
set_moonsound_cpu_mode PROC
	ld a,MOONSOUND_REG_MEMORY
	ld b,0x11; copiando programa de test de RAM
	call write_moonsound_wav_register
	ret
ENDP
	
;
; set Moonsound memory for music access only
;
set_moonsound_music_mode PROC
	ld a,MOONSOUND_REG_MEMORY
	ld b,0x10; copiando programa de test de RAM
	call write_moonsound_wav_register
	ret
ENDP	
	
;
; wait_not_busy
;
wait_moonsound_not_busy PROC
	in a,(MOONSOUND_STATUS_REG_PORT)
	rra
	jr c,wait_moonsound_not_busy
	ret
ENDP
	
;
; write_moonsound_fm1_register
;
;	Input:
; 		A = register number
;   	B = value
;
;	Output:
;		A destroyed
write_moonsound_fm1_register PROC
	out (MOONSOUND_FM_BANK1_REG_PORT),a
	call wait_moonsound_not_busy
	ld a,b
	out (MOONSOUND_FM_DATA1_PORT),a
	ret
ENDP

;
; write_moonsound_fm2_register
;
;	Input:
; 		A = register number
;   	B = value
;
;	Output:
;		A destroyed
write_moonsound_fm2_register PROC
	out (MOONSOUND_FM_BANK2_REG_PORT),a
	call wait_moonsound_not_busy
	ld a,b
	out (MOONSOUND_FM_DATA2_PORT),a
	ret
ENDP
	
;
; read_moonsound_wav_register
;
;	Input:
;		A = register number
;
;	Output:
;		A = value
read_moonsound_wav_register PROC
	out (MOONSOUND_WAVE_REG_PORT),a
	call wait_moonsound_not_busy
	in a,(MOONSOUND_WAVE_DATA_PORT)
	ret
ENDP

;
; write_moonsound_wav_register
;
;	Input:
; 		A = register number
;   	B = value
;
;	Output:
;		A destroyed
write_moonsound_wav_register PROC
	out (MOONSOUND_WAVE_REG_PORT),a	
	call wait_moonsound_not_busy
	ld a,b; moving value from B to A register in order to write
	out (MOONSOUND_WAVE_DATA_PORT),a
	ret
ENDP

;include "print.asm"
include "keyboard.asm"

By Juan Luis

Resident (46)

Juan Luis's picture

26-02-2019, 18:26

Uff, I have found the problema. FM2 data port is wrong. FM2 data port is the same that FM1 data port. I was able to listen my first tone with synthesis FM.

Thanks to everybody for the information provided. It was very helpful.

By Latok

msx guru (3672)

Latok's picture

26-02-2019, 19:47

yzi wrote:

If you just want the sample playing part of it, try looking at my SootSound project

https://sourceforge.net/p/sootsound/

MOD/XM style music player
https://sourceforge.net/p/sootsound/code/ci/master/tree/ltmp...

MoonSound routines
https://sourceforge.net/p/sootsound/code/ci/master/tree/moonsound.c

yzi, I am stupid but still want to try sootsound. Could you please make an executable for me so I can convert an XM-file to a sootsound playable file? Is the *.dsk file which is available under 'Files' the player that I can start in MSXDOS? So no need to compile that?