Actions

SCHG How-to

Difference between revisions of "Port Sonic 2 Final Sound Driver to Sonic 1"

From Sonic Retro

(Fixing the Vertical and Horizsontal Interrupts)
(Fixing the Vertical and Horizsontal Interrupts)
Line 861: Line 861:
 
move.w #$83,($FFFFF640).w
 
move.w #$83,($FFFFF640).w
 
move.w ($FFFFF640).w,(a5)
 
move.w ($FFFFF640).w,(a5)
    bsr.w sndDriverInput
+
bsr.w sndDriverInput
 
move.w #0,($A11100).l
 
move.w #0,($A11100).l
 
bsr.w PalCycle_SS
 
bsr.w PalCycle_SS
Line 874: Line 874:
 
move.w ($FFFFF640).w,(a5)
 
move.w ($FFFFF640).w,(a5)
 
move.b #0,($FFFFF767).w
 
move.b #0,($FFFFF767).w
 +
</asm>
 +
now find:
 +
<asm>
 +
loc_F54:
 +
</asm>
 +
and add below:
 +
<ams>
 +
bsr.w sndDriverInput
 
</asm>
 
</asm>
  
 
[[Category:SCHG How-tos| ]]
 
[[Category:SCHG How-tos| ]]

Revision as of 15:47, 29 August 2009

Many have proven that the Sonic1 driver can obviously be put into Sonic2, but whbout the reverse?. Well, the reverse makes a few hacks easier, but I would prefer the sonic3 driver for its rich and diverse and effects and music. I guess it is all about personal choice. One advantage with this driver over the s3 one is that you do not have to worry about where you put things, the driver comes from disasm2007 and this guide uses a disasm2005 sonic1, which are very close to each other.

Overview

There are a lot of things that need to change before we can use this:

  • First off we need to insert the disasm2007 macros, this makes it relatively easier to work with.
  • Next we will have to update the vertical and horizontal interrupts. Every time the z80 is stopped, we need to call the sound driver input routine.
  • We also need the sound driver input routine as well, obviously.
  • we also need to alter the play sound routines, sonic2 sound routines are needed instead of sonic1 ones (pause and unpause included)
  • all the places a sound is played need to be updated to play the correct sounds
  • the level select and debug need fixing.
  • sonic2 build tools need to be put in place.

importing disasm2007 macros

If we did not add the macros then we would have to expand them instead and that makes our work a lot harder. So let insert the macros: first create a new file called "s2.macrosetup.asm" and put this in it: <asm>

padding off ; we don't want AS padding out dc.b instructions listing off ; we don't need to generate anything for a listing file supmode on ; we don't need warnings about privileged instructions


paddingSoFar set 0

128 = 80h = z80, 32988 = 80DCh = z80unDoC

notZ80 function cpu,(cpu<>128)&&(cpu<>32988)

make org safer (impossible to overwrite previously assembled bytes) and count padding
and also make it work in Z80 code without creating a new segment

org macro address if notZ80(MOMCPU) if address < * if assembleZ80SoundDriver error "too much stuff before org $\{address} ($\{(*-address)} bytes)" else error "too much stuff before org $\{address} ($\{(*-address)} bytes) ... try setting assembleZ80SoundDriver=1 in the asm file" endif elseif address > * paddingSoFar set paddingSoFar + address - * !org address endif else if address < $ error "too much stuff before org 0\{address}h (0\{($-address)}h bytes)" else while address > $ db 0 endm endif endif

   endm
define the cnop pseudo-instruction

cnop macro offset,alignment if notZ80(MOMCPU) org (*-1+(alignment)-((*-1+(-(offset)))#(alignment))) else org ($-1+(alignment)-(($-1+(-(offset)))#(alignment))) endif

   endm
redefine align in terms of cnop, for the padding counter

align macro alignment cnop 0,alignment endm

define the even pseudo-instruction

even macro if notZ80(MOMCPU) if (*)&1 paddingSoFar set paddingSoFar+1 dc.b 0 ;ds.b 1 endif else if ($)&1 db 0 endif endif

   endm
make ds work in Z80 code without creating a new segment

ds macro if notZ80(MOMCPU) !ds.ATTRIBUTE ALLARGS else rept ALLARGS db 0 endm endif

  endm
 if TRUE
define a trace macro
lets you easily check what address a location in this disassembly assembles to
if used in Z80 code, the displayed PC will be relative to the start of Z80 RAM

trace macro optionalMessageWithoutQuotes

   if MOMPASS=1

if notZ80(MOMCPU) if ("ALLARGS"<>"") message "#\{tracenum/1.0}: line=\{MOMLINE/1.0} PC=$\{*} msg=ALLARGS" else message "#\{tracenum/1.0}: line=\{MOMLINE/1.0} PC=$\{*}" endif else if ("ALLARGS"<>"") message "#\{tracenum/1.0}: line=\{MOMLINE/1.0} PC=\{$}h msg=ALLARGS" else message "#\{tracenum/1.0}: line=\{MOMLINE/1.0} PC=\{$}h" endif endif tracenum := (tracenum+1)

   endif
  endm
 else

trace macro endm

 endif

tracenum := 0

   if zeroOffsetOptimization=0
   ; disable a space optimization in AS so we can build a bit-perfect rom
   ; (the hard way, but it requires no modification of AS itself)
1-arg instruction that's self-patching to remove 0-offset optimization

insn1op macro oper,x if substr("x",0,2)<>"0(" !oper x else !oper 1+x !org *-1 !dc.b 0 endif endm

2-arg instruction that's self-patching to remove 0-offset optimization

insn2op macro oper,x,y if substr("x",0,2)<>"0(" if substr("y",0,2)<>"0(" !oper x,y else !oper x,1+y !org *-1 !dc.b 0 endif else if substr("y",0,1)<>"D" if substr("y",0,2)<>"0(" !oper 1+x,y !org *-3 !dc.b 0 !org *+2 else !oper 1+x,1+y !org *-3 !dc.b 0 !org *+1 !dc.b 0 endif else !oper 1+x,y !org *-1 !dc.b 0 endif endif endm

; instructions that were used with 0(a#) syntax ; defined to assemble as they originally did _move macro insn2op move.ATTRIBUTE, ALLARGS endm _add macro insn2op add.ATTRIBUTE, ALLARGS endm _addq macro insn2op addq.ATTRIBUTE, ALLARGS endm _cmp macro insn2op cmp.ATTRIBUTE, ALLARGS endm _cmpi macro insn2op cmpi.ATTRIBUTE, ALLARGS endm _clr macro insn1op clr.ATTRIBUTE, ALLARGS endm _tst macro insn1op tst.ATTRIBUTE, ALLARGS endm

else

; regular meaning to the assembler; better but unlike original _move macro !move.ATTRIBUTE ALLARGS endm _add macro !add.ATTRIBUTE ALLARGS endm _addq macro !addq.ATTRIBUTE ALLARGS endm _cmp macro !cmp.ATTRIBUTE ALLARGS endm _cmpi macro !cmpi.ATTRIBUTE ALLARGS endm _clr macro !clr.ATTRIBUTE ALLARGS endm _tst macro !tst.ATTRIBUTE ALLARGS endm

   endif

</asm> next, we need to get the hivebrain disassembly fixed so that we can use these macros, it is rather easy actually. open sonic1.asm ans find: <asm> CPU 68000 padding off ; we don"t want AS padding out dc.b instructions listing off ; we don"t need to generate anything for a listing file supmode on ; we don"t need warnings about privileged instructions

/=========================================================================\
º This file is generated by The Interactive Disassembler (IDA) º
º Copyright (c) 2003 by DataRescue sa/nv, <[email protected]> º
\=========================================================================/
Disassembly created by Hivebrain
thanks to drx and Stealth
Processor
68000
Target Assembler
680x0 Assembler in MRI compatible mode
This file should be compiled with "as -M"
===========================================================================

StartOfRom: </asm> now replace it with: <asm> CPU 68000

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ASSEMBLY OPTIONS

padToPowerOfTwo = 1

| If 1, pads the end of the rom to the next power of two bytes (for real hardware)

skipChecksumCheck = 0

| If 1, disables the unnecessary (and slow) bootup checksum calculation

zeroOffsetOptimization = 0

| If 1, makes a handful of zero-offset instructions smaller

assembleZ80SoundDriver = 1

| If 1, the Z80 sound driver is assembled with the rest of the rom
| If 0, the Z80 sound driver is BINCLUDEd (less flexible)

include "s2.macrosetup.asm"

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Equates section - Names for variables.
---------------------------------------------------------------------------
size variables - you'll get an informational error if you need to change these...
they are all in units of bytes

Size_of_DAC_samples = $2F00 Size_of_SEGA_sound = $6174 Size_of_Snd_driver_guess = $F64 ; approximate post-compressed size of the Z80 sound driver

---------------------------------------------------------------------------
Object Status Table offsets (for everything between Object_RAM and Primary_Collision)
---------------------------------------------------------------------------
universally followed object conventions

render_flags = 1 ; bitfield ; bit 7 = onscreen flag, bit 0 = x mirror, bit 1 = y mirror, bit 2 = coordinate system art_tile = 2 ; and 3 ; start of sprite's art mappings = 4 ; and 5 and 6 and 7 x_pos = 8 ; and 9 ... some objects use $A and $B as well when extra precision is required (see ObjectMove) ... for screen-space objects this is called x_pixel instead y_pos = $C ; and $D ... some objects use $E and $F as well when extra precision is required ... screen-space objects use y_pixel instead priority = $18 ; 0 = front width_pixels = $19 mapping_frame = $1A

---------------------------------------------------------------------------
conventions followed by most objects

x_vel = $10 ; and $11 ; horizontal velocity y_vel = $12 ; and $13 ; vertical velocity y_radius = $16 ; collision width / 2 x_radius = $17 ; collision height / 2 anim_frame = $1B anim = $1C next_anim = $1D anim_frame_duration = $1E status = $22 ; note: exact meaning depends on the object... for sonic/tails: bit 0: leftfacing. bit 1: inair. bit 2: spinning. bit 3: onobject. bit 4: rolljumping. bit 5: pushing. bit 6: underwater. routine = $24 routine_secondary = $25 angle = $26 ; angle about the z=0 axis (360 degrees = 256)

---------------------------------------------------------------------------
conventions followed by many objects but NOT sonic/tails

collision_flags = $20 collision_property = $21 respawn_index = $23 subtype = $28

---------------------------------------------------------------------------
conventions specific to sonic/tails (Obj01, Obj02, and ObjDB)
note
$1F, $20, and $21 are unused and available

inertia = $14 ; and $15 ; directionless representation of speed... not updated in the air flip_angle = $27 ; angle about the x=0 axis (360 degrees = 256) (twist/tumble) air_left = $28 flip_turned = $29 ; 0 for normal, 1 to invert flipping (it's a 180 degree rotation about the axis of Sonic's spine, so he stays in the same position but looks turned around) obj_control = $2A ; 0 for normal, 1 for hanging or for resting on a flipper, $81 for going through CNZ/OOZ/MTZ tubes or stopped in CNZ cages or stoppers or flying if Tails status_secondary = $2B flips_remaining = $2C ; number of flip revolutions remaining flip_speed = $2D ; number of flip revolutions per frame / 256 move_lock = $2E ; and $2F ; horizontal control lock, counts down to 0 invulnerable_time = $30 ; and $31 ; time remaining until you stop blinking invincibility_time = $32 ; and $33 ; remaining speedshoes_time = $34 ; and $35 ; remaining next_tilt = $36 ; angle on ground in front of sprite tilt = $37 ; angle on ground stick_to_convex = $38 ; 0 for normal, 1 to make Sonic stick to convex surfaces like the rotating discs in Sonic 1 and 3 (unused in Sonic 2 but fully functional) spindash_flag = $39 ; 0 for normal, 1 for charging a spindash or forced rolling spindash_counter = $3A ; and $3B jumping = $3C interact = $3D ; RAM address of the last object Sonic stood on, minus $FFFFB000 and divided by $40 layer = $3E ; collision plane, track switching... layer_plus = $3F ; always same as layer+1 ?? used for collision somehow

---------------------------------------------------------------------------
conventions followed by several objects but NOT sonic/tails

y_pixel = 2+x_pos ; and 3+x_pos ; y coordinate for objects using screen-space coordinate system x_pixel = x_pos ; and 1+x_pos ; x coordinate for objects using screen-space coordinate system parent = $3E ; and $3F ; address of object that owns or spawned this one, if applicable

---------------------------------------------------------------------------
unknown or inconsistently used offsets that are not applicable to sonic/tails
(provided because rearrangement of the above values sometimes requires making space in here too)

objoff_A = 2+x_pos ; note: x_pos can be 4 bytes, but sometimes the last 2 bytes of x_pos are used for other unrelated things objoff_B = 3+x_pos objoff_E = 2+y_pos objoff_F = 3+y_pos objoff_14 = $14 objoff_15 = $15 objoff_1F = $1F objoff_27 = $27 objoff_28 = $28 ; overlaps subtype, but a few objects use it for other things anyway

enum               objoff_29=$29,objoff_2A=$2A,objoff_2B=$2B,objoff_2C=$2C,objoff_2D=$2D,objoff_2E=$2E,objoff_2F=$2F
enum objoff_30=$30,objoff_31=$31,objoff_32=$32,objoff_33=$33,objoff_34=$34,objoff_35=$35,objoff_36=$36,objoff_37=$37
enum objoff_38=$38,objoff_39=$39,objoff_3A=$3A,objoff_3B=$3B,objoff_3C=$3C,objoff_3D=$3D,objoff_3E=$3E,objoff_3F=$3F
---------------------------------------------------------------------------
property of all objects

next_object = $40 ; the size of an object

---------------------------------------------------------------------------
I run the main 68k RAM addresses through this function
to let them work in both 16-bit and 32-bit addressing modes.

ramaddr function x,-(-x)&$FFFFFFFF

---------------------------------------------------------------------------
RAM variables

RAM_Start = ramaddr( $FFFF0000 ) Metablock_Table = ramaddr( $FFFF0000 ) Level_Layout = ramaddr( $FFFF8000 ) Block_Table = ramaddr( $FFFF9000 ) Decomp_Buffer = ramaddr( $FFFFAA00 ) Sprite_Table_Input = ramaddr( $FFFFAC00 ) ; in custom format before being converted and stored in Sprite_Table/Sprite_Table_2 Object_RAM = ramaddr( $FFFFB000 ) ; through $FFFFD5FF MainCharacter = ramaddr( $FFFFB000 ) ; first object (usually Sonic except in a Tails Alone game) Sidekick = ramaddr( $FFFFB040 ) ; second object (Tails in a Sonic and Tails game) Tails_Tails = ramaddr( $FFFFD000 ) ; address of the Tail's Tails object Sonic_Dust = ramaddr( $FFFFD100 ) Tails_Dust = ramaddr( $FFFFD140 )

PNT_Buffer = ramaddr( $FFFFD000 ) ; in special stage Primary_Collision = ramaddr( $FFFFD600 ) Horiz_Scroll_Buf_2 = ramaddr( $FFFFD700 ) ; in special stage Secondary_Collision = ramaddr( $FFFFD900 ) VDP_Command_Buffer = ramaddr( $FFFFDC00 ) ; stores VDP commands to issue the next time ProcessDMAQueue is called VDP_Command_Buffer_Slot = ramaddr( $FFFFDCFC ) ; stores the address of the next open slot for a queued VDP command Sprite_Table_2 = ramaddr( $FFFFDD00 ) ; Sprite attribute table buffer for the bottom split screen in 2-player mode Horiz_Scroll_Buf = ramaddr( $FFFFE000 ) Sonic_Stat_Record_Buf = ramaddr( $FFFFE400 ) Sonic_Pos_Record_Buf = ramaddr( $FFFFE500 ) Tails_Pos_Record_Buf = ramaddr( $FFFFE600 ) Ring_Positions = ramaddr( $FFFFE800 ) Camera_RAM = ramaddr( $FFFFEE00 ) Camera_X_pos = ramaddr( $FFFFEE00 ) Camera_Y_pos = ramaddr( $FFFFEE04 ) Camera_Max_Y_pos = ramaddr( $FFFFEEC6 ) Camera_Min_X_pos = ramaddr( $FFFFEEC8 ) Camera_Max_X_pos = ramaddr( $FFFFEECA ) Camera_Min_Y_pos = ramaddr( $FFFFEECC ) Camera_Max_Y_pos_now = ramaddr( $FFFFEECE ) ; was "Camera_max_scroll_spd"... Sonic_Pos_Record_Index = ramaddr( $FFFFEED2 ) ; into Sonic_Pos_Record_Buf and Sonic_Stat_Record_Buf Tails_Pos_Record_Index = ramaddr( $FFFFEED6 ) ; into Tails_Pos_Record_Buf Camera_Y_pos_bias = ramaddr( $FFFFEED8 ) ; added to y position for lookup/lookdown, $60 is center Camera_Y_pos_bias_2P = ramaddr( $FFFFEEDA ) ; for Tails Dynamic_Resize_Routine = ramaddr( $FFFFEEDF ) Tails_Min_X_pos = ramaddr( $FFFFEEF8 ) Tails_Max_X_pos = ramaddr( $FFFFEEFA ) Tails_Max_Y_pos = ramaddr( $FFFFEEFE )

Underwater_palette_2 = ramaddr( $FFFFF000 ) ; not sure what it's used for but it's only used when there's water Underwater_palette = ramaddr( $FFFFF080 ) ; main palette for underwater parts of the screen Underwater_palette_line4 = ramaddr( $FFFFF0E0 )

Game_Mode = ramaddr( $FFFFF600 ) ; 1 byte ; see GameModesArray (master level trigger, Mstr_Lvl_Trigger) Ctrl_1_Logical = ramaddr( $FFFFF602 ) ; 2 bytes Ctrl_1_Held_Logical = ramaddr( $FFFFF602 ) ; 1 byte Ctrl_1_Press_Logical = ramaddr( $FFFFF603 ) ; 1 byte Ctrl_1 = ramaddr( $FFFFF604 ) ; 2 bytes Ctrl_1_Held = ramaddr( $FFFFF604 ) ; 1 byte ; (pressed and held were switched around before) Ctrl_1_Press = ramaddr( $FFFFF605 ) ; 1 byte Ctrl_2 = ramaddr( $FFFFF606 ) ; 2 bytes Ctrl_2_Held = ramaddr( $FFFFF606 ) ; 1 byte Ctrl_2_Press = ramaddr( $FFFFF607 ) ; 1 byte Demo_Time_left = ramaddr( $FFFFF614 ) ; 2 bytes

Vscroll_Factor = ramaddr( $FFFFF616 ) Hint_counter_reserve = ramaddr( $FFFFF624 ) ; Must contain a VDP command word, preferably a write to register $0A. Executed every V-INT. Delay_Time = ramaddr( $FFFFF62A ) ; number of frames to delay the game RNG_seed = ramaddr( $FFFFF636 ) ; used for random number generation Game_paused = ramaddr( $FFFFF63A ) DMA_data_thunk = ramaddr( $FFFFF640 ) ; Used as a RAM holder for the final DMA command word. Data will NOT be preserved across V-INTs, so consider this space reserved.

Water_Level_1 = ramaddr( $FFFFF646 ) Water_Level_2 = ramaddr( $FFFFF648 ) Water_Level_3 = ramaddr( $FFFFF64A ) Water_routine = ramaddr( $FFFFF64D ) Water_move = ramaddr( $FFFFF64E ) Water_on = ramaddr( $FFFFF64C ) ; is set based on Water_flag New_Water_Level = ramaddr( $FFFFF650 ) Water_change_speed = ramaddr( $FFFFF652 ) Palette_frame_count = ramaddr( $FFFFF65E ) Super_Sonic_palette = ramaddr( $FFFFF65F ) Ctrl_2_Logical = ramaddr( $FFFFF66A ) ; 2 bytes Ctrl_2_Held_Logical = ramaddr( $FFFFF66A ) ; 1 byte Ctrl_2_Press_Logical = ramaddr( $FFFFF66B ) ; 1 byte Sonic_Look_delay_counter = ramaddr( $FFFFF66C ) ; 2 bytes Tails_Look_delay_counter = ramaddr( $FFFFF66E ) ; 2 bytes Super_Sonic_frame_count = ramaddr( $FFFFF670 ) Plc_Buffer = ramaddr( $FFFFF680 ) ; Pattern load queue

Misc_Variables = ramaddr( $FFFFF700 )

extra variables for the second player (CPU) in 1-player mode

Tails_control_counter = ramaddr( $FFFFF702 ) ; how long until the CPU takes control Tails_respawn_counter = ramaddr( $FFFFF704 ) Tails_CPU_routine = ramaddr( $FFFFF708 ) Tails_CPU_target_x = ramaddr( $FFFFF70A ) Tails_CPU_target_y = ramaddr( $FFFFF70C ) Tails_interact_ID = ramaddr( $FFFFF70E ) ; object ID of last object stood on

Level_started_flag = ramaddr( $FFFFF711 ) CNZ_Bumper_routine = ramaddr( $FFFFF71A ) Dirty_flag = ramaddr( $FFFFF72C ) ; if whole screen needs to redraw Water_flag = ramaddr( $FFFFF730 ) ; if the level has water or oil

Sonic_top_speed = ramaddr( $FFFFF760 ) Sonic_acceleration = ramaddr( $FFFFF762 ) Sonic_deceleration = ramaddr( $FFFFF764 ) Obj_placement_routine = ramaddr( $FFFFF76C ) Obj_load_addr_0 = ramaddr( $FFFFF770 ) Obj_load_addr_1 = ramaddr( $FFFFF774 ) Obj_load_addr_2 = ramaddr( $FFFFF778 ) Obj_load_addr_3 = ramaddr( $FFFFF77C ) Demo_button_index = ramaddr( $FFFFF790 ) ; index into button press demo data, for player 1 Demo_press_counter = ramaddr( $FFFFF792 ) ; frames remaining until next button press, for player 1 Demo_button_index_2P = ramaddr( $FFFFF732 ) ; index into button press demo data, for player 2 Demo_press_counter_2P = ramaddr( $FFFFF734 ) ; frames remaining until next button press, for player 2 Collision_addr = ramaddr( $FFFFF796 ) Current_Boss_ID = ramaddr( $FFFFF7AA ) Control_Locked = ramaddr( $FFFFF7CC ) Chain_Bonus_counter = ramaddr( $FFFFF7D0 ) ; counts up when you destroy things that give points, resets when you touch the ground Bonus_Countdown_1 = ramaddr( $FFFFF7D2 ) ; level results time bonus or special stage sonic ring bonus Bonus_Countdown_2 = ramaddr( $FFFFF7D4 ) ; level results ring bonus or special stage tails ring bonus Update_Bonus_score = ramaddr( $FFFFF7D6 ) Camera_X_pos_coarse = ramaddr( $FFFFF7DA ) ; (Camera_X_pos - 128) / 256

Sprite_Table = ramaddr( $FFFFF800 ) ; Sprite attribute table buffer

Normal_palette = ramaddr( $FFFFFB00 ) Normal_palette_line2 = ramaddr( $FFFFFB20 ) Normal_palette_line3 = ramaddr( $FFFFFB40 ) Normal_palette_line4 = ramaddr( $FFFFFB60 ) Second_palette = ramaddr( $FFFFFB80 ) Second_palette_line2 = ramaddr( $FFFFFBA0 ) Second_palette_line3 = ramaddr( $FFFFFBC0 ) Second_palette_line4 = ramaddr( $FFFFFBE0 )

Object_Respawn_Table = ramaddr( $FFFFFC00 ) System_Stack = ramaddr( $FFFFFE00 ) Level_Inactive_flag = ramaddr( $FFFFFE02 ) ; (2 bytes) Timer_frames = ramaddr( $FFFFFE04 ) ; (2 bytes) Debug_object = ramaddr( $FFFFFE06 ) Debug_placement_mode = ramaddr( $FFFFFE08 ) Current_ZoneAndAct = ramaddr( $FFFFFE10 ) ; 2 bytes Current_Zone = ramaddr( $FFFFFE10 ) ; 1 byte Current_Act = ramaddr( $FFFFFE11 ) ; 1 byte Life_count = ramaddr( $FFFFFE12 ) Current_Special_Stage = ramaddr( $FFFFFE16 ) Continue_count = ramaddr( $FFFFFE18 ) Super_Sonic_flag = ramaddr( $FFFFFE19 ) Time_Over_flag = ramaddr( $FFFFFE1A ) Extra_life_flags = ramaddr( $FFFFFE1B )

If set, the respective HUD element will be updated.

Update_HUD_lives = ramaddr( $FFFFFE1C ) Update_HUD_rings = ramaddr( $FFFFFE1D ) Update_HUD_timer = ramaddr( $FFFFFE1E ) Update_HUD_score = ramaddr( $FFFFFE1F )

Ring_count = ramaddr( $FFFFFE20 ) ; 2 bytes Timer = ramaddr( $FFFFFE22 ) ; 4 bytes Timer_minute_word = ramaddr( $FFFFFE22 ) ; 2 bytes Timer_minute = ramaddr( $FFFFFE23 ) ; 1 byte Timer_second = ramaddr( $FFFFFE24 ) ; 1 byte Timer_centisecond = ramaddr( $FFFFFE25 ) ; 1 byte Score = ramaddr( $FFFFFE26 ) ; 4 bytes Last_star_pole_hit = ramaddr( $FFFFFE30 ) ; 1 byte -- max activated starpole ID in this act

Saved_Last_star_pole_hit = ramaddr( $FFFFFE31 ) Saved_x_pos = ramaddr( $FFFFFE32 ) Saved_y_pos = ramaddr( $FFFFFE34 ) Saved_Ring_count = ramaddr( $FFFFFE36 ) Saved_Timer = ramaddr( $FFFFFE38 ) Saved_art_tile = ramaddr( $FFFFFE3C ) Saved_layer = ramaddr( $FFFFFE3E ) Saved_Camera_X_pos = ramaddr( $FFFFFE40 ) Saved_Camera_Y_pos = ramaddr( $FFFFFE42 ) Saved_Water_Level = ramaddr( $FFFFFE50 ) Saved_Water_routine = ramaddr( $FFFFFE52 ) Saved_Water_move = ramaddr( $FFFFFE53 ) Saved_Extra_life_flags = ramaddr( $FFFFFE54 ) Saved_Extra_life_flags_2P = ramaddr( $FFFFFE55 ) Saved_Camera_Max_Y_pos = ramaddr( $FFFFFE56 ) Saved_Dynamic_Resize_Routine = ramaddr( $FFFFFE58 )

Logspike_anim_counter = ramaddr( $FFFFFEA0 ) Logspike_anim_frame = ramaddr( $FFFFFEA1 ) Rings_anim_counter = ramaddr( $FFFFFEA2 ) Rings_anim_frame = ramaddr( $FFFFFEA3 ) Unknown_anim_counter = ramaddr( $FFFFFEA4 ) ; I think this was $FFFFFEC4 in the alpha Unknown_anim_frame = ramaddr( $FFFFFEA5 ) Ring_spill_anim_counter = ramaddr( $FFFFFEA6 ) ; scattered rings Ring_spill_anim_frame = ramaddr( $FFFFFEA7 ) Ring_spill_anim_accum = ramaddr( $FFFFFEA8 )

values for the second player (some of these only apply to 2-player games)

Tails_top_speed = ramaddr( $FFFFFEC0 ) ; Tails_max_vel Tails_acceleration = ramaddr( $FFFFFEC2 ) Tails_deceleration = ramaddr( $FFFFFEC4 ) Life_count_2P = ramaddr( $FFFFFEC6 ) Extra_life_flags_2P = ramaddr( $FFFFFEC7 ) Update_HUD_lives_2P = ramaddr( $FFFFFEC8 ) Update_HUD_rings_2P = ramaddr( $FFFFFEC9 ) Update_HUD_timer_2P = ramaddr( $FFFFFECA ) Update_HUD_score_2P = ramaddr( $FFFFFECB ) ; mostly unused Time_Over_flag_2P = ramaddr( $FFFFFECC ) Ring_count_2P = ramaddr( $FFFFFED0 ) Timer_2P = ramaddr( $FFFFFED2 ) ; 4 bytes Timer_minute_word_2P = ramaddr( $FFFFFED2 ) ; 2 bytes Timer_minute_2P = ramaddr( $FFFFFED3 ) ; 1 byte Timer_second_2P = ramaddr( $FFFFFED4 ) ; 1 byte Timer_centisecond_2P = ramaddr( $FFFFFED5 ) ; 1 byte Score_2P = ramaddr( $FFFFFED6 ) Last_star_pole_hit_2P = ramaddr( $FFFFFEE0 )

Saved_Last_star_pole_hit_2P = ramaddr( $FFFFFEE1 ) Saved_x_pos_2P = ramaddr( $FFFFFEE2 ) Saved_y_pos_2P = ramaddr( $FFFFFEE4 ) Saved_Ring_count_2P = ramaddr( $FFFFFEE6 ) Saved_Timer_2P = ramaddr( $FFFFFEE8 ) Saved_art_tile_2P = ramaddr( $FFFFFEEC ) Saved_layer_2P = ramaddr( $FFFFFEEE )

Loser_Time_Left = ramaddr( $FFFFFEF8 ) Results_Screen_2P = ramaddr( $FFFFFF10 ) ; 0 = act, 1 = zone, 2 = game, 3 = SS, 4 = SS all Results_Data_2P = ramaddr( $FFFFFF20 ) ; $18 bytes EHZ_Results_2P = ramaddr( $FFFFFF20 ) ; 6 bytes MCZ_Results_2P = ramaddr( $FFFFFF26 ) ; 6 bytes CNZ_Results_2P = ramaddr( $FFFFFF2C ) ; 6 bytes SS_Results_2P = ramaddr( $FFFFFF32 ) ; 6 bytes SS_Total_Won = ramaddr( $FFFFFF38 ) ; 2 bytes (player 1 then player 2) Perfect_rings_left = ramaddr( $FFFFFF40 ) Player_mode = ramaddr( $FFFFFF70 ) ; 0 = Sonic and Tails, 1 = Sonic, 2 = Tails Player_option = ramaddr( $FFFFFF72 ) ; 0 = Sonic and Tails, 1 = Sonic, 2 = Tails

Two_player_items = ramaddr( $FFFFFF74 ) Level_select_zone = ramaddr( $FFFFFF82 ) Sound_test_sound = ramaddr( $FFFFFF84 ) Title_screen_option = ramaddr( $FFFFFF86 ) Current_Zone_2P = ramaddr( $FFFFFF88 ) Current_Act_2P = ramaddr( $FFFFFF89 ) Two_player_mode_copy = ramaddr( $FFFFFF8A ) Options_menu_box = ramaddr( $FFFFFF8C ) Level_Music = ramaddr( $FFFFFF90 ) Game_Over_2P = ramaddr( $FFFFFF98 ) Got_Emerald = ramaddr( $FFFFFFB0 ) Emerald_count = ramaddr( $FFFFFFB1 ) Got_Emeralds_array = ramaddr( $FFFFFFB2 ) ; 7 bytes Next_Extra_life_score = ramaddr( $FFFFFFC0 ) Next_Extra_life_score_2P = ramaddr( $FFFFFFC4 ) Level_Has_Signpost = ramaddr( $FFFFFFC8 ) ; 1 byte ; 1 = signpost, 0 = boss or nothing Level_select_flag = ramaddr( $FFFFFFD0 ) Slow_motion_flag = ramaddr( $FFFFFFD1 ) Correct_cheat_entries = ramaddr( $FFFFFFD4 ) Correct_cheat_entries_2 = ramaddr( $FFFFFFD6 ) ; for 14 continues or 7 emeralds codes Two_player_mode = ramaddr( $FFFFFFD8 ) ; flag (0 for main game)

Values in these variables are passed to the sound driver during V-INT.
They use a playlist index, not a sound test index.

Music_to_play = ramaddr( $FFFFFFE0 ) SFX_to_play = ramaddr( $FFFFFFE1 ) ; normal SFX_to_play_2 = ramaddr( $FFFFFFE2 ) ; alternating stereo Music_to_play_2 = ramaddr( $FFFFFFE4 ) ; alternate (higher priority?) slot

Demo_mode_flag = ramaddr( $FFFFFFF0 ) ; 1 if a demo is playing (2 bytes) Demo_number = ramaddr( $FFFFFFF2 ) ; which demo will play next (2 bytes) Graphics_Flags = ramaddr( $FFFFFFF8 ) ; misc. bitfield Debug_mode_flag = ramaddr( $FFFFFFFA ) ; (2 bytes) Checksum_fourcc = ramaddr( $FFFFFFFC ) ; (4 bytes)

---------------------------------------------------------------------------
VDP addressses

VDP_data_port = $C00000 ; (8=r/w, 16=r/w) VDP_control_port = $C00004 ; (8=r/w, 16=r/w)

---------------------------------------------------------------------------
Z80 addresses

Z80_RAM = $A00000 ; start of Z80 RAM Z80_RAM_End = $A02000 ; end of non-reserved Z80 RAM Z80_Version = $A10001 Z80_Port_1_Data = $A10002 Z80_Port_1_Control = $A10008 Z80_Port_2_Control = $A1000A Z80_Expansion_Control = $A1000C Z80_Bus_Request = $A11100 Z80_Reset = $A11200

Security_Addr = $A14000

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
simplifying macros
tells the VDP to copy a region of 68k memory to VRAM or CRAM or VSRAM

dma68kToVDP macro source,dest,length,type lea (VDP_control_port).l,a5 move.l #(($9400|((((length)>>1)&$FF00)>>8))<<16)|($9300|(((length)>>1)&$FF)),(a5) move.l #(($9600|((((source)>>1)&$FF00)>>8))<<16)|($9500|(((source)>>1)&$FF)),(a5) move.w #$9700|(((((source)>>1)&$FF0000)>>16)&$7F),(a5) move.w #((dest)&$3FFF)|((type&1)<<15)|$4000,(a5) move.w #$80|(((dest)&$C000)>>14)|((type&2)<<3),(DMA_data_thunk).w move.w (DMA_data_thunk).w,(a5)

   endm
   ; values for the type argument
   enum VRAM=0,CRAM=1,VSRAM=2
tells the VDP to fill a region of VRAM with a certain byte

dmaFillVRAM macro byte,addr,length lea (VDP_control_port).l,a5 move.w #$8F01,(a5) ; VRAM pointer increment: $0001 move.l #(($9400|((((length)-1)&$FF00)>>8))<<16)|($9300|(((length)-1)&$FF)),(a5) ; DMA length ... move.w #$9780,(a5) ; VRAM fill move.l #$40000080|(((addr)&$3FFF)<<16)|(((addr)&$C000)>>14),(a5) ; Start at ... move.w #(byte)<<8,(VDP_data_port).l ; Fill with byte - move.w (a5),d1 btst #1,d1 bne.s - ; busy loop until the VDP is finished filling... move.w #$8F02,(a5) ; VRAM pointer increment: $0002

   endm
calculates initial loop counter value for a dbf loop
that writes n bytes total at 4 bytes per iteration

bytesToLcnt function n,n>>2-1

fills a region of 68k RAM with 0 (4 bytes at a time)

clearRAM macro addr,length

   if length&3

fatal "clearRAM len must be divisible by 4, but was length"

   endif

lea (addr).w,a1 moveq #0,d0 move.w #bytesToLcnt(length),d1 - move.l d0,(a1)+ dbf d1,-

   endm
tells the Z80 to stop, and waits for it to finish stopping (acquire bus)

stopZ80 macro move.w #$100,(Z80_Bus_Request).l ; stop the Z80 - btst #0,(Z80_Bus_Request).l bne.s - ; loop until it says it's stopped

   endm
tells the Z80 to start again

startZ80 macro move.w #0,(Z80_Bus_Request).l  ; start the Z80

   endm
function to make a little-endian 16-bit pointer for the Z80 sound driver

z80_ptr function x,(x)<<8&$FF00|(x)>>8&$7F|$80

macro to declare a little-endian 16-bit pointer for the Z80 sound driver

rom_ptr_z80 macro addr dc.w z80_ptr(addr) endm

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start of ROM
/=========================================================================\
º This file is generated by The Interactive Disassembler (IDA) º
º Copyright (c) 2003 by DataRescue sa/nv, <[email protected]> º
\=========================================================================/
Disassembly created by Hivebrain
thanks to drx and Stealth
Processor
68000
Target Assembler
680x0 Assembler in MRI compatible mode
This file should be compiled with "as -M"
===========================================================================

StartOfRom: </asm> This will make our task a lot easier from this point forward as well as make most other sonic2 to sonic1 ports much easier.

Fixing the Vertical and Horizsontal Interrupts

The vertical and horizontal interrupts need to be fixed otherwise the sound driver will not work. First off, loc_B5E needs to go, it is a reloader for the s1 driver and will not work with the s2 driver, find: <asm> loc_B5E: ; XREF: loc_B88 jsr (sub_71B4C).l

</asm> and remove it, use remove that whole routine up there. Next, we will have to add calls for the s2 sound driver reloader. Find: <asm> loc_B88: ; XREF: loc_B10; off_B6E cmpi.b #$8C,($FFFFF600).w beq.s loc_B9A cmpi.b #$C,($FFFFF600).w bne.w loc_B5E </asm>

and change it to: <asm> loc_B88: ; XREF: loc_B10; off_B6E cmpi.b #$8C,($FFFFF600).w beq.s loc_B9A cmpi.b #$C,($FFFFF600).w bne.w loc_B64 stopZ80 ; stop the Z80 bsr.w sndDriverInput ; give input to the sound driver startZ80 ; start the Z80 </asm>

now find: <asm> loc_C22: ; XREF: loc_BC8 move.w ($FFFFF624).w,(a5) move.w #0,($A11100).l bra.w loc_B5E </asm> and change it to: loc_C22: ; XREF: loc_BC8 move.w ($FFFFF624).w,(a5) bsr.w sndDriverInput move.w #0,($A11100).l bra.w loc_B64 </asm> now, find: <asm> loc_C22: ; XREF: loc_BC8 move.w ($FFFFF624).w,(a5) move.w #0,($A11100).l bra.w loc_B5E </asm> and change it to: <asm> loc_C22: ; XREF: loc_BC8 move.w ($FFFFF624).w,(a5) bsr.w sndDriverInput move.w #0,($A11100).l bra.w loc_B64 </asm> now find: <asm> loc_D50: </asm> and add below: <asm> bsr.w sndDriverInput </asm> now find: <asm> loc_DAE: btst #0,($A11100).l ; has Z80 stopped? bne.s loc_DAE ; if not, branch bsr.w ReadJoypads lea ($C00004).l,a5 move.l #$94009340,(a5) move.l #$96FD9580,(a5) move.w #$977F,(a5) move.w #$C000,(a5) move.w #$80,($FFFFF640).w move.w ($FFFFF640).w,(a5) lea ($C00004).l,a5 move.l #$94019340,(a5) move.l #$96FC9500,(a5) move.w #$977F,(a5) move.w #$7800,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) lea ($C00004).l,a5 move.l #$940193C0,(a5) move.l #$96E69500,(a5) move.w #$977F,(a5) move.w #$7C00,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) move.w #0,($A11100).l bsr.w PalCycle_SS tst.b ($FFFFF767).w beq.s loc_E64 lea ($C00004).l,a5 move.l #$94019370,(a5) move.l #$96E49500,(a5) move.w #$977F,(a5) move.w #$7000,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) move.b #0,($FFFFF767).w

</asm> and change it to: <asm> loc_DAE: btst #0,($A11100).l ; has Z80 stopped? bne.s loc_DAE ; if not, branch bsr.w ReadJoypads lea ($C00004).l,a5 move.l #$94009340,(a5) move.l #$96FD9580,(a5) move.w #$977F,(a5) move.w #$C000,(a5) move.w #$80,($FFFFF640).w move.w ($FFFFF640).w,(a5) lea ($C00004).l,a5 move.l #$94019340,(a5) move.l #$96FC9500,(a5) move.w #$977F,(a5) move.w #$7800,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) lea ($C00004).l,a5 move.l #$940193C0,(a5) move.l #$96E69500,(a5) move.w #$977F,(a5) move.w #$7C00,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) bsr.w sndDriverInput move.w #0,($A11100).l bsr.w PalCycle_SS tst.b ($FFFFF767).w beq.s loc_E64 lea ($C00004).l,a5 move.l #$94019370,(a5) move.l #$96E49500,(a5) move.w #$977F,(a5) move.w #$7000,(a5) move.w #$83,($FFFFF640).w move.w ($FFFFF640).w,(a5) move.b #0,($FFFFF767).w </asm> now find: <asm> loc_F54: </asm> and add below: <ams> bsr.w sndDriverInput </asm>