// Read some bytes from the specified channel, synchronously. // TODO: Deal with early-termination properly - it should not be treated like an error. // This will require changes in usbBulkRead(). Async API is already correct. // DLLEXPORT(FLStatus) flReadChannel( struct FLContext *handle, uint8 chan, size_t count, uint8 *buffer, const char **error) { FLStatus retVal = FL_SUCCESS, fStatus; const uint8 *data; uint32 requestLength, actualLength; CHECK_STATUS( count == 0, FL_PROTOCOL_ERR, cleanup, "flReadChannel(): Zero-length reads are illegal!"); CHECK_STATUS( !handle->isCommCapable, FL_PROTOCOL_ERR, cleanup, "flReadChannel(): This device does not support CommFPGA"); if ( count >= 0x10000 ) { fStatus = flReadChannelAsyncSubmit(handle, chan, 0x10000, buffer, error); CHECK_STATUS(fStatus, fStatus, cleanup, "flReadChannel()"); count -= 0x10000; buffer += 0x10000; while ( count >= 0x10000 ) { fStatus = flReadChannelAsyncSubmit(handle, chan, 0x10000, buffer, error); CHECK_STATUS(fStatus, fStatus, cleanup, "flReadChannel()"); count -= 0x10000; buffer += 0x10000; fStatus = flReadChannelAsyncAwait(handle, &data, &requestLength, &actualLength, error); CHECK_STATUS(fStatus, fStatus, cleanup, "flReadChannel()"); CHECK_STATUS( actualLength != requestLength, FL_EARLY_TERM, cleanup, "flReadChannel()"); } if ( count ) { fStatus = flReadChannelAsyncSubmit(handle, chan, (uint32)count, buffer, error); CHECK_STATUS(fStatus, fStatus, cleanup, "flReadChannel()"); fStatus = flReadChannelAsyncAwait(handle, &data, &requestLength, &actualLength, error); CHECK_STATUS(fStatus, fStatus, cleanup, "flReadChannel()"); CHECK_STATUS( actualLength != requestLength, FL_EARLY_TERM, cleanup, "flReadChannel()"); } } else { fStatus = flReadChannelAsyncSubmit(handle, chan, (uint32)count, buffer, error); CHECK_STATUS(fStatus, fStatus, cleanup, "flReadChannel()"); } fStatus = flReadChannelAsyncAwait(handle, &data, &requestLength, &actualLength, error); CHECK_STATUS(fStatus, fStatus, cleanup, "flReadChannel()"); CHECK_STATUS( actualLength != requestLength, FL_EARLY_TERM, cleanup, "flReadChannel()"); 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; }