Compare commits
8 Commits
6adba32238
...
11d61359c1
Author | SHA1 | Date |
---|---|---|
joevt | 11d61359c1 | |
joevt | 30afcb6ddc | |
joevt | ad45ce8499 | |
joevt | 9a489d13f4 | |
joevt | 721c70166c | |
joevt | 861c2d4f04 | |
joevt | da203b69c5 | |
joevt | 159fe8d48e |
|
@ -82,6 +82,9 @@ AddressMapEntry last_write_area;
|
|||
AddressMapEntry last_exec_area;
|
||||
AddressMapEntry last_ptab_area;
|
||||
|
||||
/** Dummy pages for catching writes to physical read-only pages */
|
||||
static std::array<uint64_t, 8192 / sizeof(uint64_t)> dummy_page;
|
||||
|
||||
/** 601-style block address translation. */
|
||||
static BATResult mpc601_block_address_translation(uint32_t la)
|
||||
{
|
||||
|
@ -310,11 +313,23 @@ MapDmaResult mmu_map_dma_mem(uint32_t addr, uint32_t size, bool allow_mmio) {
|
|||
AddressMapEntry *cur_dma_rgn;
|
||||
|
||||
cur_dma_rgn = mem_ctrl_instance->find_range(addr);
|
||||
if (!cur_dma_rgn || (addr + size) > cur_dma_rgn->end)
|
||||
ABORT_F("SOS: DMA access to unmapped physical memory %08X!", addr);
|
||||
if (!cur_dma_rgn) {
|
||||
ABORT_F("SOS: DMA access to unmapped physical memory 0x%08X..0x%08X!",
|
||||
addr, addr + size - 1
|
||||
);
|
||||
}
|
||||
|
||||
if ((cur_dma_rgn->type & RT_MMIO) && !allow_mmio)
|
||||
ABORT_F("SOS: DMA access to a MMIO region is not allowed");
|
||||
if (addr + size - 1 > cur_dma_rgn->end) {
|
||||
ABORT_F("SOS: DMA access to unmapped physical memory 0x%08X..0x%08X because size extends outside region 0x%08X..0x%08X!",
|
||||
addr, addr + size - 1, cur_dma_rgn->start, cur_dma_rgn->end
|
||||
);
|
||||
}
|
||||
|
||||
if ((cur_dma_rgn->type & RT_MMIO) && !allow_mmio) {
|
||||
ABORT_F("SOS: DMA access to a MMIO region 0x%08X..0x%08X (%s) for physical memory 0x%08X..0x%08X is not allowed.",
|
||||
cur_dma_rgn->start, cur_dma_rgn->end, cur_dma_rgn->devobj->get_name().c_str(), addr, addr + size - 1
|
||||
);
|
||||
}
|
||||
|
||||
if (cur_dma_rgn->type & (RT_ROM | RT_RAM)) {
|
||||
host_va = cur_dma_rgn->mem_ptr + (addr - cur_dma_rgn->start);
|
||||
|
@ -359,9 +374,6 @@ uint32_t tlb_size_mask = TLB_SIZE - 1;
|
|||
uint64_t UnmappedVal = -1ULL;
|
||||
TLBEntry UnmappedMem = {TLB_INVALID_TAG, TLBFlags::PAGE_NOPHYS, 0, 0};
|
||||
|
||||
// Dummy page for catching writes to physical read-only pages
|
||||
static std::array<uint64_t, 4096 / sizeof(uint64_t)> dummy_page;
|
||||
|
||||
uint8_t CurITLBMode = {0xFF}; // current ITLB mode
|
||||
uint8_t CurDTLBMode = {0xFF}; // current DTLB mode
|
||||
|
||||
|
|
|
@ -39,6 +39,12 @@ void DMAChannel::set_callbacks(DbdmaCallback start_cb, DbdmaCallback stop_cb) {
|
|||
this->stop_cb = stop_cb;
|
||||
}
|
||||
|
||||
void DMAChannel::set_data_callbacks(DbdmaCallback in_cb, DbdmaCallback out_cb, DbdmaCallback flush_cb) {
|
||||
this->in_cb = in_cb;
|
||||
this->out_cb = out_cb;
|
||||
this->flush_cb = flush_cb;
|
||||
}
|
||||
|
||||
/* Load DMACmd from physical memory. */
|
||||
DMACmd* DMAChannel::fetch_cmd(uint32_t cmd_addr, DMACmd* p_cmd, bool *is_writable) {
|
||||
MapDmaResult res = mmu_map_dma_mem(cmd_addr, 16, false);
|
||||
|
@ -88,6 +94,18 @@ uint8_t DMAChannel::interpret_cmd() {
|
|||
this->queue_data = res.host_va;
|
||||
this->res_count = 0;
|
||||
this->cmd_in_progress = true;
|
||||
switch (this->cur_cmd) {
|
||||
case DBDMA_Cmd::OUTPUT_MORE:
|
||||
case DBDMA_Cmd::OUTPUT_LAST:
|
||||
if (this->out_cb)
|
||||
this->out_cb();
|
||||
break;
|
||||
case DBDMA_Cmd::INPUT_MORE:
|
||||
case DBDMA_Cmd::INPUT_LAST:
|
||||
if (this->in_cb)
|
||||
this->in_cb();
|
||||
break;
|
||||
}
|
||||
} else
|
||||
this->finish_cmd();
|
||||
break;
|
||||
|
@ -153,6 +171,7 @@ void DMAChannel::finish_cmd() {
|
|||
|
||||
if (res.is_writable)
|
||||
WRITE_WORD_LE_A(&cmd_desc[14], this->ch_stat | CH_STAT_ACTIVE);
|
||||
this->ch_stat &= ~CH_STAT_FLUSH;
|
||||
|
||||
// react to cmd.b (branch) bits
|
||||
if (cmd_desc[2] & 0xC) {
|
||||
|
@ -320,9 +339,13 @@ void DMAChannel::reg_write(uint32_t offset, uint32_t value, int size) {
|
|||
// That means we need to update memory before channel operation
|
||||
// is aborted to prevent data loss.
|
||||
if (new_stat & CH_STAT_FLUSH) {
|
||||
// NOTE: because this implementation doesn't currently support
|
||||
// partial memory updates no special action is taken here
|
||||
new_stat &= ~CH_STAT_FLUSH;
|
||||
if (this->cur_cmd <= DBDMA_Cmd::INPUT_LAST && this->flush_cb && (new_stat & CH_STAT_ACTIVE) && !(this->ch_stat & CH_STAT_DEAD)) {
|
||||
this->flush_cb();
|
||||
} else {
|
||||
// NOTE: because this implementation doesn't currently support
|
||||
// partial memory updates no special action is taken here
|
||||
new_stat &= ~CH_STAT_FLUSH;
|
||||
}
|
||||
this->ch_stat = new_stat;
|
||||
}
|
||||
|
||||
|
@ -446,6 +469,40 @@ int DMAChannel::push_data(const char* src_ptr, int len) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void DMAChannel::end_pull_data() {
|
||||
if (this->ch_stat & CH_STAT_DEAD || !(this->ch_stat & CH_STAT_ACTIVE)) {
|
||||
// dead or idle channel? -> no more data
|
||||
LOG_F(WARNING, "%s: Ending Dead/idle channel -> no more data", this->get_name().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->queue_len) {
|
||||
this->queue_len = 0;
|
||||
} else {
|
||||
this->ch_stat &= ~CH_STAT_FLUSH;
|
||||
}
|
||||
|
||||
// proceed with the DBDMA program
|
||||
this->interpret_cmd();
|
||||
}
|
||||
|
||||
void DMAChannel::end_push_data() {
|
||||
if (this->ch_stat & CH_STAT_DEAD || !(this->ch_stat & CH_STAT_ACTIVE)) {
|
||||
LOG_F(WARNING, "%s: Attempt to end push data to dead/idle channel",
|
||||
this->get_name().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->queue_len) {
|
||||
this->queue_len = 0;
|
||||
} else {
|
||||
this->ch_stat &= ~CH_STAT_FLUSH;
|
||||
}
|
||||
|
||||
// proceed with the DBDMA program
|
||||
this->interpret_cmd();
|
||||
}
|
||||
|
||||
bool DMAChannel::is_out_active() {
|
||||
if (this->ch_stat & CH_STAT_DEAD || !(this->ch_stat & CH_STAT_ACTIVE)) {
|
||||
return false;
|
||||
|
|
|
@ -109,6 +109,7 @@ public:
|
|||
~DMAChannel() = default;
|
||||
|
||||
void set_callbacks(DbdmaCallback start_cb, DbdmaCallback stop_cb);
|
||||
void set_data_callbacks(DbdmaCallback in_cb, DbdmaCallback out_cb, DbdmaCallback flush_cb);
|
||||
uint32_t reg_read(uint32_t offset, int size);
|
||||
void reg_write(uint32_t offset, uint32_t value, int size);
|
||||
void set_stat(uint8_t new_stat) { this->ch_stat = (this->ch_stat & 0xff00) | new_stat; }
|
||||
|
@ -117,6 +118,10 @@ public:
|
|||
bool is_in_active();
|
||||
DmaPullResult pull_data(uint32_t req_len, uint32_t *avail_len, uint8_t **p_data);
|
||||
int push_data(const char* src_ptr, int len);
|
||||
int get_pull_data_remaining() { return this->queue_len; }
|
||||
int get_push_data_remaining() { return this->queue_len; }
|
||||
void end_pull_data();
|
||||
void end_push_data();
|
||||
|
||||
void register_dma_int(InterruptCtrl* int_ctrl_obj, uint32_t irq_id) {
|
||||
this->int_ctrl = int_ctrl_obj;
|
||||
|
@ -138,6 +143,9 @@ protected:
|
|||
private:
|
||||
std::function<void(void)> start_cb = nullptr; // DMA channel start callback
|
||||
std::function<void(void)> stop_cb = nullptr; // DMA channel stop callback
|
||||
std::function<void(void)> in_cb = nullptr; // DMA channel in callback
|
||||
std::function<void(void)> out_cb = nullptr; // DMA channel out callback
|
||||
std::function<void(void)> flush_cb = nullptr; // DMA channel flush callback
|
||||
|
||||
uint16_t ch_stat = 0;
|
||||
uint32_t cmd_ptr = 0;
|
||||
|
|
|
@ -39,6 +39,8 @@ public:
|
|||
virtual bool is_out_active() { return true; };
|
||||
virtual DmaPullResult pull_data(uint32_t req_len, uint32_t *avail_len,
|
||||
uint8_t **p_data) = 0;
|
||||
virtual int get_pull_data_remaining() { return 1; };
|
||||
virtual void end_pull_data() { };
|
||||
|
||||
std::string get_name(void) { return this->name; };
|
||||
|
||||
|
@ -52,6 +54,8 @@ public:
|
|||
|
||||
virtual bool is_in_active() { return true; };
|
||||
virtual int push_data(const char* src_ptr, int len) = 0;
|
||||
virtual int get_push_data_remaining() { return 1; };
|
||||
virtual void end_push_data() { };
|
||||
|
||||
std::string get_name(void) { return this->name; };
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ int AthensClocks::get_dot_freq()
|
|||
22, 27, 28, 31, 35, 37, 38, 42, 49, 55, 56, 78, 125
|
||||
};
|
||||
|
||||
if (this->regs[AthensRegs::P2_MUX2] & 0xC0) {
|
||||
if (this->regs[AthensRegs::P2_MUX2] & 0x80) {
|
||||
LOG_F(INFO, "%s: dot clock disabled", this->name.c_str());
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
|
||||
/** @file Enhanced Serial Communications Controller (ESCC) emulation. */
|
||||
|
||||
#include <core/timermanager.h>
|
||||
#include <devices/deviceregistry.h>
|
||||
#include <devices/serial/chario.h>
|
||||
#include <devices/serial/escc.h>
|
||||
|
@ -72,36 +73,47 @@ void EsccController::reset()
|
|||
|
||||
uint8_t EsccController::read(uint8_t reg_offset)
|
||||
{
|
||||
uint8_t result = 0;
|
||||
uint8_t value;
|
||||
|
||||
switch(reg_offset) {
|
||||
case EsccReg::Port_B_Cmd:
|
||||
LOG_F(9, "ESCC: reading Port B register RR%d", this->reg_ptr);
|
||||
if (this->reg_ptr == 2) {
|
||||
// TODO: implement interrupt vector modifications
|
||||
result = this->int_vec;
|
||||
value = this->int_vec;
|
||||
} else {
|
||||
result = this->ch_b->read_reg(this->reg_ptr);
|
||||
value = this->ch_b->read_reg(this->reg_ptr);
|
||||
}
|
||||
this->reg_ptr = 0;
|
||||
break;
|
||||
case EsccReg::Port_A_Cmd:
|
||||
LOG_F(9, "ESCC: reading Port A register RR%d", this->reg_ptr);
|
||||
if (this->reg_ptr == 2) {
|
||||
return this->int_vec;
|
||||
value = this->int_vec;
|
||||
} else {
|
||||
return this->ch_a->read_reg(this->reg_ptr);
|
||||
value = this->ch_a->read_reg(this->reg_ptr);
|
||||
}
|
||||
break;
|
||||
case EsccReg::Port_B_Data:
|
||||
return this->ch_b->receive_byte();
|
||||
value = this->ch_b->receive_byte();
|
||||
break;
|
||||
case EsccReg::Port_A_Data:
|
||||
return this->ch_a->receive_byte();
|
||||
value = this->ch_a->receive_byte();
|
||||
break;
|
||||
case EsccReg::Enh_Reg_B:
|
||||
LOG_F(WARNING, "ESCC_B: reading from unimplemented register 0x%x", reg_offset);
|
||||
value = 0;
|
||||
break;
|
||||
case EsccReg::Enh_Reg_A:
|
||||
LOG_F(WARNING, "ESCC_A: reading from unimplemented register 0x%x", reg_offset);
|
||||
value = 0;
|
||||
break;
|
||||
default:
|
||||
LOG_F(9, "ESCC: reading from unimplemented register 0x%x", reg_offset);
|
||||
LOG_F(WARNING, "ESCC: reading from unimplemented register 0x%x", reg_offset);
|
||||
value = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
return value;
|
||||
}
|
||||
|
||||
void EsccController::write(uint8_t reg_offset, uint8_t value)
|
||||
|
@ -324,6 +336,97 @@ uint8_t EsccChannel::receive_byte()
|
|||
return c;
|
||||
}
|
||||
|
||||
void EsccChannel::dma_start_tx()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void EsccChannel::dma_start_rx()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void EsccChannel::dma_stop_tx()
|
||||
{
|
||||
if (this->timer_id_tx) {
|
||||
TimerManager::get_instance()->cancel_timer(this->timer_id_tx);
|
||||
this->timer_id_tx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void EsccChannel::dma_stop_rx()
|
||||
{
|
||||
if (this->timer_id_rx) {
|
||||
TimerManager::get_instance()->cancel_timer(this->timer_id_rx);
|
||||
this->timer_id_rx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void EsccChannel::dma_in_tx()
|
||||
{
|
||||
LOG_F(ERROR, "%s: Unexpected DMA INPUT command for transmit.", this->name.c_str());
|
||||
}
|
||||
|
||||
void EsccChannel::dma_in_rx()
|
||||
{
|
||||
if (dma_ch[1]->get_push_data_remaining()) {
|
||||
this->timer_id_rx = TimerManager::get_instance()->add_oneshot_timer(
|
||||
0,
|
||||
[this]() {
|
||||
this->timer_id_rx = 0;
|
||||
char c = receive_byte();
|
||||
int xx = dma_ch[1]->push_data(&c, 1);
|
||||
this->dma_in_rx();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void EsccChannel::dma_out_tx()
|
||||
{
|
||||
this->timer_id_tx = TimerManager::get_instance()->add_oneshot_timer(
|
||||
10,
|
||||
[this]() {
|
||||
this->timer_id_tx = 0;
|
||||
uint8_t *data;
|
||||
uint32_t avail_len;
|
||||
|
||||
if (dma_ch[1]->pull_data(256, &avail_len, &data) == MoreData) {
|
||||
while(avail_len) {
|
||||
this->send_byte(*data++);
|
||||
avail_len--;
|
||||
}
|
||||
this->dma_out_tx();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void EsccChannel::dma_out_rx()
|
||||
{
|
||||
LOG_F(ERROR, "%s: Unexpected DMA OUTPUT command for receive.", this->name.c_str());
|
||||
}
|
||||
|
||||
void EsccChannel::dma_flush_tx()
|
||||
{
|
||||
this->dma_stop_tx();
|
||||
this->timer_id_tx = TimerManager::get_instance()->add_oneshot_timer(
|
||||
10,
|
||||
[this]() {
|
||||
this->timer_id_tx = 0;
|
||||
dma_ch[1]->end_pull_data();
|
||||
});
|
||||
}
|
||||
|
||||
void EsccChannel::dma_flush_rx()
|
||||
{
|
||||
this->dma_stop_rx();
|
||||
this->timer_id_rx = TimerManager::get_instance()->add_oneshot_timer(
|
||||
10,
|
||||
[this]() {
|
||||
this->timer_id_rx = 0;
|
||||
dma_ch[1]->end_push_data();
|
||||
});
|
||||
}
|
||||
|
||||
static const vector<string> CharIoBackends = {"null", "stdio", "socket"};
|
||||
|
||||
static const PropMap Escc_Properties = {
|
||||
|
|
|
@ -25,6 +25,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
#define ESCC_H
|
||||
|
||||
#include <devices/common/hwcomponent.h>
|
||||
#include <devices/common/dbdma.h>
|
||||
#include <devices/serial/chario.h>
|
||||
|
||||
#include <cinttypes>
|
||||
|
@ -104,7 +105,55 @@ public:
|
|||
void send_byte(uint8_t value);
|
||||
uint8_t receive_byte();
|
||||
|
||||
void set_dma_channel(int dir_index, DmaBidirChannel *dma_ch) {
|
||||
this->dma_ch[dir_index] = dma_ch;
|
||||
auto dbdma_ch = dynamic_cast<DMAChannel*>(dma_ch);
|
||||
if (dbdma_ch) {
|
||||
switch (dir_index) {
|
||||
case 0:
|
||||
dbdma_ch->set_callbacks(
|
||||
std::bind(&EsccChannel::dma_start_tx, this),
|
||||
std::bind(&EsccChannel::dma_stop_tx, this)
|
||||
);
|
||||
dbdma_ch->set_data_callbacks(
|
||||
std::bind(&EsccChannel::dma_in_tx, this),
|
||||
std::bind(&EsccChannel::dma_out_tx, this),
|
||||
std::bind(&EsccChannel::dma_flush_tx, this)
|
||||
);
|
||||
break;
|
||||
case 1:
|
||||
dbdma_ch->set_callbacks(
|
||||
std::bind(&EsccChannel::dma_start_rx, this),
|
||||
std::bind(&EsccChannel::dma_stop_rx, this)
|
||||
);
|
||||
dbdma_ch->set_data_callbacks(
|
||||
std::bind(&EsccChannel::dma_in_rx, this),
|
||||
std::bind(&EsccChannel::dma_out_rx, this),
|
||||
std::bind(&EsccChannel::dma_flush_rx, this)
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
uint32_t timer_id_tx = 0;
|
||||
uint32_t timer_id_rx = 0;
|
||||
|
||||
void dma_start_tx();
|
||||
void dma_stop_tx();
|
||||
void dma_in_tx();
|
||||
void dma_out_tx();
|
||||
void dma_flush_tx();
|
||||
|
||||
void dma_start_rx();
|
||||
void dma_stop_rx();
|
||||
void dma_in_rx();
|
||||
void dma_out_rx();
|
||||
void dma_flush_rx();
|
||||
|
||||
DmaBidirChannel* dma_ch[2];
|
||||
|
||||
std::string name;
|
||||
uint8_t read_regs[16];
|
||||
uint8_t write_regs[16];
|
||||
|
@ -132,10 +181,11 @@ public:
|
|||
uint8_t read(uint8_t reg_offset);
|
||||
void write(uint8_t reg_offset, uint8_t value);
|
||||
|
||||
void dma_start();
|
||||
void dma_stop();
|
||||
void set_dma_channel(int ch_index, DmaBidirChannel *dma_ch) {
|
||||
this->dma_ch[ch_index] = dma_ch;
|
||||
void set_dma_channel(int ch_dir_index, DmaBidirChannel *dma_ch) {
|
||||
switch (ch_dir_index >> 1) {
|
||||
case 0: ch_a->set_dma_channel(ch_dir_index & 1, dma_ch); break;
|
||||
case 1: ch_b->set_dma_channel(ch_dir_index & 1, dma_ch); break;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -149,8 +199,6 @@ private:
|
|||
|
||||
uint8_t master_int_cntrl;
|
||||
uint8_t int_vec;
|
||||
|
||||
DmaBidirChannel* dma_ch[4];
|
||||
};
|
||||
|
||||
#endif // ESCC_H
|
||||
|
|
|
@ -591,7 +591,10 @@ void ControlVideo::enable_display()
|
|||
}
|
||||
|
||||
this->pixel_depth = this->radacal->get_pix_width();
|
||||
if (swatch_params[ControlRegs::HAL-1] != swatch_params[ControlRegs::PIPE_DELAY-1] + 1 || this->pixel_depth == 32) {
|
||||
if (swatch_params[ControlRegs::HAL-1] != swatch_params[ControlRegs::PIPE_DELAY-1] + 1 ||
|
||||
this->pixel_depth == 32 ||
|
||||
(this->pixel_depth == 16 && this->active_width == 1280)
|
||||
) {
|
||||
// don't know how to calculate offset from GBASE (fb_base); it is always hard coded as + 16 in the ndrv.
|
||||
this->fb_ptr += 16; // first 16 bytes are for 4 bpp HW cursor
|
||||
}
|
||||
|
|
|
@ -102,6 +102,8 @@ int initialize_tnt(std::string& id)
|
|||
std::string cpu = GET_STR_PROP("cpu");
|
||||
if (cpu == "604e")
|
||||
ppc_cpu_init(memctrl_obj, PPC_VER::MPC604E, false, 12500000ULL);
|
||||
else if (cpu == "604")
|
||||
ppc_cpu_init(memctrl_obj, PPC_VER::MPC604, false, 12500000ULL);
|
||||
else if (cpu == "601")
|
||||
ppc_cpu_init(memctrl_obj, PPC_VER::MPC601, true, 7833600ULL);
|
||||
else if (cpu == "750") {
|
||||
|
|
Loading…
Reference in New Issue