mirror of
https://github.com/dingusdev/dingusppc.git
synced 2024-06-08 16:29:33 +00:00
Merge remote-tracking branch 'origin/machine-yosemite'
This commit is contained in:
commit
03595c3940
|
@ -94,7 +94,7 @@ enum SPR : int {
|
|||
DAR = 19,
|
||||
RTCU_S = 20, // supervisor RTCU
|
||||
RTCL_S = 21, // supervisor RTCL
|
||||
DEC = 22,
|
||||
DEC = 22, // decrementer
|
||||
SDR1 = 25,
|
||||
SRR0 = 26,
|
||||
SRR1 = 27,
|
||||
|
@ -160,8 +160,10 @@ extern uint32_t opcode_value; // used for interpreting opcodes
|
|||
|
||||
extern uint64_t timebase_counter;
|
||||
extern uint64_t tbr_wr_timestamp;
|
||||
extern uint64_t dec_wr_timestamp;
|
||||
extern uint64_t rtc_timestamp;
|
||||
extern uint64_t tbr_wr_value;
|
||||
extern uint32_t dec_wr_value;
|
||||
extern uint32_t tbr_freq_ghz;
|
||||
extern uint32_t rtc_lo, rtc_hi;
|
||||
|
||||
|
|
|
@ -71,7 +71,8 @@ uint64_t rtc_timestamp; // stores vCPU virtual time of the last RTC write
|
|||
uint64_t tbr_wr_value; // last value written to the TBR
|
||||
uint32_t tbr_freq_ghz; // TBR/RTC driving frequency in GHz expressed as a 32 bit fraction less than 1.0.
|
||||
uint64_t timebase_counter; // internal timebase counter
|
||||
uint32_t decr; // current value of PPC DEC register
|
||||
uint64_t dec_wr_timestamp; // stores vCPU virtual time of the last DEC write
|
||||
uint32_t dec_wr_value; // last value written to the DEC register
|
||||
uint32_t rtc_lo; // MPC601 RTC lower, counts nanoseconds
|
||||
uint32_t rtc_hi; // MPC601 RTC upper, counts seconds
|
||||
|
||||
|
@ -771,7 +772,7 @@ void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, uint64_t tb_freq)
|
|||
exec_flags = 0;
|
||||
|
||||
timebase_counter = 0;
|
||||
decr = 0;
|
||||
dec_wr_value = 0;
|
||||
|
||||
/* zero all GPRs as prescribed for MPC601 */
|
||||
/* For later PPC CPUs, GPR content is undefined */
|
||||
|
|
|
@ -870,6 +870,35 @@ static inline void calc_rtcl_value()
|
|||
rtc_timestamp = new_ts;
|
||||
}
|
||||
|
||||
static inline uint64_t calc_tbr_value()
|
||||
{
|
||||
uint64_t tbr_inc;
|
||||
uint32_t tbr_inc_lo;
|
||||
uint64_t diff = get_virt_time_ns() - tbr_wr_timestamp;
|
||||
_u32xu64(tbr_freq_ghz, diff, tbr_inc, tbr_inc_lo);
|
||||
return (tbr_wr_value + tbr_inc);
|
||||
}
|
||||
|
||||
static inline uint32_t calc_dec_value() {
|
||||
uint64_t dec_adj;
|
||||
uint32_t dec_adj_lo;
|
||||
uint64_t diff = get_virt_time_ns() - dec_wr_timestamp;
|
||||
_u32xu64(tbr_freq_ghz, diff, dec_adj, dec_adj_lo);
|
||||
return (dec_wr_value - dec_adj);
|
||||
}
|
||||
|
||||
static void update_timebase(uint64_t mask, uint64_t new_val)
|
||||
{
|
||||
uint64_t tbr_value = calc_tbr_value();
|
||||
tbr_wr_value = (tbr_value & mask) | new_val;
|
||||
tbr_wr_timestamp = get_virt_time_ns();
|
||||
}
|
||||
|
||||
static void update_decrementer(uint32_t val) {
|
||||
dec_wr_value = val;
|
||||
dec_wr_timestamp = get_virt_time_ns();
|
||||
}
|
||||
|
||||
void dppc_interpreter::ppc_mfspr() {
|
||||
uint32_t ref_spr = (((ppc_cur_instruction >> 11) & 31) << 5) | ((ppc_cur_instruction >> 16) & 31);
|
||||
|
||||
|
@ -888,25 +917,14 @@ void dppc_interpreter::ppc_mfspr() {
|
|||
calc_rtcl_value();
|
||||
ppc_state.spr[SPR::RTCU_U] = rtc_hi;
|
||||
break;
|
||||
case SPR::DEC:
|
||||
ppc_state.spr[SPR::DEC] = calc_dec_value();
|
||||
break;
|
||||
}
|
||||
|
||||
ppc_state.gpr[(ppc_cur_instruction >> 21) & 31] = ppc_state.spr[ref_spr];
|
||||
}
|
||||
|
||||
static inline uint64_t calc_tbr_value()
|
||||
{
|
||||
uint64_t diff = get_virt_time_ns() - tbr_wr_timestamp;
|
||||
uint64_t tbr_inc; uint32_t tbr_inc_lo; _u32xu64(tbr_freq_ghz, diff, tbr_inc, tbr_inc_lo);
|
||||
return (tbr_wr_value + tbr_inc);
|
||||
}
|
||||
|
||||
static void update_timebase(uint64_t mask, uint64_t new_val)
|
||||
{
|
||||
uint64_t tbr_value = calc_tbr_value();
|
||||
tbr_wr_value = (tbr_value & mask) | new_val;
|
||||
tbr_wr_timestamp = get_virt_time_ns();
|
||||
}
|
||||
|
||||
void dppc_interpreter::ppc_mtspr() {
|
||||
uint32_t ref_spr = (((ppc_cur_instruction >> 11) & 31) << 5) | ((ppc_cur_instruction >> 16) & 31);
|
||||
reg_s = (ppc_cur_instruction >> 21) & 31;
|
||||
|
@ -936,6 +954,9 @@ void dppc_interpreter::ppc_mtspr() {
|
|||
calc_rtcl_value();
|
||||
rtc_hi = val;
|
||||
break;
|
||||
case SPR::DEC:
|
||||
update_decrementer(val);
|
||||
break;
|
||||
case SPR::TBL_S:
|
||||
update_timebase(0xFFFFFFFF00000000ULL, val);
|
||||
break;
|
||||
|
|
|
@ -410,7 +410,7 @@ void enter_debugger() {
|
|||
int log_level, context;
|
||||
size_t separator_pos;
|
||||
|
||||
unique_ptr<OfNvramUtils> ofnvram = unique_ptr<OfNvramUtils>(new OfNvramUtils);
|
||||
unique_ptr<OfConfigUtils> ofnvram = unique_ptr<OfConfigUtils>(new OfConfigUtils);
|
||||
|
||||
context = 1; /* start with the PowerPC context */
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||
Copyright (C) 2018-22 divingkatae and maximum
|
||||
Copyright (C) 2018-23 divingkatae and maximum
|
||||
(theweirdo) spatium
|
||||
|
||||
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
|
||||
|
@ -44,77 +44,100 @@ void DMAChannel::fetch_cmd(uint32_t cmd_addr, DMACmd* p_cmd) {
|
|||
|
||||
uint8_t DMAChannel::interpret_cmd() {
|
||||
DMACmd cmd_struct;
|
||||
bool is_writable;
|
||||
int cmd;
|
||||
bool is_writable, branch_taken = false;
|
||||
|
||||
if (this->cmd_in_progress) {
|
||||
// return current command if there is data to transfer
|
||||
if (this->queue_len)
|
||||
return cmd;
|
||||
return this->cur_cmd;
|
||||
|
||||
// obtain real pointer to the descriptor of the completed command
|
||||
uint8_t *curr_cmd = mmu_get_dma_mem(this->cmd_ptr - 16, 16, &is_writable);
|
||||
uint8_t *cmd_desc = mmu_get_dma_mem(this->cmd_ptr, 16, &is_writable);
|
||||
|
||||
// get command code
|
||||
cmd = curr_cmd[3] >> 4;
|
||||
this->cur_cmd = cmd_desc[3] >> 4;
|
||||
|
||||
// all commands except STOP update cmd.xferStatus
|
||||
if (cmd < 7 && is_writable) {
|
||||
WRITE_WORD_LE_A(&curr_cmd[14], this->ch_stat | CH_STAT_ACTIVE);
|
||||
// all commands except STOP update cmd.xferStatus and
|
||||
// perform actions under control of "i", "b" and "w" bits
|
||||
if (this->cur_cmd < DBDMA_Cmd::STOP) {
|
||||
if (is_writable)
|
||||
WRITE_WORD_LE_A(&cmd_desc[14], this->ch_stat | CH_STAT_ACTIVE);
|
||||
|
||||
if (cmd_desc[2] & 3) {
|
||||
ABORT_F("DBDMA: cmd.w bit not implemented");
|
||||
}
|
||||
|
||||
// react to cmd.b bit
|
||||
if (cmd_desc[2] & 0xC) {
|
||||
bool cond = true;
|
||||
if ((cmd_desc[2] & 0xC) != 0xC) {
|
||||
uint16_t br_mask = this->branch_select >> 16;
|
||||
cond = (this->ch_stat & br_mask) == (this->branch_select & br_mask);
|
||||
if ((cmd_desc[2] & 0xC) == 0x8) { // branch if cond cleared?
|
||||
cond = !cond;
|
||||
}
|
||||
}
|
||||
if (cond) {
|
||||
this->cmd_ptr = READ_DWORD_LE_A(&cmd_desc[8]);
|
||||
branch_taken = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd_desc[2] & 0x30) {
|
||||
ABORT_F("DBDMA: cmd.i bit not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
// all INPUT and OUTPUT commands update cmd.resCount
|
||||
if (cmd < 4 && is_writable) {
|
||||
WRITE_WORD_LE_A(&curr_cmd[12], this->queue_len & 0xFFFFUL);
|
||||
if (this->cur_cmd < DBDMA_Cmd::STORE_QUAD && is_writable) {
|
||||
WRITE_WORD_LE_A(&cmd_desc[12], this->queue_len & 0xFFFFUL);
|
||||
}
|
||||
|
||||
if (!branch_taken)
|
||||
this->cmd_ptr += 16;
|
||||
|
||||
this->cmd_in_progress = false;
|
||||
}
|
||||
|
||||
fetch_cmd(this->cmd_ptr, &cmd_struct);
|
||||
|
||||
cmd = cmd_struct.cmd_key >> 4;
|
||||
this->ch_stat &= ~CH_STAT_WAKE; // clear wake bit (DMA spec, 5.5.3.4)
|
||||
|
||||
this->ch_stat &= ~CH_STAT_WAKE; /* clear wake bit (DMA spec, 5.5.3.4) */
|
||||
this->cur_cmd = cmd_struct.cmd_key >> 4;
|
||||
|
||||
switch (cmd_struct.cmd_key >> 4) {
|
||||
case 0: // OUTPUT_MORE
|
||||
case 1: // OUTPUT_LAST
|
||||
case 2: // INPUT_MORE
|
||||
case 3: // INPUT_LAST
|
||||
switch (this->cur_cmd) {
|
||||
case DBDMA_Cmd::OUTPUT_MORE:
|
||||
case DBDMA_Cmd::OUTPUT_LAST:
|
||||
case DBDMA_Cmd::INPUT_MORE:
|
||||
case DBDMA_Cmd::INPUT_LAST:
|
||||
if (cmd_struct.cmd_key & 7) {
|
||||
LOG_F(ERROR, "Key > 0 not implemented");
|
||||
break;
|
||||
}
|
||||
if (cmd_struct.cmd_bits & 0x3F) {
|
||||
LOG_F(ERROR, "non-zero i/b/w not implemented");
|
||||
break;
|
||||
}
|
||||
this->queue_data = mmu_get_dma_mem(cmd_struct.address, cmd_struct.req_count, &is_writable);
|
||||
this->queue_len = cmd_struct.req_count;
|
||||
this->cmd_ptr += 16;
|
||||
this->cmd_in_progress = true;
|
||||
break;
|
||||
case 4:
|
||||
case DBDMA_Cmd::STORE_QUAD:
|
||||
LOG_F(ERROR, "Unsupported DMA Command STORE_QUAD");
|
||||
break;
|
||||
case 5:
|
||||
case DBDMA_Cmd::LOAD_QUAD:
|
||||
LOG_F(ERROR, "Unsupported DMA Command LOAD_QUAD");
|
||||
break;
|
||||
case 6:
|
||||
case DBDMA_Cmd::NOP:
|
||||
LOG_F(ERROR, "Unsupported DMA Command NOP");
|
||||
break;
|
||||
case 7:
|
||||
case DBDMA_Cmd::STOP:
|
||||
this->ch_stat &= ~CH_STAT_ACTIVE;
|
||||
this->cmd_in_progress = false;
|
||||
break;
|
||||
default:
|
||||
LOG_F(ERROR, "Unsupported DMA command 0x%X", cmd_struct.cmd_key >> 4);
|
||||
LOG_F(ERROR, "Unsupported DMA command 0x%X", this->cur_cmd);
|
||||
this->ch_stat |= CH_STAT_DEAD;
|
||||
this->ch_stat &= ~CH_STAT_ACTIVE;
|
||||
}
|
||||
|
||||
return (cmd_struct.cmd_key >> 4);
|
||||
return this->cur_cmd;
|
||||
}
|
||||
|
||||
|
||||
|
@ -184,6 +207,10 @@ void DMAChannel::reg_write(uint32_t offset, uint32_t value, int size) {
|
|||
new_stat &= ~CH_STAT_FLUSH;
|
||||
this->ch_stat = new_stat;
|
||||
}
|
||||
// update ch_stat.s0...s7 if requested
|
||||
if ((new_stat & 0xFF) != (old_stat & 0xFF)) {
|
||||
this->ch_stat |= new_stat & 0xFF;
|
||||
}
|
||||
break;
|
||||
case DMAReg::CH_STAT:
|
||||
break; /* ingore writes to ChannelStatus */
|
||||
|
@ -193,6 +220,9 @@ void DMAChannel::reg_write(uint32_t offset, uint32_t value, int size) {
|
|||
LOG_F(9, "CommandPtrLo set to 0x%X", this->cmd_ptr);
|
||||
}
|
||||
break;
|
||||
case DMAReg::BRANCH_SELECT:
|
||||
this->branch_select = value & 0xFF00FFUL;
|
||||
break;
|
||||
default:
|
||||
LOG_F(WARNING, "Unsupported DMA channel register 0x%X", offset);
|
||||
}
|
||||
|
@ -294,6 +324,8 @@ void DMAChannel::resume() {
|
|||
|
||||
void DMAChannel::abort() {
|
||||
LOG_F(9, "Aborting DMA channel");
|
||||
if (this->stop_cb)
|
||||
this->stop_cb();
|
||||
}
|
||||
|
||||
void DMAChannel::pause() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||
Copyright (C) 2018-22 divingkatae and maximum
|
||||
Copyright (C) 2018-23 divingkatae and maximum
|
||||
(theweirdo) spatium
|
||||
|
||||
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
|
||||
|
@ -36,9 +36,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
|
||||
/** DBDMA Channel registers offsets */
|
||||
enum DMAReg : uint32_t {
|
||||
CH_CTRL = 0,
|
||||
CH_STAT = 4,
|
||||
CMD_PTR_LO = 12,
|
||||
CH_CTRL = 0,
|
||||
CH_STAT = 4,
|
||||
CMD_PTR_LO = 12,
|
||||
INT_SELECT = 16,
|
||||
BRANCH_SELECT = 20,
|
||||
};
|
||||
|
||||
/** Channel Status bits (DBDMA spec, 5.5.3) */
|
||||
|
@ -62,6 +64,19 @@ typedef struct DMACmd {
|
|||
uint16_t xfer_stat;
|
||||
} DMACmd;
|
||||
|
||||
namespace DBDMA_Cmd {
|
||||
enum : uint8_t {
|
||||
OUTPUT_MORE = 0,
|
||||
OUTPUT_LAST = 1,
|
||||
INPUT_MORE = 2,
|
||||
INPUT_LAST = 3,
|
||||
STORE_QUAD = 4,
|
||||
LOAD_QUAD = 5,
|
||||
NOP = 6,
|
||||
STOP = 7
|
||||
};
|
||||
};
|
||||
|
||||
typedef std::function<void(void)> DbdmaCallback;
|
||||
|
||||
class DMAChannel : public DmaBidirChannel {
|
||||
|
@ -90,12 +105,14 @@ private:
|
|||
std::function<void(void)> start_cb = nullptr; // DMA channel start callback
|
||||
std::function<void(void)> stop_cb = nullptr; // DMA channel stop callback
|
||||
|
||||
uint16_t ch_stat = 0;
|
||||
uint32_t cmd_ptr = 0;
|
||||
uint32_t queue_len = 0;
|
||||
uint8_t* queue_data = 0;
|
||||
uint16_t ch_stat = 0;
|
||||
uint32_t cmd_ptr = 0;
|
||||
uint32_t queue_len = 0;
|
||||
uint8_t* queue_data = 0;
|
||||
uint32_t branch_select = 0;
|
||||
|
||||
bool cmd_in_progress = false;
|
||||
bool cmd_in_progress = false;
|
||||
uint8_t cur_cmd;
|
||||
};
|
||||
|
||||
#endif /* DB_DMA_H */
|
||||
|
|
|
@ -45,6 +45,7 @@ enum HWCompType {
|
|||
SCSI_DEV = 1ULL << 22, /* SCSI device */
|
||||
IDE_BUS = 1ULL << 23, /* IDE bus */
|
||||
IDE_DEV = 1ULL << 25, /* IDE device */
|
||||
SND_CODEC = 1ULL << 30, /* Sound codec */
|
||||
SND_SERVER = 1ULL << 31, /* host sound server */
|
||||
FLOPPY_CTRL = 1ULL << 32, /* floppy disk controller */
|
||||
FLOPPY_DRV = 1ULL << 33, /* floppy disk drive */
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||
Copyright (C) 2018-22 divingkatae and maximum
|
||||
Copyright (C) 2018-23 divingkatae and maximum
|
||||
(theweirdo) spatium
|
||||
|
||||
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
|
||||
|
@ -19,7 +19,7 @@ You should have received a copy of the GNU General Public License
|
|||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file Utilities for working with the Apple Open Firmware NVRAM partition. */
|
||||
/** Utilities for working with Apple Open Firmware and CHRP NVRAM partitions. */
|
||||
|
||||
#include <devices/common/ofnvram.h>
|
||||
#include <endianswap.h>
|
||||
|
@ -32,11 +32,19 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
using namespace std;
|
||||
|
||||
static std::string hex2str(uint32_t n)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << setw(8) << setfill('0') << hex << n;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
static uint32_t str2env(string& num_str) {
|
||||
try {
|
||||
return (uint32_t)stoul(num_str, NULL, 0);
|
||||
|
@ -50,22 +58,15 @@ static uint32_t str2env(string& num_str) {
|
|||
}
|
||||
}
|
||||
|
||||
int OfNvramUtils::init()
|
||||
{
|
||||
this->nvram_obj = dynamic_cast<NVram*>(gMachineObj->get_comp_by_name("NVRAM"));
|
||||
return this->nvram_obj == nullptr;
|
||||
}
|
||||
|
||||
bool OfNvramUtils::validate()
|
||||
{
|
||||
int i;
|
||||
OfNvramHdr hdr;
|
||||
bool OfConfigAppl::validate() {
|
||||
int i;
|
||||
OfConfigHdrAppl hdr;
|
||||
|
||||
if (this->nvram_obj == nullptr)
|
||||
return false;
|
||||
|
||||
// read OF partition header
|
||||
for (i = 0; i < sizeof(OfNvramHdr); i++) {
|
||||
for (i = 0; i < sizeof(OfConfigHdrAppl); i++) {
|
||||
((uint8_t*)&hdr)[i] = this->nvram_obj->read_byte(OF_NVRAM_OFFSET + i);
|
||||
}
|
||||
|
||||
|
@ -75,7 +76,7 @@ bool OfNvramUtils::validate()
|
|||
|
||||
this->size = hdr.num_pages * 256;
|
||||
|
||||
if (this->size != 0x800)
|
||||
if (this->size != OF_CFG_SIZE)
|
||||
return false;
|
||||
|
||||
// read the entire partition into the local buffer
|
||||
|
@ -88,10 +89,9 @@ bool OfNvramUtils::validate()
|
|||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
uint16_t OfNvramUtils::checksum_partition()
|
||||
{
|
||||
uint16_t OfConfigAppl::checksum_partition() {
|
||||
uint32_t acc = 0;
|
||||
|
||||
for (int i = 0; i < this->size; i += 2) {
|
||||
|
@ -101,25 +101,6 @@ uint16_t OfNvramUtils::checksum_partition()
|
|||
return acc + (acc >> 16);
|
||||
}
|
||||
|
||||
void OfNvramUtils::update_partition()
|
||||
{
|
||||
// set checksum in the header to zero
|
||||
this->buf[4] = 0;
|
||||
this->buf[5] = 0;
|
||||
|
||||
// calculate new checksum
|
||||
uint16_t checksum = this->checksum_partition();
|
||||
checksum = checksum ? ~checksum : 0xFFFFU;
|
||||
|
||||
// stuff it into the header
|
||||
WRITE_WORD_BE_A(&this->buf[4], checksum);
|
||||
|
||||
// write the entire partition back to NVRAM
|
||||
for (int i = 0; i < this->size; i++) {
|
||||
this->nvram_obj->write_byte(OF_NVRAM_OFFSET + i, this->buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static const string flag_names[8] = {
|
||||
"little-endian?",
|
||||
"real-mode?",
|
||||
|
@ -154,58 +135,74 @@ static const map<string, std::tuple<int, uint16_t>> of_vars = {
|
|||
{"boot-command", {OF_VAR_TYPE_STR, 0x58}},
|
||||
};
|
||||
|
||||
void OfNvramUtils::printenv()
|
||||
{
|
||||
int i;
|
||||
const OfConfigImpl::config_dict& OfConfigAppl::get_config_vars() {
|
||||
this->_config_vars.clear();
|
||||
|
||||
if (!this->validate()) {
|
||||
cout << "Invalid Open Firmware partition content!" << endl;
|
||||
return;
|
||||
}
|
||||
if (!this->validate())
|
||||
return _config_vars;
|
||||
|
||||
uint8_t of_flags = this->buf[12];
|
||||
|
||||
cout << endl;
|
||||
|
||||
// print flags
|
||||
for (i = 0; i < 8; i++) {
|
||||
cout << setw(20) << left << flag_names[i] <<
|
||||
(((of_flags << i) & 0x80) ? "true" : "false") << endl;
|
||||
// populate flags
|
||||
for (int i = 0; i < 8; i++) {
|
||||
_config_vars.push_back(std::make_pair(flag_names[i],
|
||||
((of_flags << i) & 0x80) ? "true" : "false"));
|
||||
}
|
||||
|
||||
// print the remaining variables
|
||||
// populate the remaining variables
|
||||
for (auto& var : of_vars) {
|
||||
auto name = var.first;
|
||||
auto type = std::get<0>(var.second);
|
||||
auto offset = std::get<1>(var.second);
|
||||
|
||||
cout << setw(20) << left << var.first;
|
||||
|
||||
switch (type) {
|
||||
case OF_VAR_TYPE_INT:
|
||||
cout << hex << READ_DWORD_BE_A(&this->buf[offset]) << endl;
|
||||
_config_vars.push_back(std::make_pair(name,
|
||||
hex2str(READ_DWORD_BE_A(&this->buf[offset]))));
|
||||
break;
|
||||
case OF_VAR_TYPE_STR:
|
||||
uint16_t str_offset = READ_WORD_BE_A(&this->buf[offset]) - OF_NVRAM_OFFSET;
|
||||
uint16_t str_len = READ_WORD_BE_A(&this->buf[offset+2]);
|
||||
for (i = 0; i < str_len; i++) {
|
||||
char ch = *(char *)&(this->buf[str_offset + i]);
|
||||
if (ch == '\x0D') cout << endl; else cout << ch;
|
||||
|
||||
if ((str_offset + str_len) > OF_CFG_SIZE) {
|
||||
cout << "string property too long - skip it" << endl;
|
||||
break;
|
||||
}
|
||||
cout << endl;
|
||||
|
||||
char prop_val[OF_CFG_SIZE] = "";
|
||||
memcpy(prop_val, &this->buf[str_offset], str_len);
|
||||
prop_val[str_len] = '\0';
|
||||
|
||||
_config_vars.push_back(std::make_pair(name, prop_val));
|
||||
}
|
||||
}
|
||||
|
||||
cout << endl;
|
||||
return _config_vars;
|
||||
};
|
||||
|
||||
void OfConfigAppl::update_partition() {
|
||||
// set checksum in the header to zero
|
||||
this->buf[4] = 0;
|
||||
this->buf[5] = 0;
|
||||
|
||||
// calculate new checksum
|
||||
uint16_t checksum = this->checksum_partition();
|
||||
checksum = checksum ? ~checksum : 0xFFFFU;
|
||||
|
||||
// stuff checksum into the header
|
||||
WRITE_WORD_BE_A(&this->buf[4], checksum);
|
||||
|
||||
// write the entire partition back to NVRAM
|
||||
for (int i = 0; i < this->size; i++) {
|
||||
this->nvram_obj->write_byte(OF_NVRAM_OFFSET + i, this->buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void OfNvramUtils::setenv(string var_name, string value)
|
||||
{
|
||||
bool OfConfigAppl::set_var(std::string& var_name, std::string& value) {
|
||||
int i, flag;
|
||||
|
||||
if (!this->validate()) {
|
||||
cout << "Invalid Open Firmware partition content!" << endl;
|
||||
return;
|
||||
}
|
||||
if (!this->validate())
|
||||
return false;
|
||||
|
||||
// check if the user tries to change a flag
|
||||
for (i = 0; i < 8; i++) {
|
||||
|
@ -216,7 +213,7 @@ void OfNvramUtils::setenv(string var_name, string value)
|
|||
flag = 0;
|
||||
else {
|
||||
cout << "Invalid property value: " << value << endl;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
uint8_t flag_bit = 0x80U >> i;
|
||||
uint8_t of_flags = this->buf[12];
|
||||
|
@ -228,15 +225,14 @@ void OfNvramUtils::setenv(string var_name, string value)
|
|||
|
||||
this->buf[12] = of_flags;
|
||||
this->update_partition();
|
||||
cout << " ok" << endl; // mimic Forth
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// see if one of the standard properties should be changed
|
||||
if (of_vars.find(var_name) == of_vars.end()) {
|
||||
cout << "Attempt to change unknown variable " << var_name << endl;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto type = std::get<0>(of_vars.at(var_name));
|
||||
|
@ -248,7 +244,7 @@ void OfNvramUtils::setenv(string var_name, string value)
|
|||
num = str2env(value);
|
||||
} catch (invalid_argument& exc) {
|
||||
cout << exc.what() << endl;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
WRITE_DWORD_BE_A(&this->buf[offset], num);
|
||||
this->update_partition();
|
||||
|
@ -257,7 +253,7 @@ void OfNvramUtils::setenv(string var_name, string value)
|
|||
uint16_t str_offset = READ_WORD_BE_A(&this->buf[offset]);
|
||||
uint16_t str_len = READ_WORD_BE_A(&this->buf[offset+2]);
|
||||
|
||||
OfNvramHdr *hdr = (OfNvramHdr *)&this->buf[0];
|
||||
OfConfigHdrAppl *hdr = (OfConfigHdrAppl *)&this->buf[0];
|
||||
uint16_t here = READ_WORD_BE_A(&hdr->here);
|
||||
uint16_t top = READ_WORD_BE_A(&hdr->top);
|
||||
|
||||
|
@ -266,11 +262,13 @@ void OfNvramUtils::setenv(string var_name, string value)
|
|||
uint16_t new_top = top + str_len - value.length();
|
||||
if (new_top < here) {
|
||||
cout << "No room in the heap!" << endl;
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// remove the old string
|
||||
std::memmove(&this->buf[top + str_len - OF_NVRAM_OFFSET], &this->buf[top - OF_NVRAM_OFFSET], str_offset - top);
|
||||
std::memmove(&this->buf[top + str_len - OF_NVRAM_OFFSET],
|
||||
&this->buf[top - OF_NVRAM_OFFSET], str_offset - top);
|
||||
|
||||
for (auto& var : of_vars) {
|
||||
auto type = std::get<0>(var.second);
|
||||
auto offset = std::get<1>(var.second);
|
||||
|
@ -299,7 +297,271 @@ void OfNvramUtils::setenv(string var_name, string value)
|
|||
|
||||
// update physical NVRAM
|
||||
this->update_partition();
|
||||
cout << " ok" << endl; // mimic Forth
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
uint8_t OfConfigChrp::checksum_hdr(const uint8_t* data)
|
||||
{
|
||||
uint16_t sum = data[0];
|
||||
|
||||
for (int i = 2; i < 16; i++) {
|
||||
sum += data[i];
|
||||
if (sum >= 256)
|
||||
sum = (sum + 1) & 0xFFU;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
bool OfConfigChrp::validate()
|
||||
{
|
||||
int i, pos, len;
|
||||
uint8_t sig;
|
||||
bool wip = true;
|
||||
bool of_part_found = false;
|
||||
|
||||
// search the entire 8KB NVRAM for CHRP OF config partition.
|
||||
// Bail out if an unknown partition or free space is encountered.
|
||||
// Skip over known partitions.
|
||||
for (pos = 0; wip && pos < 8192;) {
|
||||
sig = this->nvram_obj->read_byte(pos);
|
||||
switch (sig) {
|
||||
case NVRAM_SIG_OF_ENV:
|
||||
of_part_found = true;
|
||||
// fall-through
|
||||
case NVRAM_SIG_FREE:
|
||||
wip = false;
|
||||
break;
|
||||
case NVRAM_SIG_VPD:
|
||||
case NVRAM_SIG_DIAG:
|
||||
case NVRAM_SIG_OF_CFG:
|
||||
case NVRAM_SIG_MAC_OS:
|
||||
case NVRAM_SIG_ERR_LOG:
|
||||
// skip valid partitions we're not interested in
|
||||
len = (this->nvram_obj->read_byte(pos + 2) << 8) |
|
||||
this->nvram_obj->read_byte(pos + 3);
|
||||
if (!len || (len * 16) >= 8192)
|
||||
break;
|
||||
pos += len * 16;
|
||||
break;
|
||||
default:
|
||||
wip = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!of_part_found)
|
||||
return false;
|
||||
|
||||
OfConfigHdrChrp hdr;
|
||||
|
||||
// read OF partition header
|
||||
for (i = 0; i < sizeof(OfConfigHdrChrp); i++) {
|
||||
((uint8_t*)&hdr)[i] = this->nvram_obj->read_byte(pos + i);
|
||||
}
|
||||
|
||||
len = BYTESWAP_16(hdr.length) * 16;
|
||||
|
||||
// sanity checks
|
||||
if (hdr.sig != NVRAM_SIG_OF_ENV || len < 16 || len > (4096 + sizeof(OfConfigHdrChrp)))
|
||||
return false;
|
||||
|
||||
// calculate partition header checksum
|
||||
uint8_t chk_sum = this->checksum_hdr((uint8_t*)&hdr);
|
||||
|
||||
if (chk_sum != hdr.checksum)
|
||||
return false;
|
||||
|
||||
len -= sizeof(OfConfigHdrChrp);
|
||||
pos += sizeof(OfConfigHdrChrp);
|
||||
|
||||
this->data_offset = pos;
|
||||
|
||||
// read the entire partition into the local buffer
|
||||
for (i = 0; i < len; i++) {
|
||||
this->buf[i] = this->nvram_obj->read_byte(pos + i);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const OfConfigImpl::config_dict& OfConfigChrp::get_config_vars() {
|
||||
int len;
|
||||
|
||||
this->_config_vars.clear();
|
||||
|
||||
if (!this->validate())
|
||||
return _config_vars;
|
||||
|
||||
for (int pos = 0; pos < 4096;) {
|
||||
char *pname = (char *)&this->buf[pos];
|
||||
|
||||
// scan property name until '=' is encountered
|
||||
// or max length is reached
|
||||
for (len = 0; len < 32; pos++, len++) {
|
||||
if (pname[len] == '=' || pname[len] == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
// empty property name -> free space reached
|
||||
if (!len) {
|
||||
this->data_length = pos;
|
||||
break;
|
||||
}
|
||||
|
||||
if (pname[len] != '=') {
|
||||
cout << "no = sign found or name > 31 chars" << endl;
|
||||
break;
|
||||
}
|
||||
|
||||
char prop_name[32];
|
||||
memcpy(prop_name, pname, len);
|
||||
prop_name[len] = '\0';
|
||||
|
||||
pos++; // skip past '='
|
||||
char *pval = (char *)&this->buf[pos];
|
||||
|
||||
// determine property value length
|
||||
for (len = 0; pos < 4096; pos++, len++) {
|
||||
if (pval[len] == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
// ensure each property value is null-terminated
|
||||
if (pos >= 4096) {
|
||||
cout << "ran off partition end" << endl;
|
||||
break;
|
||||
}
|
||||
|
||||
this->_config_vars.push_back(std::make_pair(prop_name, pval));
|
||||
pos++; // skip past null terminator
|
||||
}
|
||||
|
||||
return this->_config_vars;
|
||||
}
|
||||
|
||||
bool OfConfigChrp::update_partition() {
|
||||
unsigned pos = 0;
|
||||
|
||||
memset(this->buf, 0, 4096);
|
||||
|
||||
for (auto& var : this->_config_vars) {
|
||||
if ((var.first.length() + var.second.length() + 2) >= 4096) {
|
||||
cout << "No room in the partition!" << endl;
|
||||
return false;
|
||||
}
|
||||
memcpy(&this->buf[pos], var.first.c_str(), var.first.length());
|
||||
pos += var.first.length();
|
||||
this->buf[pos++] = '=';
|
||||
memcpy(&this->buf[pos], var.second.c_str(), var.second.length());
|
||||
pos += var.second.length();
|
||||
this->buf[pos++] = '\0';
|
||||
}
|
||||
|
||||
// write the entire partition back to NVRAM
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
this->nvram_obj->write_byte(this->data_offset + i, this->buf[i]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OfConfigChrp::set_var(std::string& var_name, std::string& value) {
|
||||
if (!this->validate())
|
||||
return false;
|
||||
|
||||
bool found = false;
|
||||
|
||||
// see if the user tries to change an existing property
|
||||
for (auto& var : this->_config_vars) {
|
||||
if (var.first == var_name) {
|
||||
found = true;
|
||||
|
||||
// see if we're about to change a flag
|
||||
if (var_name.back() == '?') {
|
||||
if (value != "true" && value != "false") {
|
||||
cout << "Flag value can be 'true' or 'false'" << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (value.length() > var.second.length()) {
|
||||
unsigned free_space = 4096 - this->data_length;
|
||||
if ((value.length() - var.second.length()) >= free_space) {
|
||||
cout << "No room for new data!" << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var.second = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
cout << "Attempt to change unknown variable " << var_name << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return this->update_partition();
|
||||
};
|
||||
|
||||
int OfConfigUtils::init()
|
||||
{
|
||||
this->nvram_obj = dynamic_cast<NVram*>(gMachineObj->get_comp_by_name("NVRAM"));
|
||||
return this->nvram_obj == nullptr;
|
||||
}
|
||||
|
||||
bool OfConfigUtils::open_container() {
|
||||
OfConfigImpl* cfg_obj;
|
||||
|
||||
if (this->cfg_impl == nullptr) {
|
||||
cfg_obj = new OfConfigAppl(this->nvram_obj);
|
||||
if (cfg_obj->validate()) {
|
||||
this->cfg_impl = std::unique_ptr<OfConfigImpl>(cfg_obj);
|
||||
return true;
|
||||
} else {
|
||||
delete(cfg_obj);
|
||||
|
||||
cfg_obj = new OfConfigChrp(this->nvram_obj);
|
||||
if (cfg_obj->validate()) {
|
||||
this->cfg_impl = std::unique_ptr<OfConfigImpl>(cfg_obj);
|
||||
return true;
|
||||
} else {
|
||||
delete(cfg_obj);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return this->cfg_impl->validate();
|
||||
}
|
||||
|
||||
cout << "No valid Open Firmware partition found!" << endl;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void OfConfigUtils::printenv() {
|
||||
OfConfigImpl::config_dict vars;
|
||||
|
||||
if (!this->open_container())
|
||||
return;
|
||||
|
||||
vars = this->cfg_impl->get_config_vars();
|
||||
|
||||
for (auto& var : vars) {
|
||||
cout << setw(34) << left << var.first << var.second << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void OfConfigUtils::setenv(string var_name, string value)
|
||||
{
|
||||
if (!this->open_container())
|
||||
return;
|
||||
|
||||
if (!this->cfg_impl->set_var(var_name, value)) {
|
||||
cout << " Please try again" << endl;
|
||||
} else {
|
||||
cout << " ok" << endl; // mimic Forth
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||
Copyright (C) 2018-22 divingkatae and maximum
|
||||
Copyright (C) 2018-23 divingkatae and maximum
|
||||
(theweirdo) spatium
|
||||
|
||||
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
|
||||
|
@ -19,20 +19,30 @@ You should have received a copy of the GNU General Public License
|
|||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** Apple/CHRP Open Firmware NVRAM partition definitions. */
|
||||
|
||||
#ifndef OF_NVRAM_H
|
||||
#define OF_NVRAM_H
|
||||
|
||||
#include <devices/common/nvram.h>
|
||||
|
||||
/** @file Utilities for working with the Apple Open Firmware NVRAM partition. */
|
||||
#include <cinttypes>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
/** ========== Apple Open Firmware 1.x/2.x partition definitions. ========== */
|
||||
#define OF_NVRAM_OFFSET 0x1800
|
||||
#define OF_NVRAM_SIG 0x1275
|
||||
#define OF_CFG_SIZE 0x800
|
||||
|
||||
// OF Variable types
|
||||
enum {
|
||||
OF_VAR_TYPE_INT = 1,
|
||||
OF_VAR_TYPE_STR = 2,
|
||||
OF_VAR_TYPE_INT = 1, // integer
|
||||
OF_VAR_TYPE_STR = 2, // string
|
||||
OF_VAR_TYPE_FLG = 3, // boolean flag
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
|
@ -42,26 +52,97 @@ typedef struct {
|
|||
uint16_t checksum; // >nv.checksum partition checksum
|
||||
uint16_t here; // >nv.here offset to the next free byte (offset of after last string length; = 0x185c)
|
||||
uint16_t top; // >nv.top offset to the last free byte (offset of string with lowest offset; < 0x2000)
|
||||
} OfNvramHdr;
|
||||
} OfConfigHdrAppl;
|
||||
|
||||
class OfNvramUtils {
|
||||
/** ================== CHRP NVRAM partition definitions. ================== */
|
||||
|
||||
/** CHRP NVRAM partition signatures. */
|
||||
enum {
|
||||
NVRAM_SIG_OF_CFG = 0x50, // of-config (unused in Power Macintosh)
|
||||
NVRAM_SIG_VPD = 0x52, // of-vpd (unused in Power Macintosh)
|
||||
NVRAM_SIG_DIAG = 0x5F, // diagnostics partition
|
||||
NVRAM_SIG_OF_ENV = 0x70, // common partition with OF environment vars
|
||||
NVRAM_SIG_ERR_LOG = 0x72, // post-err-log
|
||||
NVRAM_SIG_FREE = 0x7F, // free space
|
||||
NVRAM_SIG_MAC_OS = 0xA0, // APL,MacOS75
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t sig; // partition signature, must be 0x70
|
||||
uint8_t checksum; // partition header checksum (sig, length & name)
|
||||
uint16_t length; // partition length in 16-byte blocks
|
||||
char name[12]; // null-terminated partition name
|
||||
} OfConfigHdrChrp;
|
||||
|
||||
// - interface
|
||||
class OfConfigImpl {
|
||||
public:
|
||||
OfNvramUtils() = default;
|
||||
~OfNvramUtils() = default;
|
||||
using config_dict = std::vector<std::pair<std::string, std::string>>;
|
||||
|
||||
virtual ~OfConfigImpl() = default;
|
||||
virtual bool validate() = 0;
|
||||
virtual const config_dict& get_config_vars() = 0;
|
||||
virtual bool set_var(std::string& name, std::string& val) = 0;
|
||||
};
|
||||
|
||||
// Old World implementation
|
||||
class OfConfigAppl : public OfConfigImpl {
|
||||
public:
|
||||
OfConfigAppl(NVram* nvram_obj) { this->nvram_obj = nvram_obj; };
|
||||
~OfConfigAppl() = default;
|
||||
|
||||
bool validate();
|
||||
const config_dict& get_config_vars();
|
||||
bool set_var(std::string& var_name, std::string& value);
|
||||
|
||||
protected:
|
||||
uint16_t checksum_partition();
|
||||
void update_partition();
|
||||
|
||||
private:
|
||||
NVram* nvram_obj = nullptr;
|
||||
uint8_t buf[OF_CFG_SIZE];
|
||||
int size = 0;
|
||||
config_dict _config_vars;
|
||||
};
|
||||
|
||||
// New World implementation
|
||||
class OfConfigChrp : public OfConfigImpl {
|
||||
public:
|
||||
OfConfigChrp(NVram* nvram_obj) { this->nvram_obj = nvram_obj; };
|
||||
~OfConfigChrp() = default;
|
||||
|
||||
bool validate();
|
||||
const config_dict& get_config_vars();
|
||||
bool set_var(std::string& var_name, std::string& value);
|
||||
|
||||
protected:
|
||||
uint8_t checksum_hdr(const uint8_t* data);
|
||||
bool update_partition();
|
||||
|
||||
private:
|
||||
NVram* nvram_obj = nullptr;
|
||||
uint8_t buf[4096];
|
||||
unsigned data_offset = 0; // offset to the OF config data
|
||||
unsigned data_length = 0; // length of the OF config data
|
||||
config_dict _config_vars;
|
||||
};
|
||||
|
||||
class OfConfigUtils {
|
||||
public:
|
||||
OfConfigUtils() = default;
|
||||
~OfConfigUtils() = default;
|
||||
|
||||
int init();
|
||||
void printenv();
|
||||
void setenv(std::string var_name, std::string value);
|
||||
|
||||
protected:
|
||||
bool validate();
|
||||
uint16_t checksum_partition();
|
||||
void update_partition();
|
||||
bool open_container();
|
||||
|
||||
private:
|
||||
NVram* nvram_obj = nullptr;
|
||||
int size = 0;
|
||||
uint8_t buf[0x800];
|
||||
std::unique_ptr<OfConfigImpl> cfg_impl = nullptr;
|
||||
NVram* nvram_obj = nullptr;
|
||||
};
|
||||
|
||||
#endif // OF_NVRAM_H
|
||||
|
|
94
devices/common/pci/dec21154.cpp
Normal file
94
devices/common/pci/dec21154.cpp
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||
Copyright (C) 2018-23 divingkatae and maximum
|
||||
(theweirdo) spatium
|
||||
|
||||
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** DEC 21154 PCI-to-PCI bridge emulation. */
|
||||
|
||||
#include <devices/common/hwcomponent.h>
|
||||
#include <devices/common/pci/dec21154.h>
|
||||
#include <devices/deviceregistry.h>
|
||||
#include <endianswap.h>
|
||||
#include <loguru.hpp>
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
DecPciBridge::DecPciBridge(std::string name) : PCIBridge(name)
|
||||
{
|
||||
supports_types(HWCompType::PCI_HOST | HWCompType::PCI_DEV);
|
||||
|
||||
// initialize PCI config header
|
||||
this->vendor_id = PCI_VENDOR_DEC;
|
||||
this->device_id = 0x0026;
|
||||
this->class_rev = 0x06040002;
|
||||
this->cache_ln_sz = 0;
|
||||
this->command = 0;
|
||||
this->status = 0x2B0;
|
||||
}
|
||||
|
||||
uint32_t DecPciBridge::pci_cfg_read(uint32_t reg_offs, AccessDetails &details)
|
||||
{
|
||||
if (reg_offs < 64) {
|
||||
return PCIBridge::pci_cfg_read(reg_offs, details);
|
||||
}
|
||||
|
||||
switch (reg_offs) {
|
||||
case CHIP_CTRL:
|
||||
return (this->arb_ctrl << 16) | (this-> diag_ctrl << 8) | this->chip_ctrl;
|
||||
case PSERR_EVENT_DIS:
|
||||
return (this->gpio_out_en << 16) | this->pserr_event_dis;
|
||||
case SEC_CLK_CTRL:
|
||||
return this->sec_clock_ctrl;
|
||||
default:
|
||||
LOG_READ_UNIMPLEMENTED_CONFIG_REGISTER();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DecPciBridge::pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &details)
|
||||
{
|
||||
if (reg_offs < 64) {
|
||||
PCIBridge::pci_cfg_write(reg_offs, value, details);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (reg_offs) {
|
||||
case CHIP_CTRL:
|
||||
this->chip_ctrl = value & 0xFFU;
|
||||
this->diag_ctrl = (value >> 8) & 0xFFU;
|
||||
this->arb_ctrl = value >> 16;
|
||||
break;
|
||||
case PSERR_EVENT_DIS:
|
||||
this->pserr_event_dis = value & 0xFFU;
|
||||
this->gpio_out_en = (value >> 16) & 0xFFU;
|
||||
break;
|
||||
case SEC_CLK_CTRL:
|
||||
this->sec_clock_ctrl = value & 0xFFFFU;
|
||||
break;
|
||||
default:
|
||||
LOG_WRITE_UNIMPLEMENTED_CONFIG_REGISTER();
|
||||
}
|
||||
}
|
||||
|
||||
static const DeviceDescription Dec21154_Descriptor = {
|
||||
DecPciBridge::create, {}, {}
|
||||
};
|
||||
|
||||
REGISTER_DEVICE(Dec21154, Dec21154_Descriptor);
|
62
devices/common/pci/dec21154.h
Normal file
62
devices/common/pci/dec21154.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||
Copyright (C) 2018-23 divingkatae and maximum
|
||||
(theweirdo) spatium
|
||||
|
||||
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** DEC 21154 PCI-to-PCI bridge definitions. */
|
||||
|
||||
#ifndef DEC_P2P_H
|
||||
#define DEC_P2P_H
|
||||
|
||||
#include <devices/common/pci/pcibridge.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <string>
|
||||
|
||||
enum {
|
||||
CHIP_CTRL = 0x40,
|
||||
PSERR_EVENT_DIS = 0x64,
|
||||
SEC_CLK_CTRL = 0x68,
|
||||
};
|
||||
|
||||
class DecPciBridge : public PCIBridge {
|
||||
public:
|
||||
DecPciBridge(std::string name);
|
||||
~DecPciBridge() = default;
|
||||
|
||||
static std::unique_ptr<HWComponent> create() {
|
||||
return std::unique_ptr<DecPciBridge>(new DecPciBridge("DEC21154"));
|
||||
}
|
||||
|
||||
// PCIDevice methods
|
||||
uint32_t pci_cfg_read(uint32_t reg_offs, AccessDetails &details);
|
||||
void pci_cfg_write(uint32_t reg_offs, uint32_t value, AccessDetails &details);
|
||||
|
||||
private:
|
||||
uint8_t chip_ctrl = 0;
|
||||
uint8_t diag_ctrl = 0;
|
||||
uint16_t arb_ctrl = 0x0200;
|
||||
uint8_t pserr_event_dis = 0;
|
||||
uint8_t gpio_out_data = 0;
|
||||
uint8_t gpio_out_en = 0;
|
||||
uint8_t gpio_in_data = 0;
|
||||
uint16_t sec_clock_ctrl = 0;
|
||||
};
|
||||
|
||||
#endif // DEC_P2P_H
|
|
@ -54,6 +54,7 @@ enum {
|
|||
/** PCI Vendor IDs for devices used in Power Macintosh computers. */
|
||||
enum {
|
||||
PCI_VENDOR_ATI = 0x1002,
|
||||
PCI_VENDOR_DEC = 0x1011,
|
||||
PCI_VENDOR_MOTOROLA = 0x1057,
|
||||
PCI_VENDOR_APPLE = 0x106B,
|
||||
PCI_VENDOR_NVIDIA = 0x10DE,
|
||||
|
|
|
@ -26,6 +26,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
#include <devices/deviceregistry.h>
|
||||
#include <endianswap.h>
|
||||
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
@ -78,6 +79,13 @@ public:
|
|||
|
||||
virtual PCIDevice *pci_find_device(uint8_t bus_num, uint8_t dev_num, uint8_t fun_num);
|
||||
|
||||
virtual uint32_t pci_t1_read(uint8_t dev, uint32_t fun, uint32_t reg, AccessDetails &details) {
|
||||
return 0;
|
||||
};
|
||||
|
||||
virtual void pci_t1_write(uint8_t dev, uint32_t fun, uint32_t reg, uint32_t value,
|
||||
AccessDetails &details) {};
|
||||
|
||||
protected:
|
||||
std::unordered_map<int, PCIDevice*> dev_map;
|
||||
std::vector<PCIDevice*> io_space_devs;
|
||||
|
|
|
@ -235,7 +235,7 @@ void AMIC::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size)
|
|||
this->snd_buf_size);
|
||||
this->snd_out_dma->enable();
|
||||
this->awacs->set_sample_rate((this->snd_out_ctrl >> 1) & 3);
|
||||
this->awacs->start_output_dma();
|
||||
this->awacs->dma_out_start();
|
||||
} else {
|
||||
LOG_F(9, "AMIC Sound Out DMA disabled!");
|
||||
this->snd_out_dma->disable();
|
||||
|
|
|
@ -60,8 +60,8 @@ GrandCentral::GrandCentral() : PCIDevice("mac-io/grandcentral"), InterruptCtrl()
|
|||
this->snd_out_dma = std::unique_ptr<DMAChannel> (new DMAChannel());
|
||||
this->awacs->set_dma_out(this->snd_out_dma.get());
|
||||
this->snd_out_dma->set_callbacks(
|
||||
std::bind(&AwacsScreamer::dma_start, this->awacs.get()),
|
||||
std::bind(&AwacsScreamer::dma_end, this->awacs.get())
|
||||
std::bind(&AwacsScreamer::dma_out_start, this->awacs.get()),
|
||||
std::bind(&AwacsScreamer::dma_out_stop, this->awacs.get())
|
||||
);
|
||||
|
||||
// connect serial HW
|
||||
|
|
|
@ -67,13 +67,14 @@ HeathrowIC::HeathrowIC() : PCIDevice("mac-io/heathrow"), InterruptCtrl()
|
|||
// connect Cuda
|
||||
this->viacuda = dynamic_cast<ViaCuda*>(gMachineObj->get_comp_by_name("ViaCuda"));
|
||||
|
||||
// initialize sound chip and its DMA output channel, then wire them together
|
||||
this->screamer = std::unique_ptr<AwacsScreamer> (new AwacsScreamer());
|
||||
// find appropriate sound chip, create a DMA output channel for sound,
|
||||
// then wire everything together
|
||||
this->snd_codec = dynamic_cast<MacioSndCtrl*>(gMachineObj->get_comp_by_type(HWCompType::SND_CODEC));
|
||||
this->snd_out_dma = std::unique_ptr<DMAChannel> (new DMAChannel());
|
||||
this->screamer->set_dma_out(this->snd_out_dma.get());
|
||||
this->snd_codec->set_dma_out(this->snd_out_dma.get());
|
||||
this->snd_out_dma->set_callbacks(
|
||||
std::bind(&AwacsScreamer::dma_start, this->screamer.get()),
|
||||
std::bind(&AwacsScreamer::dma_end, this->screamer.get())
|
||||
std::bind(&AwacsScreamer::dma_out_start, this->snd_codec),
|
||||
std::bind(&AwacsScreamer::dma_out_stop, this->snd_codec)
|
||||
);
|
||||
|
||||
// connect SCSI HW
|
||||
|
@ -102,7 +103,7 @@ void HeathrowIC::notify_bar_change(int bar_num)
|
|||
|
||||
if (this->base_addr != (this->bars[bar_num] & 0xFFFFFFF0UL)) {
|
||||
if (this->base_addr) {
|
||||
LOG_F(WARNING, "Heathrow: deallocating I/O memory not implemented");
|
||||
this->host_instance->pci_unregister_mmio_region(this->base_addr, 0x80000, this);
|
||||
}
|
||||
this->base_addr = this->bars[0] & 0xFFFFFFF0UL;
|
||||
this->host_instance->pci_register_mmio_region(this->base_addr, 0x80000, this);
|
||||
|
@ -162,7 +163,7 @@ uint32_t HeathrowIC::read(uint32_t rgn_start, uint32_t offset, int size) {
|
|||
case 0x13: // ESCC MacRISC addressing
|
||||
return this->escc->read((offset >> 4) & 0xF);
|
||||
case 0x14:
|
||||
res = this->screamer->snd_ctrl_read(offset - 0x14000, size);
|
||||
res = this->snd_codec->snd_ctrl_read(offset - 0x14000, size);
|
||||
break;
|
||||
case 0x15: // SWIM3
|
||||
return this->swim3->read((offset >> 4 )& 0xF);
|
||||
|
@ -212,7 +213,7 @@ void HeathrowIC::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int
|
|||
this->escc->write((offset >> 4) & 0xF, value);
|
||||
break;
|
||||
case 0x14:
|
||||
this->screamer->snd_ctrl_write(offset - 0x14000, value, size);
|
||||
this->snd_codec->snd_ctrl_write(offset - 0x14000, value, size);
|
||||
break;
|
||||
case 0x15: // SWIM3
|
||||
this->swim3->write((offset >> 4) & 0xF, value);
|
||||
|
|
|
@ -268,7 +268,7 @@ private:
|
|||
uint8_t emmo_pin; // factory tester status, active low
|
||||
|
||||
// subdevice objects
|
||||
std::unique_ptr<AwacsScreamer> screamer; // Screamer audio codec instance
|
||||
MacioSndCtrl* snd_codec; // audio codec instance
|
||||
|
||||
NVram* nvram; // NVRAM
|
||||
ViaCuda* viacuda; // VIA cell with Cuda MCU attached to it
|
||||
|
|
|
@ -54,8 +54,8 @@ OHare::OHare() : PCIDevice("mac-io/ohare"), InterruptCtrl()
|
|||
this->snd_out_dma = std::unique_ptr<DMAChannel> (new DMAChannel());
|
||||
this->awacs->set_dma_out(this->snd_out_dma.get());
|
||||
this->snd_out_dma->set_callbacks(
|
||||
std::bind(&AwacsScreamer::dma_start, this->awacs.get()),
|
||||
std::bind(&AwacsScreamer::dma_end, this->awacs.get())
|
||||
std::bind(&AwacsScreamer::dma_out_start, this->awacs.get()),
|
||||
std::bind(&AwacsScreamer::dma_out_stop, this->awacs.get())
|
||||
);
|
||||
|
||||
// connect serial HW
|
||||
|
|
|
@ -227,16 +227,18 @@ bool MemCtrlBase::add_mem_mirror(uint32_t start_addr, uint32_t dest_addr) {
|
|||
}
|
||||
|
||||
|
||||
bool MemCtrlBase::set_data(uint32_t reg_addr, const uint8_t* data, uint32_t size) {
|
||||
bool MemCtrlBase::set_data(uint32_t load_addr, const uint8_t* data, uint32_t size) {
|
||||
AddressMapEntry* ref_entry;
|
||||
uint32_t cpy_size;
|
||||
|
||||
ref_entry = find_range(reg_addr);
|
||||
ref_entry = find_range(load_addr);
|
||||
if (!ref_entry)
|
||||
return false;
|
||||
|
||||
uint32_t load_offset = load_addr - ref_entry->start;
|
||||
|
||||
cpy_size = std::min(ref_entry->end - ref_entry->start + 1, size);
|
||||
memcpy(ref_entry->mem_ptr, data, cpy_size);
|
||||
memcpy(ref_entry->mem_ptr + load_offset, data, cpy_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -171,6 +171,18 @@ uint32_t MPC106::pci_cfg_read(uint32_t reg_offs, AccessDetails &details) {
|
|||
return 0;
|
||||
case GrackleReg::PMCR1:
|
||||
return (this->odcr << 24) | (this->pmcr2 << 16) | this->pmcr1;
|
||||
case GrackleReg::MSAR1:
|
||||
case GrackleReg::MSAR2:
|
||||
return this->mem_start[(reg_offs >> 2) & 1];
|
||||
case GrackleReg::EMSAR1:
|
||||
case GrackleReg::EMSAR2:
|
||||
return this->ext_mem_start[(reg_offs >> 2) & 1];
|
||||
case GrackleReg::MEAR1:
|
||||
case GrackleReg::MEAR2:
|
||||
return this->mem_end[(reg_offs >> 2) & 1];
|
||||
case GrackleReg::EMEAR1:
|
||||
case GrackleReg::EMEAR2:
|
||||
return this->ext_mem_end[(reg_offs >> 2) & 1];
|
||||
case GrackleReg::MBER:
|
||||
return this->mem_bank_en;
|
||||
case GrackleReg::PICR1:
|
||||
|
@ -179,6 +191,12 @@ uint32_t MPC106::pci_cfg_read(uint32_t reg_offs, AccessDetails &details) {
|
|||
return this->picr2;
|
||||
case GrackleReg::MCCR1:
|
||||
return this->mccr1;
|
||||
case GrackleReg::MCCR2:
|
||||
return this->mccr2;
|
||||
case GrackleReg::MCCR3:
|
||||
return this->mccr3;
|
||||
case GrackleReg::MCCR4:
|
||||
return this->mccr4;
|
||||
default:
|
||||
LOG_READ_UNIMPLEMENTED_CONFIG_REGISTER();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||
Copyright (C) 2018-21 divingkatae and maximum
|
||||
Copyright (C) 2018-23 divingkatae and maximum
|
||||
(theweirdo) spatium
|
||||
|
||||
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
|
||||
|
@ -24,11 +24,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
Currently supported audio codecs:
|
||||
- PDM AWACs in Nubus Power Macintosh models
|
||||
- Screamer AWACs in Beige G3
|
||||
|
||||
Author: Max Poliakovski 2019-21
|
||||
*/
|
||||
|
||||
#include <devices/common/dbdma.h>
|
||||
#include <devices/deviceregistry.h>
|
||||
#include <devices/sound/awacs.h>
|
||||
#include <devices/sound/soundserver.h>
|
||||
#include <endianswap.h>
|
||||
|
@ -38,8 +36,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
#include <loguru.hpp>
|
||||
#include <memory>
|
||||
|
||||
AwacsBase::AwacsBase()
|
||||
AwacsBase::AwacsBase(std::string name)
|
||||
{
|
||||
supports_types(HWCompType::SND_CODEC);
|
||||
|
||||
this->name = name;
|
||||
|
||||
// connect to SoundServer
|
||||
this->snd_server = dynamic_cast<SoundServer *>
|
||||
(gMachineObj->get_comp_by_name("SoundServer"));
|
||||
|
@ -49,31 +51,43 @@ AwacsBase::AwacsBase()
|
|||
void AwacsBase::set_sample_rate(int sr_id)
|
||||
{
|
||||
if (sr_id > this->max_sr_id) {
|
||||
LOG_F(ERROR, "AWACs: invalid sample rate ID %d!", sr_id);
|
||||
LOG_F(ERROR, "%s: invalid sample rate ID %d!", this->name.c_str(), sr_id);
|
||||
} else {
|
||||
this->cur_sample_rate = this->sr_table[sr_id];
|
||||
}
|
||||
};
|
||||
|
||||
void AwacsBase::start_output_dma()
|
||||
void AwacsBase::dma_out_start()
|
||||
{
|
||||
int err;
|
||||
int err;
|
||||
bool reopen = false;
|
||||
|
||||
if ((err = this->snd_server->open_out_stream(this->cur_sample_rate,
|
||||
(void *)this->dma_out_ch))) {
|
||||
LOG_F(ERROR, "AWACs: unable to open sound output stream: %d", err);
|
||||
if (this->out_stream_ready && this->out_sample_rate != this->cur_sample_rate)
|
||||
reopen = true;
|
||||
|
||||
if (reopen) {
|
||||
snd_server->close_out_stream();
|
||||
this->out_stream_ready = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this->out_stream_ready = true;
|
||||
if (!this->out_stream_ready || reopen) {
|
||||
if ((err = this->snd_server->open_out_stream(this->cur_sample_rate,
|
||||
(void *)this->dma_out_ch))) {
|
||||
LOG_F(ERROR, "%s: unable to open sound output stream: %d",
|
||||
this->name.c_str(), err);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((err = snd_server->start_out_stream())) {
|
||||
LOG_F(ERROR, "AWACs: could not start sound output stream");
|
||||
if ((err = snd_server->start_out_stream())) {
|
||||
LOG_F(ERROR, "%s: could not start sound output stream", this->name.c_str());
|
||||
}
|
||||
|
||||
this->out_sample_rate = this->cur_sample_rate;
|
||||
this->out_stream_ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AwacsBase::stop_output_dma()
|
||||
void AwacsBase::dma_out_stop()
|
||||
{
|
||||
if (this->out_stream_ready) {
|
||||
snd_server->close_out_stream();
|
||||
|
@ -82,7 +96,7 @@ void AwacsBase::stop_output_dma()
|
|||
}
|
||||
|
||||
//=========================== PDM-style AWACs =================================
|
||||
AwacDevicePdm::AwacDevicePdm() : AwacsBase()
|
||||
AwacDevicePdm::AwacDevicePdm() : AwacsBase("AWAC-PDM")
|
||||
{
|
||||
static int pdm_awac_freqs[3] = {22050, 29400, 44100};
|
||||
|
||||
|
@ -93,31 +107,24 @@ AwacDevicePdm::AwacDevicePdm() : AwacsBase()
|
|||
|
||||
uint32_t AwacDevicePdm::read_stat()
|
||||
{
|
||||
LOG_F(INFO, "AWACs-PDM: status requested!");
|
||||
LOG_F(INFO, "%s: status requested!", this->name.c_str());
|
||||
// TODO: return valid status including manufacturer & device IDs
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AwacDevicePdm::write_ctrl(uint32_t addr, uint16_t value)
|
||||
{
|
||||
LOG_F(9, "AWACs-PDM: control updated, addr=0x%X, val=0x%X", addr, value);
|
||||
|
||||
if (addr <= 4) {
|
||||
this->ctrl_regs[addr] = value;
|
||||
} else {
|
||||
LOG_F(ERROR, "AWACs-PDM: invalid control register address %d!", addr);
|
||||
LOG_F(ERROR, "%s: invalid control register address %d!", this->name.c_str(),
|
||||
addr);
|
||||
}
|
||||
}
|
||||
|
||||
//============================= Screamer AWACs ================================
|
||||
AwacsScreamer::AwacsScreamer() : AwacsBase()
|
||||
AwacsScreamer::AwacsScreamer(std::string name) : AwacsBase(name)
|
||||
{
|
||||
this->audio_proc = std::unique_ptr<AudioProcessor> (new AudioProcessor());
|
||||
|
||||
/* register audio processor chip with the I2C bus */
|
||||
I2CBus* i2c_bus = dynamic_cast<I2CBus*>(gMachineObj->get_comp_by_type(HWCompType::I2C_HOST));
|
||||
i2c_bus->register_device(0x45, this->audio_proc.get());
|
||||
|
||||
static int screamer_freqs[8] = {
|
||||
44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350
|
||||
};
|
||||
|
@ -126,6 +133,16 @@ AwacsScreamer::AwacsScreamer() : AwacsBase()
|
|||
this->max_sr_id = 7;
|
||||
}
|
||||
|
||||
int AwacsScreamer::device_postinit() {
|
||||
this->audio_proc = std::unique_ptr<AudioProcessor> (new AudioProcessor());
|
||||
|
||||
/* register audio processor chip with the I2C bus */
|
||||
I2CBus* i2c_bus = dynamic_cast<I2CBus*>(gMachineObj->get_comp_by_type(HWCompType::I2C_HOST));
|
||||
i2c_bus->register_device(0x45, this->audio_proc.get());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t AwacsScreamer::snd_ctrl_read(uint32_t offset, int size)
|
||||
{
|
||||
switch (offset) {
|
||||
|
@ -136,7 +153,7 @@ uint32_t AwacsScreamer::snd_ctrl_read(uint32_t offset, int size)
|
|||
case AWAC_CODEC_STATUS_REG:
|
||||
return (AWAC_AVAILABLE << 8) | (AWAC_MAKER_CRYSTAL << 16) | (AWAC_REV_SCREAMER << 20);
|
||||
default:
|
||||
LOG_F(ERROR, "Screamer: unsupported register at offset 0x%X", offset);
|
||||
LOG_F(ERROR, "%s: unsupported register at offset 0x%X", this->name.c_str(), offset);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -150,58 +167,25 @@ void AwacsScreamer::snd_ctrl_write(uint32_t offset, uint32_t value, int size)
|
|||
switch (offset) {
|
||||
case AWAC_SOUND_CTRL_REG:
|
||||
this->snd_ctrl_reg = BYTESWAP_32(value);
|
||||
LOG_F(9, "Screamer: new sound control value = 0x%X", this->snd_ctrl_reg);
|
||||
this->set_sample_rate((this->snd_ctrl_reg >> 8) & 7);
|
||||
break;
|
||||
case AWAC_CODEC_CTRL_REG:
|
||||
subframe = (value >> 14) & 3;
|
||||
reg_num = (value >> 20) & 7;
|
||||
data = ((value >> 8) & 0xF00) | ((value >> 24) & 0xFF);
|
||||
LOG_F(9, "Screamer subframe = %d, reg = %d, data = %08X",
|
||||
LOG_F(9, "%s subframe = %d, reg = %d, data = %08X", this->name.c_str(),
|
||||
subframe, reg_num, data);
|
||||
if (!subframe)
|
||||
this->control_regs[reg_num] = data;
|
||||
break;
|
||||
default:
|
||||
LOG_F(ERROR, "Screamer: unsupported register at offset 0x%X", offset);
|
||||
LOG_F(ERROR, "%s: unsupported register at offset 0x%X", this->name.c_str(),
|
||||
offset);
|
||||
}
|
||||
}
|
||||
|
||||
void AwacsScreamer::open_stream(int sample_rate)
|
||||
{
|
||||
int err;
|
||||
static const DeviceDescription Screamer_Descriptor = {
|
||||
AwacsScreamer::create, {}, {}
|
||||
};
|
||||
|
||||
if ((err = this->snd_server->open_out_stream(sample_rate, (void *)this->dma_out_ch))) {
|
||||
LOG_F(ERROR, "Screamer: unable to open sound output stream: %d", err);
|
||||
this->out_stream_ready = false;
|
||||
} else {
|
||||
this->out_sample_rate = sample_rate;
|
||||
this->out_stream_ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AwacsScreamer::dma_start()
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!this->out_stream_ready) {
|
||||
this->open_stream(this->sr_table[(this->snd_ctrl_reg >> 8) & 7]);
|
||||
} else if (this->out_sample_rate != this->sr_table[(this->snd_ctrl_reg >> 8) & 7]) {
|
||||
snd_server->close_out_stream();
|
||||
this->open_stream(this->sr_table[(this->snd_ctrl_reg >> 8) & 7]);
|
||||
} else {
|
||||
LOG_F(ERROR, "Screamer: unpausing attempted!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->out_stream_ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((err = snd_server->start_out_stream())) {
|
||||
LOG_F(ERROR, "Screamer: could not start sound output stream");
|
||||
}
|
||||
}
|
||||
|
||||
void AwacsScreamer::dma_end()
|
||||
{
|
||||
}
|
||||
REGISTER_DEVICE(ScreamerSnd, Screamer_Descriptor);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||
Copyright (C) 2018-21 divingkatae and maximum
|
||||
Copyright (C) 2018-23 divingkatae and maximum
|
||||
(theweirdo) spatium
|
||||
|
||||
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
|
||||
|
@ -28,18 +28,19 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
#ifndef AWAC_H
|
||||
#define AWAC_H
|
||||
|
||||
#include <devices/common/dbdma.h>
|
||||
#include <devices/common/dmacore.h>
|
||||
#include <devices/common/hwcomponent.h>
|
||||
#include <devices/common/i2c/i2c.h>
|
||||
#include <devices/sound/soundserver.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
/** Base class for the AWACs codecs. */
|
||||
class AwacsBase {
|
||||
class AwacsBase : public HWComponent {
|
||||
public:
|
||||
AwacsBase();
|
||||
AwacsBase(std::string name);
|
||||
~AwacsBase() = default;
|
||||
|
||||
void set_dma_out(DmaOutChannel *dma_out_ch) {
|
||||
|
@ -47,8 +48,8 @@ public:
|
|||
};
|
||||
|
||||
void set_sample_rate(int sr_id);
|
||||
void start_output_dma();
|
||||
void stop_output_dma();
|
||||
void dma_out_start();
|
||||
void dma_out_stop();
|
||||
|
||||
protected:
|
||||
SoundServer *snd_server; // SoundServer instance pointer
|
||||
|
@ -57,8 +58,9 @@ protected:
|
|||
int *sr_table; // pointer to the table of supported sample rates
|
||||
int max_sr_id; // maximum value for sample rate ID
|
||||
|
||||
bool out_stream_ready;
|
||||
int cur_sample_rate;
|
||||
bool out_stream_ready = false;
|
||||
int cur_sample_rate = -1;
|
||||
int out_sample_rate = -1;
|
||||
};
|
||||
|
||||
/** AWACs PDM-style sound codec. */
|
||||
|
@ -70,24 +72,17 @@ public:
|
|||
uint32_t read_stat(void);
|
||||
void write_ctrl(uint32_t addr, uint16_t value);
|
||||
|
||||
void dma_out_start(uint8_t sample_rate_id);
|
||||
void dma_out_stop();
|
||||
|
||||
private:
|
||||
uint16_t ctrl_regs[5]; // 12-bit wide control registers
|
||||
};
|
||||
|
||||
/** AWACs Screamer registers offsets. */
|
||||
/** Offsets to MacIO sound codec registers. */
|
||||
enum {
|
||||
AWAC_SOUND_CTRL_REG = 0x00,
|
||||
AWAC_CODEC_CTRL_REG = 0x10,
|
||||
AWAC_CODEC_STATUS_REG = 0x20,
|
||||
};
|
||||
|
||||
/** AWACs Screamer manufacturer and revision. */
|
||||
#define AWAC_MAKER_CRYSTAL 1
|
||||
#define AWAC_REV_SCREAMER 3
|
||||
|
||||
/** Apple source calls this kValidData but doesn't explain
|
||||
what it actually means. It seems like it's used to check
|
||||
if the sound codec is available.
|
||||
|
@ -145,27 +140,36 @@ private:
|
|||
int auto_inc;
|
||||
};
|
||||
|
||||
/** AWACs Screamer sound codec. */
|
||||
class AwacsScreamer : public AwacsBase {
|
||||
/** Sound codec interface with the typical MacIO access. */
|
||||
class MacioSndCtrl : public virtual AwacsBase {
|
||||
public:
|
||||
AwacsScreamer();
|
||||
virtual uint32_t snd_ctrl_read(uint32_t offset, int size) = 0;
|
||||
virtual void snd_ctrl_write(uint32_t offset, uint32_t value, int size) = 0;
|
||||
};
|
||||
|
||||
/** AWACs Screamer manufacturer and revision. */
|
||||
#define AWAC_MAKER_CRYSTAL 1
|
||||
#define AWAC_REV_SCREAMER 3
|
||||
|
||||
/** Screamer sound codec. */
|
||||
class AwacsScreamer : public MacioSndCtrl {
|
||||
public:
|
||||
AwacsScreamer(std::string name = "Screamer");
|
||||
~AwacsScreamer() = default;
|
||||
|
||||
uint32_t snd_ctrl_read(uint32_t offset, int size);
|
||||
void snd_ctrl_write(uint32_t offset, uint32_t value, int size);
|
||||
|
||||
void dma_start();
|
||||
void dma_end();
|
||||
int device_postinit();
|
||||
|
||||
protected:
|
||||
void open_stream(int sample_rate);
|
||||
static std::unique_ptr<HWComponent> create() {
|
||||
return std::unique_ptr<AwacsScreamer>(new AwacsScreamer("Screamer"));
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t snd_ctrl_reg = { 0 };
|
||||
uint16_t control_regs[8] = { 0 }; /* control registers, each 12-bits wide */
|
||||
uint8_t is_busy = 0;
|
||||
|
||||
int out_sample_rate;
|
||||
uint16_t control_regs[8] = { 0 }; // control registers, each 12-bits wide
|
||||
uint8_t is_busy = 0;
|
||||
|
||||
std::unique_ptr<AudioProcessor> audio_proc;
|
||||
};
|
||||
|
|
104
devices/sound/burgundy.cpp
Normal file
104
devices/sound/burgundy.cpp
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||
Copyright (C) 2018-23 divingkatae and maximum
|
||||
(theweirdo) spatium
|
||||
|
||||
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** Burgundy sound codec emulation. */
|
||||
|
||||
#include <core/timermanager.h>
|
||||
#include <devices/common/hwcomponent.h>
|
||||
#include <devices/deviceregistry.h>
|
||||
#include <devices/sound/burgundy.h>
|
||||
#include <endianswap.h>
|
||||
#include <loguru.hpp>
|
||||
#include <memaccess.h>
|
||||
|
||||
BurgundyCodec::BurgundyCodec(std::string name) : AwacsBase(name)
|
||||
{
|
||||
supports_types(HWCompType::SND_CODEC);
|
||||
|
||||
static int burgundy_sample_rates[1] = { 44100 };
|
||||
|
||||
// Burgundy seems to supports only one sample rate
|
||||
this->sr_table = burgundy_sample_rates;
|
||||
this->max_sr_id = 1;
|
||||
|
||||
this->set_sample_rate(0); // set default sample rate
|
||||
}
|
||||
|
||||
uint32_t BurgundyCodec::snd_ctrl_read(uint32_t offset, int size) {
|
||||
uint32_t result = 0;
|
||||
|
||||
switch (offset) {
|
||||
case AWAC_CODEC_CTRL_REG:
|
||||
result = this->last_ctrl_data;
|
||||
break;
|
||||
case AWAC_CODEC_STATUS_REG:
|
||||
result = (this->first_valid << 23) | BURGUNDY_READY |
|
||||
(this->byte_counter << 14) | (this->read_pos << 12) |
|
||||
(this->data_byte << 4);
|
||||
break;
|
||||
default:
|
||||
LOG_F(ERROR, "%s: read from unsupported register 0x%X", this->name.c_str(),
|
||||
offset);
|
||||
}
|
||||
|
||||
return BYTESWAP_32(result);
|
||||
}
|
||||
|
||||
void BurgundyCodec::snd_ctrl_write(uint32_t offset, uint32_t value, int size) {
|
||||
uint8_t reg_addr, cur_byte, last_byte;
|
||||
|
||||
value = BYTESWAP_32(value);
|
||||
|
||||
switch (offset) {
|
||||
case AWAC_CODEC_CTRL_REG:
|
||||
this->last_ctrl_data = value;
|
||||
reg_addr = (value >> 12) & 0xFF;
|
||||
cur_byte = (value >> 8) & 3;
|
||||
last_byte = (value >> 10) & 3;
|
||||
if (value & BURGUNDY_REG_WR) {
|
||||
uint32_t mask = 0xFFU << (cur_byte * 8);
|
||||
this->reg_array[reg_addr] = (this->reg_array[reg_addr] & ~mask) |
|
||||
((value & 0xFFU) << (cur_byte * 8));
|
||||
} else {
|
||||
this->reg_addr = reg_addr;
|
||||
this->read_pos = cur_byte;
|
||||
this->data_byte = (this->reg_array[reg_addr] >> (cur_byte * 8)) & 0xFFU;
|
||||
this->first_valid = 1;
|
||||
|
||||
TimerManager::get_instance()->add_oneshot_timer(
|
||||
USECS_TO_NSECS(10), // not sure if this is the correct delay
|
||||
[this]() {
|
||||
this->first_valid = 0;
|
||||
this->byte_counter = (this->byte_counter + 1) & 3;
|
||||
});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_F(ERROR, "%s: write to unsupported register 0x%X", this->name.c_str(),
|
||||
offset);
|
||||
}
|
||||
}
|
||||
|
||||
static const DeviceDescription Burgundy_Descriptor = {
|
||||
BurgundyCodec::create, {}, {}
|
||||
};
|
||||
|
||||
REGISTER_DEVICE(BurgundySnd, Burgundy_Descriptor);
|
64
devices/sound/burgundy.h
Normal file
64
devices/sound/burgundy.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||
Copyright (C) 2018-23 divingkatae and maximum
|
||||
(theweirdo) spatium
|
||||
|
||||
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** Burgundy sound codec definitions. */
|
||||
|
||||
#ifndef BURGUNDY_H
|
||||
#define BURGUNDY_H
|
||||
|
||||
#include <devices/common/hwcomponent.h>
|
||||
#include <devices/sound/awacs.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <string>
|
||||
|
||||
enum {
|
||||
BURGUNDY_REG_WR = 1 << 21,
|
||||
BURGUNDY_READY = 1 << 22,
|
||||
};
|
||||
|
||||
/** Number of internal registers implemented in Burgundy. */
|
||||
#define BURGUNDY_NUM_REGS 123
|
||||
|
||||
class BurgundyCodec : public MacioSndCtrl {
|
||||
public:
|
||||
BurgundyCodec(std::string name);
|
||||
~BurgundyCodec() = default;
|
||||
|
||||
uint32_t snd_ctrl_read(uint32_t offset, int size);
|
||||
void snd_ctrl_write(uint32_t offset, uint32_t value, int size);
|
||||
|
||||
static std::unique_ptr<HWComponent> create() {
|
||||
return std::unique_ptr<BurgundyCodec>(new BurgundyCodec("Burgundy"));
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t last_ctrl_data = 0;
|
||||
uint8_t byte_counter = 0;
|
||||
uint8_t reg_addr = 0;
|
||||
uint8_t first_valid = 0;
|
||||
uint8_t read_pos = 0;
|
||||
uint8_t data_byte = 0;
|
||||
|
||||
uint32_t reg_array[BURGUNDY_NUM_REGS] = {};
|
||||
};
|
||||
|
||||
#endif // BURGUNDY_H
|
|
@ -331,7 +331,8 @@ bail_out:
|
|||
int MachineFactory::load_boot_rom(string& rom_filepath) {
|
||||
ifstream rom_file;
|
||||
size_t file_size;
|
||||
int result;
|
||||
int result = 0;
|
||||
uint32_t rom_load_addr;
|
||||
AddressMapEntry *rom_reg;
|
||||
|
||||
rom_file.open(rom_filepath, ios::in | ios::binary);
|
||||
|
@ -345,10 +346,16 @@ int MachineFactory::load_boot_rom(string& rom_filepath) {
|
|||
file_size = rom_file.tellg();
|
||||
rom_file.seekg(0, rom_file.beg);
|
||||
|
||||
if (file_size != 0x400000UL) {
|
||||
LOG_F(ERROR, "Unxpected ROM File size. Expected size is 4 megabytes.");
|
||||
result = -1;
|
||||
if (file_size == 0x400000UL) { // Old World ROMs
|
||||
rom_load_addr = 0xFFC00000UL;
|
||||
} else if (file_size == 0x100000UL) { // New World ROMs
|
||||
rom_load_addr = 0xFFF00000UL;
|
||||
} else {
|
||||
LOG_F(ERROR, "Unxpected ROM File size: %zu bytes.", file_size);
|
||||
result = -1;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
unsigned char* sysrom_mem = new unsigned char[file_size];
|
||||
|
||||
rom_file.seekg(0, ios::beg);
|
||||
|
@ -358,14 +365,13 @@ int MachineFactory::load_boot_rom(string& rom_filepath) {
|
|||
gMachineObj->get_comp_by_type(HWCompType::MEM_CTRL));
|
||||
|
||||
if ((rom_reg = mem_ctrl->find_rom_region())) {
|
||||
mem_ctrl->set_data(rom_reg->start, sysrom_mem, (uint32_t)file_size);
|
||||
mem_ctrl->set_data(rom_load_addr, sysrom_mem, (uint32_t)file_size);
|
||||
} else {
|
||||
ABORT_F("Could not locate physical ROM region!");
|
||||
LOG_F(ERROR, "Could not locate physical ROM region!");
|
||||
result = -1;
|
||||
}
|
||||
|
||||
delete[] sysrom_mem;
|
||||
|
||||
result = 0;
|
||||
}
|
||||
|
||||
rom_file.close();
|
||||
|
|
|
@ -166,7 +166,7 @@ static const PropMap gossamer_settings = {
|
|||
};
|
||||
|
||||
static vector<string> pmg3_devices = {
|
||||
"Grackle", "Heathrow", "AtiRageGT"
|
||||
"Grackle", "ScreamerSnd", "Heathrow", "AtiRageGT"
|
||||
};
|
||||
|
||||
static const MachineDescription pmg3dt_descriptor = {
|
||||
|
|
111
machines/machineyosemite.cpp
Normal file
111
machines/machineyosemite.cpp
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||
Copyright (C) 2018-23 divingkatae and maximum
|
||||
(theweirdo) spatium
|
||||
|
||||
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file Construct the Yosemite machine (Power Macintosh G3 Blue & White). */
|
||||
|
||||
#include <cpu/ppc/ppcemu.h>
|
||||
#include <devices/common/pci/dec21154.h>
|
||||
#include <devices/memctrl/mpc106.h>
|
||||
#include <devices/memctrl/spdram.h>
|
||||
#include <machines/machinefactory.h>
|
||||
#include <machines/machineproperties.h>
|
||||
|
||||
static void setup_ram_slot(std::string name, int i2c_addr, int capacity_megs) {
|
||||
if (!capacity_megs)
|
||||
return;
|
||||
|
||||
gMachineObj->add_device(name, std::unique_ptr<SpdSdram168>(new SpdSdram168(i2c_addr)));
|
||||
SpdSdram168* ram_dimm = dynamic_cast<SpdSdram168*>(gMachineObj->get_comp_by_name(name));
|
||||
ram_dimm->set_capacity(capacity_megs);
|
||||
|
||||
// register RAM DIMM with the I2C bus
|
||||
I2CBus* i2c_bus = dynamic_cast<I2CBus*>(gMachineObj->get_comp_by_type(HWCompType::I2C_HOST));
|
||||
i2c_bus->register_device(i2c_addr, ram_dimm);
|
||||
}
|
||||
|
||||
int initialize_yosemite(std::string& id)
|
||||
{
|
||||
LOG_F(INFO, "Building machine Yosemite...");
|
||||
|
||||
// get pointer to the memory controller/primary PCI bridge object
|
||||
MPC106* grackle_obj = dynamic_cast<MPC106*>(gMachineObj->get_comp_by_name("Grackle"));
|
||||
|
||||
// get pointer to the bridge of the secondary PCI bus
|
||||
DecPciBridge *sec_bridge = dynamic_cast<DecPciBridge*>(gMachineObj->get_comp_by_name("Dec21154"));
|
||||
|
||||
// connect PCI devices
|
||||
grackle_obj->pci_register_device(DEV_FUN(13,0),
|
||||
dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("Dec21154")));
|
||||
|
||||
sec_bridge->pci_register_device(DEV_FUN(5,0),
|
||||
dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("Heathrow")));
|
||||
|
||||
// allocate ROM region
|
||||
if (!grackle_obj->add_rom_region(0xFFF00000, 0x100000)) {
|
||||
LOG_F(ERROR, "Could not allocate ROM region!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// configure RAM slots
|
||||
setup_ram_slot("RAM_DIMM_1", 0x50, GET_INT_PROP("rambank1_size"));
|
||||
setup_ram_slot("RAM_DIMM_2", 0x51, GET_INT_PROP("rambank2_size"));
|
||||
setup_ram_slot("RAM_DIMM_3", 0x52, GET_INT_PROP("rambank3_size"));
|
||||
setup_ram_slot("RAM_DIMM_4", 0x53, GET_INT_PROP("rambank4_size"));
|
||||
|
||||
// configure CPU clocks
|
||||
uint64_t bus_freq = 66820000ULL;
|
||||
uint64_t timebase_freq = bus_freq / 4;
|
||||
|
||||
// initialize virtual CPU and request MPC750 CPU aka G3
|
||||
ppc_cpu_init(grackle_obj, PPC_VER::MPC750, timebase_freq);
|
||||
|
||||
// set CPU PLL ratio to 3.5
|
||||
ppc_state.spr[SPR::HID1] = 0xE << 28;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const PropMap yosemite_settings = {
|
||||
{"rambank1_size",
|
||||
new IntProperty(256, vector<uint32_t>({8, 16, 32, 64, 128, 256}))},
|
||||
{"rambank2_size",
|
||||
new IntProperty( 0, vector<uint32_t>({0, 8, 16, 32, 64, 128, 256}))},
|
||||
{"rambank3_size",
|
||||
new IntProperty( 0, vector<uint32_t>({0, 8, 16, 32, 64, 128, 256}))},
|
||||
{"rambank4_size",
|
||||
new IntProperty( 0, vector<uint32_t>({0, 8, 16, 32, 64, 128, 256}))},
|
||||
{"emmo",
|
||||
new BinProperty(0)},
|
||||
};
|
||||
|
||||
static vector<string> pmg3nw_devices = {
|
||||
"Grackle", "Dec21154", "BurgundySnd", "Heathrow"
|
||||
};
|
||||
|
||||
static const MachineDescription pmg3nw_descriptor = {
|
||||
.name = "pmg3dt",
|
||||
.description = "Power Macintosh G3 Blue and White",
|
||||
.devices = pmg3nw_devices,
|
||||
.settings = yosemite_settings,
|
||||
.init_func = &initialize_yosemite
|
||||
};
|
||||
|
||||
REGISTER_MACHINE(pmg3nw, pmg3nw_descriptor);
|
Loading…
Reference in New Issue
Block a user