mirror of
https://github.com/open-goal/jak-project.git
synced 2024-11-23 14:20:07 +00:00
78cde74d5a
* update readme deps * replace assert * bump timeout * fix memory corruption in kernel * use unknown if level name is invalid
123 lines
3.5 KiB
C++
123 lines
3.5 KiB
C++
#pragma once
|
|
|
|
#include <cstring>
|
|
#include "common/dma/dma.h"
|
|
#include "common/util/Assert.h"
|
|
|
|
/*!
|
|
* @file dma_chain_read.h
|
|
* This file contains utilities for reading/following a DMA chain.
|
|
*
|
|
* This allows you to iterate through transfers like this:
|
|
*
|
|
* DmaFollower dma(mem, start_addr);
|
|
* while (!reader.ended) {
|
|
* DmaTransfer transfer = reader.advance_to_next_transfer();
|
|
* // do something with the data in transfer.
|
|
* }
|
|
*/
|
|
|
|
/*!
|
|
* Represents a DMA transfer, including 64-bits of VIF tag.
|
|
*/
|
|
struct DmaTransfer {
|
|
const u8* data = nullptr;
|
|
u32 data_offset = 0;
|
|
u32 size_bytes = 0;
|
|
u64 transferred_tag = 0;
|
|
|
|
u32 vif0() const { return transferred_tag & 0xffffffff; }
|
|
u32 vif1() const { return (transferred_tag >> 32) & 0xffffffff; }
|
|
|
|
VifCode vifcode0() const { return VifCode(vif0()); }
|
|
VifCode vifcode1() const { return VifCode(vif1()); }
|
|
|
|
template <typename T>
|
|
T read_val(u32 offset) const {
|
|
T result;
|
|
memcpy(&result, (const u8*)data + offset, sizeof(T));
|
|
return result;
|
|
}
|
|
};
|
|
|
|
class DmaFollower {
|
|
public:
|
|
DmaFollower() { m_ended = true; }
|
|
DmaFollower(const void* data, u32 start_offset) : m_base(data), m_tag_offset(start_offset) {}
|
|
template <typename T>
|
|
T read_val(u32 offset) const {
|
|
T result;
|
|
memcpy(&result, (const u8*)m_base + offset, sizeof(T));
|
|
return result;
|
|
}
|
|
|
|
/*!
|
|
* Read the current tag, return its transfer, then advance to the next.
|
|
*/
|
|
DmaTransfer read_and_advance() {
|
|
DmaTag tag(read_val<u64>(m_tag_offset));
|
|
DmaTransfer result;
|
|
result.transferred_tag = read_val<u64>(m_tag_offset + 8);
|
|
result.size_bytes = (u32)tag.qwc * 16;
|
|
ASSERT(!tag.spr);
|
|
ASSERT(!m_ended);
|
|
switch (tag.kind) {
|
|
case DmaTag::Kind::CNT:
|
|
// data, then next tag. doesn't read address.
|
|
ASSERT(tag.addr == 0);
|
|
result.data_offset = m_tag_offset + 16;
|
|
m_tag_offset = result.data_offset + result.size_bytes;
|
|
break;
|
|
case DmaTag::Kind::NEXT:
|
|
result.data_offset = m_tag_offset + 16;
|
|
m_tag_offset = tag.addr;
|
|
break;
|
|
case DmaTag::Kind::REF:
|
|
case DmaTag::Kind::REFS:
|
|
result.data_offset = tag.addr;
|
|
m_tag_offset = m_tag_offset + 16;
|
|
break;
|
|
case DmaTag::Kind::REFE:
|
|
result.data_offset = tag.addr;
|
|
m_tag_offset = m_tag_offset + 16;
|
|
m_ended = true;
|
|
break;
|
|
case DmaTag::Kind::CALL:
|
|
result.data_offset = m_tag_offset + 16;
|
|
ASSERT(m_sp <= 1);
|
|
m_tag_offset = tag.addr;
|
|
m_stack[m_sp++] = result.data_offset + tag.qwc * 16;
|
|
break;
|
|
case DmaTag::Kind::RET:
|
|
ASSERT(m_sp > 0);
|
|
result.data_offset = m_tag_offset + 16;
|
|
m_tag_offset = m_stack[--m_sp];
|
|
break;
|
|
case DmaTag::Kind::END:
|
|
result.data_offset = m_tag_offset + 16;
|
|
m_ended = true;
|
|
break;
|
|
|
|
default:
|
|
ASSERT(false);
|
|
}
|
|
result.data = (const u8*)m_base + result.data_offset;
|
|
return result;
|
|
}
|
|
|
|
DmaTag current_tag() const { return DmaTag(read_val<u64>(m_tag_offset)); }
|
|
u32 current_tag_vif0() const { return read_val<u32>(m_tag_offset + 8); }
|
|
u32 current_tag_vif1() const { return read_val<u32>(m_tag_offset + 12); }
|
|
VifCode current_tag_vifcode0() const { return VifCode(current_tag_vif0()); }
|
|
VifCode current_tag_vifcode1() const { return VifCode(current_tag_vif1()); }
|
|
u32 current_tag_offset() const { return m_tag_offset; }
|
|
bool ended() const { return m_ended; }
|
|
|
|
private:
|
|
const void* m_base = nullptr;
|
|
u32 m_tag_offset = 0;
|
|
s32 m_sp = 0;
|
|
s32 m_stack[2] = {-1, -1};
|
|
bool m_ended = false;
|
|
};
|