diff options
| author | Tharo <17233964+Thar0@users.noreply.github.com> | 2022-11-17 02:57:02 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-11-16 21:57:02 -0500 |
| commit | 7ecafcfe7d357ff1cbcf49ce4d87a9efedae552e (patch) | |
| tree | ba036ae7cdce859adb4e0f036fcc28284be22423 /src/boot | |
| parent | 40639e698d0b4681d088194dc72a6a3b910e7d13 (diff) | |
More documentation for `z_std_dma.c` (#1415)
* More documentation for z_std_dma
* uintptr casts for rom symbols in z64animation.h and z_kanfont.c
* Format
* Suggested changes, more defines for static texture sizes
* PI Interface -> PI
* Further suggested changes
* Format
* Comments about item_name and map_name texture assumptions
Diffstat (limited to 'src/boot')
| -rw-r--r-- | src/boot/idle.c | 4 | ||||
| -rw-r--r-- | src/boot/yaz0.c | 12 | ||||
| -rw-r--r-- | src/boot/z_std_dma.c | 249 |
3 files changed, 210 insertions, 55 deletions
diff --git a/src/boot/idle.c b/src/boot/idle.c index 99a0276b4..30c244876 100644 --- a/src/boot/idle.c +++ b/src/boot/idle.c @@ -23,8 +23,8 @@ void Main_ThreadEntry(void* arg) { DmaMgr_Init(); osSyncPrintf("codeセグメントロード中..."); time = osGetTime(); - DmaMgr_SendRequest1(_codeSegmentStart, (uintptr_t)_codeSegmentRomStart, _codeSegmentRomEnd - _codeSegmentRomStart, - "../idle.c", 238); + DmaMgr_RequestSyncDebug(_codeSegmentStart, (uintptr_t)_codeSegmentRomStart, + _codeSegmentRomEnd - _codeSegmentRomStart, "../idle.c", 238); time -= osGetTime(); osSyncPrintf("\rcodeセグメントロード中...完了\n"); osSyncPrintf("転送時間 %6.3f\n"); diff --git a/src/boot/yaz0.c b/src/boot/yaz0.c index 914560bc4..80d850928 100644 --- a/src/boot/yaz0.c +++ b/src/boot/yaz0.c @@ -3,13 +3,13 @@ u8 sYaz0DataBuffer[0x400]; u8* sYaz0DataBufferEnd; uintptr_t sYaz0CurRomStart; -u32 sYaz0CurSize; +size_t sYaz0CurSize; u8* sYaz0MaxPtr; void* Yaz0_FirstDMA(void) { s32 pad[2]; - u32 dmaSize; - u32 bufferSize; + size_t dmaSize; + size_t bufferSize; sYaz0MaxPtr = sYaz0DataBufferEnd - 0x19; @@ -24,8 +24,8 @@ void* Yaz0_FirstDMA(void) { void* Yaz0_NextDMA(u8* curSrcPos) { u8* dst; - u32 restSize; - u32 dmaSize; + size_t restSize; + size_t dmaSize; restSize = sYaz0DataBufferEnd - curSrcPos; dst = (restSize & 7) ? (sYaz0DataBuffer - (restSize & 7)) + 8 : sYaz0DataBuffer; @@ -92,7 +92,7 @@ void Yaz0_DecompressImpl(Yaz0Header* hdr, u8* dst) { } while (dst != dstEnd); } -void Yaz0_Decompress(uintptr_t romStart, u8* dst, u32 size) { +void Yaz0_Decompress(uintptr_t romStart, u8* dst, size_t size) { sYaz0CurRomStart = romStart; sYaz0CurSize = size; sYaz0DataBufferEnd = sYaz0DataBuffer + sizeof(sYaz0DataBuffer); diff --git a/src/boot/z_std_dma.c b/src/boot/z_std_dma.c index 140b966f9..3b6320b85 100644 --- a/src/boot/z_std_dma.c +++ b/src/boot/z_std_dma.c @@ -1,3 +1,23 @@ +/** + * @file z_std_dma.c + * + * This file implements a system for structuring the ROM image and retrieving data. It is designed to have the same + * external interface regardless of whether the ROM segments are compressed or not. + * + * The ROM image is partitioned into regions that are entered into the DMA data table `gDmaDataTable`. External code + * does not directly address locations on the ROM image directly, instead a "Virtual ROM" addressing is used. Virtual + * ROM is defined to be the ROM address of a segment in a totally uncompressed ROM. For uncompressed ROMs, "physical" + * ROM and VROM addresses coincide. The DMA data table converts VROM to ROM addresses so that code may exclusively use + * VROM addresses even if the ROM is compressed. + * + * External code typically submits requests to the DMA Manager asking for a transfer in terms of Virtual ROM; the DMA + * Manager translates this to the physical ROM address, performs the transfer to RAM and decompresses the data if + * required. + * Requests are processed in the order they are received and may be submitted both synchronously and asynchronously. + * + * There are some additional provisions to ensure that audio DMA is particularly high-speed, the audio data is assumed + * to be uncompressed and the request queue and address translation is skipped. + */ #include "global.h" #include "terminal.h" @@ -10,7 +30,7 @@ const char* sDmaMgrCurFileName; s32 sDmaMgrCurFileLine; u32 gDmaMgrVerbose = 0; -u32 gDmaMgrDmaBuffSize = 0x2000; +size_t gDmaMgrDmaBuffSize = DMAMGR_DEFAULT_BUFSIZE; u32 sDmaMgrIsRomCompressed = false; // dmadata filenames @@ -22,37 +42,54 @@ const char* sDmaMgrFileNames[] = { #undef DEFINE_DMA_ENTRY -s32 DmaMgr_CompareName(const char* name1, const char* name2) { - while (*name1 != '\0') { - if (*name1 > *name2) { +/** + * Compares `str1` and `str2`. + * + * @return + * 0 if str1 and str2 are the same, + * -1 if the first character that does not match has a smaller value in str1 than str2, + * +1 if the first character that does not match has a greater value in str1 than str2 + */ +s32 DmaMgr_StrCmp(const char* str1, const char* str2) { + while (*str1 != '\0') { + if (*str1 > *str2) { return 1; } - - if (*name1 < *name2) { + if (*str1 < *str2) { return -1; } - - name1++; - name2++; + str1++; + str2++; } - if (*name2 > '\0') { + if (*str2 > '\0') { return -1; } - return 0; } -s32 DmaMgr_DmaRomToRam(uintptr_t rom, void* ram, u32 size) { +/** + * Transfer `size` bytes from physical ROM address `rom` to `ram`. + * + * This function is intended for internal use only, however it is possible to use this function externally in which + * case it behaves as a synchronous transfer, data is available as soon as this function returns. + * + * Transfers are divided into chunks based on the current value of `gDmaMgrDmaBuffSize` to avoid congestion of the PI + * so that higher priority transfers can still be carried out in a timely manner. The transfers are sent in a queue to + * the OS PI Manager which performs the transfer. + * + * @return 0 if successful, -1 if the DMA could not be queued with the PI Manager. + */ +s32 DmaMgr_DmaRomToRam(uintptr_t rom, void* ram, size_t size) { OSIoMesg ioMsg; OSMesgQueue queue; OSMesg msg; s32 ret; - u32 buffSize = gDmaMgrDmaBuffSize; + size_t buffSize = gDmaMgrDmaBuffSize; s32 pad[2]; if (buffSize == 0) { - buffSize = 0x2000; + buffSize = DMAMGR_DEFAULT_BUFSIZE; } osInvalICache(ram, size); @@ -60,6 +97,9 @@ s32 DmaMgr_DmaRomToRam(uintptr_t rom, void* ram, u32 size) { osCreateMesgQueue(&queue, &msg, 1); while (size > buffSize) { + // The system avoids large DMAs as these would stall the PI for too long, potentially causing issues with + // audio. To allow audio to continue to DMA whenever it needs to, other DMAs are split into manageable chunks. + if (1) {} // Necessary to match ioMsg.hdr.pri = OS_MESG_PRI_NORMAL; @@ -124,7 +164,17 @@ end: return ret; } -s32 DmaMgr_DmaHandler(OSPiHandle* pihandle, OSIoMesg* mb, s32 direction) { +/** + * Callback function to facilitate audio DMA. Audio DMA does not use the request queue as audio data is often needed + * very soon after the request is sent, requiring a higher priority method for enqueueing a DMA on the OS PI command + * queue. + * + * @param pihandle Cartridge ROM PI Handle. + * @param mb IO Message describing the transfer. + * @param direction Read or write. (Only read is allowed) + * @return 0 if the IO Message was successfully put on the OS PI command queue, < 0 otherwise + */ +s32 DmaMgr_AudioDmaHandler(OSPiHandle* pihandle, OSIoMesg* mb, s32 direction) { s32 ret; ASSERT(pihandle == gCartHandle, "pihandle == carthandle", "../z_std_dma.c", 530); @@ -143,7 +193,14 @@ s32 DmaMgr_DmaHandler(OSPiHandle* pihandle, OSIoMesg* mb, s32 direction) { return ret; } -void DmaMgr_DmaFromDriveRom(void* ram, uintptr_t rom, u32 size) { +/** + * DMA read from disk drive. Blocks the current thread until DMA completes. + * + * @param ram RAM address to write data to. + * @param rom ROM address to read from. + * @param size Size of transfer. + */ +void DmaMgr_DmaFromDriveRom(void* ram, uintptr_t rom, size_t size) { OSPiHandle* handle = osDriveRomInit(); OSMesgQueue queue; OSMesg msg; @@ -164,20 +221,31 @@ void DmaMgr_DmaFromDriveRom(void* ram, uintptr_t rom, u32 size) { osRecvMesg(&queue, NULL, OS_MESG_BLOCK); } +/** + * DMA error encountered, print error messages and bring up the crash screen. + * + * @param req DMA Request causing the error. + * @param file DMA data filename associated with the operation that errored. + * @param errorName Error name string. + * @param errorDesc Error description string. + * + * This function does not return. + */ void DmaMgr_Error(DmaRequest* req, const char* file, const char* errorName, const char* errorDesc) { uintptr_t vrom = req->vromAddr; void* ram = req->dramAddr; - u32 size = req->size; + size_t size = req->size; char buff1[80]; char buff2[80]; osSyncPrintf("%c", BEL); osSyncPrintf(VT_FGCOL(RED)); + // "DMA Fatal Error" osSyncPrintf("DMA致命的エラー(%s)\nROM:%X RAM:%X SIZE:%X %s\n", errorDesc != NULL ? errorDesc : (errorName != NULL ? errorName : "???"), vrom, ram, size, file != NULL ? file : "???"); - if (req->filename != NULL) { + if (req->filename != NULL) { // Source file name that issued the DMA request osSyncPrintf("DMA ERROR: %s %d", req->filename, req->line); } else if (sDmaMgrCurFileName != NULL) { osSyncPrintf("DMA ERROR: %s %d", sDmaMgrCurFileName, sDmaMgrCurFileLine); @@ -197,7 +265,14 @@ void DmaMgr_Error(DmaRequest* req, const char* file, const char* errorName, cons Fault_AddHungupAndCrashImpl(buff1, buff2); } -const char* DmaMgr_GetFileNameImpl(uintptr_t vrom) { +/** + * Searches the filesystem for the entry containing the address `vrom`. Retrieves the name of this entry from + * the array of file names. + * + * @param vrom Virtual ROM location + * @return Pointer to associated filename + */ +const char* DmaMgr_FindFileName(uintptr_t vrom) { DmaEntry* iter = gDmaDataTable; const char** name = sDmaMgrFileNames; @@ -217,46 +292,56 @@ const char* DmaMgr_GetFileNameImpl(uintptr_t vrom) { } const char* DmaMgr_GetFileName(uintptr_t vrom) { - const char* ret = DmaMgr_GetFileNameImpl(vrom); + const char* ret = DmaMgr_FindFileName(vrom); if (ret == NULL) { return "(unknown)"; } - if (DmaMgr_CompareName(ret, "kanji") == 0 || DmaMgr_CompareName(ret, "link_animetion") == 0) { + if (DmaMgr_StrCmp(ret, "kanji") == 0 || DmaMgr_StrCmp(ret, "link_animetion") == 0) { + // This check may be related to these files being too large to be loaded all at once, however a NULL filename + // does not prevent them from being loaded. return NULL; } - return ret; } -void DmaMgr_ProcessMsg(DmaRequest* req) { +void DmaMgr_ProcessRequest(DmaRequest* req) { uintptr_t vrom = req->vromAddr; void* ram = req->dramAddr; - u32 size = req->size; + size_t size = req->size; uintptr_t romStart; - u32 romSize; + size_t romSize; u8 found = false; DmaEntry* iter; const char* filename; if (0) { - // the string is defined in .rodata but not used, suggesting - // a debug print is here but optimized out in some way + // The string is defined in .rodata but not used, suggesting a debug print is here but was optimized out in + // some way. The last arg of this print looks like it may be filename, but filename above this block does not + // match. osSyncPrintf("DMA ROM:%08X RAM:%08X SIZE:%08X %s\n"); - // the last arg of this print looks like it may be filename, but - // filename above this block does not match } + // Get the filename (for debugging) filename = DmaMgr_GetFileName(vrom); - iter = gDmaDataTable; + // Iterate through the DMA data table until the region containing the vrom address for this request is found + iter = gDmaDataTable; while (iter->vromEnd != 0) { if (vrom >= iter->vromStart && vrom < iter->vromEnd) { + // Found the region this request falls into + if (1) {} // Necessary to match if (iter->romEnd == 0) { + // romEnd of 0 indicates that the file is uncompressed. Files that are stored uncompressed can have + // only part of their content loaded into RAM, so DMA only the requested region. + if (iter->vromEnd < vrom + size) { + // Error, vrom + size ends up in a different file than it started in + + // "DMA transfers cannot cross segment boundaries" DmaMgr_Error(req, filename, "Segment Alignment Error", "セグメント境界をまたがってDMA転送することはできません"); } @@ -268,19 +353,29 @@ void DmaMgr_ProcessMsg(DmaRequest* req) { osSyncPrintf("No Press ROM:%08X RAM:%08X SIZE:%08X\n", vrom, ram, size); } } else { + // File is compressed. Files that are stored compressed must be loaded into RAM all at once. + romStart = iter->romStart; romSize = iter->romEnd - iter->romStart; if (vrom != iter->vromStart) { + // Error, requested vrom is not the start of a file + + // "DMA transfer cannot be performed from the middle of a compressed segment" DmaMgr_Error(req, filename, "Can't Transfer Segment", "圧縮されたセグメントの途中からはDMA転送することはできません"); } if (size != iter->vromEnd - iter->vromStart) { + // Error, only part of the file was requested + + // "It is not possible to DMA only part of a compressed segment" DmaMgr_Error(req, filename, "Can't Transfer Segment", "圧縮されたセグメントの一部だけをDMA転送することはできません"); } + // Reduce the thread priority and decompress the file, the decompression routine handles the DMA + // in chunks. Restores the thread priority when done. osSetThreadPri(NULL, THREAD_PRI_DMAMGR_LOW); Yaz0_Decompress(romStart, ram, romSize); osSetThreadPri(NULL, THREAD_PRI_DMAMGR); @@ -296,15 +391,21 @@ void DmaMgr_ProcessMsg(DmaRequest* req) { } if (!found) { + // Requested region was not found in the filesystem + if (sDmaMgrIsRomCompressed) { + // Error, rom is compressed so DMA may only be requested within the filesystem bounds + + // "Corresponding data does not exist" DmaMgr_Error(req, NULL, "DATA DON'T EXIST", "該当するデータが存在しません"); return; - } - - DmaMgr_DmaRomToRam(vrom, ram, size); + } else { + // ROM is uncompressed, allow arbitrary DMA even if the region is not marked in the filesystem + DmaMgr_DmaRomToRam(vrom, ram, size); - if (0) { - osSyncPrintf("No Press ROM:%08X RAM:%08X SIZE:%08X (非公式)\n", vrom, ram, size); + if (0) { + osSyncPrintf("No Press ROM:%08X RAM:%08X SIZE:%08X (非公式)\n", vrom, ram, size); + } } } } @@ -313,8 +414,11 @@ void DmaMgr_ThreadEntry(void* arg) { OSMesg msg; DmaRequest* req; + // "DMA manager thread execution start" osSyncPrintf("DMAマネージャスレッド実行開始\n"); + while (true) { + // Wait for DMA Requests to arrive from other threads osRecvMesg(&sDmaMgrMsgQueue, &msg, OS_MESG_BLOCK); req = (DmaRequest*)msg; if (req == NULL) { @@ -325,7 +429,10 @@ void DmaMgr_ThreadEntry(void* arg) { osSyncPrintf("DMA登録受付 dmap=%08x\n", req); } - DmaMgr_ProcessMsg(req); + // Process the DMA request + DmaMgr_ProcessRequest(req); + + // Notify the sender that the request has been processed if (req->notifyQueue != NULL) { osSendMesg(req->notifyQueue, req->notifyMsg, OS_MESG_NOBLOCK); if (0) { @@ -333,11 +440,24 @@ void DmaMgr_ThreadEntry(void* arg) { } } } + + // "DMA manager thread execution end" osSyncPrintf("DMAマネージャスレッド実行終了\n"); } -s32 DmaMgr_SendRequestImpl(DmaRequest* req, void* ram, uintptr_t vrom, u32 size, u32 unk, OSMesgQueue* queue, - OSMesg msg) { +/** + * Submits a DMA request to the DMA manager. For internal use only. + * + * @param req DMA request, filled out internally. + * @param ram Location in DRAM for data to be written. + * @param vrom Virtual ROM location for data to be read. + * @param size Transfer size. + * @param queue Message queue to notify with `msg` once the transfer is complete. + * @param msg Message to send to `queue` once the transfer is complete. + * @return 0 + */ +s32 DmaMgr_SendRequest(DmaRequest* req, void* ram, uintptr_t vrom, size_t size, u32 unk, OSMesgQueue* queue, + OSMesg msg) { static s32 sDmaMgrQueueFullLogged = 0; if ((1 && (ram == NULL)) || (osMemSize < OS_K0_TO_PHYSICAL(ram) + size) || (vrom & 1) || (vrom > 0x4000000) || @@ -357,6 +477,7 @@ s32 DmaMgr_SendRequestImpl(DmaRequest* req, void* ram, uintptr_t vrom, u32 size, sDmaMgrQueueFullLogged++; osSyncPrintf("%c", BEL); osSyncPrintf(VT_FGCOL(RED)); + // "dmaEntryMsgQ is full. Reconsider your queue size." osSyncPrintf("dmaEntryMsgQが一杯です。キューサイズの再検討をおすすめします。"); LOG_NUM("(sizeof(dmaEntryMsgBufs) / sizeof(dmaEntryMsgBufs[0]))", ARRAY_COUNT(sDmaMgrMsgBuf), "../z_std_dma.c", 952); @@ -367,15 +488,24 @@ s32 DmaMgr_SendRequestImpl(DmaRequest* req, void* ram, uintptr_t vrom, u32 size, return 0; } -s32 DmaMgr_SendRequest0(void* ram, uintptr_t vrom, u32 size) { +/** + * Submit a synchronous DMA request. This will block the current thread until the requested transfer is complete. Data + * is immediately available as soon as this function returns. + * + * @param ram Location in DRAM for data to be written. + * @param vrom Virtual ROM location for data to be read. + * @param size Transfer size. + * @return 0 + */ +s32 DmaMgr_RequestSync(void* ram, uintptr_t vrom, size_t size) { DmaRequest req; OSMesgQueue queue; OSMesg msg; s32 ret; osCreateMesgQueue(&queue, &msg, 1); - ret = DmaMgr_SendRequestImpl(&req, ram, vrom, size, 0, &queue, NULL); - if (ret == -1) { + ret = DmaMgr_SendRequest(&req, ram, vrom, size, 0, &queue, NULL); + if (ret == -1) { // DmaMgr_SendRequest only returns 0 return ret; } @@ -388,6 +518,7 @@ void DmaMgr_Init(void) { s32 idx; DmaEntry* iter; + // DMA the dma data table to RAM DmaMgr_DmaRomToRam((uintptr_t)_dmadataSegmentRomStart, _dmadataSegmentStart, (u32)(_dmadataSegmentRomEnd - _dmadataSegmentRomStart)); osSyncPrintf("dma_rom_ad[]\n"); @@ -397,6 +528,7 @@ void DmaMgr_Init(void) { iter = gDmaDataTable; idx = 0; + // Check if the ROM is compressed (romEnd not 0) while (iter->vromEnd != 0) { if (iter->romEnd != 0) { sDmaMgrIsRomCompressed = true; @@ -415,12 +547,15 @@ void DmaMgr_Init(void) { } } + // Ensure that the boot segment always follows after the makerom segment. if ((uintptr_t)_bootSegmentRomStart != gDmaDataTable[0].vromEnd) { osSyncPrintf("_bootSegmentRomStart(%08x) != dma_rom_ad[0].rom_b(%08x)\n", _bootSegmentRomStart, gDmaDataTable[0].vromEnd); + //! @bug The main code file where fault.c resides is not yet loaded Fault_AddHungupAndCrash("../z_std_dma.c", 1055); } + // Start the DMA manager osCreateMesgQueue(&sDmaMgrMsgQueue, sDmaMgrMsgBuf, ARRAY_COUNT(sDmaMgrMsgBuf)); StackCheck_Init(&sDmaMgrStackInfo, sDmaMgrStack, STACK_TOP(sDmaMgrStack), 0, 0x100, "dmamgr"); osCreateThread(&sDmaMgrThread, THREAD_ID_DMAMGR, DmaMgr_ThreadEntry, NULL, STACK_TOP(sDmaMgrStack), @@ -428,14 +563,34 @@ void DmaMgr_Init(void) { osStartThread(&sDmaMgrThread); } -s32 DmaMgr_SendRequest2(DmaRequest* req, void* ram, uintptr_t vrom, u32 size, u32 unk5, OSMesgQueue* queue, OSMesg msg, - const char* file, s32 line) { +/** + * Submit an asynchronous DMA request. Unlike other DMA requests, this will not block the current thread. Data arrival + * is not immediate however, ensure that the request has completed by awaiting a message sent to `queue` when the DMA + * operation has completed. + * + * @param req DMA request structure, filled out internally. + * @param ram Location in DRAM for data to be written. + * @param vrom Virtual ROM location for data to be read. + * @param size Transfer size. + * @param queue Message queue to notify with `msg` once the transfer is complete. + * @param msg Message to send to `queue` once the transfer is complete. + * @param file Debug filename of caller. + * @param line Debug line number of caller. + * @return 0 + */ +s32 DmaMgr_RequestAsync(DmaRequest* req, void* ram, uintptr_t vrom, size_t size, u32 unk5, OSMesgQueue* queue, + OSMesg msg, const char* file, s32 line) { req->filename = file; req->line = line; - return DmaMgr_SendRequestImpl(req, ram, vrom, size, unk5, queue, msg); + return DmaMgr_SendRequest(req, ram, vrom, size, unk5, queue, msg); } -s32 DmaMgr_SendRequest1(void* ram, uintptr_t vrom, u32 size, const char* file, s32 line) { +/** + * Synchronous DMA Request with source file and line info for debugging. + * + * @see DmaMgr_RequestSync + */ +s32 DmaMgr_RequestSyncDebug(void* ram, uintptr_t vrom, size_t size, const char* file, s32 line) { DmaRequest req; s32 ret; OSMesgQueue queue; @@ -445,8 +600,8 @@ s32 DmaMgr_SendRequest1(void* ram, uintptr_t vrom, u32 size, const char* file, s req.filename = file; req.line = line; osCreateMesgQueue(&queue, &msg, 1); - ret = DmaMgr_SendRequestImpl(&req, ram, vrom, size, 0, &queue, NULL); - if (ret == -1) { + ret = DmaMgr_SendRequest(&req, ram, vrom, size, 0, &queue, NULL); + if (ret == -1) { // DmaMgr_SendRequest only returns 0 return ret; } |
