Music - How to code music in ASM with (or without) BIOS routines ?

By granced

Supporter (6)

granced's picture

28-04-2010, 16:13

Hi to all,

I'm a newbie in ASM, and I'm trying to understand how my MSX works !!
My problem here is that wherever I go, I found NOTHING on how to code music in ASM, using or not the BIOS routines... oO

Can anyone here teach me how to do ?

I guess I'll have to use routines such as GICINI or STRTMS, but I don't really understand how to use them... How is the music coded in the queues ?

Thanx a lot !!

Login or register to post comments

By PingPong

Prophet (3889)

PingPong's picture

28-04-2010, 21:49

the same as the format of play statement in basic. 32 bytes per channel

By Edwin

Paragon (1182)

Edwin's picture

28-04-2010, 21:59

It's not actually the easiest thing to use for music. You'd be better off using some of the available replayers. Even making one yourself is not actually that difficult. The PSG doesn't exactly have a lot of features.

By ARTRAG

Enlighted (6844)

ARTRAG's picture

28-04-2010, 22:29

use this
http://z80st.auic.es/ficheros/ayFX-replayer-1.2.rar
and this
http://z80st.auic.es/downloads/PT3-ROM4asMSX.rar

The pt3 format has a very good editor
Also the AyFX has a good editor
You cannot ask anything better in asm form msx (for psg only machines)

By NYYRIKKI

Enlighted (5918)

NYYRIKKI's picture

29-04-2010, 00:00

Here is one way to start thinking of own music replayer routine:

STEP 1: Make multitasking music replayer in BASIC:
----------------------------------------------------------------

10 INTERVAL ON
20 ON INTERVAL=20 GOSUB 100 ' (TEMPO)
30 '"Demo"
40 IF T=0 OR T=30 THEN K=2-K
50 T=T+K-1:LOCATET:PRINT"-=-";
60 IF STRIG(0)=0 THEN 40
70 INTERVAL OFF:BEEP
80 END
90 ' Interrupt routine
100 READ A
110 IF A=254 THEN RESTORE:GOTO 100
120 IF A=255 THEN RETURN
130 READ E
140 SOUND A,E
150 GOTO 100
160 ' Music data
170 DATA 0,0, 1,1
180 DATA 8,15,255
190 DATA 1,2,8,11,255,255
200 DATA 8,0,255,255,255
210 DATA 254


STEP 2: Translate music routines to Assembler:
------------------------------------------------------------




TIMI	  EQU	0FD9FH		;timer interrupt hook
JPCODE    EQU	0C3H
INTERVAL  EQU   20              ; 20 ON INTERVAL=20 GOSUB 100 ' (TEMPO)



	db $fe
	dw beginProgram,end,startProgram

	org $B000
beginProgram:

        ; These "entry points" are used by BASIC loader

	JP INTON  ; INTERVAL = ON  (Address #B000)
	JP INTOFF ; INTERVAL = OFF (Address #B003)

startProgram:
        RET  ; Don't do anything when BLOAD "",R

;----- interrupt on ----- Note:  restore the former hook
				 ;when changing the hook

INTON:

        ;10 INTERVAL ON

	LD	HL,TIMI 	;OLD HOOK SAVE
	LD	DE,HKSAVE
	LD	BC,5
	LDIR

	LD	A,JPCODE	;NEW HOOK SET
	DI
	LD	(TIMI),A
	LD	HL,INT
	LD	(TIMI+1),HL
	EI
	RET


;------ Some internal variables... look forward

TEMP:    DB INTERVAL
POINTER: DW DATA

;----- interrupt routine -----


        ;20 ON INTERVAL=20 GOSUB 100 ' (TEMPO)


INT:	PUSH	AF  ; This routine is called 50 or 60 times / sec

        LD A,(TEMP) ; ~=  TEMP=TEMP-1:IF TEMP=0 THEN GOSUB 100: TEMP=20
        DEC A
        LD (TEMP),A
        JR NZ,.EXIT ; Is it time to make "GOSUB" ? No -> continue what you were doing...
        CALL LINE100  ; Yes -> execute effect
        LD A,INTERVAL ; How often the effect on "start" should be called (20 in this example)
        LD (TEMP),A
.EXIT
 	POP	AF
HKSAVE:
	NOP			;old HOOK save area
	NOP
	NOP
	NOP
	RET
	
	

;----- interrupt off ----- Note:  restore the reserved hook and exit

INTOFF:
        ; 70 INTERVAL OFF:BEEP

	LD	HL,HKSAVE
	LD	DE,TIMI
	LD	BC,5
	DI
	LDIR
	EI  
	CALL $90 ; resetta canale audio
	RET

        ; 90 ' Interrupt routine

LINE100:
        LD HL,(POINTER)
        LD A,(HL)              ; 100 READ A
        INC HL                 ; =~  A=PEEK(POINTER):POINTER=POINTER+1
        LD (POINTER),HL

        CP 254                 ;110 IF A=254 THEN RESTORE:GOTO 100
        JR NZ,.SKIP
        LD DE,DATA
        LD (POINTER),DE
        JR LINE100

.SKIP:                         ;120 IF A=255 THEN RETURN
        CP 255
        RET Z
        
        LD E,(HL)              ;130 READ E
        INC HL
        LD (POINTER),HL
        
        CALL 147               ;140 SOUND A,E
        
        JR LINE100             ;150 GOTO 100
        
DATA:                          ;160 ' Music data
        DB 0,0, 1,1            ;170 DATA 0,0, 1,1
        DB 8,15,255            ;180 DATA 8,15,255
        DB 1,2, 8,11, 255,255  ;190 DATA 1,2, 8,11, 255,255
        DB 8,0, 255,255,255    ;200 DATA 8,0, 255,255,255
        DB 254                 ;210 DATA 254
        
end

STEP 3: Make LOADER by removing music routines and adding entrypoints
---------------------------------------------------------------------------------------------

Loader:
(Same program but without the music part)

10 BLOAD"<filename>"
20 DEFUSR=&HB000:U=USR(0)
30 '"Demo"
40 IF T=0 OR T=30 THEN K=2-K
50 T=T+K-1:LOCATET:PRINT"-=-";
60 IF STRIG(0)=0 THEN 40
70 DEFUSR=&HB003:U=USR(0)
80 END

By nikodr

Paladin (748)

nikodr's picture

29-04-2010, 03:52

NYYRIKKI as always has the solution Smile

OFFtopic:Nyyrikki is there any chance that you could ever rewrite the rom loading program you wrote under symbos to have an exit routine that would restore symbos?I remember asking you about it and you telling me it's not impossible but very difficult and not currently on your plans.IS there any chance that 8kbyte or 16kbyte roms could be loaded and then exit back to symbos?

By granced

Supporter (6)

granced's picture

30-04-2010, 08:21

Thanks a lot for your answers, especially NYYRIKKI's one, very complete.

I don't want to use a tracker/replayer, I just want to understand the process ! Sure that I'm not choosing the easiest way, but I'm not fond on being lazy ! Tongue

If I understand the NYYRIKKI's code, he defines the tempo first, and uses the refresh of the screen combinated with this tempo, and then send a whole bunch of "SOUND" using WRTPSG. Fine, but quite long if you use a longer music, or modify the length of the notes, or even the tempo.

There seems to have other possibilities with the BIOS and the system variables. Example : when you use PLAY "A" in Basic, you can see that VOICAQ,VCBA and other things modified. By reading MSX Red book, I saw that the PLAY statement uses all these things, but I can't see what is done first and how the basic string is coded in VOICAQ... Seems to me that there are 5 bytes per note : The 2 last ones seems to be a copy of registers 0 & 1 of PSG, the third deals with the volume, the second with the length of the note, but I don't know the sense of the first !! Anyone knows ?