// Direct-write a sequence of bytes to the specified PHYSICAL address. Bear in mind that the // mapping from logical to physical addresses are done via the SSF2 registers; the mapping is // 1:1 only for the lowest 512KiB, and is fixed for only the lowest 512KiB and the top 512KiB. // The area to be written must have an even start address and length. The MegaDrive need not // be suspended at the monitor, but overwriting the code that the MD is executing is obviously // a recipe for disaster. // int umdkPhysicalWriteBytes( struct FLContext *handle, uint32 address, const uint32 count, const uint8 *const data, const char **error) { int retVal = 0; FLStatus status; uint8 command[8]; uint32 wordAddr; uint32 wordCount; // Next verify that the write is to an even address, and has even length CHECK_STATUS(address&1, 2, cleanup, "umdkPhysicalWriteBytes(): Address must be even!"); CHECK_STATUS(count&1, 3, cleanup, "umdkPhysicalWriteBytes(): Count must be even!"); // Get word-count and word-addres wordAddr = address / 2; wordCount = count / 2; // Prepare the write command prepMemCtrlCmd(0x00, wordAddr, command); prepMemCtrlCmd(0x80, wordCount, command+4); // Do the write status = flWriteChannelAsync(handle, 0x00, 8, command, error); CHECK_STATUS(status, 4, cleanup); status = flWriteChannelAsync(handle, 0x00, count, data, error); CHECK_STATUS(status, 5, cleanup); cleanup: return retVal; }
// Direct-write a binary file to the specified address. The area of memory to be written must reside // entirely within one of the two direct-writable memory areas (0x000000-0x07FFFF and // 0x400000-0x47FFFF, mapped to SDRAM pages 0 and 31 respectively) and must have an even start // address and length. The MegaDrive need not be suspended at the monitor. // int umdkDirectWriteFile( struct FLContext *handle, uint32 address, const char *fileName, const char **error) { int retVal = 0; FLStatus status; uint8 command[8]; uint32 wordAddr; size_t byteCount; size_t wordCount; uint8 *const fileData = flLoadFile(fileName, &byteCount); CHECK_STATUS(!fileData, 1, cleanup, "umdkDirectWriteFile(): Cannot read from %s!", fileName); // Verify the write is in a legal range if ( isInside(MONITOR, 0x80000, address, byteCount) ) { // Write is to the UMDKv2-reserved 512KiB of address-space at 0x400000. The mapping for this // is fixed to the top 512KiB of SDRAM, so we need to transpose the MD virtual address to // get the correct SDRAM physical address. address += 0xb80000; } else if ( !isInside(0, 0x80000, address, byteCount) ) { // The only other region of the address-space which is directly host-addressable is the // bottom 512KiB of address-space, which has a logical-physical mapping that is guaranteed // by SEGA to be 1:1 (i.e no transpose necessary). So any attempt to directly access memory // anywhere else is an error. CHECK_STATUS( true, 1, cleanup, "umdkDirectWriteFile(): Illegal direct-write to 0x%06X-0x%06X range!", address, address+byteCount-1 ); } // Next verify that the write is to an even address, and has even length CHECK_STATUS(address&1, 2, cleanup, "umdkDirectWriteFile(): Address must be even!"); CHECK_STATUS(byteCount&1, 3, cleanup, "umdkDirectWriteFile(): File must have even length!"); // Get word-count and word-address wordAddr = address / 2; wordCount = byteCount / 2; // Prepare the write command prepMemCtrlCmd(0x00, wordAddr, command); prepMemCtrlCmd(0x80, wordCount, command+4); // Do the write status = flWriteChannelAsync(handle, 0x00, 8, command, error); CHECK_STATUS(status, 4, cleanup); status = flWriteChannelAsync(handle, 0x00, byteCount, fileData, error); CHECK_STATUS(status, 5, cleanup); cleanup: flFreeFile(fileData); return retVal; }
// Write some bytes to the specified channel, synchronously. // DLLEXPORT(FLStatus) flWriteChannel( struct FLContext *handle, uint8 chan, size_t count, const uint8 *data, const char **error) { FLStatus retVal = FL_SUCCESS, fStatus; fStatus = flWriteChannelAsync(handle, chan, count, data, error); CHECK_STATUS(fStatus, fStatus, cleanup, "flWriteChannel()"); fStatus = flAwaitAsyncWrites(handle, error); CHECK_STATUS(fStatus, fStatus, cleanup, "flWriteChannel()"); cleanup: return retVal; }
// Asynchronously direct-read a sequence of bytes from the specified address. The area of memory to // be read must reside entirely within one of the two direct-readable memory areas // (0x000000-0x07FFFF and 0x400000-0x47FFFF, mapped to SDRAM pages 0 and 31 respectively). Unlike // umdkDirectReadBytes(), this function must be given an even start address and count. The // MegaDrive need not be suspended at the monitor. This does an asynchronous read, so each call to // this function must match a later call to FPGALink's flReadChannelAsyncAwait(), to retrieve the // actual data. // int umdkDirectReadBytesAsync( struct FLContext *handle, uint32 address, const uint32 count, const char **error) { int retVal = 0; FLStatus status; uint8 command[8]; // First verify the read is in a legal range if ( isInside(MONITOR, 0x80000, address, count) ) { // Read is from the UMDKv2-reserved 512KiB of address-space at 0x400000. The mapping for this // is fixed to the top 512KiB of SDRAM, so we need to transpose the MD virtual address to // get the correct SDRAM physical address. address += 0xb80000; } else if ( !isInside(0, 0x80000, address, count) ) { // The only other region of the address-space which is directly host-addressable is the // bottom 512KiB of address-space, which has a logical-physical mapping that is guaranteed // by SEGA to be 1:1 (i.e no transpose necessary). So any attempt to directly access memory // anywhere else is an error. CHECK_STATUS( true, 1, cleanup, "umdkDirectReadBytesAsync(): Illegal direct-read from 0x%06X-0x%06X range!", address, address+count-1 ); } // Next verify that the read is to an even address, and has even length CHECK_STATUS(address&1, 2, cleanup, "umdkDirectReadBytesAsync(): Address must be even!"); CHECK_STATUS(count&1, 3, cleanup, "umdkDirectReadBytesAsync(): Count must be even!"); // Prepare the read command prepMemCtrlCmd(0x00, address/2, command); prepMemCtrlCmd(0x40, count/2, command+4); // Send the read request status = flWriteChannelAsync(handle, 0x00, 8, command, error); CHECK_STATUS(status, 8, cleanup); // Submit the read status = flReadChannelAsyncSubmit(handle, 0x00, count, NULL, error); CHECK_STATUS(status, 9, cleanup); cleanup: return retVal; }
// Tell the monitor to continue execution with the (possibly new) register/memory context, until // a breakpoint is hit. If there is no breakpoint in the execution-path, this function will wait // forever. // int umdkContWait( struct FLContext *handle, bool debug, struct Registers *regs, const char **error) { int retVal = 0, status, i; uint8 tmpData[65536]; size_t scrapSize; uint32 vbAddr, actualLength; uint16 oldOp; union RegUnion { struct Registers reg; uint32 longs[18]; uint8 bytes[18*4]; } *const u = (union RegUnion *)regs; const uint8 *recvData; FILE *file = NULL; if ( debug ) { // Read RAM status = umdkReadBytes(handle, 0xFF0000, 65536, tmpData, error); CHECK_STATUS(status, status, cleanup); // Save it file = fopen("ramBefore.bin", "wb"); CHECK_STATUS(!file, 13, cleanup, "umdkContWait(): Unable to open ramBefore.bin for writing!"); fwrite(tmpData, 1, 65536, file); fclose(file); file = NULL; // Open trace log file = fopen("trace.log", "wb"); CHECK_STATUS(!file, 13, cleanup, "umdkContWait(): Unable to open trace.log for writing!"); } // Get address of VDP vertical interrupt routine and its first opcode status = umdkDirectReadLong(handle, VB_VEC, &vbAddr, error); CHECK_STATUS(status, status, cleanup); status = umdkDirectReadWord(handle, vbAddr, &oldOp, error); CHECK_STATUS(status, status, cleanup); // Write monitor address to illegal instruction vector status = umdkDirectWriteLong(handle, IL_VEC, MONITOR, error); CHECK_STATUS(status, status, cleanup); if ( debug ) { // Disable tracing (if any) & clear junk from trace FIFO tmpData[0] = 0x00; status = flWriteChannel(handle, 0x01, 1, tmpData, error); CHECK_STATUS(status, 25, cleanup); status = flReadChannel(handle, 0x03, 1, tmpData, error); CHECK_STATUS(status, 20, cleanup); scrapSize = tmpData[0] << 8; status = flReadChannel(handle, 0x04, 1, tmpData, error); CHECK_STATUS(status, 20, cleanup); scrapSize |= tmpData[0]; while ( scrapSize ) { // Clear junk from FIFO status = flReadChannel(handle, 0x02, scrapSize, tmpData, error); CHECK_STATUS(status, 20, cleanup); // Verify no junk remaining status = flReadChannel(handle, 0x03, 1, tmpData, error); CHECK_STATUS(status, 20, cleanup); scrapSize = tmpData[0] << 8; status = flReadChannel(handle, 0x04, 1, tmpData, error); CHECK_STATUS(status, 20, cleanup); scrapSize |= tmpData[0]; } // Enable tracing tmpData[0] = 0x02; status = flWriteChannelAsync(handle, 0x01, 1, tmpData, error); CHECK_STATUS(status, 25, cleanup); } // Set up the continue command and execute it status = umdkDirectWriteWord(handle, CB_INDEX, CMD_CONT, error); CHECK_STATUS(status, status, cleanup); status = umdkDirectWriteWord(handle, CB_FLAG, CF_CMD, error); CHECK_STATUS(status, status, cleanup); if ( debug ) { // Submit 1st read for some trace data status = flReadChannelAsyncSubmit(handle, 2, 22528, NULL, error); CHECK_STATUS(status, 28, cleanup); } // Submit 1st read for the command status flag status = umdkDirectReadBytesAsync(handle, CB_FLAG, 2, error); CHECK_STATUS(status, status, cleanup); do { // If interrupted (escape or ctrl-c in gdb), induce a suspend at the next vblank if ( isInterrupted() ) { status = umdkDirectWriteWord(handle, vbAddr, ILLEGAL, error); CHECK_STATUS(status, status, cleanup); } if ( debug ) { // Submit a read for some trace data status = flReadChannelAsyncSubmit(handle, 2, 22528, NULL, error); CHECK_STATUS(status, 28, cleanup); } // Submit a read for the command status flag status = umdkDirectReadBytesAsync(handle, CB_FLAG, 2, error); CHECK_STATUS(status, status, cleanup); if ( debug ) { // Await the requested trace data status = flReadChannelAsyncAwait(handle, &recvData, &actualLength, &actualLength, error); CHECK_STATUS(status, status, cleanup); // Write it to the trace-log fwrite(recvData, 1, actualLength, file); } // Await the requested command status flag status = flReadChannelAsyncAwait(handle, &recvData, &actualLength, &actualLength, error); CHECK_STATUS(status, status, cleanup); } while ( recvData[0] != 0x00 || recvData[1] != CF_READY ); if ( debug ) { // Await the final block of trace-data status = flReadChannelAsyncAwait(handle, &recvData, &actualLength, &actualLength, error); CHECK_STATUS(status, status, cleanup); // Write it to the trace-log fwrite(recvData, 1, actualLength, file); fclose(file); file = NULL; } // Await the final command-flag status = flReadChannelAsyncAwait(handle, &recvData, &actualLength, &actualLength, error); CHECK_STATUS(status, status, cleanup); // Restore old opcode to vbAddr status = umdkDirectWriteWord(handle, vbAddr, oldOp, error); CHECK_STATUS(status, status, cleanup); // Read saved registers, if necessary if ( regs ) { status = umdkDirectReadBytes(handle, CB_REGS, 18*4, u->bytes, error); CHECK_STATUS(status, status, cleanup); for ( i = 0; i < 18; i++ ) { u->longs[i] = bigEndian32(u->longs[i]); } } if ( debug ) { // Read RAM status = umdkReadBytes(handle, 0xFF0000, 65536, tmpData, error); CHECK_STATUS(status, status, cleanup); // Save it file = fopen("ramAfter.bin", "wb"); CHECK_STATUS(!file, 13, cleanup, "umdkContWait(): Unable to open ramAfter.bin for writing!"); fwrite(tmpData, 1, 65536, file); fclose(file); file = NULL; } cleanup: if ( file ) { fclose(file); } return retVal; }
// Synchronously direct-read a sequence of bytes from the specified address. The area of memory to // be read must reside entirely within one of the two direct-readable memory areas // (0x000000-0x07FFFF and 0x400000-0x47FFFF, mapped to SDRAM pages 0 and 31 respectively). It need // not have an even start address or length. The MegaDrive need not be suspended at the monitor. // int umdkDirectReadBytes( struct FLContext *handle, uint32 address, const uint32 count, uint8 *const data, const char **error) { int retVal = 0; FLStatus status; uint8 command[8]; uint32 wordAddr; uint32 wordCount; uint8 *tmpBuf = NULL; // First verify the read is in a legal range if ( isInside(MONITOR, 0x80000, address, count) ) { // Read is from the UMDKv2-reserved 512KiB of address-space at 0x400000. The mapping for this // is fixed to the top 512KiB of SDRAM, so we need to transpose the MD virtual address to // get the correct SDRAM physical address. address += 0xb80000; } else if ( !isInside(0, 0x80000, address, count) ) { // The only other region of the address-space which is directly host-addressable is the // bottom 512KiB of address-space, which has a logical-physical mapping that is guaranteed // by SEGA to be 1:1 (i.e no transpose necessary). So any attempt to directly access memory // anywhere else is an error. CHECK_STATUS( true, 1, cleanup, "umdkDirectReadBytes(): Illegal direct-read from 0x%06X-0x%06X range!", address, address+count-1 ); } // Reads from odd addresses or for odd lengths need to be done via a temporary buffer if ( address & 1 ) { // Odd address tmpBuf = (uint8*)malloc(count); CHECK_STATUS(!tmpBuf, 2, cleanup, "umdkDirectReadBytes(): Allocation error!"); wordAddr = address / 2; wordCount = 1 + count / 2; // Prepare the read command prepMemCtrlCmd(0x00, wordAddr, command); prepMemCtrlCmd(0x40, wordCount, command+4); // Send the read request status = flWriteChannelAsync(handle, 0x00, 8, command, error); CHECK_STATUS(status, 3, cleanup); // Receive the data status = flReadChannel(handle, 0x00, 2*wordCount, tmpBuf, error); CHECK_STATUS(status, 4, cleanup); memcpy(data, tmpBuf+1, count); } else { // Even address if ( count & 1 ) { // Even address, odd count tmpBuf = (uint8*)malloc(count); CHECK_STATUS(!tmpBuf, 5, cleanup, "umdkDirectReadBytes(): Allocation error!"); wordAddr = address / 2; wordCount = 1 + count / 2; // Prepare the read command prepMemCtrlCmd(0x00, wordAddr, command); prepMemCtrlCmd(0x40, wordCount, command+4); // Send the read request status = flWriteChannelAsync(handle, 0x00, 8, command, error); CHECK_STATUS(status, 6, cleanup); // Receive the data status = flReadChannel(handle, 0x00, 2*wordCount, tmpBuf, error); CHECK_STATUS(status, 7, cleanup); memcpy(data, tmpBuf, count); } else { // Even address, even count wordAddr = address / 2; wordCount = count / 2; // Prepare the read command prepMemCtrlCmd(0x00, wordAddr, command); prepMemCtrlCmd(0x40, wordCount, command+4); // Send the read request status = flWriteChannelAsync(handle, 0x00, 8, command, error); CHECK_STATUS(status, 8, cleanup); // Receive the data status = flReadChannel(handle, 0x00, count, data, error); CHECK_STATUS(status, 9, cleanup); } } cleanup: free(tmpBuf); return retVal; }