Compare commits

...

8 Commits

Author SHA1 Message Date
joevt 11d61359c1 dbdma: Add data callbacks.
Allow the dbdma program to initiate reading/writing by adding in and out callbacks.
Support the DBDMA flush command by adding a flush callback. If the transfer completes, then clear the flush flag, otherwise leave it unchanged.
Clear the flush flag after it is copied to the xferStatus of the DBDMA command.
2024-04-24 07:09:56 -07:00
joevt 30afcb6ddc ppcmmu: Allow map dma for last byte of region.
cur_dma_rgn->end is the last byte of a region. It is not the byte after the region. Therefore, subtract 1 from size before doing compare.

Also add more detail to the abort messages.
2024-04-24 06:50:53 -07:00
joevt ad45ce8499 ppcmmu: Make dummy pages two pages.
In case we need a write to span a page boundary.
2024-04-24 06:49:11 -07:00
joevt 9a489d13f4 machinetnt: Add 604 cpu option. 2024-04-24 06:48:52 -07:00
joevt 721c70166c control: Fix 1280x960 and 1280x1024 15bpp.
Change the fb_ptr. This also affects the hardware cursor.
Tested with 4MB of VRAM.
2024-04-24 06:48:41 -07:00
joevt 861c2d4f04 athens: Allow P2_MUX bit 6 to be 1.
For modes 12 inch RGB 512x384@60 and VGA 800x600@72.
2024-04-24 06:48:27 -07:00
joevt da203b69c5 escc: One return path for read. 2024-04-24 06:48:02 -07:00
joevt 159fe8d48e escc: Attempt to add DBDMA support.
Move dma channels from EsccController to EsccChannel.
Add DBDMA callbacks.
2024-04-24 06:46:35 -07:00
9 changed files with 264 additions and 27 deletions

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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; };

View File

@ -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;
}

View File

@ -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 = {

View File

@ -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

View File

@ -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
}

View File

@ -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") {