Hi all, I finally have my SNASM2 and SNSERVER ISA cards fully functioning! Now I'm trying to figure out the debugging process. I've successfully built my code with SNASM68K.EXE, outputting a COFF file with debugging info, and the debugger (SNBUG68K.EXE) recognises the debug info and shows me the source code alongside the disassembly (F**K YEAH!). Opening the Memory window shows me some recognisable strings from the binary, so I can assume it's correctly uploaded to the target and is waiting to run. Unfortunately, hitting Run (or trying to step over the first line of code) immediately halts the machine and I lose SCSI connectivity. If I don't load my own binary at all, I can successfully run, step through and watch whatever startup code gets loaded on bootup (the MegaCD intro screen and main menu, possibly), so I think the debugger and target are functioning fine, it's just my code isn't suitable. A few things from the SNASM2 and MegaCD Development manuals caught my eye: The appendix shows the mentioned CPU code: Apologies for the poor scans, my manual is a copy of a copy of a copy, which I then copied. So my question is: Where does this code go? The top of the binary is for the header and CPU vector table, and since it halts on the first line there's no way this code would actually run to be able to do anything useful. At the moment all of my exceptions/traps/interrupts just point to an empty subroutine which calls RTE.
I can explain that documentation. Basically, the SNASM system can only "take over" the physical hardware when one of the specified exceptions occur. The dev unit is looking for one of these exceptions to be triggered. This means, in order to properly debug on the hardware, you actually need to have an exception being triggered at a key point in your code, so that you can then drop down into the debugger and examine the current device state, step through code, etc. Now, for most games, the exception vectors they list should never occur, and if they do, the game will usually enter some kind of error state, IE, hang, or display an error screen. What that first document is telling you is that not only can your program not do this, you also need to think about deliberately generating one or more of these exceptions at some point, so that the debugger can take control. So, what should you do in code? You've got two main options: 1. Enable trace mode on startup ("ORI #$8000,SR") 2. At an appropriate point (probably the VINT handler) insert a "TRAP #0" opcode. The second option is probably better. Enabling trace mode will slow down program execution significantly, and the trace bit may be cleared by other code later than manually sets the supervisor register contents. In either case, you need to make sure that the exception you use to attach the debugger (either Trap0 or Trace) doesn't cause the program itself to stop running, so just do an RTE instruction for these exceptions. The documentation in Appendix A is talking about a MegaCD setup, where you're loading from a CD. In this case, you don't define a vector table yourself, that's defined by the BIOS. To load your program, you've got a fairly complex special CD "header" structure if you will, that the BIOS uses to load an initial program into RAM buffers for the main cpu (the one in the actual Mega Drive console) and the sub cpu (the one in the MegaCD console, usually used to handle CD access). In this scenario, usually the vector table for the main CPU would be write-only, and you couldn't modify it. On the Cross Products dev unit though, the ROM data is actually uploaded into a RAM buffer (a virtual boot ROM), and there's special internal memory addresses you can write to that enable and disable software write protect to these memory addresses. You need to disable write protect, and patch the vector table for the main CPU, to point to some kind of special handler for these exceptions. This handler is probably just doing an RTE too, but they've defined that instruction somewhere in memory for convenience, so you can just include this code block and not have to define a separate label. The sub cpu has a vector table located in RAM too, which is initialized by the BIOS. You need to disable write protect and patch this table too. That's what the supplied code does, and you would need to include this code as one of the first steps in your programs. Once this is done, the same rules apply as with a non-CD based game: you need to generate one of the specified exceptions at regular intervals so that the debugger can gain control.
Pretty sure the second and third images come from the SNASM-CD Installation Manual scans RetroJunkie released here: http://www.assemblergames.com/forum...SM-CD-Installation-Manual-05-1993-Version-1-0 As for the first, that comes from the SNASM2 MegaCD Development Manual. I know Teancum has scanned that manual in, but I don't know if a download of it has been made publicly available.
Yep, the scans were from the SNASM-CD Installation Manual, and the non-MegaCD version of the SNASM2 Development Manual, which doesn't contain anything that the MegaCD version already provides. Thanks Nemesis, I'll try those suggestions later. I take it once execution has been started, this "SCSI connectivity lost" behaviour will only happen for the brief period before Trace mode gets enabled, then the debugger reattaches? I'm only interested in the Genesis side at the moment, so I'll ignore that MegaCD code.
Detailed information such as the one provided by Nemesis is the reason I love this community Thank you!
I honestly don't know. I own a Cross Products dev unit, but no SNASM card at this point, so I've never been able to use the debugging features. Hopefully the error you're getting is just because no exceptions are being triggered, but it's possible there's something else wrong.
YESSSS!! It's working Enabling Trace wasn't enough, it seems some of the code from that MegaCD doc is necessary. I stripped it down and got away with just this: MSCSITrap equ 0x108008 MSCSIExcept equ 0x10800C move.b d0, 0x108000 ; Allow write to SNASM RAM move.l #0x02<<24+MSCSIExcept, 8+(4*0) move.l #0x03<<24+MSCSIExcept, 8+(4*1) move.l #0x04<<24+MSCSIExcept, 8+(4*2) move.l #0x05<<24+MSCSIExcept, 8+(4*3) move.l #0x06<<24+MSCSIExcept, 8+(4*4) move.l #0x07<<24+MSCSIExcept, 8+(4*5) move.l #0x08<<24+MSCSIExcept, 8+(4*6) move.l #0x09<<24+MSCSIExcept, 8+(4*7) move.l #MSCSITrap, 0x80 move.b d0, 0x10F001 ; Write protect SNASM RAM ori #0x8000, sr ; Enable TRACE exception An educated guess: those two memory locations (0x108008 and 0x10800C) contain some known code on a chip/in the BIOS of the devkit somewhere, which is designed to poke the debugger to give it entry (as per Nemesis' explanation), and the above code write-enables the "ROM inside RAM" to fudge the vector table and pull the old switcharoo on 8 exceptions and TRAP 0. If I set a breakpoint on any code after this block, I can step through the disassembly AND MY SOURCE (!!!) and view memory and registers and whatnot. I've uploaded a barebones sample here: http://www.fileden.com/files/2012/3/23/3282359/dbgtest.asm, hopefully someone else can use it as a starting point. I finally have a working SEGA Megadrive development kit! This is an absolute game changer for my project, and has been a dream of mine for many many years. Thank you so much to everyone who has helped out so far! I just need to wait for my SCART cable to arrive (I can't see anything!) and I'll get my "Hello World" demo running, then write a blog post of everything I've learned so far. Officially the happiest guy in the known universe.
Glad to hear it's working! Based on what you've reported, you should also be able to set your exception vector for the trap exception to 0x108008, and the other listed vectors to 0x10800C when compiling the ROM, and get the same result without needing that code block, other than enabling the trace mode that is.
Ok, some new findings: Setting the vector addresses directly in the table works great! I've kept the original block, though, since I can neatly wrap it around a #define when I don't want a debug build. However... I can't get this Trace exception working (with either method). If I sprinkle all of my code with TRAP #0 I can debug fine (until I leave a large enough gap for the debugger to lose track, then it disconnects) but I was under the impression the Trace exception gets called after every opcode. Is this the case? I'm setting up the status register with move.w #0x8000, sr (move instead or ori just to make sure the rest of the bits are cleared) which should give me: Trace ON, exception level 0 (all firing), supervisor mode OFF. It's not working though I've tried manually registering my own Trace exception handler and sticking TRAP #0 inside but it doesn't seem to get fired. Is there anything else I need to do for Trace to work?
Have a try at this: MSCSITrap equ 0x108008 MSCSIExcept equ 0x10800C move.b d0, 0x108000 ; Allow write to SNASM RAM move.l #0x02<<24+MSCSIExcept, 8+(4*0) ; set BUS ERROR vector move.l #0x03<<24+MSCSIExcept, 8+(4*1) ; set ADDRESS ERROR vector move.l #0x04<<24+MSCSIExcept, 8+(4*2) ; set ILLEGAL INSTRUCTION vector move.l #0x05<<24+MSCSIExcept, 8+(4*3) ; set DIVISION BY ZERO vector move.l #0x06<<24+MSCSIExcept, 8+(4*4) ; set CHK vector move.l #0x07<<24+MSCSIExcept, 8+(4*5) ; set TRAPV vector move.l #0x08<<24+MSCSIExcept, 8+(4*6) ; set PRIVILEGE INSTRUCTION vector move.l #0x09<<24+MSCSITrap, 8+(4*7) ; set TRACE vector move.l #MSCSITrap, 0x80 ; set TRAP #0 vector move.b d0, 0x10F001 ; Write protect SNASM RAM ori #0x8000, sr ; Enable TRACE exception Now location 0x108008 (MSCSITrap) must contain a jump to the debugger or something similar, while location 0x10800c (MSCSIExcept) must contain a RTE instruction. That explains why TRAP #0 works but trace does not. My theory anyway
I gave it a go but it didn't make much of a difference. It turns out the Trace exception IS being called, but my debugger wasn't stopping on the breakpoint (perhaps breakpoints inside interrupts is a grey area). I'm close to blaming my PC (again) - with the L1 and L2 cache switched off it is unbearably slow, and doing a single step in the debugger needs a few seconds to repopulate the code window and it's at that point I usually lose sync with the target. Maybe it's starving the ISA card of data while it's doing some processing? I really need a 486-based machine
Been a while (house move, job move, company breakdown, PC parts exploding) but I'm back on track and have news to share. I got my hands on an AMD K6 200mhz machine (£5 from a car boot sale!) and re-setup the SNASM2 rig. At first I had the same issue - drivers wouldn't load unless I disabled the L1 and L2 caches, but this BIOS has far more options than my old machine so I was able to tinker a bit and get it working with the caches enabled. Here's my findings: Problem 1: BIOS - Turn off anything mentioning 'shadowing' - Set Bus Speed to 7.19mhz or 7.16mhz - this is the biggie, it fixes incompatibilities with old ISA cards, found here: http://www.oldskool.org/guides/oldonnew/cripple - Enable ISA Line Buffer Problem 2: Debugger setup code in subroutine I tried being neat and tidy by shoving the debugger init code (see post #8 above) into a subroutine in another file. I think messing with the vector table in this way fudges something and the return address for the PC gets lost. Inserting it inline with my code worked. I might experiment with an inline 'include' to keep this block out of my nice clean init code. Problem 3: The TMSS Although polling the machine version returns >1, writing the 'SEGA' TMSS crashes the machine and I lose SCSI connectivity. All other exceptions I've seen handle correctly and just halt the CPU, but this one seems to be a special case. I'll revisit a better way to detect machine version/write TMSS when I get my UMDK (I'll need it running on a retail machine to figure it out). Problem 4: Status register In my framework init code I set the status register to 0x2700, which disables the trace bit (required for the debugger to poll states). It turns out changing this, even to what I consider the correct value, causes the system to lock up. It's ALWAYS initialised to 0x2700 when starting the machine so I'm just going to leave it be, and hope that's not just a nicety of the devkit/SNASM. Again, I'll waiting for my UMDK to figure out this one. Problem 5: Resetting the kit There were many resets, since I made many mistakes, but it was only after a while I realised that hitting the OFF switch didn't reset the machine as I'd expect - when turning back on the registers would hold their old values (the Status register was the main problem), some recognisable patterns would still be in RAM, and occasionally it would refuse to accept my binary and shut down the SCSI link. Using the debugger's "Reset Processor" option after turning the machine back on became a worthwhile habit. Anyway, I can now properly debug! Still nothing on screen yet, but that's my code and not the fault of the dev environment. I'll post back when I have Hello World!
Wow thanks for this info! I've been thinking about taking another crack at this and this is just what I need to give it another shot.
I'd be very interested to see if your TMSS is misbehaving, too. Last night I figured out that my DIP switch settings were the cause of the bad value when polling the machine version. I've properly set it to PAL/US and I now get a version of '1', and trying to access the VDP will immediately lock up the machine (as expected). However... writing the TMSS also locks it up Code: move.b 0x00A10001, d0 ; Move Megadrive hardware version to d0 andi.b #0x0F, d0 ; The version is stored in last four bits, so mask it with 0F beq @Skip ; If version is equal to 0, skip TMSS signature move.l #'SEGA', 0x00A14000 ; Move the string "SEGA" to 0xA14000 <-- locks up! @Skip: I've verified the header is correct (the machines title is "SEGA GENESIS", with the "SEGA" at location 0x100). To check if it's not a problem with the TMSS ROM itself in the machine, I've tried the following: Boot Sonic 1 from cartridge - displays "Produced by or under license from SEGA Enterprises..." message Upload a Sonic 1 ROM using the debugger and set it running - displays the red "checksum error" screen, which is bad, but at least it verifies the TMSS passed and the VDP was able to display some red I've also tried copying the TMSS setup code from the Sonic 1 disassembly at SonicRetro.org and turning it into a test ROM, still locks up. Running out of ideas. Anyone?
Ok, figured that one out, and it's really obvious now that I think about it. Adding to the list: Problem 6: You can't single step over the TMSS write My educated guess is this: Writing the TMSS string prompts the hardware to load a new ROM to display the "Produced by or under license from SEGA Enterprises..." screen before returning to user code. The process is probably something like: Backup regs Jump PC and run Restore regs Return PC This more than likely cuts the trace bit on the status register, so the debugger instructs the machine to "run instruction then halt" then sits and waits for the trace after the op has completed, which will never arrive, causing a deadlock. Problem 7: Alignment I was getting increasingly worried that after making very subtle changes (adding/removing a palette, putting a new variable in my memory map) I'd end up with spurious results - previously working code would crash with no indication of what's wrong, freezes and lockups all over the show, and I was terrified that it may be the old hardware (temperature of the caps, power issues, dry solder joints, etc). After some messing around I think my problem is alignment, something which has tripped me up in the past but I've managed to solve with a few NOPs, but now's probably the time to educate myself. I have no idea what I'm doing here, my only experience with alignment is from a C side (vector/matrix alignment for hardware float ops, audio data buffers, etc) but I have no idea about PC alignment, why it matters, or what I'm doing wrong to break it. To the internet!
More: (this is now a running log so I can type up a blog post on it all later ) Problem 8: My initial VDP registers were ass "Master System" mode was set for some reason (not picked up by emuators?), CRAM address didn't match what I was writing to, DMA settings were all kinds of wrong. I'll go through every single bit with a fine tooth comb this evening and make sure each one is correct, but for now I've copied them from the Sonic 1 disassembly. Problem 9: VRAM needed clearing Again, I've been spoiled by emulators and their clean initial state. The CRAM, VRAM and VSRAM were full of junk on startup, I've written some routines to clear them (I'll share in my next blog post). So far I can load palettes and change the backdrop colour. My first thing on screen! Getting there slowly I can't believe how different an experience this has been from working on an emulator. I have a full game written, dammit
Words cannot describe how happy I am right now Alignment is STILL giving me headaches, this time something is messing with the data alignment, I had to cheat by including my font and palettes above my code (they're all longword sized lines of data) else they'd become garbled. My DrawText routine doesn't seem to be setting the palette right, either, in an emulator the text is coloured. I need to strip down my framework piece by piece to figure out what's offsetting it. Either that, or there's some rules about reading from addresses that far down the ROM, is this a thing? Anyway, time for some sleep. Tomorrow, Pong! Then a two year overdue blog post Thank you SO MUCH to everyone who's helped me in this adventure so far! Finally some real progress!