I am trying to implement background music by following the advice in this thread. By using a short piece of music I wrote some time ago with Vortex Tracker 1.2 RCD and a starter code modified by Jerri, I managed to start the music with the appropriate ASM commands placed in the Game Inizialization event.
However, instead of playing in the background, the music just plays as soon as all bits of code - game, starter, player and music - have been loaded, and stops when a key is pressed.
How can I make the music play while the game runs? I suppose I should modify the starter code and use interrupt mode 2 but due to my sketchy knowledge of machine code I was unable to find a solution because I did not yet understand where to exactly put the IM2 code in and how to recall the music starter from there. Here is the starter code (in this example, the VT2 player is located at 45000):
org 49968 LD A,1 LD (45000+10),A CALL 45000 EI LOOP: HALT CALL 45000+5 ;test key xor a in a,(#fe) cpl and #1f ;if key pressed jr nz,end_this
LD A,(45000+10) RLA JR NC,LOOP end_this call 45000+8 RET
Should someone provide me with some commented sample code I could adapt to my situation, I would be very grateful
Also, will the SILENCE command stop the AY chip playing within the game?
Update. I remembered that code came from a thread on the WOS forum. It is still here.
I tried using this routine by recalling it from the AGD game. I inserted the following lines in the Game Initialization event:
ASM 205 ASM 242 ASM 173
and this is the code I recalled, with some tweaking (the registry PUSH and POP part was somewhat messed up). Unfortunately it does not work, the game plays the first note of the tune and then resets to the old 1982 screen, with the note still playing. I also tried substituting the RST 56 instruction with
ld hl,23672 inc (hl)
but nothing changed.
im2_enable di call 45000 ;init music ; make table #fd (257 items) on #fe00 ld hl,#fe00 ld a,h ld i,a im 2 dec a ld (hl),a inc l jr nz,$-2 inc h ld (hl),a ;make interrupt executor ;#fdfd jp im2_proc
ld l,a ld h,a ld de,im2_proc
ld (hl),#c3 inc l ld (hl),e inc l ld (hl),d ei ret
im2_disable di call 45000+8 ;mute music ld a,#3f ld i,a im 1 ei ret
im2_proc ;interrupt routine di ;store all registers push af push hl push bc push de push ix push iy exx ex af,af' push af push hl push bc push de
call 45000+5 ;play music rst 56
pop de pop bc pop hl pop af ex af,af' exx pop iy pop ix pop de pop bc pop hl pop af
Last Edit: Sept 6, 2015 20:14:54 GMT by alessandro
The problem was that the IM2 vector table was put at 65024, thus interfering with the dummy collision map area, which takes the last 768 bytes of RAM. By setting the table at 64256 (251*256) everything went fine.
Of course a set of ASM instructions in both the Lost Game and Completed Game events, pointing at the im2_disable part (located at 44652 in this example) was also needed in order to silence the AY chip.
Another update (I hope there is someone around interested in this...). I decided to play it safe by putting the IM2 vector table at 64256; in later experiments I put it at 64512 (252*256) - which is the highest available location when using running a game authored with AGD, because the last 768 bytes of RAM (from 64768 to 65535) must be reserved for the collision map area - and the AY music was heard during game play as expected.
I would also like to underline that:
the above code marked by the im2_disable label is also needed to enable interrupts again on returning either to BASIC or to the previous MC instructions - for instance, to go back to the external game management program;
as stated in the AGD manual, if you employ the EFFECTS specialization, make sure to leave a reserved area of 300 bytes after the end of the game code for the particle engine; if you try to write other data on it the game will crash right at the start.
Post by andrewvanbeck on Nov 19, 2015 11:46:53 GMT
Ditto retrophase - this is all useful info and any insight that Alessandro can share I'm interested in. I'd love to have in-game AY music as an option, even just knowing it's possible can be enough to fuel the work involved in figuring it out.
If you could post the actual code that worked, and which address in speccy memory to put the music, that would be great!
The working player calling code is already there. The actual address of the player calling code - and therefore of the player and music code exported from Vortex Tracker II - varies according to the end of the game code, plus 300 bytes more if your game features the PARTICLE specialization.
E.g. if your AGD game code begins at 31000 and is 24000 bytes long, you must assemble the player calling code above 55000. With the PARTICLE specialization, the limit is raised to 55300.
Since the routine above is 83 bytes long, you should also export the player and music code from VT2 above, say, 55385. Of course you should vary the CALL instructions in the code accordingly.
Last Edit: Apr 14, 2016 12:29:38 GMT by alessandro
Another example. This is the code which calls the music player for the first and third levels of Seto Taisho.
PLAYER EQU 52055 ; music player address (as exported from VT2)
ORG 51968 ; address of player calling code (must be inserted as ASM commands in the Game Initalisation event)
im2_enable: ; initializes interrupts di call PLAYER ; recalls music player ld hl,64512 ; creates interrupt table (256 bytes) at the specified address; must not exceed 64768 (AGD sprite buffer) ld a,h ld i,a im 2 dec a ld (hl),a inc l jr nz,$-2 inc h ld (hl),a ld l,a ; presets music playback ld h,a ld de,im2_proc ; loads execution part (im2_proc) ld (hl),195 inc l ld (hl),e inc l ld (hl),d ei ret
im2_disable: ; (ORG+33) disables music - point here in the Lost Game and Completed Game events di call PLAYER+8 ; AY chip is turned off ld a,63 ld i,a im 1 ei ret
im2_proc: ; start of interrupt routines di push af ; stores registers push hl push bc push de push ix push iy exx ex af,af' push af push hl push bc push de call PLAYER+5 ; music playback ld hl,23672 inc (hl) ; keyboard input is read pop de ; restores registers pop bc pop hl pop af ex af,af' exx pop iy pop ix pop de pop bc pop hl pop af ei reti
I've written a couple of tracks in Beepola before and it would be great to understand how to call the various players as part of AGD projects. However I am starting from the point where I don't know Z80. One of the simpler Beepola engines has this note info:
"Fixed note lengths, and the fact that it does not use an Interrupt Service Routine mean that this tone generator is well suited for producing 2 channel in-game music. The "play next note" routine call be called at regular intervals from within a main game loop, or from an IM2 interrupt routine, to achieve song playback while a game or other code is running."
Those in the know .. does this sound easier/simpler to get something basic going? I was interested in the "within main game loop" part vs IM2 stuff. Ideally we'd end up with a simple dummy-guide tutorial to learn from, covering everything from exporting the track in Beepola (or other), to writing the BASIC loader code, to any Z80 stuff and AGD calls one step at a time I know.
I've been trying to piece this together, and I think I'm getting close. I have the ASM call in the intro/menu code and also a delay .. when I start the game, I hear the first few notes (yay!) but once the delay runs out and it loads up the first level/screen, it crashes. Any ideas?
I am using the example code above, assembled using spin, I'm using the demo track back2mystyle from VT, and I've assembled the loader code and tap in ZX Editor.
Here is the loader
1 REM 2 PAPER 7: INK 0 5 CLEAR 31097 10 LOAD ""CODE 31098,6532 <-- this is the saved AGD file 20 LOAD ""CODE 51968,83 <-- this is the .asm example code above 30 LOAD ""CODE 52055,1617 <-- this is vtplayer (exported at 52055) 40 LOAD ""CODE 54213,1766 <-- this is the demo tune 50 RANDOMIZE USR 32000 In INTRO/MAIN I have
ASM 205 ASM 0 ASM 203 DELAY 100 As 51968/256 = 203 R 0
In screen 0, I have a couple of sprites bounding around indefinitely. It feels so close, and has been quite fun getting to this stage.
1 REM 2 PAPER 7: INK 0 5 CLEAR 31097 10 LOAD ""CODE 31098,6532 <-- this is the saved AGD file 20 LOAD ""CODE 51968,83 <-- this is the .asm example code above 30 LOAD ""CODE 52055,1617 <-- this is vtplayer (exported at 52055) 40 LOAD ""CODE 54213,1766 <-- this is the demo tune 50 RANDOMIZE USR 32000
You do not have to load all of the code parts separately each time. You can load them under emulation (in 48K emulation mode for compatibility reasons) and then save the whole lot of code all at once by creating a new virtual tape file (TAP or TZX), then entering:
SAVE "filename" CODE a,b
where a is the address of the first part of code and b is the total code length, obtained by adding the address of the last block of code to its length and subtracting this sum from the address of the first block. In your specific case:
SAVE "filename" CODE 31098,24881
because 54213 + 1766 - 31098 = 24881.
Going further, if you compress the code with a good compressing algorithm like ZX7 and load the block without the header or even by implementing a turbo loading scheme, making use of a simple custom M/C loading routine in both cases, the game will load much faster on a real machine.