I've looked more into the reading/writing process. I have now an idea of it actually works... and how stupidly easy it can be if you don't care of the data correction and just dump everything. OzOnE, can you also dump the GAP sector as well (since it's just one more)? So polling might be actually enough. DATA_REQ is for the sector, and C2_XFER is for the C2 sectors. Happy that I'm finally understanding it.
I'm not sure about the gap sector, but imagine it could probably be dumped too. Does it matter for dumping the user data sectors, since that's the main thing? We may need to dump the C1 + C2 data though, in case it does have the false data for copy protection. Yep, just like with ATAPI and many other disk protocols, it just sets the DATA_REQ flag whenever it has data ready to be transferred. btw, I'm just in the process of trying to add an int1 handler to Libdragon. I don't really know quite what I'm doing, but I think it will work as long as int1 is enabled by default. The rest of the RCP ints do seem to go through the MI (MIPS Interface), but looks like int1 has to be enabled separately. In inthandler.s under Libdragon, I'm checking for int1 by changing it like this... justaninterrupt: /* check for "pre-NMI" (reset) from PIF INT2*/ andi $30,k1,0x1000 beqz $30, notprenmi nop /* handle reset */ jal __onResetException nop j endint nop notprenmi: /* check for count=compare (COMPARE Int)*/ and $30,k1,0x8000 beqz $30,notcount nop mtc0 $0,$11 /* handle timer exception */ jal __TI_handler nop j endint nop notcount: /* check for cart/expansion INT1*/ and $30,k1,0x800 beqz $30,notexpint nop /* call int1 / expansion int handler */ OzOnE jal __EXP_handler nop notexpint: /* pass anything else along to handler */ jal __MI_handler nop Need to add the C stuff now and see if it compiles. OzOnE.
I asked for Gap data mostly for dumping the most data as possible, possibly doing like C2 data then USER + C1 data sectors then GAP sector for every blocks. I prefer to have everything, then for emulation purposes, in case there's something wrong, the game will correct the data on its own with C2 buffer. Even though C1 will already do the first work when dumping sectors. I'm really looking to get as most data as possible. Anyway, looking for it.
True. Will be good if we can capture the disks complete I guess, then we can generate everything possible for emulation like you say. I think the 7 C1 bytes are always available at the end of each sector, but not sure whether the C2 data is always read into the C2 buffer unless the Micro finds a C1 error it can't correct? I managed to get libdragon to compile with the interrupt 1 stuff added, but it's not triggering yet because I need to set the correct enable bit as well. Having to use Ubunutu on a VM is still a pain, but much faster on the new i5 PC. Even it's basic file manager is not always intuitive. lol I'll let you know if I get the interrupt working. OzOnE Still struggling with the interrupt stuff, but it's slowly making more sense... Page 351 onwards of the PDF shows how the ints are masked etc... http://datasheets.chipdb.org/NEC/Vr-Series/Vr43xx/U10504EJ7V0UMJ1.pdf It also says that there is no direct "Status Register" on the R4300, but it is accessed via the control part instead (COP0) using mfc0 / mtc0 etc. Libdragon normally only enables interrupt level 3, so only includes the two software ints, and int0 (RCP)... /* Interrupt pending bits */ #define CAUSE_IP8 0x00008000 /* External level 8 pending - COMPARE */ #define CAUSE_IP7 0x00004000 /* External level 7 pending - INT4 (GIO debug on U64 Board) */ #define CAUSE_IP6 0x00002000 /* External level 6 pending - INT3 (GIO debug on U64 Board) */ #define CAUSE_IP5 0x00001000 /* External level 5 pending - INT2 (PIF) */ #define CAUSE_IP4 0x00000800 /* External level 4 pending - INT1 (Exp / Cart / 64DD) */ #define CAUSE_IP3 0x00000400 /* External level 3 pending - INT0 (RCP) */ #define CAUSE_SW2 0x00000200 /* Software level 2 pending */ #define CAUSE_SW1 0x00000100 /* Software level 1 pending */ Libdragon enables int level 3 in the init_interrupts() routine in interrupt.c directly with asm... asm("\tmfc0 $8,$12\n\tori $8,0x801\n\tmtc0 $8,$12\n\tnop":::"$8"); ie... mfc0 $8,$12 // Move Status Reg ($12) from COP0 to reg $8 ori $8,0x401 // OR Immediate value 0x401 (set int level 3, and enable global ints.) mtc0 $8,$12 // Move back to SR in COP0. nop I tried hooking up the new int routine in the 64ddtest code and modifying inthandler.S in Libdragon like in post #25. I also changed the above asm to write 0x801 instead (to enable ints up to level 4), re-compiled Libdragon... make clean make all make tools install make install ...re-compiled 64ddtest (checksum does change, so Libdragon must have compiled OK), but 64ddtest now just freezes on the N64. I tried changing the asm line back to 0x401 as well, but it's still not working? I might have messed something else up, because it shows a blank screen even if I don't enable int1? The tricky part with the N64 is remembering which block the ints are coming from (CPU or RCP), and how it all links together. This is quite helpful though... http://level42.ca/projects/ultra64/Documentation/man/pro-man/pro07/index7.7.html I think I'll have a break for a while and try to debug this problem in a few hours. OzOnE. P.S. As an interesting side note, the above link shows that interrupts 3 and 4 on the R4300 (CAUSE_IP6 and CAUSE_IP7) are used by the U64 Dev board to talk to the SGI Indy and debug the code. Hmmm, I wonder if any commercial game carts still have debug handlers in there? OK, so it's when I set the Status reg to 0x801 to enable int1 that makes it freeze. I think the int is triggering the routine OK, but I'm not clearing the int on the 64DD afterwards, so it gets stuck in a loop. (The int from the 64DD shouldn't affect the RCP regs directly since it goes directly to the R4300.) Ahhh, seems setting the Status reg bits (COP0) is just a mask for the ints. I should have been setting bits 10 AND 11 to allow hardware ints 0 and 1 (RCP and 64DD / Expansion). Looks like Libdragon does only using int0 so it can use the basic RCP stuff, but it doesn't appear to enable any of the other ints (normally). So, the test program is running again, but now it's freezing at the "Resetting 64DD..." message. Hmmm. I think we're getting somewhere though. Better to have something on screen rather than nothing. OzOnE. Right, I spent many hours trying to get interrupts working properly, but it just freezes whenever I leave the code in inthandler.S in Libdragon. It freezes even if I'm not enabling int1, so the int handler must be seeing bit 11 of the Status reg set (int1 flag) whenever the 64DD triggers an int?... notcount: // check for cart/expansion INT1 // and $30,k1,0x800 // beqz $30,notexpint // nop // call int1 / expansion int handler // jal __EXP_handler // nop // j endint // nop Anywho, long story short, I got it to read the disk ID by polling instead. https://mega.co.nz/#!SlxyjT5A!ezsW6RhJBYXrnzkDhkr91kINfWCvXmAbBftJqebhmm4 You have to press the A button to read the ID atm, but I wanted to post the code as soon as it worked. It also sometimes needs a second button press to read a valid ID, so some timing / flags issues to sort out. Basically, you need to do the BM Reset and Start BM stuff just after do the Read Seek. This gets the Buffer Manager on the drive ready to receive the sector data from the Micro. We should soon be able to read full blocks of data from the disk, then work out how to write it back to SD card. @Luigiblood - please ignore the interrupt routine for now, it's not being used. I also commented out the extra parts in inthandler.S in my Libdragon sources anyway, so I'm not using 64DD ints atm. OzOnE.
At least happy to see that I got part of the code just right. Anyway, let's forget interrupts since it seems we can work out without them, and working them out seems to be more of a pain than anything since libdragon isn't really great in that matter. Polling does seem to be enough, and that's cool.
USA/JAP GoldenEye. There are others too that don't come to mind. I only work at ASM level so I can't help you with source code, but the interrupt flag numbers are nothing more than the bitshift needed to read them. Typically you don't read them directly though but set a callback to occur when they flip. Needless to say, libdragon does threading a little different from the official libraries (or at least in a legally-distinguishable way) so you'd have to look at the mechanism them implemented.
I don't know if I did it just right, but I made a generalized version of the read sector function (LBA, and Track based): https://github.com/LuigiBlood/64dd/tree/master/64ddtest Made it based on your code, OzOnE. I have no idea if it works.
Very nice update since I last tested it; the full date and time work and update in real time! The disk ID does not work, however. Pressing A makes the drive spin and access light blink, but no disk ID is printed.
Oh man I'm having no luck. But I tested it a lot with someone else and I pretty much came to the conclusion... Timing issue. I don't think I did anything different than OzOnE, which worked. I'll give it an update tomorrow.
The system region (LBAs before 24) are read in (I think) block mode by the library. You should be able to find that in the doc I sent you. Could be a peculiarity preventing larger grabs in that region, or they're bonkers.
FINALLY GOT IT TO WORK with a generalized function. You can look at the beauty here: https://github.com/LuigiBlood/64dd However, it sometimes needs a few more tries to get it read right. But apparently once it's read right it's always that way, so I'm guessing it's all about doing it at the right time. I tried with LBA 14 and 15. Both worked, so I assume I got the Sector offset right as well. Also, maybe the HARD reset is kind of taking a while for the disk. I got some really weird values. Anyway, Sector reading is getting somewhere. The next part is getting it stable and then block reading, since we can make it faster that way I imagine.
Not to burst your bubble, but I'm not getting the correct disk ID. I'm getting the same result on two different disks.
Did you press A several times? Like 5 or 6 times? It's totally what happened in the case of someone who tested private builds for me.
I did, and I got some thing like ↕VxM. I'm unsure of what the last letter was, but the double sided arrow is the main problem there. I'll try it again later, however, to double check.
OK, I think we do need ints in this case. I think what's happening is that we're grabbing the sector data as soon as the BM (Buffer Manager) stops running, but it could be that the interrupt itself happens shortly after that. So, it's possible the data isn't quite ready once the BM stops, and the only way to reliably capture this is with an int routine. I'm pretty sure the ints are working (when I enable int1), but I can't tell if it's actually running my int routine or not and it just freezes. I'll have another play with the code though. @LuigiBlood - it's good that you've added the offset stuff to BM_CTL as well now... io_write(ASIC_BM_CTL, (START_BM | BM_MODE | ((uint32_t)sector << 16))); // Set SECTOR offset (within the Track) This sets the start offset of the chosen sector based on whether the LBA is even or odd (0 or 90)... #define USR_SECS_PER_BLK 85 // Number of user sectors in a logical block #define C2_SECS_PER_BLK 4 // Number of C2 sectors in a logical block #define GAP_SECS_PER_BLK 1 // Number of GAP sectors in a logical block #define HALF_SECS_U16 0x5900 // Valid number of sectors in a logical block(excluding GAP)<<16 // Note, 89 decimal (0x59 !) #define ALL_SECS_PER_BLK (USR_SECS_PER_BLK + C2_SECS_PER_BLK + GAP_SECS_PER_BLK) // Note, 90 decimal (0x5A !) Each block on the disk has the 85 User (data) sectors, then 4 sectors of C2 data, then 1 Gap sector. To read the second (odd) block in a track, you have to add 90, so it "skips" the first block a track. To read the C1 data as well, I think we can just change the SecSize in HOST_SECBYTE. There are 7 extra Bytes of data at the end of each sector for C1 data (AFAIK). I see that we don't really need the Offset variable as you're setting the offset correctly with the readDiskSectorLBA() routine. void readDiskSectorLBA(uint8_t LBA, uint8_t sector, void * buffer) { readDiskSector((LBA >> 1), (sector + (ALL_SECS_PER_BLK * (LBA & 1))), buffer); } This makes more sense really, since the readDiskSector() routine then gives us "raw" access to each sector. A bit different from the way I did it, but I see that it's a good thing. I wonder if anyone knows Shaun who made Libdragon? https://dragonminded.com/n64dev/libdragon/ I'll just e-mail him about adding interrupts and see if he replies... OzOnE.
I am happy to say that I finally got some good reading results today. With no interrupts, it's been reported that my readSector function is working the first time, with just polling. You can see the source at the same place: https://github.com/LuigiBlood/64dd And the ROM is here: https://raw.githubusercontent.com/LuigiBlood/64dd/master/64ddtest/64ddtest.v64 You can try it yourself, it should get the Disk ID with ONLY ONE PRESS. The guy changed disks as well and he got the same good results. There are weird stuff though, the 64DD reports a BM error even though the work is done right.
Ahh, you're checking for the BM_INT - genius! lol We don't need no stinking interrupts. I completely forgot that flag even existed. Yep, the 64DD is quite fussy about the exact order of things, and you have to grab the data pretty soon after the int triggers. I found before that there seems to be a finite time between when you grab the sector data and when it wipes the buffer ready for the next incoming sector. Excellent - it works every time for me too, DMPJ from the Disk ID. Right, now we have to write back to SD card. Personally, I don't care if I have to swap SD cards before writing (remove the card with the ROM / ED64 on), then just write raw sectors directly to the second card. We'll probably come across the false C2 data at some point, but we'll worry about that if / when it happens. I don't have much time to test tonight, but will have a look tomorrow. EDIT: I did notice the BM Error thing on the Arduino version as well. I'll check my N64 logs later to see if it happened with the real disks / transfers too. btw, I'm fairly sure the zones count in the order 0,1,2,3,4,5,6,7 then 8,7,6,5,4,3,2,1. A good way to compare the zones and tracks is using the DDDump proggy in the N64 SDK. IIRC, they removed it from the newest SDK, but you can still find the older version on the Interwebz. OzOnE.
We should figure out the Block transfer though, that would be a bit faster to get everything... and would be interesting for me to emulate it. Also, I looked into the zones... Based on the SDK documentation, it's ROM area first, then RAM area... meaning we might need to set the Disk Type first. The zones actually changes. Type 0: ROM(0;1;2;2;1);RAM(3;4;5;6;7;8;7;6;5;4;3) Type 1: ROM(0;1;2;3;3;2;1);RAM(4;5;6;7;8;7;6;5;4) Type 2: ROM(0;1;2;3;4;4;3;2;1);RAM(5;6;7;8;7;6;5) [...] Type 6: ROM(0;1;2;3;4;5;6;7;8;7;6;5;4;3;2;1) And there's a Disk Type command actually. (ASIC_SET_DTYPE)
I'd never seen that table in the SDK before. Interesting. Ohh, I see now - So, the number of bytes per sector / tracks in each zone does change, but the disk type also defines which zones are used for ROM / RAM... (difference between the top and bottom tables in the intro manual)... http://n64.icequake.net/doc/n64intro/kantan/step4/index2.html I can only imagine that the reason it counts from say 0,1,2,3,3,2,1 is because the last three zones are actually when it switches from head 0 (top side) to head 1 (underside). We'll have to read the disk ID first as you say, then also have a table which defines when the head switch should take place. Actually, for ripping the whole disk, we don't really need to read the ROM and RAM areas separately, but it will probably need that alternative MSEQ data for reading the RAM areas. Hmm, a little bit more complexity to the code, but shouldn't be too bad. We can just read the Disk ID, define the ROM and RAM areas based on disk type, then choose to only read the ROM area for example. That could also explain why I was getting bad blocks around LBA 1228 on Mario Artist because those were in the RAM zones, and the tracks / blocks were unformatted? I'm not sure what the SET_DTYPE command does tbh, I don't think I've ever Mario Artist write that command? btw, reading whole blocks should just be a case of setting the Xferblks bit, and looping to read all the sectors in the block. OzOnE.
The Disk Type isn't told on the Disk ID information. And looking at a specific header file, I think we need to look at LBA 0/1 instead to have that information. Probably it is that way. Possibly because the IPL did it, and Mario Artist didn't need to do that. Or there's a default Disk Type, or the 64DD actually does that automatically. My guess is that it's all about looking at BM_INT and then copy the data?