don't click here

ASM Using the Mega Everdrive X7's USB Port In Your Hack

Discussion in 'Engineering & Reverse Engineering' started by Kilo, Apr 9, 2024.

Tags:
  1. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Tech Member
    334
    339
    63
    Canada
    Introduction
    Recently, I've been working on a hack that required me to record player data for playback. Typically, you would use this old guide. Nothing wrong with it for what it does, it is great guide that still holds up 14 years later. However for my purposes I needed a different format that tracked the player's render flags, priority, X position, Y position, ground velocity, and angle. I could've done it through SRAM like the guide, as I would really only be using ~7kb of 64kb available to SRAM, and I have made a guide before on using SRAM, however, I recently got a Mega Drive in the mail and wanting to make the most out of my Mega Everdrive that I already owned, I wanted to try out using the USB port on top, as the manual said it could be used for debugging. Then I come to find that there's practically 0 documentation on it! There is a single example ROM provided, however it's source code was written in C, with very few comments or directions. And after trying to port the C to assembly it gave me a massive headache. So to end the cycle of poor souls asking on all sorts of hacking/homebrew forums asking how to use the damn thing, I wanted to make something of a definitive guide to reading/writing to your PC using the USB port. Let's dig in!

    Requirements
    • Mega Drive, any official model works. Clones that emulate the console don't work with Everdrives in the first place to my knowledge.
    • Mega Everdrive X7. I believe Krikzz has discontinued these in favor of the Everdrive Pro unfortunately. And while that has a USB port, it wraps serial data in a command, which the X7 does not need, and I cannot cover it as I do not own one to test. And the X3 and X5 just don't have USB ports. So hopefully you scooped up an X7 in time.
    • Mini USB Cable. They're pretty old, having been replaced by Micro USB which is even becoming obsolete in of itself for USB-C, so you probably won't find a new one on a store shelf. But in a thrift store or Amazon? Probably $5 at worst. Here's a visual aid just to be sure you know what you're getting:
    upload_2024-4-9_8-39-16.png

    Setup
    Now we can actually get into writing code to work with the USB. I'll be using the Sonic 1 Hivebrain 2005 disassembly just because I'm stuck in my ways of doing things, but this works for any disassembly and any game, we're only interacting with I/O and not any actual game logic.

    To make our lives easier as we do this, let's define some things. You can put this where ever you'd like, the start of your main asm file, a variables file. As long as it's included somewhere:
    Code (Text):
    1. USE_USB = 1         ; Set to 1 to set up the console for USB use, 0 to disable.
    2.  
    3. usb_port = $A13000  ; The actual port we'll be interfacing with.
    4.  
    5. ; Port offsets
    6. USB_DATA = 227      ; Usually, this is 226 because it's used to send word sized data to the SD card, but we're not doing that so we just need the upper byte.
    7. USB_STATUS = 228
    8. USB_CONFIG = 230
    9. USB_CTRL = 240
    10.  
    11. USB_WRREADY = 2 ; Flags for the USB's state.
    12. USB_RDREADY = 4
    13.  

    Next, we need to change the header to support the USB port. The Everdrive will only listen to the $A13000 port if it uses the Super Street Fighter 2 mapper header... For some reason, but fret not as you don't need to actually set up the mapper, the header simply enables the /TIME pin on the cartridge.
    Head over to the ROM header, and after the exceptions vector table you should see something similar to this:
    Code (Text):
    1. Console:    dc.b 'SEGA MEGA DRIVE ' ; Hardware system ID
    This is what we need to change to enable the SSF2 mapper in theory, so we'll turn it into this instead:
    Code (Text):
    1.     if USE_USB=1
    2. Console:    dc.b 'SEGA SSF        '    ; SSF2 Header.
    3.     else
    4. Console:    dc.b 'SEGA MEGA DRIVE ' ; Hardware system ID
    5.     endif
    You'll want to make sure that if you're not using the USB, likely for testing other features on emulator, that you use the normal header, as emulators may warn you about the SSF2 mapper not being initialized which is annoying. If your hack or game entirely relies on this USB port though, then these if statements aren't necessary since you'd always have the USE_USB flag on.

    Now we've got to set up the USB port. Somewhere after console initialization (I place it in GameClrRAM just after the dbf) you'll place this.
    Code (Text):
    1.     if USE_USB=1
    2.         move.w  #$8000,USB_CTRL(a0) ; Enable register access
    3.         move.w  #1,USB_CONFIG(a0)   ; Set USB config
    4.     endif
    Next we can start reading and writing!

    Reading/Writing
    Reading and writing is very similar, and honestly pretty easy. You really only have to do 2 things. Like working with the Z80 you need to wait for the USB port to be ready to take or send data. The way we do this is like so:
    Code (Text):
    1. ; USB read wait
    2. @Wait:
    3.         move.w    (usb_port+USB_STATUS).l,d0    ; Get USB status.
    4.         and.l    #USB_RDREADY,d0                ; Check if it's ready for a read.
    5.         beq.s    @Wait                        ; If not, wait.
    6.  
    7. ; USB write wait
    8. @Wait:
    9.         move.w    (usb_port+USB_STATUS).l,d0    ; Get USB status.
    10.         and.l    #USB_WRREADY,d0                ; Check if it's ready for a write.
    11.         beq.s    @Wait                        ; If not, wait.
    Then to actually read and write:
    Code (Text):
    1. ; USB read
    2.         move.b    (usb_port+USB_DATA).l,XX
    3.  
    4. ; USB write
    5.         move.b    XX,(usb_port+USB_DATA).l
    Replace XX with your destination or source of choice.

    Keep in mind that you can only send or receive 1 byte of data at a time, so if you want to send words, longs, or full arrays, you'll have to make loops for that.

    From here, it's up to you interpret the data on PC. Krikzz's website has an example program to send and receive text (you can find it in ssf-ex-example.zip), however this is not a one size fits all tool. When I get the chance I'll write a tool that can read and write any hex data for PC, and keep the thread updated on that. The tools now been written! Check the replies. Or click this if you're too lazy to scroll for 2 seconds.

    Edit: Have some macros, they're healthy for you.
    Code (Text):
    1. USB_Read:    macro dest
    2. @Wait:
    3.         move.w    (usb_port+USB_STATUS).l,d0    ; Get USB status.
    4.         and.l    #USB_RDREADY,d0                ; Check if it's ready for a read.
    5.         beq.s    @Wait                        ; If not, wait.
    6.         move.b    (usb_port+USB_DATA).l,\dest    ; Receive data from the PC.
    7.     endm
    8.  
    9. USB_Write:    macro src
    10. @Wait:
    11.         move.w    (usb_port+USB_STATUS).l,d0    ; Get USB status.
    12.         and.l    #USB_WRREADY,d0                ; Check if it's ready for a write.
    13.         beq.s    @Wait                        ; If not, wait.
    14.         move.b    \src,(usb_port+USB_DATA).l    ; Send data to the PC.
    15.     endm
    Example
    To test what we've learned today, let's do something simple just to verify it all works. We'll simply send the letter 'A' to our PC whenever Sonic jumps.
    After following the steps to set up the USB port, under loc_1341C place this:
    Code (Text):
    1. @Wait:
    2.         move.w    (usb_port+USB_STATUS).l,d0    ; Get USB status.
    3.         and.l    #USB_WRREADY,d0                ; Check if it's ready for a write.
    4.         beq.s    @Wait                        ; If not, wait.
    5.         move.b    #"A",(usb_port+USB_DATA).l    ; Send 'A' to the PC.
    Build and put your ROM into your Everdrive, plug the cartridge into your PC, and turn on the console.
    Run VCPTestCENET.exe from the ssf-ex-example.zip linked earlier, and select the port the Everdrive is plugged into, the application can detect this so don't worry about not being sure what to chose. Hit open so the application can send and receive data to the Mega Drive.
    upload_2024-4-9_8-47-24.png
    Next, boot your ROM, and jump around!
    ezgif-1-a7ee8e924f.gif

    What Else Can I Do With This?
    ANYTHING. Just a few examples off my head:
    • Demo recording. Sonic Team used a similar setup back in the day, just with official development carts and old serial cables.
    • Crowd control. Write a program that connects to Twitch which allows viewers to cause chaos on your Mega Drive!
    • Live level editing. Spend less time saving and building ROMs while working on your layouts and test them immediately!
    • Online multiplayer. Sure, emulator Netplay exists, and the Mega Wifi cart, but this is another option!
    • Real time data. Sync the time of day or weather from your PC to the console!
    There are so many possibilities and I hope now that there's a comprehensive guide on how to use it that more people will try it out.
     
    Last edited: Apr 17, 2024
    • Like Like x 4
    • Informative Informative x 1
    • List
  2. nineko

    nineko

    I am the Holy Cat Tech Member
    6,310
    486
    63
    italy
  3. BenoitRen

    BenoitRen

    Tech Member
    420
    186
    43
    Given your signature, should I disregard this guide? :P

    Might be time to change it, because this looks like solid work. :)
     
  4. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Tech Member
    334
    339
    63
    Canada
    Well you see, this guide took 3 days to write because I wanted to be absolutely sure all my information was correct. For the most part thanks to OCD I'll end up posting something I'm excited about and end up making 30 revisions because I keep realizing I miss something obvious. The signature stays. :V
     
  5. Sonic Hachelle-Bee

    Sonic Hachelle-Bee

    Taking a Sand Shower Tech Member
    808
    201
    43
    Lyon, France
    Sonic 2 Long Version
    That is a cool little guide. Is there a place for it somewhere on the Wiki?

    I wonder if it is possible to implement a full debug environment with breakpoints and all and link all that to an IDE. I started doing something like this (I was able to stop the runtime and resume it, as well as print variables on the fly on the serial link). However I never went too far into this. It seems that debugging with accurrate emulators like Exodus is always more practical.
     
  6. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Tech Member
    334
    339
    63
    Canada
    Debugging is what Krikzz listed the port for on their website, so absolutely. I'm just more creatively orientated and thought about using the port from a gameplay perspective.
    As for putting it on the wiki, I'd have to contact an admin to get back into my account first. But I think a forum post will do, because I also want this thread to be something of a showcase for cool uses and tools using the port.
     
  7. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Tech Member
    334
    339
    63
    Canada
    Here's that hex dumper I was talking about. It runs on Python, and utilizes pySerial and keyboard which can be installed via pip.
    I kind of just assumed the serial port is COM3 and runs with default properties (baud = 9600, data bits = 8, stop bits = 1, parity = none, flow control = none) if you do need to change this for whatever reason, check this out. The way it works is simple, give it a file name to write to (If it exists already it'll give you the option to append incoming data to the existing file), and then when you're done recording data, press X on your keyboard to exit out, you must do this because of the way Python buffers data before actually writing to the destination file, or it won't save.
    Edit: Github mirror.
     

    Attached Files:

    Last edited: Apr 15, 2024
  8. Sonic Hachelle-Bee

    Sonic Hachelle-Bee

    Taking a Sand Shower Tech Member
    808
    201
    43
    Lyon, France
    Sonic 2 Long Version
    If you can dump the Genesis controller inputs onto the serial link, maybe you can do it in reverse? I mean, provide a file to read with predefined inputs, send the inputs through the serial port, and let the game on the Genesis play itself. If this is possible, there may be a way to support a wide variety of PC controllers like keyboard, mouse, last generation gaming controllers with analog controls... etc. I wonder if this can work well enough for the game to be playable.
     
    • Like Like x 1
    • Agree Agree x 1
    • List
  9. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Tech Member
    334
    339
    63
    Canada
    Again, you can quite literally do anything with the USB port, and it should be seamless and instant assuming it's a small amount of data. It's entirely down to what you can come up with and your ability to write a companion app for the computer. If you got an X7 try it out! Would love to see it. I've got to make a new level select menu for my hack, but I'll demonstrate my use for the port in a video soon enough.

    Edit: And this one might be kind of important for anyone using the USB port, but I tried replacing the joypad read function to read from the serial port and the console got trapped, I think this is because the USB port can't be worked on during V-Int, which is when joypad reading is done. So keep that in mind.

    Edit: I've finally gone and added the ability to record data for Metal Sonic for my hack. Here's a look at it in action! (Apologies for recording vertically)
     
    Last edited: Apr 20, 2024
  10. MarkeyJester

    MarkeyJester

    Original, No substitute Resident Jester
    2,202
    432
    63
    Japan
    I was immediately thinking of this when Kilo brought the USB support to my attention.

    The only drawback I'm thinking is a potential delay between input and action.

    Might be worth investigating that a bit more carefully, I'm not entirely sure I believe V-int would have a unique restriction on access like this. You are fiddling with the area close by the bank switch and read/write access. If you've accidentally set that off while the CPU conveniently happens to be in a particular ROM offset area, I can see that being potentially responsible. At least one of many reasons why. So don't give that up too quickly.
     
    • Like Like x 1
    • Agree Agree x 1
    • List
  11. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Tech Member
    334
    339
    63
    Canada
    I'll probably just move input processing to run during the main game loop. PC input is something I'd like to do as a mini hack in general. But I just didn't have enough time to problem solve it while in between my main project and art orders.
     
  12. Cooljerk

    Cooljerk

    Professional Electromancer Oldbie
    4,519
    208
    43
    gdb can target the m68000 architecture and remote debug through serial interfaces. If you can create a gdb-stub, then you can compile gdb with python and it'll work with ides like vscode and qtcreator. I use GDB to debug like this on the old umdk and sega dreamcast. Jorge goes into the GDB remote serial protocol here: https://hackaday.io/project/1507-usb-megadrive-devkit

    and he wrote a 68000 monitor to receive gdb commands for gdb-stub: https://github.com/makestuff/umdkv2/blob/20140524/gdb-bridge/monitor/monitor.s

    I don't have an everdrive x7 otherwise I'd be all over this, but what you ask for is definitely 100% doable.

    EDIT: Just realized my skunkboard for the Jaguar has a gdb-stub/m68k in it called jserve. I can provide it if you'd like, it should allow you to remote connect to a gdb session.
     
    Last edited: Apr 29, 2024 at 1:25 AM