Atari ST Music Ripping

Summary

It is possible to use the Power Computing Ultimate Ripper cartridge to rip Mad Max, David Whittaker and Quartet music. The Ultimate Ripper can save these tunes to disk or as an executable with a reference player however the reference player makes the tune Terminate and Stay Resident (TSR) which is not ideal if you wish to stop the music or have full control over it. Their replay routine is not 100% reliable because it didn't work with Speedball by David Whittaker for example.

In this document I intend to list the details on how to rip these tunes without the assistance of the Ultimate Ripper Cartridge to gain an understanding of how these tunes are actually implemented.

Music Replay

Every music format is different but they all contain a play routine and notation. Some composers may have had a chip-tracker, or some may have written the notation and effects using machine code. Most formats have a function to initialize the tune, usually passing the subtune number in register D0. Secondly there is a function to play a small part of the tune every vertical blank.

The music could be played by the following:

1. Tight vertical blank loop. This is ideal if the assembled tune has to be called from STOS BASIC,
2. $70.w Vertical Blank (VBL) interrupt,
3. Using one of _vbl_list vectors ($4CE-$4EA). It is the VBL interrupt routine in TOS that walks through this list.

The main difference between this list and directly using the VBL vector is, that this list is skipped if the vbl is already executing, or blocked by some other code (like the floppy handler which periodically polls the write-protect bit).

Also note that this queue is not hardwired to $4CE: the longword at $456 tells you where it starts, and the word at $454 tells you how many entries it has.

Formats

Mad Max
David Whittaker
Jas C. Brooke
Rob Hubbard

Download

Download my examples here.

Details

Mad Max

One of the most popular composers on the ST was Mad Max (Jochen Hippel). He had a chip-tracker called TFMX (The Final Music eXtension?) which he kept to himself. A lot of tunes were composed using it. It seemed to be able to export the tune including the play routine making it very easy to reuse tunes in demos.

You will need MonST to try this:
1. Load the program into MonST (decompress it first if necessary),
2. Search memory for the string TFMX,
3. Work back in memory to 3 branch instructions (BRA). This is the start of the replay routine,
4. Save the memory from the start of the first branch to 15-30k. You can work out the correct size by successive approximation.

Format Evolution:
Early Mad Max tunes (LCD Demo, BIG Demo, BMX Simulator 2, Union Demo) did not use this format and have to be ripped differently, but it gives you an idea. Some of the early tunes had to be relocated by placing the load base address in the header. Later TFMX tunes used PC-relative code so they could be loaded anywhere.

Replay code:

The general replay routine uses bsr music to initialize the tune and bsr music+8 every VBL.


	clr.w	-(sp)
	move.w	#$20,-(sp)
	trap	#1
	addq.l	#6,sp
	move.l	d0,old_sp

	moveq	#2,d0
	jsr	music

loop:
	jsr	music+8
	jsr	wait_vbl

	cmp.b	#57,$fffffc02
	bne.s	loop

	moveq	#0,d0
	jsr	music

	move.l 	old_sp,-(sp)	; User mode.
	move.w 	#$20,-(sp)
	trap   	#1
	addq.l 	#6,sp


	clr.w	-(sp)
	trap	#1

old_sp	dc.l	0

wait_vbl
	move.w	#37,-(sp)	; Wait vbl
	trap 	#14
	add.l 	#2,sp
	rts

music:
	incbin "c:\dev\musicrip\madmax\5th_gear.dat"


David Whittaker

David Whittaker is also one of the most well-known composers on the ST. He composed music for about 105 games on the ST making a total of over 400 sub-tunes and sound effects.

You will need MonST to try this:
1. Load the program into MonST (decompress it first if necessary),
2. Search memory for the sequence 48E7FFFE. This is the start of the tune. Note the memory address (start).
3. Search memory for the sequence 4E71 (nop). This is the end of the tune. Note the end address (end).
4. Press S to save memory. e.g. c:\a.dat,$4D000,$4E100-$4D000 (MonST allows this to quickly calculate the length)
Save the memory block from start address to the end address including the 48E7FFFE and 4E71 instructions.

Replay code:

The general replay routine uses MOVEQ #tune,D0 and BSR TUNE to start the tune. A recommended method to play the routine is to use the ST's VBL List which are run every vertical blank to replay the next part of the tune. This example changes the subtune when the user presses the + or - key.


	clr.w	-(sp)
	move.w	#$20,-(sp)
	trap	#1
	addq.l	#6,sp
	move.l	d0,old_sp

; save old $4da vector
	lea	$4da,a0
	move.l	(a0),old4da

; music init
	LEA       L0007(PC),A0
	MOVE.L    D0,(A0) 
	ANDI.B    #$FE,$484.W	; clear keyclick
	LEA       L0005(PC),A0
	MOVEQ     #0,D0			; tune number
	MOVE.W    D0,(A0) 
	BSR       L0009 
	LEA       L0009(PC),A0
	ADDA.L    #$E,A0
	LEA       L0006(PC),A1
	MOVE.L    A0,(A1) 
	LEA       L0001(PC),A0
	MOVE.L    A0,$4DA.W

loop:
	BSR	wait_vbl
	cmp.b	#57,$fffffc02
	bne.s	loop

; music off
	lea	$4da,a0
	move.l	old4da,(a0)				; restore old vbl vector

	move.l	#$08000000,$ffff8800.w	; turn off YM
	move.l	#$09000000,$ffff8800.w	;
	move.l	#$0A000000,$ffff8800.w	;

	move.l 	old_sp,-(sp)	; User mode.
	move.w 	#$20,-(sp)
	trap   	#1
	addq.l 	#6,sp

	clr.w	-(sp)
	trap	#1

old4da	dc.l	0
old_sp	dc.l	0

wait_vbl
	move.w	#37,-(sp)	; Wait vbl
	trap 	#14
	add.l 	#2,sp
	rts

L0001:MOVEM.L   A0-A6/D0-D7,-(A7) 
      LEA       L0004(PC),A0
      ADDQ.W    #1,(A0) 
      CMPI.W    #5,(A0) 
      BNE.S     L0003 
      CLR.W     (A0)
      MOVE.B    $FFFFFC02.W,D0
      CMP.B     #$4E,D0 
      BNE.S     L0002 
      LEA       L0005(PC),A0
      ADDQ.W    #1,(A0) 
      MOVE.W    (A0),D0 
      BSR       L0009 
      BRA.S     L0003 
L0002:CMP.B     #$4A,D0 
      BNE.S     L0003 
      LEA       L0005(PC),A0
      MOVE.W    (A0),D0 
      SUBQ.W    #1,D0 
      BMI.S     L0003 
      MOVE.W    D0,(A0) 
      BSR       L0009 
L0003:MOVEA.L   L0006(PC),A0
      JSR       (A0)
      MOVEM.L   (A7)+,A0-A6/D0-D7 
      RTS

L0004:DC.B      $00,$00
L0005:DC.B      $00,$00
L0006:DCB.W     2,0
L0007:DCB.W     2,0
L0008:DCB.W     2,0
L0009:
	incbin	"C:\DEV\MUSICRIP\DWHITT\SB.DAT"


Jas C. Brooke

Jason Brooke (usually written Jas C. Brooke) wrote the tunes for about 20 games on the ST including Outrun, Pac-Land and Buggy Boy. He worked with David Whittaker at Musicon Design.

Outrun

This game has a few interesting features:

1. The tune is loaded using FOPEN, FREAD and FCLOSE.
2. The memory block where it is loaded is overlaid with symbols so that the replay code can tweak variables and reference offsets in the file.

I disassembled Outrun to isolate the tune and replay the music independent of the game code. To make it easier to replay I include the tune into the executable using incbin and copy the tune to the variable section so they can be referenced by name if need be. This uses twice the memory for the tune but makes understanding the code much easier.


	clr.w	-(sp)
	move.w	#$20,-(sp)
	trap	#1
	addq.l	#6,sp
	move.l	d0,old_sp

	MOVE.B	$484.W,old_kc
	ANDI.B	#$FE,$484.W	; clear keyclick
	BSR	LOADTUNE

; INIT
	BSR	INITTUNE
	MOVEQ	#0,D0
	BSR 	DOTUNE

loop:
	BSR	REFRESH
	BSR	wait_vbl
	cmp.b	#57,$fffffc02
	bne.s	loop

; music off
	BSR	TURNOFF
	move.l	#$08000000,$ffff8800.w	; turn off YM
	move.l	#$09000000,$ffff8800.w	;
	move.l	#$0A000000,$ffff8800.w	;
	move.b	old_kc,$484.w

	move.l 	old_sp,-(sp)	; User mode.
	move.w 	#$20,-(sp)
	trap   	#1
	addq.l 	#6,sp

	clr.w	-(sp)
	trap	#1

old_sp	dc.l	0
old_kc	dc.b	0
	even

wait_vbl
	move.w	#37,-(sp)	; Wait vbl
	trap 	#14
	add.l 	#2,sp
	rts

LOADTUNE:
	LEA	TUNE(PC),A0
	LEA	L0366(PC),A1
	MOVE.L	#$11B0-1,D0
COPYLOOP:
	MOVE.B	(A0)+,(A1)+
	DBF	D0,COPYLOOP
	RTS

TUNE:	INCBIN	"C:\DEV\MUSICRIP\JCB\OUTRUN.PIN"
		EVEN

L0366:            DCB.W     15,0
                  DC.B      $00 
FXFLAG:           DC.B      $00 
EGFLAG:           DC.B      $00 
EGFREQ:           DC.B      $00,$00,$00,$00,$00,$00 
VOLUME:           DC.B      $00 
REFRESH:          DCB.W     227,0 
EGOFF:            DCB.W     13,0
TURNOFF:          DCB.W     385,0 
INITFX:           DCB.W     30,0
DOTUNE:           DCB.W     1589,0
INITTUNE:         TST.W     MUSIC_F 
                  BNE.S     L0371 
                  ADDQ.W    #1,L04AE
                  MOVE.W    L04AE,D0
                  ANDI.W    #1,D0 
                  BRA       DOTUNE
L0371:            RTS

MUSIC_F:	DS.W	1
L04AE:		DS.W	1

Pac-Land

Pac-Land is totally different from Outrun. First of all the original game has one file per level, a TITLE.PRG in the AUTO folder and PACLAND.PRG in the main directory and no debug symbols in the code.

I guessed that the music would be in PACLAND.PRG so I loaded that into MonST. Stepping through the code trace mode is disabled as part of the copy protection so that was a problem. I then disassembled the code with Easy Rider 4.

Looking through the disassembled code there is one section that gave me an idea where the tune started.


L001B:
  MOVE.W    L0018,D0
  BPL.S     L001C 
  NOT.W     D0
  BRA       L01A8 
L001C:
  JMP       L01E2 

The branch here looks weird because if the routine it goes to calls an RTS the next JMP would not be executed. The JMP looks like an entry point to the start of the game, so that could be the start of the tune.

Loading PACLAND.PRG into MonST again and without executing it I look for the NOT.W instruction and save from the next address to +6k to test if that is the start of the tune. I also looked at the space after the the first function and got the address of the next instruction after that point.

Estimated VBL play routine offset:
0x226F8 - 0x225F2 = 206 bytes (decimal).

I then took a standard replay framework and adjusted the init and play routine calls. Subtune number 2 is the famous level 1 tune! It works!


	clr.w	-(sp)
	move.w	#$20,-(sp)
	trap	#1
	addq.l	#6,sp
	move.l	d0,old_sp

	MOVE.B	$484.W,old_kc
	ANDI.B	#$FE,$484.W	; clear keyclick

; INIT
	MOVEQ	#2,D0
	BSR 	TUNE

loop:
	BSR	TUNE+262
	BSR	wait_vbl
	cmp.b	#57,$fffffc02
	bne.s	loop

; music off
;	BSR	TURNOFF
	move.l	#$08000000,$ffff8800.w	; turn off YM
	move.l	#$09000000,$ffff8800.w	;
	move.l	#$0A000000,$ffff8800.w	;
	move.b	old_kc,$484.w

	move.l 	old_sp,-(sp)	; User mode.
	move.w 	#$20,-(sp)
	trap   	#1
	addq.l 	#6,sp

	clr.w	-(sp)
	trap	#1

old_sp	dc.l	0
old_kc	dc.b	0
	even

wait_vbl
	move.w	#37,-(sp)	; Wait vbl
	trap 	#14
	add.l 	#2,sp
	rts

TUNE:	INCBIN	"C:\DEV\MUSICRIP\JCB\PACLAND.DAT" 
		EVEN


Rob Hubbard

Rob Hubbard didn't write too many tunes on the Atari ST but he was the most well known composer on the C64! His version of Warhawk on the ST is superb.

Warhawk by Rob Hubbard

When Mad Max converted this tune to the ST he didn't include the intro part of the tune, but Rob did. It was also used in one of the first game menus I saw on the ST in Automation 133 ripped by Neil. It's not difficult to rip but it requires a bit of research to find the correct player replay routine offsets.

Menu 133 when disassembled with Easy Rider shows tons of code and graphics.
There is a distorting scroller in the lower border. Lower border removal code and Timer B and Timer C code.
The tune is called in the VBL loop and initialised just after going into supervisor mode. It is too hard to extract the tune from this.

Going back to the original game I guessed the tune was WARS.OBJ as it was the smallest file. It has an executable header. Include that and play on a standard VBL loop by jumping to the correct offset. The executable header is 28 bytes long (0x1c).

From the Automation 133 demo we can see the offsets to the init and VBL routines in its disassembly. This is shown in the replay routine below.

Replay code:

The general replay routine uses BSR TUNE+$1C+$38 to initialize the tune and BSR TUNE+$1C+$98 every VBL.


	clr.w	-(sp)
	move.w	#$20,-(sp)
	trap	#1
	addq.l	#6,sp
	move.l	d0,old_sp

	MOVE.B	$484.W,old_kc
	ANDI.B	#$FE,$484.W	; clear keyclick

	MOVEQ	#0,D0
	BSR	TUNE+$1C+$38
loop:
	BSR	TUNE+$1C+$98
	BSR	wait_vbl
	cmp.b	#57,$fffffc02
	bne.s	loop

; music off
	move.l	#$08000000,$ffff8800.w	; turn off YM
	move.l	#$09000000,$ffff8800.w	;
	move.l	#$0A000000,$ffff8800.w	;
	move.b	old_kc,$484.w

	move.l 	old_sp,-(sp)	; User mode.
	move.w 	#$20,-(sp)
	trap   	#1
	addq.l 	#6,sp

	clr.w	-(sp)
	trap	#1

old_sp	dc.l	0
old_kc	dc.b	0
	even

wait_vbl
	move.w	#37,-(sp)	; Wait vbl
	trap 	#14
	add.l 	#2,sp
	rts

TUNE:	
	INCBIN	"C:\DEV\MUSICRIP\HUBBARD\WARHAWK\WARS.OBJ"
	EVEN


References

The ST's Memory Map

Back to index.