News: 11 March 2016 - Forum Rules

Author Topic: Sword of Mana - 2 Player Couch Coop Hack  (Read 1181 times)

illinx

  • Newbie
  • *
  • Posts: 1
    • View Profile
Sword of Mana - 2 Player Couch Coop Hack
« on: March 27, 2021, 03:44:45 pm »
Hi! This post is an announcement, and cancellation announcement of my Sword of Mana couch coop hack. I started it many years ago when I had free time, but life kept getting in the way so I want to just release what I have and see if anybody wants to pick it up and finish it.

What does the hack do?

The goal of this hack is to add single-GBA coop (ie "couch coop") to Sword of Mana.

Why though?

This game is kind of the red-headed stepchild of the classic Mana games, and IMO a lot of that is down to the fact that it lacks coop. It's not that I'm defending any of the design decisions they made this time--just that those would have gone down better if you could play the game in coop.

Why not linked multiplayer?

That would be way more complicated--it's my first romhack and supporting 2 linked systems would involve way too many new things to learn. I had heard rumors that there was a scrapped linked multiplayer option--maybe there was but I didn't find any evidence of it in the code. Also, like many people I have a ton of great memories playing Secret/Trials of Mana in couch coop, and wanted to try and bring that experience to Sword of Mana. Setting up two GBA's and a link cable is kind of a hassle, I suspect most players who would be interested in this hack would be more interested in something that works on a big screen.

How does it work?

My first idea was to support 2 controllers for the Gameboy Player, like some Super Gameboy games do, but it appears that this is not possible for the GB Player. All four controller ports send inputs to the GBA's button inputs.

I would love to support a second gamepad plugged into the GBA's link port, but I'm not a hardware hacker and I don't know of any existing gamepad like this. If someone knows this area well it would be a really cool direction to go in.

As it stands, this hack currently works on emulator-only (though runs fine on real hw). It simply treats address 4000138h, currently unused, as a second controller input that works identically to the primary one at 4000130h.

I have been testing with a modified version of VBA, however one of the reasons I'm abandoning this project is I had only modified the MFC codepath which is no longer supported. You can see my branch here and the CL to implement the second player here. To be honest if I were starting this from scratch today I would have the second player use the local second player inputs rather than making each local player have 2 gamepads worth of controls.

How finished is this?

I've added polling for the second player controls and logic for player 2 joining/un-joining the game (giving control back to the AI) by pressing start. I have a code shim at the location where you need to set P2's current action, however the thing that tripped me up the first time was a proper mapping of player inputs -> actions. I started working it out by hand, but then figured there must be a function in the code that does this mapping for me, and that's where I got lost. To finish this hack all you need to do is either find that function and call it or implement the mapping yourself. I can share my notes which should help with either of these tasks but right now they are pretty messy so I don't want to post them publicly just yet.

What files are attached?

It doesn't look like I can upload attachments, so I've included both of these files in code blocks below.

swom_coop.asm : The hack itself. Did my best to comment everything
swom_2nd_player.asm : Proof of concept hack--identical to the stock rom but polls the second gamepad instead of the GBA buttons.

I also have: tons of memory layout notes, as well as a windows build of VBA with my change from the linked Github above, that I can share directly with anyone who is interested.

Lastly, if anyone is interested in this idea, there are a ton of other games this approach could work for. I have some notes for Double Dragon Advance, which is way easier to support than Sword of Mana, however I just do not have the time to work on this anymore.

Thanks for reading this far.

swom_2nd_player.asm
Code: [Select]
; Sword of Mana Couch Coop Proof of Concept by illinx
 ; main.asm

 ; v1--right now the patch just replaces the register
 ; used to query controls for p1, and replaces them with
 ; the register used for p2


.gba ; Set the architecture to GBA
.thumb ; THUMB instructions
.open "Sword of Mana (USA, Australia).gba", "Sword of Mana 2nd Player Test.gba", 8000000h

.org 0x080010F4 ; the first location the literal pool is defined

.word 0x4000138

.org 0x0808E1CC ; the second location the literal pool is queried

.word 0x4000138

.close

; blank line

swom_coop.asm
Code: [Select]
; Sword of Mana Couch Coop Hack by illinx
 ; main.asm

 ; v2-samples 2nd player controls in some dead code
 ; around the 1p control sampling


.gba ; Set the architecture to GBA
.thumb ; THUMB instructions
.open "Sword of Mana (USA, Australia).gba", "Sword of Mana Couch Coop.gba", 8000000h

.org 0x0800107E ; where the controls are sampled

ldr r0, =1000100000000111011000110001b ; =8807630h, but with the lsb set to on so it stays in thumb mode
bx r0

.pool

.org 0x08807630

.area 0CCh
ldr r0,=4000138h ;load address of p2 masked controls to r0
ldrh r1,[r0] ;load masked controls into r1
ldr     r2,=3FFh    ;load control mask into r2 
mov     r0,r2        ;copy control mask into r0
mov     r4,r0        ;copy control mask into r4
bic     r4,r1        ;unmask controls, leave result in r4
mov     r1,r4        ;copy unmasked controls to r1
ldr r0,=2000160h ;put address 2000160h into r0 for p2 controls
strh    r1,[r0]      ;store unmasked controls into register 2000160h
   
    ; pseudocode to let p2 join and leave the game
    ; we have r0-r4 to play with
    ; unmasked p2 controls are in r1
    ; 02000100 == P2 has joined
    ; 02000102 == P2start was pressed last frame
    ;
    ; load WasDown into r2
    ldr r0,=2000102h
    ldrh r2, [r0]

    ; mask off the p2start button input from this frame (in r1) and copy it to r3
    mov r4, 8h ; start button is value 8
    and r1, r4
    mov r3, r1

    ;copy r3 (is start button pressed) back to the address in r0
    strh r3, [r0]

    ; set r4 to 0, assume p2 start wasn't pressed this frame
    mov r4, 0h

    ; if !WasDown && IsDown, set r4 to 8--p2start was pressed this frame
    ; r4 = !r2 && r3
    mvn r1, r2   ; NOT r2 and put it in r1
    and r1, r3
    mov r4, r1

    ; load address of "p2 is in the game" variable to r0
    ; load value at address in r0 to r1
    ldr r0,=2000100h
    ldrh r1, [r0]

    ; if p2start was pressed this frame (r4 is nonzero),
    mov r2, 0h
    cmp r4, r2
    beq LoadP1Controls

    ;invert r1
    mvn r1, r1

    ; copy r1 back to address in r0
    strh r1, [r0]

LoadP1Controls:
ldr     r0,=4000130h ;load address of masked controls to r0                 
ldrh    r1,[r0]      ;load masked controls into r1                             
ldr     r2,=3FFh    ;load control mask into r2                                 
mov     r0,r2        ;copy control mask into r0                                 
mov     r4,r0        ;copy control mask into r4                                 
bic     r4,r1        ;unmask controls, leave result in r4                       
mov     r1,r4        ;copy unmasked controls to r1                                             
ldr     r0,=2000170h ;put address 2000170h into r0                             
strh    r1,[r0]      ;store unmasked controls into register 2000170h

ldr     r0,=2000000h ;load address 2000000h into r0                             
ldrb    r0,[r0] ;load contents of 2000000h into r0 (always 0 from what ive seen)
ldr r3, =1000000000000001000010010101b ; =8001094h, but with the lsb set to on so it stays in thumb mode
bx r3
;bl 8001094h

.pool   

.endarea 

.close

; blank line

« Last Edit: March 29, 2021, 02:06:09 pm by illinx »

FAST6191

  • Hero Member
  • *****
  • Posts: 3237
    • View Profile
Re: Sword of Mana - Couch Coop Hack
« Reply #1 on: March 28, 2021, 12:13:30 pm »
"Why not linked multiplayer?

That would be way more complicated--it's my first romhack and supporting 2 linked systems would involve way too many new things to learn."

Yet you hacked up controller inputs for a game and made a custom emulator build? OK then.
I would have thought making an utterly barebones GBA ROM that reads controller state and fires that over the link cable for the other game to copy down (don't think that area is writeable on a normal GBA but if you have it there no great shakes to have it read a byte of normal memory somewhere) once a frame or something.

As far as controller to the link port then something related
http://www.brolinembedded.se/projects/keyboard/