Hardware/emulator quirks

Discussion in 'Discussion & Q&A' started by Clownacy, Sep 10, 2015.

  1. Clownacy

    Clownacy Retired Staff lolololo Member

    Joined:
    Aug 15, 2014
    Messages:
    1,021
    Recently, someone reminded me that Mega Drive emulators kinda suck, as does the hardware they emulate. Not keeping this in mind may lead to your hacks not working on hardware or even certain emulators, so I've decided to make this thread to list as many as we can so that money I spent on an Everdrive doesn't go to waste...

    Hardware

    Hardware's very picky, but in an age where emulators are getting better in terms of accuracy, hardware is no longer alone. If you want compatibility with the likes of Regen and Exodus, you gotta keep an eye out for hardware quirks.

    Odd Address Error

    I don't know the technicals behind it, but the 68k can't access a word/longword of data that starts on an odd address. This can be code, data or even RAM variables. Attempting to do so will trigger an exception, which, depending on the game you're hacking, can have different results:
    • Sonic 1 - enters debugger - game freezes and debug text appears (can be escaped by pressing C)
    • Sonic 2 - enters ErrorTrap - game hangs with no way out
    • Sonic & Knuckles - enters ErrorTrap and then EntryPoint - game resets

    None of these are really something you want to see happen during normal gameplay. Regen and hardware are the only platforms I've noticed that trigger an odd-address exception. If you want to support those platforms, make it a goal to correct anything that can cause that (and even then, plain Gens doesn't seem to agree with code that starts on an odd address, so it's probably just worth doing in general).

    The assembler itself can help with detecting any fishy reads/writes. Well, asm68k doesn't, but AS at least raises a warning when obvious broken reads/writes are detected, like this little gem from Sonic 2 REV00:

    Code:
    > > >s2.asm(27974): warning: address is not properly aligned
    > > >     move.w    (1).w,d0    ; causes a crash on hardware because of the word operation at an odd address
    This here is what causes REV00 to crash when you try to place an object in debug mode while dead.

    Note that the more cryptic instances like this will not be detected:

    Code:
            lea    (1).w,a0
            move.l    #'lolo',(a0)
    As for what you can do... there's the 'even' macro, which aligns things to an even address. Consider the following example:

    Code:
    byte_6E36:    dc.b   0,  0,$FF,  0,  0
        even
    
    ; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
    
    ;sub_6E3C
    SSPlaneB_SetHorizOffset:
        moveq    #0,d7
        ...
    byte_6E36 starts on an even address, continues for five bytes, then ends. Directly following that is code. That code must remain on an even address. The presence of an 'even' macro ensures that the code will be placed on an even address.

    You might ask why 'even's should be used when a simple 'dc.b 0' can do the same thing. The reason is that 'even's are safe to use even where they're not needed: placing an 'even' on an already-even address will not cause it to misalign everything after it, or waste space by padding until the next even address; it will simply not do anything. It is for this reason that it is advisable for 'even's to be placed after any and all data, even if you know the data won't end on an odd address. One less thing to worry about, you know? The programmer can be at ease knowing there's no chance their shiny new table will cause any odd address issues down the road. As for code, I'm pretty darn sure all 68k instructions are even in size, so there's no need to worry about that. Code just needs to start on an even address.

    RAM works a bit differently. You can't use an 'even' macro on 'ramaddr( $FFFFEE0B )'. That's definitely an area where AS' detector comes in handy. As for the S2 disassembly's way of declaring RAM, 'even' should work well enough, I guess. You'd be wasting RAM by filling it with padding, though. It's usually worth just rearranging your RAM so little byte-sized variables act as the padding instead.

    VDP Read Error

    Why was that last section so big but these ones aren't?

    Here's something that eludes even Regen, and has haunted my main hack for ages. You may be tempted to optimise the following instruction by changing it to a 'clr.l'...

    Code:
            move.l    #0,(a6)    ; a6 is VDP_data_port
    This, however, causes the game to hang. Again, I don't know the technicals behind it, but it's there, so it should be avoided. 'move' seems to be the only safe instruction to use. I hear it's got something to do with how 'clr' involves reading the destination, but I can't be too sure on that. Just remember to be careful if you batch replace 'move #0', to 'clr', like I did.

    CRAM Dot Bug

    Due to a bug with the VDP, if the CRAM (palette) is updated mid-frame (that is, while the hardware is still drawing the screen), stray dots of varying colour will appear all over the screen. It doesn't cause any crashes, but it's pretty distracting. You can see this in S1, S2 and some areas of S3K in the water levels, by looking where the level's colours change from normal to water-tinted.

    Normally, you never see this in the likes of Sandopolis act 2, where the palette is always changing, because the Sonic engine queues changes to CRAM, and only actually does them at the end of the frame, where there's no risk of the dots appear over anything important.

    For zones like AIZ, S3K found a clever way of avoiding it, even with the water palette: by only updating small parts of CRAM per H-Int, the dots only appear in the overscan to the left of the screen, but this comes at the cost of the colours only being updated gradually, with some colours changing a significant distance below the actual water line (dip half of Tails in water in AIZ1 to see what I mean).

    Basically, don't update CRAM directly unless you really know what you're doing, especially if you don't know exactly when it's gonna update.

    Z80 DMA Pause

    EDIT: Apparently there's actually a reason the Z80 is paused. See here for more.

    In stock S1/S2/S&K, before beginning a DMA transfer, the game manually stops the Z80, and starts it back up afterwards. This is likely so the Z80 doesn't try to use the 68k's bus while it's being used for DMA, but this is unnecessary, as, if this does happen, the Z80 will just wait it out and pause, anyway. Or, at least, that's what it does on hardware. Every emulator I've checked lets the Z80 keep running, leading to such things as brilliant PCM playback in emulators, but the same stuttering mess we've grown used to on hardware.

    Emulators

    There are also quirks that can break compatibility with emulators, inaccurate or not. Being fancy with your hack can lead to odd cases where only one certain emulator is left in the dust, with the others seemingly completely okay with it.

    Regen SRAM Scrambling

    If your hack uses SRAM, don't expect Regen to play ball. It seems to byteswap SRAM when you close it, so the average user will pick up your hack after leaving it for a while only to find their save data gone. No fun.

    Fusion mode 1 DMA Failure

    Many look at Kega Fusion as the only way to play hacks that take advantage of the Mega CD's mode 1 functionality (coughgenplusgxcough) but it has a pretty nasty problem in that DMA transfers never seem to occur. In games like Sonic 2, this can impact Sonic & Tails' displaying, the animated level art, and even the animated menu backgrounds. You can see this just by loading stock S2 and doing CTRL-B, which boots the Mega CD (make sure you have Mode 1 enabled in the INI file).

    Well, that came to an abrupt end. If anyone else knows any other cryptic stuff like this, please do tell.
     
    Last edited: Apr 8, 2021
  2. vladikcomper

    vladikcomper Well-Known Member Member

    Joined:
    Dec 2, 2009
    Messages:
    421
    This reminds me of my old post in Basic Q & A thread from 2013. It was more of a reply to someone's question, but I mostly speak about Kega's emulation quirks here.

    The following is a full C/P of the original post, hope this will be a good contribution to the "Emulators" section:

    "I've been using Kega all the time, although it's far from being accurate in some parts.
     
    To be fair, Kega isn't really designed for developing and debugging homebrew. By far, Regen is the most accurate emulator and Kega isn't even the second accurate emulator nowadays (I believe GensGS is better in emulating all those hardware quirks now). However, Kega's emulation is nearly perfect despite a lot of inaccuracies - it can emulate some exotic VDP tricks that even Regen fails at. How is that possible? Let me explain.
     
    You see, Kega isn't designed for developing purposes, it's more oriented for emulating a ready production (and it does quite good with it). If there is a certain hardware quirk or rare bug that none on known games/programs relies on - Kega won't bother emulating it. This sort of inaccuracies is intentional. Some hardware features are ignored in a smart way: as long as it doesn't affect perfect game emulation per se.

    Here is an example: you have to set a certain system port to initialize hardware correctly. This is a standard routine and you have only one way - set the port or else hardware won't run. But this action doesn't help in emulating game itself at all. This is where Kega comes with intended inaccuracies: it won't emulate this, just because this is useless in terms of emulation - you don't actually have to set this port to run emulator correctly, this is all hardware quirks. So Kega just assumes it's everything okay with this port, it won't emulate hardware crashes just because of this quirk. But when when it comes to emulating your game's sound and video, Kega will do its best to produce perfect and most accurate results. I'm pretty sure most of inaccuracies were intended to provide the fastest emulation possible - missed features simply optimize it and give less times less CPU load.

    To sum it up, Kega wasn't really intended as your crash test emulator, so I'd recommend using Regen for these purposes. But as I mentioned earlier, Kega is really good in VDP emulation, it can handle some crazy VDP tricks that even Regen fails to emulate.

    I've been working with Kega for years and I've figured out a lot of missed features. If you woder, here's the list of most noticeable ones:

    • No Address Error emulation. This the biggest and the most insidious problem. Everything works perfectly, but when you try it on real hw... Most of emulators don't emulate it to optimize CPU emulation. As the result, you don't see it, you don't know it. This is why the issue is really insidious, apparently it became the most common reason why your perfectly working hack suddenly refuses to work on hardware. The only emulator I know that emulates it is Regen (I heard GensGS now does that too, didn't check).
    • Accessing odd memory addresses. Since it has no Address Error emulation, Kega allows you to read or write words on odd addresses. Here is where another insidious problem hides. Both Kega and Gens allow you do this, however, they handle accessing misaligned addresses differently. Gens applies a bit mask to ensure the address you're accessing is even. For example, if you're writing a word to $FF0001, Gens will write to $FF0000 instead. Kega, in turn, will write data exactly to $FF0001, which seems more legit than weird alignment that Gens does. So beware. If you're mistakenly accessing odd addresses, expect your code to behave totally differently in Kega and Gens.
    • No Z80 BUSREQ emulation. Recently I've discovered that Kega simply doesn't emulate Z80 bus request and release - it always allows you to access Z80 memory area whether you requested it or not. I admit this isn't really important in terms in emulation, but this won't help to detect missed BUSREQs if you're debugging the code. Other emulators, including old Gens seem to emulate this properly. If you accessing memory area of working Z80 without BUSREQ, you won't be able to write or read properly. If you're waiting Z80 to provide the bus and you accidentally pushed it into reset state, you'll end up in a dead loop. None of this will happen in Kega.
    • Z80 isn't stopped on DMA when it accesses 68K. Nah, this would be really heavy to emulate, even Regen doesn't go for it.
    • No cycle stealing emulation on accessing 68K memory. Z80 accesses 68K by so-called cycle stealing. It requests 68K to transfer a byte from a certain address, which takes 4 cycles from 68K side. 68K can be requested when it finished processing the last opcode, it happens between the opcodes. The Z80 has to wait 68K to respond. Again, this is way tricky to emulate, and none of games attempt to have a full 68K and Z80 synchronization relying on this.
    • Problem with restoring YM register port when loading a savestate. Found that one out when playing a digital audio with Mega PCM in Sonic 1 Megahack. The music stopped playing after loading a savestate until YM register port is updated. Looks like it's nulled after loading a savestate. I think it's an issue with savestate format. Same happens in Regen, if I'm right, because it uses the same format. This won't lead to any serious problems though.
    • YM busy state isn't emulated. On real hardware, you must ensure YM is not in the busy state before accessing it from 68K side. As I remember, even Regen doesn't handle missed YM writes if the chip is busy at the moment, on the hardware, it causes some sound distortion.
     
  3. Clownacy

    Clownacy Retired Staff lolololo Member

    Joined:
    Aug 15, 2014
    Messages:
    1,021
    I've had my Clone Driver v2 make odd noises on Regen if I didn't pad WriteFMI with 'nop's, so I think at least that emulator supports the YM busy state.
     
  4. Crash

    Crash Well-Known Member Member

    Joined:
    Jul 15, 2010
    Messages:
    302
    Location:
    Australia
    As nice as Kega has been to use in the past, it doesn't seem to play nice with Windows 8 or 10 on the couple of computers I've tried it on. Very stuttery, seems to frameskip, runs about 30 fps. Exodus runs at 60 fps for me now though >:)

    e: more on topic, Regen (and most other emulators) doesn't seem to mind if the 68k reads from the z80's ram without stopping the z80 first, whereas when I try it on real hardware it cracks the shits
     
    Last edited by a moderator: Sep 11, 2015
  5. AURORA☆FIELDS

    AURORA☆FIELDS so uh yes Exiled

    Joined:
    Oct 7, 2011
    Messages:
    759
    Some very dangerous Regen behavior I would like to report:

    If you construct illegal VDP commands, Regen will often quit its window instantly with no harm caused. However in some rare cases it will pop up the 68k debugger window (and sometimes it will be entirely blank). But by far the most dangerous case I heard is where my friend got this freeze to happen, where both Skype and Chrome crashed and his PC would not respond for 5 minutes, and afterwards CPU usage shot up to 100% until Regen's process was closed. This is a major flaw in the software, so if writing custom VDP code make sure it can not write illegal VDP commands. I do not know about hardware, but I guess it only locks up with no harm done.
     
    Matt likes this.
  6. MarkeyJester

    MarkeyJester ♡ ! Member

    Joined:
    Jun 27, 2009
    Messages:
    2,867
    You may want to add this one to your list.

    DMA copying data from/to VRAM to the location of the sprite table, No emulators update the sprite caché correctly.  The Y position, shape and priority are collected from random aspects of the FIFO, thereas emulators either perform a correct sprite caché update, or don't at all.  I've attempted to push data into the FIFO before activating DMA, but without disrupting it obviously.  It seems some of the data is in fact collected from the data FIFO (assume a6 contains the control port, and a5 contains the data port), I've deliberately kept the register writes single instruction to keep it clean and simple for this example:

    Code:
            move.w    #$8F01,(a6)                ; set VDP auto increment by 1
            move.w    #$9302,(a6)                ; set DMA size (??80)
            move.w    #$9400,(a6)                ; set DMA size (0280)
            move.w    #$9511,(a6)                ; set DMA source (??00)
            move.w    #$9620,(a6)                ; set DMA source (2000)
            move.w    #$97C0,(a6)                ; set DMA mode (11000000 = Copy)
        move.l    #$01234567,(a5)
        move.l    #$89ABCDEF,(a5)
            move.w    #$1011,(a6)                ; set DMA destination Part 1 (1000) [VDP modes omitted]
            move.w    #$04C0,(a6)                ; set DMA destination part 2 (11000000 = Copy).
    0123 4567 89AB CDEF

    Regardless of where in the FIFO the data was written to, it is always collected starting from 0123 (e.g. if the FIFO now contains CDEF 0123 4567 89AB in that order, 0123 is still the first data collected).  I'd therefore assume the entry position/pointer is used for collection.

    When DMA copying a byte to byte 1 or 3 of the sprite in the table, byte "01" is always collected from the FIFO.  Though it is sent to bytes 0 and 2 of the sprite in the table (respectively; 1 to 0, 3 to 2).  When DMA copying a byte to byte 0 or 2 of the sprite in the table, the data does not "seem" to come from the FIFO.  Copying to byte 2 will always send the byte (wherever it collected it from) to byte 3 of the sprite table (altering it's priority), the byte (once again) has to be either 00 or 50 to FF, this is known due to the remaining sprites in the chain disappearing after that current sprite only.  Copying to byte 0, there's no major proof of where it's collected from or sent to, though I will make the assumption that it's send to byte 1 of the sprite in the table.  On the two basis that; A: copying bytes 2 or 3 appeared to have a "byte swap" effect, therefore, copying to 0 would write to 1, and vis versa.  B: The sprite disappears but leaves the other sprites intact.  The sprite could very well disappear for a many number of other ways, but it seems likely that the Y position of that current sprite was pushed to a position off screen.

    http://pastebin.com/7FQkk8aF

    I haven't fully tested copying more than one byte, though with the sprite disappearing and not knowing where the source byte is from, it's difficult to locate and confirm.
     
    Last edited by a moderator: Sep 12, 2015
  7. Flamewing

    Flamewing Elite Hacker Member

    Joined:
    Aug 28, 2011
    Messages:
    37
    Location:
    France
    Another hardware quirk: the VDP has a shadow/highlight mode which... well, allows you to do this:

    [​IMG]

    (credit Retro wiki).

    Now, notice Tails' bright orange; that color corresponds to palette entry 14 (0-based). In shadow/highlight mode, entries 14 and 15 of palette line 3 (0-based) are special when used on sprites: they either highlight (14) or shadow (15) the corresponding pixels of lower-priority plane tiles instead of being rendered. But due to a bug in the VDP, a sprite using color 14 in any palette line is never shadowed, and is, instead, displayed in normal brightness. Which is Tails' orange.

    Regen, Fusion, Gens/GS, Gens/GS II and Genesis Plus GX all render it correctly; plain Gens (even the rerecording version) and Exodus do not. Let that sink in…
     
    Last edited by a moderator: Sep 12, 2015
  8. Irixion

    Irixion Well-Known Member Member

    Joined:
    Aug 11, 2007
    Messages:
    670
    Location:
    Ontario, Canada
    Does this topic exist because "it's 2015" and our hacks are "shit and fatal"? In all seriousness, how likely are people to encounter some of these issues? It's interesting to see what a 'hardware' approach to rom hacks is ingenious. Regen is generally my test bench, and then Exodus, but it doesn't run at full fps on my system. I have avoided using vanilla Gens for years now, because I myself have even ran into things going wonky on that.
     
  9. MarkeyJester

    MarkeyJester ♡ ! Member

    Joined:
    Jun 27, 2009
    Messages:
    2,867
    If you would like my opinion on the matter of preferred emulation, never rely on a single emulator, this includes Regen and Exodus.  I have on many occasions come across issues that do not occur on hardware, Kega, or Gens, yet Regen and Exodus show to be termpermental and incorrect with.  No emulators currently available are perfect, though don't let hardware/emulation compatibility deter you from getting your work done.

    It's a matter of balance.  Your work is no good if you've wasted your time making it compatible, rather than playable/enjoyable, likewise, it would be unwise not to perform standard tests now and again.  Test on as much as possible, try not to lock it to a specific emulator out of some "arrogant preference principle", but don't get swept up in the process and forget that the sole purpose is; to make an enjoyable game.
     
  10. Clownacy

    Clownacy Retired Staff lolololo Member

    Joined:
    Aug 15, 2014
    Messages:
    1,021
    You should have seen my post's original opening, but, you know, Pro Members gotta set an example and all.

    But really, he can't stand that hacks aren't 100% to his liking, yet he defends himself in the description with "if you don't like it, don't watch it". Bloody hypocrite.
    Not many people get too extravagant with their engine changes, which I guess is why I haven't seen another one of these threads, but, as you know by this point, the odd address thing is everywhere, and I can only assume, with how much one can do with a hack nowadays, someone's gonna run into one of these problems sooner or later. I only noticed the VDP read thing because I went mad with regular expression, and that thing's cause took reverting half of my SVN commit log to find. I don't regularly test with hardware, though: I use RegenD all the time, and just mentally note all the hardware quirks I've encountered. It's worked well, so far.

    Speaking of, I should note that one quirk with DMA-ing from the MCD's Word-RAM, but I have no first-hand experience with it. Apparently, if you DMA from Word-RAM, the first two transferred bytes will always be incorrect. I think I also heard that either the source or destination pointer is always off by two bytes, as well. I dunno, it's not something I've ever looked into.
     
    Last edited: Jun 24, 2018
  11. redhotsonic

    redhotsonic Also known as RHS Member

    Joined:
    Aug 10, 2007
    Messages:
    2,969
    Location:
    England
     Don't forget Sonic 3D's errortrap; the best one out there!

    (forgive me on my formatting on the quote; I'm on my phone).
     
  12. Pacca

    Pacca Having an online identity crisis since 2019 Member

    Joined:
    Jul 5, 2014
    Messages:
    1,175
    Location:
    Limbo
    I wonder if any of these could be used to make a hack test if it's being run on emulator or hardware, and potentially even which emulator it's running in. A lot of these seem to just result in crashes, and I guess a feature like that could be minor, but it'd be cool if a hack rewarded you for playing on hardware, or even tried to compensate for some emulator flaws by running differently. Just a thought...
     
  13. Clownacy

    Clownacy Retired Staff lolololo Member

    Joined:
    Aug 15, 2014
    Messages:
    1,021
    Recently, I ran into a bug while playing Big's Fishing Derby on hardware. Apparently, the cause was a rare hardware bug, where, if the destination address of a DMA transfer is written to the mirror address of the VDP data port, the transfer would occasionally go wrong. flamewing fixed this in his Ultra DMA Queue with this commit.

    Here's an odd tidbit. Find a collection of official hardware manuals, and look for GENESIS TECHNICAL BULLETIN #27 (sega/Bulletins/Gen-tech/Tech27-02.gif in the DDK). According to it, Joypad_Read (S2 Git label) should have twice as many NOP instructions as it normally does, so there should be groups of four rather than two. Apparently just two NOPs isn't long enough for a port to update reliably. Considering the date printed on this bulletin (January 24th, 1994), it's likely this wasn't known at the time of S1/S2's development, but even S&K still uses 2 NOPs, which begs the question of if Sega's own first-party developers were just lazy, or if the bug isn't real.
     
  14. MarkeyJester

    MarkeyJester ♡ ! Member

    Joined:
    Jun 27, 2009
    Messages:
    2,867
    For the NOP related issue, it might be worth considering that the speed of the ROM access would have increased during the years of the Mega Drive's lifetime, as the cartridge hardware would have improved.  It might be the explanation for the need of more NOP instructions (though I do lack the proper equipement to test this).
     
    Last edited by a moderator: Nov 4, 2015
  15. AURORA☆FIELDS

    AURORA☆FIELDS so uh yes Exiled

    Joined:
    Oct 7, 2011
    Messages:
    759
    Ok I ran into some really weird quirk with Regen. It seems to overwrite random parts of RAM with garbage after I give myself hyper Sonic. It doesnt always do this but does so very often. No other emulator or hardware I tested have this issue, and nothing in the code should be able to do this either. Even moving the Hint and Vint addresses to beginning of RAM crashes the game on Vertical Interrupt (because of garbage data).

    There is also random crashing in parts of the level, which also depends on how the RAM is laid out. Again, no other emulator nor hardware do this, and since Regen closes immediately I can't even debug it.
     
  16. redhotsonic

    redhotsonic Also known as RHS Member

    Joined:
    Aug 10, 2007
    Messages:
    2,969
    Location:
    England
    I wonder if it works on real hardware? Seeming as Regen is meant to act as close to hardware as possible (more than Kega and others anyway).
     
  17. AURORA☆FIELDS

    AURORA☆FIELDS so uh yes Exiled

    Joined:
    Oct 7, 2011
    Messages:
    759
    Yes, tested on both EU Mega Drive and US Genesis; works 100% at all of these cases. As I said "no other emulator or hardware have this issue". =p
     
  18. redhotsonic

    redhotsonic Also known as RHS Member

    Joined:
    Aug 10, 2007
    Messages:
    2,969
    Location:
    England
    Shit, sorry, I must have missed that bit.

    The only bug I was aware is that when Regen saves to SRAM and when you try to create files like CRAM or VRAM files, it always swaps the bytes over. So this new bug is new to me. One last suggestion, have you tried a different version of Regen? And/or does it work on another system (like a 32-bit/64-bit)? It's know they can interrupt with emulators at certain things.
     
  19. Clownacy

    Clownacy Retired Staff lolololo Member

    Joined:
    Aug 15, 2014
    Messages:
    1,021
    YM D0/D1 Writing

    Here's an interesting one. This is more of an issue with emulators, but it seems like no one even knows you can do this on hardware.

    The YM2612/YM3438 has two sets of ports: A0 and A1, and D0 and D1. What's weird is that there doesn't seem to be any point to D1. You can send all data to D0, and it works fine. The problem is, this bit of trivia is so obscure that I've only seen it supported in one emulator: it breaks Kega, Regen, and Gens/GS, but it works in Genesis Plus GX.

    I've tested this with both my Model 2 MD, which uses an ASIC YM3438, and my Model 1, which uses a YM2612.

    The odd thing about this is that you can't do it the other way around: writing all data to D1 just breaks everything.

    YM Status Reading

    While I'm here, here's another quirk with the YM ports: it turns out there's a pretty important difference between the YM2612 and the YM3438 - while on the YM3438 you can read the status from any of its four ports, on the YM2612 you can only read it from A0. This caused a bug in my Super/Hyper Sonic in S1 hack where the audio was completely broken on most Model 1 MDs because it was reading the status from whatever port happened to be used last.

    Emulators only ever seem to emulate the YM3438 behaviour, with the exception of Genesis Plus GX's cycle-accurate Nuked 2612 core.

    EDIT: Looking at Nuked-OPN2's source, this actually only affects ASIC YM3438s. Discrete YM3438s should have the YM2612 behaviour. This isn't the most noteworthy, since no regular Mega Drives use the discrete versions, but I think it's still worth pointing out.

    Mega CD Word-RAM DMA Bug

    I know I've brought this up previously in this thread, but I was vague, and just relaying what I'd heard from other people. Technically I still am, but that's not the point...

    Anyway, while reading through a Mega CD BIOS disassembly, I was able to find out the specifics of this bug.

    As I've already explained, this is a bug that occurs when you perform a DMA from the Mega CD's Word-RAM. I don't know if this is the case for Prg-RAM. The problem is that the transfer will always go wrong, corrupting the first word of transferred data, and also reading from the wrong source address.

    Here's the disassembled DMA function:

    Code:
    ; =============== S U B R O U T I N E =======================================
    
    ; Input parameters:
    ; d0 - Dest address
    ; d1 - Source address
    ; d2 - DMA length
    
    dmaTransferToVramWithRewrite:       ; CODE XREF: ROM:000002D4j processDmaTransferQueue+8p ...
       move.l  a1, -(sp)
    
       movea.l d1, a1
       addq.l  #2, d1
       asr.l   #1, d1
    
       lea (VDP_CONTROL).l, a6
    
       ; Set auto-increment to 2
       move.w  #$8F02, (a6)
    
       ; Enable DMA operation
       move.w  (vdpRegCache+2).w, d3
       bset    #4, d3
       move.w  d3, (a6)
    
       ; Set transfer count
       move.l  #$940000, d3
       move.w  d2, d3
       lsl.l   #8, d3
       move.w  #$9300, d3
       move.b  d2, d3
       move.l  d3, (a6)
    
       ; Set source
       move.l  #$960000, d3
       move.w  d1, d3
       lsl.l   #8, d3
       move.w  #$9500, d3
       move.b  d1, d3
       move.l  d3, (a6)
    
       swap    d1
       move.w  #$9700, d3
       move.b  d1, d3
       move.w  d3, (a6)
    
       ; Set destination
       ori.l   #$40000080, d0
       swap    d0
       move.w  d0, (a6)
    
       swap    d0
       move.w  d0, -(sp)
       move.w  (sp)+, (a6)
    
       ; Disable DMA operation
       move.w  (vdpRegCache+2).w, (a6)
    
       ; Rewrite the first dword (I don't know why)
       andi.w  #$FF7F, d0
       move.l  d0, (a6)
    
       move.l  (a1), -4(a6)
    
       ; Reset previous auto-increment
       move.w  (vdpRegCache+$1E).w, (a6)
    
       movea.l (sp)+, a1
       rts
    ; End of function dmaTransferToVramWithRewrite
    As it shows, not only does the first word have to be rewritten, but the source address needs to be offset by 2.

    Oddly, Quackshot does a similar thing whenever it DMAs from regular RAM: though it doesn't offset the source address, it does rewrite the first word. I have no idea why it does that, since Sonic 1 doesn't bother, and it works just fine.

    EDIT: It turns out the DDK recommends this, for some reason.

    Sega Mega Drive & Genesis Classics DISK Flag Bug

    The following two bugs might be fixed by now. I haven't tested the new update that added Mirror Mode and all that gaff.

    There are a few ways to detect if a Mega CD is attached: you can try to poke the BIOS and find a magic number, or you can go the 'official' way and check the DISK bit. The DISK bit is the fifth (counting from zero) bit of the byte at $A10001. It's set to 0 when there's a Mega CD attached, and 1 when there isn't.

    Sega Mega Drive & Genesis Classics' emulator doesn't emulate this: the bit is 0 no matter what, and since the emulator doesn't support the Mega CD... this can lead to some crashes.

    Just as a heads-up, checking the DISK bit isn't really the best way to detect a Mega CD anyway: the major problem being the bit gets set to 0 even if the Mega CD doesn't have its power plugged-in. IIRC that's not a problem for the BIOS magic method.

    Sega Mega Drive & Genesis Classics SRAM Bug

    Chances are you've already heard of this one: Sega Mega Drive & Genesis Classics sucks so much that SRAM doesn't even work properly in hacks.

    The issue is that hacks seem to inherit the SRAM properties of the 'parent' ROM. For example, if you upload a hack of Sonic 1, it will have no SRAM because S1 didn't have SRAM. Likewise, if you make a hack of Sonic 3, you will only get $200(?) bytes of SRAM because that's what S3 had.

    I wrote a wrapper for the emulator DLL to fix this here, but good luck getting people to use it. Other than that, there's nothing you can do about it.

    Controller Reads Break Z80 Timings

    This is a pretty cryptic one. You might notice, for example in S2's Vint_PCM, that the game stops the Z80 before reading from the controller. This has to do with a hardware bug, where reading from the controller area at a very specific time causes the Z80's timings to be sped up. This supposedly causes the Z80 to be unable to read from ROM.

    This bug was mentioned by Sega themselves in Addendum 3 of the DDK (sega/Bulletins/Addendum/Add03-01.gif), showing that this affects the entire $A100XX area.

    This doesn't seem to affect modern flashcarts because they're fast enough to handle the faster Z80 timings.

    Z80 DMA Pause Redux

    Previously I had no idea why the Sonic games felt the need to pause the Z80 before performing a DMA. However, the Overdrive 2 technical document claims to shed some light on this:

     
    Last edited: Mar 1, 2019
  20. Clownacy

    Clownacy Retired Staff lolololo Member

    Joined:
    Aug 15, 2014
    Messages:
    1,021
    I can't believe it's taken me over three years to notice I never mentioned this one, considering how often people run into it:

    The DMA Boundary Bug

    While working on a hack, you might have noticed something like this:

    [​IMG]

    It happens at seemingly random, and goes away when you edit the ROM some more. In S2 and S3K (and S1 if you follow the Spin Dash guide), Sonic's art is transferred from ROM to VRAM via DMA. What triggers this error is that the transferred art crosses a 128KiB ($20000-byte) ROM boundary.

    As the DMA is copying data, if it reaches a boundary such as $5FFFE, instead of continuing onto $60000, it will wrap back around to the start of the 128KiB block, which in this case is $40000. This causes garbage data to be sent to VRAM, resulting in the glitched sprite seen above.

    Considering the DMA system copies words, not bytes, my guess is that this bug happens because the VDP internally only increments its pointer as a 16-bit value ($20000 in bytes is $10000 in words).

    As this is an issue with the VDP itself, there's no proper fix for it. The best you can do is work around it. Depending on what exactly you're doing, you have a few different options available:

    1. Carefully arrange the data that you intend to DMA, so that none of it crosses any boundaries. Helper macros that make the assembler throw an error whenever a 'binclude' or table crosses a boundary can be handy. This works for just about anything, but can be a little tedious.
    2. If you're using the DMA queue, you can modify it to detect and break boundary-crossing transfers into two separate transfers instead. Flamewing's Ultra DMA Queue and the 'Sonic3_Complete' DMA queue in the S&K disassembly do this. While this is a nice automated way of working around the issue, it comes at an unwelcome performance cost, even for DMA transfers that don't cross a boundary..
    3. Specifically for objects that use DPLCs, you have the option of using MainMemory's MapMacros (the updated version found in KEH's source code). These work similarly to the DMA queue method mentioned above, except it works by breaking boundary-crossing DPLCs in two instead. This is done at build-time, so there's no run-time penalty with this option (besides the obvious cost of still needing to perform two DMA transfers instead of one).
     
    Last edited: Mar 1, 2019