Compare commits
16 Commits
33e72a144a
...
1c8702d67a
Author | SHA1 | Date |
---|---|---|
Maxim Poliakovski | 1c8702d67a | |
Mihai Parparita | cf4913deb0 | |
Maxim Poliakovski | bdd441b1b6 | |
Maxim Poliakovski | 4c9fe06229 | |
Maxim Poliakovski | e7da98b6bd | |
Maxim Poliakovski | dcdfaabedf | |
Maxim Poliakovski | 524daa45a5 | |
Maxim Poliakovski | 073b8fd981 | |
Maxim Poliakovski | d7749e0a2c | |
Maxim Poliakovski | 7972a0f2a8 | |
Maxim Poliakovski | 19dcb43658 | |
joevt | 9ed1a118e6 | |
dingusdev | a5a5410515 | |
dingusdev | 40a4ca31b9 | |
dingusdev | 7f44ab2262 | |
dingusdev | 123c927b1a |
|
@ -277,9 +277,9 @@ void ppc_main_opcode()
|
|||
OpcodeGrabber[(ppc_cur_instruction >> 26) & 0x3F]();
|
||||
}
|
||||
|
||||
static long long now_ns()
|
||||
{
|
||||
return duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
|
||||
static long long now_ns() {
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::high_resolution_clock::now().time_since_epoch()).count();
|
||||
}
|
||||
|
||||
uint64_t get_virt_time_ns()
|
||||
|
|
|
@ -40,13 +40,13 @@ uint64_t fp_return_uint64(uint32_t reg) {
|
|||
return ppc_state.fpr[reg].int64_r;
|
||||
}
|
||||
|
||||
inline void ppc_update_cr1() {
|
||||
inline static void ppc_update_cr1() {
|
||||
// copy FPSCR[FX|FEX|VX|OX] to CR1
|
||||
ppc_state.cr = (ppc_state.cr & ~CR_select::CR1_field) |
|
||||
((ppc_state.fpscr >> 4) & CR_select::CR1_field);
|
||||
}
|
||||
|
||||
int32_t round_to_nearest(double f) {
|
||||
static int32_t round_to_nearest(double f) {
|
||||
return static_cast<int32_t>(static_cast<int64_t> (std::floor(f + 0.5)));
|
||||
}
|
||||
|
||||
|
@ -74,68 +74,40 @@ void update_fpscr(uint32_t new_fpscr) {
|
|||
ppc_state.fpscr = new_fpscr;
|
||||
}
|
||||
|
||||
int32_t round_to_zero(double f) {
|
||||
static int32_t round_to_zero(double f) {
|
||||
return static_cast<int32_t>(std::trunc(f));
|
||||
}
|
||||
|
||||
int32_t round_to_pos_inf(double f) {
|
||||
static int32_t round_to_pos_inf(double f) {
|
||||
return static_cast<int32_t>(std::ceil(f));
|
||||
}
|
||||
|
||||
int32_t round_to_neg_inf(double f) {
|
||||
static int32_t round_to_neg_inf(double f) {
|
||||
return static_cast<int32_t>(std::floor(f));
|
||||
}
|
||||
|
||||
void update_fex() {
|
||||
int fex_result = !!((ppc_state.fpscr & (ppc_state.fpscr << 22)) & 0x3E000000);
|
||||
ppc_state.fpscr = (ppc_state.fpscr & ~0x40000000) | (fex_result << 30);
|
||||
inline static bool check_snan(int check_reg) {
|
||||
uint64_t check_int = ppc_state.fpr[check_reg].int64_r;
|
||||
return (((check_int & (0x7FFULL << 52)) == (0x7FFULL << 52)) &&
|
||||
((check_int & ~(0xFFFULL << 52)) != 0ULL) &&
|
||||
((check_int & (0x1ULL << 51)) == 0ULL));
|
||||
}
|
||||
|
||||
template <const FPOP fpop>
|
||||
void ppc_confirm_inf_nan(int chosen_reg_1, int chosen_reg_2, field_rc rec = RC0) {
|
||||
double input_a = ppc_state.fpr[chosen_reg_1].dbl64_r;
|
||||
double input_b = ppc_state.fpr[chosen_reg_2].dbl64_r;
|
||||
inline static void snan_single_check(int reg_a) {
|
||||
if (check_snan(reg_a))
|
||||
ppc_state.fpscr |= FX | VX | VXSNAN;
|
||||
}
|
||||
|
||||
ppc_state.fpscr &= 0x7fbfffff;
|
||||
inline static void snan_double_check(int reg_a, int reg_b) {
|
||||
if (check_snan(reg_a) || check_snan(reg_b))
|
||||
ppc_state.fpscr |= FX | VX | VXSNAN;
|
||||
}
|
||||
|
||||
switch (fpop) {
|
||||
case FPOP::DIV:
|
||||
if (std::isinf(input_a) && std::isinf(input_b)) {
|
||||
ppc_state.fpscr |= (FPSCR::FX | FPSCR::VXIDI);
|
||||
} else if ((input_a == FP_ZERO) && (input_b == FP_ZERO)) {
|
||||
ppc_state.fpscr |= (FPSCR::FX | FPSCR::VXZDZ);
|
||||
}
|
||||
update_fex();
|
||||
break;
|
||||
case FPOP::SUB:
|
||||
if (std::isinf(input_a) && std::isinf(input_b)) {
|
||||
ppc_state.fpscr |= (FPSCR::FX | FPSCR::VXISI);
|
||||
}
|
||||
if (std::isnan(input_a) && std::isnan(input_b)) {
|
||||
ppc_state.fpscr |= (FPSCR::FX | FPSCR::VXISI);
|
||||
}
|
||||
update_fex();
|
||||
break;
|
||||
case FPOP::ADD:
|
||||
if (std::isnan(input_a) && std::isnan(input_b)) {
|
||||
ppc_state.fpscr |= (FPSCR::FX | FPSCR::VXISI);
|
||||
}
|
||||
update_fex();
|
||||
break;
|
||||
case FPOP::SQRT:
|
||||
if (std::isnan(input_b) || (input_b == -1.0)) {
|
||||
ppc_state.fpscr |= (FPSCR::FX | FPSCR::VXSQRT);
|
||||
}
|
||||
update_fex();
|
||||
break;
|
||||
case FPOP::MUL:
|
||||
if (std::isnan(input_a) && std::isnan(input_b)) {
|
||||
ppc_state.fpscr |= (FPSCR::FX);
|
||||
}
|
||||
|
||||
update_fex();
|
||||
break;
|
||||
}
|
||||
inline static bool check_qnan(int check_reg) {
|
||||
uint64_t check_int = ppc_state.fpr[check_reg].int64_r;
|
||||
return (((check_int & (0x7FFULL << 52)) == (0x7FFULL << 52)) &&
|
||||
((check_int & ~(0xFFFULL << 52)) == 0ULL) &&
|
||||
((check_int & (0x1ULL << 51)) == (0x1ULL << 51)));
|
||||
}
|
||||
|
||||
static void fpresult_update(double set_result) {
|
||||
|
@ -160,10 +132,7 @@ template <field_rc rec>
|
|||
void dppc_interpreter::ppc_fadd() {
|
||||
ppc_grab_regsfpdab(ppc_cur_instruction);
|
||||
|
||||
if (std::isnan(val_reg_a) || std::isnan(val_reg_b)) {
|
||||
ppc_state.fpscr |= FPCC_FUNAN;
|
||||
ppc_confirm_inf_nan<ADD>(reg_a, reg_b, rec);
|
||||
}
|
||||
snan_double_check(reg_a, reg_b);
|
||||
|
||||
double ppc_dblresult64_d = val_reg_a + val_reg_b;
|
||||
ppc_store_dfpresult_flt(reg_d, ppc_dblresult64_d);
|
||||
|
@ -180,10 +149,7 @@ template <field_rc rec>
|
|||
void dppc_interpreter::ppc_fsub() {
|
||||
ppc_grab_regsfpdab(ppc_cur_instruction);
|
||||
|
||||
if (std::isnan(val_reg_a) || std::isnan(val_reg_b)) {
|
||||
ppc_state.fpscr |= FPCC_FUNAN;
|
||||
ppc_confirm_inf_nan<SUB>(reg_a, reg_b, rec);
|
||||
}
|
||||
snan_double_check(reg_a, reg_b);
|
||||
|
||||
double ppc_dblresult64_d = val_reg_a - val_reg_b;
|
||||
ppc_store_dfpresult_flt(reg_d, ppc_dblresult64_d);
|
||||
|
@ -200,9 +166,7 @@ template <field_rc rec>
|
|||
void dppc_interpreter::ppc_fdiv() {
|
||||
ppc_grab_regsfpdab(ppc_cur_instruction);
|
||||
|
||||
if (std::isnan(val_reg_a) || std::isnan(val_reg_b)) {
|
||||
ppc_confirm_inf_nan<DIV>(reg_a, reg_b, rec);
|
||||
}
|
||||
snan_double_check(reg_a, reg_b);
|
||||
|
||||
double ppc_dblresult64_d = val_reg_a / val_reg_b;
|
||||
ppc_store_dfpresult_flt(reg_d, ppc_dblresult64_d);
|
||||
|
@ -219,9 +183,7 @@ template <field_rc rec>
|
|||
void dppc_interpreter::ppc_fmul() {
|
||||
ppc_grab_regsfpdac(ppc_cur_instruction);
|
||||
|
||||
if (std::isnan(val_reg_a) || std::isnan(val_reg_c)) {
|
||||
ppc_confirm_inf_nan<MUL>(reg_a, reg_c, rec);
|
||||
}
|
||||
snan_double_check(reg_a, reg_c);
|
||||
|
||||
double ppc_dblresult64_d = val_reg_a * val_reg_c;
|
||||
ppc_store_dfpresult_flt(reg_d, ppc_dblresult64_d);
|
||||
|
@ -238,12 +200,8 @@ template <field_rc rec>
|
|||
void dppc_interpreter::ppc_fmadd() {
|
||||
ppc_grab_regsfpdabc(ppc_cur_instruction);
|
||||
|
||||
if (std::isnan(val_reg_a) || std::isnan(val_reg_c)) {
|
||||
ppc_confirm_inf_nan<MUL>(reg_a, reg_c, rec);
|
||||
}
|
||||
if (std::isnan(val_reg_b)) {
|
||||
ppc_confirm_inf_nan<ADD>(reg_a, reg_b, rec);
|
||||
}
|
||||
snan_double_check(reg_a, reg_c);
|
||||
snan_single_check(reg_b);
|
||||
|
||||
double ppc_dblresult64_d = std::fma(val_reg_a, val_reg_c, val_reg_b);
|
||||
ppc_store_dfpresult_flt(reg_d, ppc_dblresult64_d);
|
||||
|
@ -260,12 +218,8 @@ template <field_rc rec>
|
|||
void dppc_interpreter::ppc_fmsub() {
|
||||
ppc_grab_regsfpdabc(ppc_cur_instruction);
|
||||
|
||||
if (std::isnan(val_reg_a) || std::isnan(val_reg_c)) {
|
||||
ppc_confirm_inf_nan<MUL>(reg_a, reg_c, rec);
|
||||
}
|
||||
if (std::isnan(val_reg_b)) {
|
||||
ppc_confirm_inf_nan<SUB>(reg_a, reg_b, rec);
|
||||
}
|
||||
snan_double_check(reg_a, reg_c);
|
||||
snan_single_check(reg_b);
|
||||
|
||||
double ppc_dblresult64_d = std::fma(val_reg_a, val_reg_c, -val_reg_b);
|
||||
ppc_store_dfpresult_flt(reg_d, ppc_dblresult64_d);
|
||||
|
@ -282,12 +236,8 @@ template <field_rc rec>
|
|||
void dppc_interpreter::ppc_fnmadd() {
|
||||
ppc_grab_regsfpdabc(ppc_cur_instruction);
|
||||
|
||||
if (std::isnan(val_reg_a) || std::isnan(val_reg_c)) {
|
||||
ppc_confirm_inf_nan<MUL>(reg_a, reg_c, rec);
|
||||
}
|
||||
if (std::isnan(val_reg_b)) {
|
||||
ppc_confirm_inf_nan<ADD>(reg_a, reg_b, rec);
|
||||
}
|
||||
snan_double_check(reg_a, reg_c);
|
||||
snan_single_check(reg_b);
|
||||
|
||||
double ppc_dblresult64_d = -std::fma(val_reg_a, val_reg_c, val_reg_b);
|
||||
ppc_store_dfpresult_flt(reg_d, ppc_dblresult64_d);
|
||||
|
@ -304,14 +254,10 @@ template <field_rc rec>
|
|||
void dppc_interpreter::ppc_fnmsub() {
|
||||
ppc_grab_regsfpdabc(ppc_cur_instruction);
|
||||
|
||||
if (std::isnan(val_reg_a) || std::isnan(val_reg_c)) {
|
||||
ppc_confirm_inf_nan<MUL>(reg_a, reg_c, rec);
|
||||
}
|
||||
if (std::isnan(val_reg_b)) {
|
||||
ppc_confirm_inf_nan<SUB>(reg_a, reg_b, rec);
|
||||
}
|
||||
snan_double_check(reg_a, reg_c);
|
||||
snan_single_check(reg_b);
|
||||
|
||||
double ppc_dblresult64_d = std::fma(-val_reg_a, val_reg_c, val_reg_b);
|
||||
double ppc_dblresult64_d = -std::fma(val_reg_a, val_reg_c, -val_reg_b);
|
||||
ppc_store_dfpresult_flt(reg_d, ppc_dblresult64_d);
|
||||
fpresult_update(ppc_dblresult64_d);
|
||||
|
||||
|
@ -326,12 +272,9 @@ template <field_rc rec>
|
|||
void dppc_interpreter::ppc_fadds() {
|
||||
ppc_grab_regsfpdab(ppc_cur_instruction);
|
||||
|
||||
if (std::isnan(val_reg_a) || std::isnan(val_reg_b)) {
|
||||
ppc_confirm_inf_nan<ADD>(reg_a, reg_b, rec);
|
||||
}
|
||||
snan_double_check(reg_a, reg_b);
|
||||
|
||||
float ppc_fltresult32_d = val_reg_a + val_reg_b;
|
||||
double ppc_dblresult64_d = (double)ppc_fltresult32_d;
|
||||
double ppc_dblresult64_d = (float)(val_reg_a + val_reg_b);
|
||||
ppc_store_sfpresult_flt(reg_d, ppc_dblresult64_d);
|
||||
|
||||
fpresult_update(ppc_dblresult64_d);
|
||||
|
@ -347,9 +290,7 @@ template <field_rc rec>
|
|||
void dppc_interpreter::ppc_fsubs() {
|
||||
ppc_grab_regsfpdab(ppc_cur_instruction);
|
||||
|
||||
if (std::isnan(val_reg_a) || std::isnan(val_reg_b)) {
|
||||
ppc_confirm_inf_nan<SUB>(reg_a, reg_b, rec);
|
||||
}
|
||||
snan_double_check(reg_a, reg_b);
|
||||
|
||||
double ppc_dblresult64_d = (float)(val_reg_a - val_reg_b);
|
||||
ppc_store_sfpresult_flt(reg_d, ppc_dblresult64_d);
|
||||
|
@ -366,9 +307,7 @@ template <field_rc rec>
|
|||
void dppc_interpreter::ppc_fdivs() {
|
||||
ppc_grab_regsfpdab(ppc_cur_instruction);
|
||||
|
||||
if (std::isnan(val_reg_a) || std::isnan(val_reg_b)) {
|
||||
ppc_confirm_inf_nan<DIV>(reg_a, reg_b, rec);
|
||||
}
|
||||
snan_double_check(reg_a, reg_b);
|
||||
|
||||
double ppc_dblresult64_d = (float)(val_reg_a / val_reg_b);
|
||||
ppc_store_sfpresult_flt(reg_d, ppc_dblresult64_d);
|
||||
|
@ -385,9 +324,7 @@ template <field_rc rec>
|
|||
void dppc_interpreter::ppc_fmuls() {
|
||||
ppc_grab_regsfpdac(ppc_cur_instruction);
|
||||
|
||||
if (std::isnan(val_reg_a) || std::isnan(val_reg_c)) {
|
||||
ppc_confirm_inf_nan<MUL>(reg_a, reg_c, rec);
|
||||
}
|
||||
snan_double_check(reg_a, reg_c);
|
||||
|
||||
double ppc_dblresult64_d = (float)(val_reg_a * val_reg_c);
|
||||
ppc_store_sfpresult_flt(reg_d, ppc_dblresult64_d);
|
||||
|
@ -404,12 +341,8 @@ template <field_rc rec>
|
|||
void dppc_interpreter::ppc_fmadds() {
|
||||
ppc_grab_regsfpdabc(ppc_cur_instruction);
|
||||
|
||||
if (std::isnan(val_reg_a) || std::isnan(val_reg_c)) {
|
||||
ppc_confirm_inf_nan<MUL>(reg_a, reg_c, rec);
|
||||
}
|
||||
if (std::isnan(val_reg_b)) {
|
||||
ppc_confirm_inf_nan<ADD>(reg_a, reg_b, rec);
|
||||
}
|
||||
snan_double_check(reg_a, reg_c);
|
||||
snan_single_check(reg_b);
|
||||
|
||||
double ppc_dblresult64_d = (float)std::fma(val_reg_a, val_reg_c, val_reg_b);
|
||||
ppc_store_sfpresult_flt(reg_d, ppc_dblresult64_d);
|
||||
|
@ -426,12 +359,8 @@ template <field_rc rec>
|
|||
void dppc_interpreter::ppc_fmsubs() {
|
||||
ppc_grab_regsfpdabc(ppc_cur_instruction);
|
||||
|
||||
if (std::isnan(val_reg_a) || std::isnan(val_reg_c)) {
|
||||
ppc_confirm_inf_nan<MUL>(reg_a, reg_c, rec);
|
||||
}
|
||||
if (std::isnan(val_reg_b)) {
|
||||
ppc_confirm_inf_nan<SUB>(reg_a, reg_b, rec);
|
||||
}
|
||||
snan_double_check(reg_a, reg_c);
|
||||
snan_single_check(reg_b);
|
||||
|
||||
double ppc_dblresult64_d = (float)std::fma(val_reg_a, val_reg_c, -val_reg_b);
|
||||
ppc_store_sfpresult_flt(reg_d, ppc_dblresult64_d);
|
||||
|
@ -448,12 +377,8 @@ template <field_rc rec>
|
|||
void dppc_interpreter::ppc_fnmadds() {
|
||||
ppc_grab_regsfpdabc(ppc_cur_instruction);
|
||||
|
||||
if (std::isnan(val_reg_a) || std::isnan(val_reg_c)) {
|
||||
ppc_confirm_inf_nan<MUL>(reg_a, reg_c, rec);
|
||||
}
|
||||
if (std::isnan(val_reg_b)) {
|
||||
ppc_confirm_inf_nan<ADD>(reg_a, reg_b, rec);
|
||||
}
|
||||
snan_double_check(reg_a, reg_c);
|
||||
snan_single_check(reg_b);
|
||||
|
||||
double ppc_dblresult64_d = -(float)std::fma(val_reg_a, val_reg_c, val_reg_b);
|
||||
ppc_store_sfpresult_flt(reg_d, ppc_dblresult64_d);
|
||||
|
@ -470,14 +395,10 @@ template <field_rc rec>
|
|||
void dppc_interpreter::ppc_fnmsubs() {
|
||||
ppc_grab_regsfpdabc(ppc_cur_instruction);
|
||||
|
||||
if (std::isnan(val_reg_a) || std::isnan(val_reg_c)) {
|
||||
ppc_confirm_inf_nan<MUL>(reg_a, reg_c, rec);
|
||||
}
|
||||
if (std::isnan(val_reg_b)) {
|
||||
ppc_confirm_inf_nan<SUB>(reg_a, reg_b, rec);
|
||||
}
|
||||
snan_double_check(reg_a, reg_c);
|
||||
snan_single_check(reg_b);
|
||||
|
||||
double ppc_dblresult64_d = (float)std::fma(-val_reg_a, val_reg_c, val_reg_b);
|
||||
double ppc_dblresult64_d = -(float)std::fma(val_reg_a, val_reg_c, -val_reg_b);
|
||||
ppc_store_sfpresult_flt(reg_d, ppc_dblresult64_d);
|
||||
fpresult_update(ppc_dblresult64_d);
|
||||
|
||||
|
@ -492,6 +413,8 @@ template <field_rc rec>
|
|||
void dppc_interpreter::ppc_fabs() {
|
||||
ppc_grab_regsfpdb(ppc_cur_instruction);
|
||||
|
||||
snan_single_check(reg_b);
|
||||
|
||||
double ppc_dblresult64_d = abs(GET_FPR(reg_b));
|
||||
|
||||
ppc_store_dfpresult_flt(reg_d, ppc_dblresult64_d);
|
||||
|
@ -507,6 +430,8 @@ template <field_rc rec>
|
|||
void dppc_interpreter::ppc_fnabs() {
|
||||
ppc_grab_regsfpdb(ppc_cur_instruction);
|
||||
|
||||
snan_single_check(reg_b);
|
||||
|
||||
double ppc_dblresult64_d = abs(GET_FPR(reg_b));
|
||||
ppc_dblresult64_d = -ppc_dblresult64_d;
|
||||
|
||||
|
@ -523,6 +448,8 @@ template <field_rc rec>
|
|||
void dppc_interpreter::ppc_fneg() {
|
||||
ppc_grab_regsfpdb(ppc_cur_instruction);
|
||||
|
||||
snan_single_check(reg_b);
|
||||
|
||||
double ppc_dblresult64_d = -(GET_FPR(reg_b));
|
||||
|
||||
ppc_store_dfpresult_flt(reg_d, ppc_dblresult64_d);
|
||||
|
@ -552,10 +479,12 @@ template void dppc_interpreter::ppc_fsel<RC1>();
|
|||
template <field_rc rec>
|
||||
void dppc_interpreter::ppc_fsqrt() {
|
||||
ppc_grab_regsfpdb(ppc_cur_instruction);
|
||||
|
||||
snan_single_check(reg_b);
|
||||
|
||||
double testd2 = (double)(GET_FPR(reg_b));
|
||||
double ppc_dblresult64_d = std::sqrt(testd2);
|
||||
ppc_store_dfpresult_flt(reg_d, ppc_dblresult64_d);
|
||||
ppc_confirm_inf_nan<SQRT>(0, reg_b, rec);
|
||||
|
||||
if (rec)
|
||||
ppc_update_cr1();
|
||||
|
@ -567,10 +496,12 @@ template void dppc_interpreter::ppc_fsqrt<RC1>();
|
|||
template <field_rc rec>
|
||||
void dppc_interpreter::ppc_fsqrts() {
|
||||
ppc_grab_regsfpdb(ppc_cur_instruction);
|
||||
double testd2 = (double)(GET_FPR(reg_b));
|
||||
|
||||
snan_single_check(reg_b);
|
||||
|
||||
double testd2 = (double)(GET_FPR(reg_b));
|
||||
double ppc_dblresult64_d = (float)std::sqrt(testd2);
|
||||
ppc_store_sfpresult_flt(reg_d, ppc_dblresult64_d);
|
||||
ppc_confirm_inf_nan<SQRT>(0, reg_b, rec);
|
||||
|
||||
if (rec)
|
||||
ppc_update_cr1();
|
||||
|
@ -582,11 +513,10 @@ template void dppc_interpreter::ppc_fsqrts<RC1>();
|
|||
template <field_rc rec>
|
||||
void dppc_interpreter::ppc_frsqrte() {
|
||||
ppc_grab_regsfpdb(ppc_cur_instruction);
|
||||
double testd2 = (double)(GET_FPR(reg_b));
|
||||
snan_single_check(reg_b);
|
||||
|
||||
double testd2 = (double)(GET_FPR(reg_b));
|
||||
double ppc_dblresult64_d = 1.0 / sqrt(testd2);
|
||||
ppc_confirm_inf_nan<SQRT>(0, reg_b, rec);
|
||||
|
||||
ppc_store_dfpresult_flt(reg_d, ppc_dblresult64_d);
|
||||
|
||||
if (rec)
|
||||
|
@ -599,6 +529,8 @@ template void dppc_interpreter::ppc_frsqrte<RC1>();
|
|||
template <field_rc rec>
|
||||
void dppc_interpreter::ppc_frsp() {
|
||||
ppc_grab_regsfpdb(ppc_cur_instruction);
|
||||
snan_single_check(reg_b);
|
||||
|
||||
double ppc_dblresult64_d = (float)(GET_FPR(reg_b));
|
||||
ppc_store_dfpresult_flt(reg_d, ppc_dblresult64_d);
|
||||
|
||||
|
@ -612,6 +544,9 @@ template void dppc_interpreter::ppc_frsp<RC1>();
|
|||
template <field_rc rec>
|
||||
void dppc_interpreter::ppc_fres() {
|
||||
ppc_grab_regsfpdb(ppc_cur_instruction);
|
||||
|
||||
snan_single_check(reg_b);
|
||||
|
||||
double start_num = GET_FPR(reg_b);
|
||||
double ppc_dblresult64_d = (float)(1.0 / start_num);
|
||||
ppc_store_dfpresult_flt(reg_d, ppc_dblresult64_d);
|
||||
|
@ -642,7 +577,7 @@ static void round_to_int(const uint8_t mode, field_rc rec) {
|
|||
ppc_state.fpscr &= ~(FPSCR::FR | FPSCR::FI);
|
||||
ppc_state.fpscr |= (FPSCR::VXCVI | FPSCR::VX);
|
||||
|
||||
if (!(ppc_state.fpr[reg_b].int64_r & 0x0008000000000000)) // issnan
|
||||
if (check_snan(reg_b)) // issnan
|
||||
ppc_state.fpscr |= FPSCR::VXSNAN;
|
||||
|
||||
if (ppc_state.fpscr & FPSCR::VE) {
|
||||
|
@ -956,7 +891,7 @@ template void dppc_interpreter::ppc_mtfsfi<RC1>();
|
|||
template <field_rc rec>
|
||||
void dppc_interpreter::ppc_mtfsb0() {
|
||||
int crf_d = (ppc_cur_instruction >> 21) & 0x1F;
|
||||
if (!crf_d || (crf_d > 2)) { // FEX and VX can't be explicitely cleared
|
||||
if (!crf_d || (crf_d > 2)) { // FEX and VX can't be explicitly cleared
|
||||
ppc_state.fpscr &= ~(0x80000000UL >> crf_d);
|
||||
}
|
||||
|
||||
|
@ -970,7 +905,7 @@ template void dppc_interpreter::ppc_mtfsb0<RC1>();
|
|||
template <field_rc rec>
|
||||
void dppc_interpreter::ppc_mtfsb1() {
|
||||
int crf_d = (ppc_cur_instruction >> 21) & 0x1F;
|
||||
if (!crf_d || (crf_d > 2)) { // FEX and VX can't be explicitely set
|
||||
if (!crf_d || (crf_d > 2)) { // FEX and VX can't be explicitly set
|
||||
ppc_state.fpscr |= (0x80000000UL >> crf_d);
|
||||
}
|
||||
|
||||
|
@ -1006,9 +941,14 @@ void dppc_interpreter::ppc_fcmpo() {
|
|||
uint32_t cmp_c = 0;
|
||||
|
||||
if (std::isnan(db_test_a) || std::isnan(db_test_b)) {
|
||||
// TODO: test for SNAN operands
|
||||
// for now, assume that at least one of the operands is QNAN
|
||||
cmp_c |= CRx_bit::CR_SO;
|
||||
ppc_state.fpscr |= FX | VX;
|
||||
if (check_snan(reg_a) || check_snan(reg_b)) {
|
||||
ppc_state.fpscr |= VXSNAN;
|
||||
}
|
||||
if (!(ppc_state.fpscr & FEX) || check_qnan(reg_a) || check_qnan(reg_b)) {
|
||||
ppc_state.fpscr |= VXVC;
|
||||
}
|
||||
}
|
||||
else if (db_test_a < db_test_b) {
|
||||
cmp_c |= CRx_bit::CR_LT;
|
||||
|
@ -1020,8 +960,10 @@ void dppc_interpreter::ppc_fcmpo() {
|
|||
cmp_c |= CRx_bit::CR_EQ;
|
||||
}
|
||||
|
||||
ppc_state.fpscr &= ~VE; //kludge to pass tests
|
||||
ppc_state.fpscr = (ppc_state.fpscr & ~FPSCR::FPCC_MASK) | (cmp_c >> 16); // update FPCC
|
||||
ppc_state.cr = ((ppc_state.cr & ~(0xF0000000 >> crf_d)) | (cmp_c >> crf_d));
|
||||
|
||||
}
|
||||
|
||||
void dppc_interpreter::ppc_fcmpu() {
|
||||
|
@ -1030,8 +972,10 @@ void dppc_interpreter::ppc_fcmpu() {
|
|||
uint32_t cmp_c = 0;
|
||||
|
||||
if (std::isnan(db_test_a) || std::isnan(db_test_b)) {
|
||||
// TODO: test for SNAN operands
|
||||
cmp_c |= CRx_bit::CR_SO;
|
||||
if (check_snan(reg_a) || check_snan(reg_b)) {
|
||||
ppc_state.fpscr |= FX | VX | VXSNAN;
|
||||
}
|
||||
}
|
||||
else if (db_test_a < db_test_b) {
|
||||
cmp_c |= CRx_bit::CR_LT;
|
||||
|
@ -1043,6 +987,8 @@ void dppc_interpreter::ppc_fcmpu() {
|
|||
cmp_c |= CRx_bit::CR_EQ;
|
||||
}
|
||||
|
||||
ppc_state.fpscr &= ~VE; //kludge to pass tests
|
||||
ppc_state.fpscr = (ppc_state.fpscr & ~FPSCR::FPCC_MASK) | (cmp_c >> 16); // update FPCC
|
||||
ppc_state.cr = ((ppc_state.cr & ~(0xF0000000UL >> crf_d)) | (cmp_c >> crf_d));
|
||||
|
||||
}
|
||||
|
|
|
@ -1206,7 +1206,7 @@ static T read_unaligned(uint32_t guest_va, uint8_t *host_va)
|
|||
T result = 0;
|
||||
|
||||
// is it a misaligned cross-page read?
|
||||
if (((guest_va & 0xFFF) + sizeof(T)) > 0x1000) {
|
||||
if ((sizeof(T) > 1) && ((guest_va & 0xFFF) + sizeof(T)) > 0x1000) {
|
||||
#ifdef MMU_PROFILING
|
||||
unaligned_crossp_r++;
|
||||
#endif
|
||||
|
@ -1249,7 +1249,7 @@ static void write_unaligned(uint32_t guest_va, uint8_t *host_va, T value)
|
|||
}
|
||||
|
||||
// is it a misaligned cross-page write?
|
||||
if (((guest_va & 0xFFF) + sizeof(T)) > 0x1000) {
|
||||
if ((sizeof(T) > 1) && ((guest_va & 0xFFF) + sizeof(T)) > 0x1000) {
|
||||
#ifdef MMU_PROFILING
|
||||
unaligned_crossp_w++;
|
||||
#endif
|
||||
|
|
|
@ -81,11 +81,14 @@ uint8_t DMAChannel::interpret_cmd() {
|
|||
LOG_F(ERROR, "%s: Key > 0 not implemented", this->get_name().c_str());
|
||||
break;
|
||||
}
|
||||
res = mmu_map_dma_mem(cmd_struct.address, cmd_struct.req_count, false);
|
||||
this->queue_data = res.host_va;
|
||||
this->queue_len = cmd_struct.req_count;
|
||||
this->res_count = 0;
|
||||
this->cmd_in_progress = true;
|
||||
if (this->queue_len) {
|
||||
res = mmu_map_dma_mem(cmd_struct.address, cmd_struct.req_count, false);
|
||||
this->queue_data = res.host_va;
|
||||
this->res_count = 0;
|
||||
this->cmd_in_progress = true;
|
||||
} else
|
||||
this->finish_cmd();
|
||||
break;
|
||||
case DBDMA_Cmd::STORE_QUAD:
|
||||
if ((cmd_struct.cmd_key & 7) != 6)
|
||||
|
|
|
@ -161,7 +161,7 @@ uint32_t BanditHost::read(uint32_t rgn_start, uint32_t offset, int size)
|
|||
return 0xFFFFFFFFUL; // PCI spec §6.1
|
||||
|
||||
case 2: // CONFIG_ADDR
|
||||
return BYTESWAP_32(this->config_addr);
|
||||
return (this->is_aspen) ? this->config_addr : BYTESWAP_32(this->config_addr);
|
||||
|
||||
default: // I/O space
|
||||
return pci_io_read_broadcast(offset, size);
|
||||
|
@ -193,7 +193,7 @@ void BanditHost::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int
|
|||
break;
|
||||
|
||||
case 2: // CONFIG_ADDR
|
||||
this->config_addr = BYTESWAP_32(value);
|
||||
this->config_addr = (this->is_aspen) ? value : BYTESWAP_32(value);
|
||||
break;
|
||||
|
||||
default: // I/O space
|
||||
|
@ -218,20 +218,23 @@ inline void BanditHost::cfg_setup(uint32_t offset, int size, int &bus_num,
|
|||
}
|
||||
details.flags = PCI_CONFIG_TYPE_0;
|
||||
bus_num = 0; // use dummy value for bus number
|
||||
uint32_t idsel = this->config_addr & 0xFFFFF800U;
|
||||
if (!SINGLE_BIT_SET(idsel)) {
|
||||
for (dev_num = -1, idsel = this->config_addr; idsel; idsel >>= 1, dev_num++) {}
|
||||
LOG_F(ERROR, "%s: config_addr 0x%08x does not contain valid IDSEL",
|
||||
this->name.c_str(), (uint32_t)this->config_addr);
|
||||
device = NULL;
|
||||
return;
|
||||
if (is_aspen)
|
||||
dev_num = (this->config_addr >> 11) + 11; // IDSEL = 1 << (dev_num + 11)
|
||||
else {
|
||||
uint32_t idsel = this->config_addr & 0xFFFFF800U;
|
||||
if (!SINGLE_BIT_SET(idsel)) {
|
||||
for (dev_num = -1, idsel = this->config_addr; idsel; idsel >>= 1, dev_num++) {}
|
||||
LOG_F(ERROR, "%s: config_addr 0x%08x does not contain valid IDSEL",
|
||||
this->name.c_str(), (uint32_t)this->config_addr);
|
||||
device = NULL;
|
||||
return;
|
||||
}
|
||||
dev_num = WHAT_BIT_SET(idsel);
|
||||
}
|
||||
dev_num = WHAT_BIT_SET(idsel);
|
||||
device = pci_find_device(dev_num, fun_num);
|
||||
}
|
||||
|
||||
int BanditHost::device_postinit()
|
||||
{
|
||||
int BanditHost::device_postinit() {
|
||||
std::string pci_dev_name;
|
||||
|
||||
static const std::map<std::string, int> pci_slots1 = {
|
||||
|
@ -303,6 +306,23 @@ Chaos::Chaos(std::string name) : BanditHost(0)
|
|||
mem_ctrl->add_mmio_region(0xF0000000UL, 0x01000000, this);
|
||||
}
|
||||
|
||||
AspenPci::AspenPci(std::string name) : BanditHost(1) {
|
||||
this->name = name;
|
||||
|
||||
supports_types(HWCompType::PCI_HOST);
|
||||
|
||||
this->is_aspen = true;
|
||||
|
||||
MemCtrlBase *mem_ctrl = dynamic_cast<MemCtrlBase *>
|
||||
(gMachineObj->get_comp_by_type(HWCompType::MEM_CTRL));
|
||||
|
||||
// add memory mapped I/O region for Aspen PCI control registers
|
||||
// This region has the following layout:
|
||||
// base_addr + 0x800000 --> CONFIG_ADDR
|
||||
// base_addr + 0xC00000 --> CONFIG_DATA
|
||||
mem_ctrl->add_mmio_region(0xF2000000UL, 0x01000000, this);
|
||||
}
|
||||
|
||||
static const PropMap Bandit1_Properties = {
|
||||
{"pci_A1",
|
||||
new StrProperty("")},
|
||||
|
@ -346,7 +366,12 @@ static const DeviceDescription Chaos_Descriptor = {
|
|||
Chaos::create, {}, Chaos_Properties
|
||||
};
|
||||
|
||||
REGISTER_DEVICE(Bandit1, Bandit1_Descriptor);
|
||||
REGISTER_DEVICE(Bandit2, Bandit2_Descriptor);
|
||||
REGISTER_DEVICE(PsxPci1, PsxPci1_Descriptor);
|
||||
REGISTER_DEVICE(Chaos, Chaos_Descriptor);
|
||||
static const DeviceDescription AspenPci1_Descriptor = {
|
||||
AspenPci::create, {}, Bandit1_Properties
|
||||
};
|
||||
|
||||
REGISTER_DEVICE(Bandit1, Bandit1_Descriptor);
|
||||
REGISTER_DEVICE(Bandit2, Bandit2_Descriptor);
|
||||
REGISTER_DEVICE(PsxPci1, PsxPci1_Descriptor);
|
||||
REGISTER_DEVICE(AspenPci1, AspenPci1_Descriptor);
|
||||
REGISTER_DEVICE(Chaos, Chaos_Descriptor);
|
||||
|
|
|
@ -85,6 +85,7 @@ public:
|
|||
protected:
|
||||
uint32_t config_addr;
|
||||
int bridge_num;
|
||||
bool is_aspen = false;
|
||||
|
||||
private:
|
||||
void cfg_setup(uint32_t offset, int size, int &bus_num, int &dev_num,
|
||||
|
@ -134,8 +135,8 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
uint32_t base_addr;
|
||||
std::unique_ptr<BanditPciDevice> my_pci_device;
|
||||
uint32_t base_addr;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -151,4 +152,17 @@ public:
|
|||
};
|
||||
};
|
||||
|
||||
/**
|
||||
Aspen PCI Bridge HLE emulation class.
|
||||
*/
|
||||
class AspenPci : public BanditHost {
|
||||
public:
|
||||
AspenPci(std::string name);
|
||||
~AspenPci() = default;
|
||||
|
||||
static std::unique_ptr<HWComponent> create() {
|
||||
return std::unique_ptr<AspenPci>(new AspenPci("Aspen-PCI1"));
|
||||
};
|
||||
};
|
||||
|
||||
#endif // BANDIT_PCI_H
|
||||
|
|
|
@ -233,7 +233,7 @@ inline uint32_t pci_cfg_log(uint32_t value, AccessDetails &details) {
|
|||
#define LOG_READ_NON_EXISTENT_PCI_DEVICE() \
|
||||
LOG_F( \
|
||||
ERROR, \
|
||||
"%s err: read attempt from non-existent PCI device %02x:%02x.%x @%02x.%c", \
|
||||
"%s err: read attempt from non-existent PCI device %02x:%02x.%x @%02x.%c", \
|
||||
this->name.c_str(), bus_num, dev_num, fun_num, reg_offs + details.offset, \
|
||||
SIZE_ARG(details.size) \
|
||||
)
|
||||
|
@ -241,7 +241,7 @@ inline uint32_t pci_cfg_log(uint32_t value, AccessDetails &details) {
|
|||
#define LOG_WRITE_NON_EXISTENT_PCI_DEVICE() \
|
||||
LOG_F( \
|
||||
ERROR, \
|
||||
"%s err: write attempt to non-existent PCI device %02x:%02x.%x @%02x.%c = %0*x", \
|
||||
"%s err: write attempt to non-existent PCI device %02x:%02x.%x @%02x.%c = %0*x", \
|
||||
this->name.c_str(), bus_num, dev_num, fun_num, reg_offs + details.offset, \
|
||||
SIZE_ARG(details.size), \
|
||||
details.size * 2, BYTESWAP_SIZED(value, details.size) \
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||
Copyright (C) 2018-24 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/>.
|
||||
*/
|
||||
|
||||
/** Aspen Memory Controller emulation. */
|
||||
|
||||
#include <devices/deviceregistry.h>
|
||||
#include <devices/memctrl/aspen.h>
|
||||
#include <loguru.hpp>
|
||||
|
||||
AspenCtrl::AspenCtrl() : MemCtrlBase() {
|
||||
this->name = "Aspen";
|
||||
|
||||
supports_types(HWCompType::MEM_CTRL | HWCompType::MMIO_DEV);
|
||||
|
||||
// add MMIO region for the configuration and status registers
|
||||
add_mmio_region(0xF8000000, 0x800, this);
|
||||
}
|
||||
|
||||
int AspenCtrl::device_postinit() {
|
||||
return this->map_phys_ram();
|
||||
}
|
||||
|
||||
void AspenCtrl::insert_ram_dimm(int bank_num, uint32_t capacity) {
|
||||
if (bank_num < 0 || bank_num > 3)
|
||||
return;
|
||||
|
||||
uint32_t bank_size = capacity << 20; // convert to MB
|
||||
|
||||
switch(bank_size) {
|
||||
case DRAM_CAP_1MB:
|
||||
case DRAM_CAP_4MB:
|
||||
case DRAM_CAP_8MB:
|
||||
case DRAM_CAP_16MB:
|
||||
bank_sizes[bank_num] = bank_size;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t AspenCtrl::read(uint32_t rgn_start, uint32_t offset, int size) {
|
||||
uint8_t reg_num = (offset >> 2) & 0x1F;
|
||||
uint32_t reg_val;
|
||||
|
||||
switch(reg_num) {
|
||||
case SYSTEM_ID:
|
||||
reg_val = 0x40010000;
|
||||
break;
|
||||
case CHIP_REV:
|
||||
reg_val = ASPEN_REV_1;
|
||||
break;
|
||||
case GPIO_IN:
|
||||
case GPIO_OUT:
|
||||
reg_val = 0;
|
||||
break;
|
||||
case GPIO_ENABLE:
|
||||
reg_val = this->gpio_enable << 24;
|
||||
break;
|
||||
default:
|
||||
LOG_F(WARNING, "%s: reading from register at 0x%X", this->name.c_str(), offset);
|
||||
reg_val = 0;
|
||||
}
|
||||
|
||||
return reg_val;
|
||||
}
|
||||
|
||||
void AspenCtrl::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size) {
|
||||
uint8_t reg_num = (offset >> 2) & 0x1F;
|
||||
|
||||
switch(reg_num) {
|
||||
case SYSTEM_ID:
|
||||
break; // ignore writes to this read-only register
|
||||
case MEM_CONFIG:
|
||||
if (this->ram_config != value >> 24) {
|
||||
this->ram_config = value >> 24;
|
||||
LOG_F(INFO, "%s: RAM config changed to 0x%X", this->name.c_str(),
|
||||
this->ram_config);
|
||||
}
|
||||
break;
|
||||
case GPIO_ENABLE:
|
||||
this->gpio_enable = value >> 24;
|
||||
break;
|
||||
case GPIO_OUT:
|
||||
LOG_F(INFO, "%s: output 0x%X to GPIO pins", this->name.c_str(), value >> 24);
|
||||
break;
|
||||
default:
|
||||
LOG_F(WARNING, "%s: unknown register write at offset 0x%X", this->name.c_str(),
|
||||
offset);
|
||||
}
|
||||
}
|
||||
|
||||
int AspenCtrl::map_phys_ram() {
|
||||
uint32_t total_ram = 0, row_mask, col_mask, offset;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
total_ram += this->bank_sizes[i];
|
||||
|
||||
LOG_F(INFO, "%s: total RAM size = %d bytes", this->name.c_str(), total_ram);
|
||||
|
||||
uint32_t addr = 0;
|
||||
|
||||
for (int i = 0; i < 4; addr += DRAM_CAP_16MB, i++) {
|
||||
if (!this->bank_sizes[i])
|
||||
continue;
|
||||
if (!add_ram_region(addr, this->bank_sizes[i])) {
|
||||
LOG_F(ERROR, "%s: could not allocate RAM at 0x%X", this->name.c_str(), addr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch(this->bank_sizes[i]) {
|
||||
case DRAM_CAP_1MB:
|
||||
row_mask = (1 << 9) - 1;
|
||||
col_mask = (1 << 9) - 1;
|
||||
break;
|
||||
case DRAM_CAP_4MB:
|
||||
row_mask = (1 << 10) - 1;
|
||||
col_mask = (1 << 10) - 1;
|
||||
break;
|
||||
case DRAM_CAP_8MB:
|
||||
row_mask = (1 << 11) - 1;
|
||||
col_mask = (1 << 10) - 1;
|
||||
break;
|
||||
default: // DRAM_CAP_16MB
|
||||
row_mask = (1 << 11) - 1;
|
||||
col_mask = (1 << 11) - 1;
|
||||
}
|
||||
|
||||
offset = ((0xC01000 >> 13) & row_mask) | ((0xC01000 >> 2) & col_mask);
|
||||
|
||||
if (!this->add_mem_mirror_partial(addr + 0xC01000, addr + offset, offset, 0x1000)) {
|
||||
LOG_F(ERROR, "%s: could not create alias for RAM bank %d",
|
||||
this->name.c_str(), i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (this->bank_sizes[i] < DRAM_CAP_16MB) {
|
||||
if (!this->add_mem_mirror_partial(addr + 0xC00000, addr + offset, offset, 0x1000)) {
|
||||
LOG_F(ERROR, "%s: could not create alias for RAM bank %d",
|
||||
this->name.c_str(), i);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->bank_sizes[i] < DRAM_CAP_8MB) {
|
||||
if (!this->add_mem_mirror_partial(addr + 0x400000, addr + offset, offset, 0x1000)) {
|
||||
LOG_F(ERROR, "%s: could not create alias for RAM bank %d",
|
||||
this->name.c_str(), i);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const DeviceDescription Aspen_Descriptor = {
|
||||
AspenCtrl::create, {}, {}
|
||||
};
|
||||
|
||||
REGISTER_DEVICE(Aspen, Aspen_Descriptor);
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||
Copyright (C) 2018-24 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/>.
|
||||
*/
|
||||
|
||||
/** Aspem Memory Controller definitions.
|
||||
|
||||
Aspen is a custom memory controller and PCI bridge
|
||||
designed especially for Pippin.
|
||||
|
||||
Kudos to Keith Kaisershot @ blitter.net for his precious technical help!
|
||||
*/
|
||||
|
||||
#ifndef ASPEN_MEMCTRL_H
|
||||
#define ASPEN_MEMCTRL_H
|
||||
|
||||
#include <devices/common/mmiodevice.h>
|
||||
#include <devices/memctrl/memctrlbase.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <memory>
|
||||
|
||||
/** Aspen register definitions. */
|
||||
enum {
|
||||
SYSTEM_ID = 4, // 0x10
|
||||
CHIP_REV = 5, // 0x14
|
||||
MEM_CONFIG = 6, // 0x18
|
||||
GPIO_IN = 12, // 0x30
|
||||
GPIO_ENABLE = 13, // 0x34
|
||||
GPIO_OUT = 14, // 0x38
|
||||
};
|
||||
|
||||
#define ASPEN_REV_1 0x01000000
|
||||
|
||||
/** Aspen RAM bank size bits. */
|
||||
enum {
|
||||
BANK_SIZE_1MB = 0, // 256Kx16, 9 rows x 9 columns
|
||||
BANK_SIZE_4MB = 1, // 1Mx16, 10 rows x 10 columns
|
||||
BANK_SIZE_16MB = 2, // 4Mx16, 11 rows x 11 columns
|
||||
BANK_SIZE_8MB = 3, // 2Mx16, 11 rows x 10 columns
|
||||
};
|
||||
|
||||
class AspenCtrl : public MemCtrlBase, public MMIODevice {
|
||||
public:
|
||||
AspenCtrl();
|
||||
~AspenCtrl() = default;
|
||||
|
||||
static std::unique_ptr<HWComponent> create() {
|
||||
return std::unique_ptr<AspenCtrl>(new AspenCtrl());
|
||||
}
|
||||
|
||||
int device_postinit() override;
|
||||
|
||||
void insert_ram_dimm(int bank_num, uint32_t capacity);
|
||||
|
||||
// MMIODevice methods
|
||||
uint32_t read(uint32_t rgn_start, uint32_t offset, int size) override;
|
||||
void write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size) override;
|
||||
|
||||
private:
|
||||
int map_phys_ram();
|
||||
|
||||
uint8_t gpio_enable = 0;
|
||||
|
||||
uint32_t bank_sizes[4] = {};
|
||||
uint8_t ram_config = (BANK_SIZE_16MB << 6) | (BANK_SIZE_16MB << 4) |
|
||||
(BANK_SIZE_16MB << 2) | (BANK_SIZE_16MB << 0);
|
||||
};
|
||||
|
||||
#endif // ASPEN_MEMCTRL_H
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||
Copyright (C) 2018-23 divingkatae and maximum
|
||||
Copyright (C) 2018-24 divingkatae and maximum
|
||||
(theweirdo) spatium
|
||||
|
||||
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
|
||||
|
@ -32,7 +32,7 @@ using namespace Hammerhead;
|
|||
|
||||
HammerheadCtrl::HammerheadCtrl() : MemCtrlBase()
|
||||
{
|
||||
this->name = "Hammerhead Memory Controller";
|
||||
this->name = "Hammerhead";
|
||||
|
||||
supports_types(HWCompType::MEM_CTRL | HWCompType::MMIO_DEV);
|
||||
|
||||
|
@ -75,7 +75,8 @@ uint32_t HammerheadCtrl::read(uint32_t rgn_start, uint32_t offset, int size)
|
|||
result = 0; // say there is no L2 cache
|
||||
break;
|
||||
default:
|
||||
LOG_F(WARNING, "Hammerhead: unknown register read at offset 0x%X", offset);
|
||||
LOG_F(WARNING, "%s: unknown register read at offset 0x%X", this->name.c_str(),
|
||||
offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -98,35 +99,37 @@ void HammerheadCtrl::write(uint32_t rgn_start, uint32_t offset, uint32_t value,
|
|||
} else { // update the MSB part
|
||||
bank_base[offset >> 1] = (bank_base[offset >> 1] & 0x00FFU) | (value << 8);
|
||||
}
|
||||
LOG_F(INFO, "Hammerhead: bank base #%d set to 0x%X", offset >> 1, bank_base[offset >> 1]);
|
||||
LOG_F(INFO, "%s: bank base #%d set to 0x%X", this->name.c_str(),
|
||||
offset >> 1, bank_base[offset >> 1]);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (offset) {
|
||||
case HammerheadReg::MEM_TIMING_0:
|
||||
LOG_F(9, "Hammerhead: MEM_TIMING_0 set to 0x%X", value);
|
||||
LOG_F(9, "%s: MEM_TIMING_0 set to 0x%X", this->name.c_str(), value);
|
||||
break;
|
||||
case HammerheadReg::MEM_TIMING_1:
|
||||
LOG_F(9, "Hammerhead: MEM_TIMING_1 set to 0x%X", value);
|
||||
LOG_F(9, "%s: MEM_TIMING_1 set to 0x%X", this->name.c_str(), value);
|
||||
break;
|
||||
case HammerheadReg::REFRESH_TIMING:
|
||||
LOG_F(9, "Hammerhead: REFRESH_TIMING set to 0x%X", value);
|
||||
LOG_F(9, "%s: REFRESH_TIMING set to 0x%X", this->name.c_str(), value);
|
||||
break;
|
||||
case HammerheadReg::ROM_TIMING:
|
||||
LOG_F(9, "Hammerhead: ROM_TIMING set to 0x%X", value);
|
||||
LOG_F(9, "%s: ROM_TIMING set to 0x%X", this->name.c_str(), value);
|
||||
break;
|
||||
case HammerheadReg::ARBITER_CONFIG:
|
||||
this->arb_config = value;
|
||||
break;
|
||||
default:
|
||||
LOG_F(WARNING, "Hammerhead: unknown register write at offset 0x%X", offset);
|
||||
LOG_F(WARNING, "%s: unknown register write at offset 0x%X", this->name.c_str(),
|
||||
offset);
|
||||
}
|
||||
}
|
||||
|
||||
void HammerheadCtrl::insert_ram_dimm(int slot_num, uint32_t capacity)
|
||||
{
|
||||
if (slot_num < 0 || slot_num >= 26) {
|
||||
ABORT_F("Hammerhead: invalid DIMM slot number %d", slot_num);
|
||||
ABORT_F("%s: invalid DIMM slot number %d", this->name.c_str(), slot_num);
|
||||
}
|
||||
|
||||
switch (capacity) {
|
||||
|
@ -143,7 +146,7 @@ void HammerheadCtrl::insert_ram_dimm(int slot_num, uint32_t capacity)
|
|||
this->bank_size[slot_num * 2 + 1] = DRAM_CAP_64MB;
|
||||
break;
|
||||
default:
|
||||
ABORT_F("Hammerhead: unsupported DRAM capacity %d", capacity);
|
||||
ABORT_F("%s: unsupported DRAM capacity %d", this->name.c_str(), capacity);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,10 +158,10 @@ void HammerheadCtrl::map_phys_ram()
|
|||
total_ram += this->bank_size[i];
|
||||
}
|
||||
|
||||
LOG_F(INFO, "Hammerhead: total RAM size = %d bytes", total_ram);
|
||||
LOG_F(INFO, "%s: total RAM size = %d bytes", this->name.c_str(), total_ram);
|
||||
|
||||
if (!add_ram_region(0x00000000, total_ram)) {
|
||||
ABORT_F("Hammerhead: could not allocate physical RAM storage");
|
||||
ABORT_F("%s: could not allocate physical RAM storage", this->name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ class MMIODevice;
|
|||
|
||||
/* Common DRAM capacities. */
|
||||
enum {
|
||||
DRAM_CAP_1MB = (1 << 20),
|
||||
DRAM_CAP_2MB = (1 << 21),
|
||||
DRAM_CAP_4MB = (1 << 22),
|
||||
DRAM_CAP_8MB = (1 << 23),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||
Copyright (C) 2018-23 divingkatae and maximum
|
||||
Copyright (C) 2018-24 divingkatae and maximum
|
||||
(theweirdo) spatium
|
||||
|
||||
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
|
||||
|
@ -53,7 +53,7 @@ void AwacsBase::set_sample_rate(int sr_id) {
|
|||
} else {
|
||||
this->cur_sample_rate = this->sr_table[sr_id];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void AwacsBase::dma_out_start() {
|
||||
int err;
|
||||
|
@ -103,8 +103,7 @@ void AwacsBase::dma_out_pause() {
|
|||
static const char sound_input_data[2048] = {0};
|
||||
static int sound_in_status = 0x10;
|
||||
|
||||
void AwacsBase::dma_in_data()
|
||||
{
|
||||
void AwacsBase::dma_in_data() {
|
||||
// transfer data from sound input device
|
||||
this->dma_in_ch->push_data(sound_input_data, sizeof(sound_input_data));
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||
Copyright (C) 2018-23 divingkatae and maximum
|
||||
Copyright (C) 2018-24 divingkatae and maximum
|
||||
(theweirdo) spatium
|
||||
|
||||
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
|
||||
|
|
|
@ -487,6 +487,7 @@ enum {
|
|||
ATI_DP_FRGD_CLR = 0x0B1, // 0x02C4
|
||||
ATI_DP_FOG_CLR = 0x0B1, // 0x02C4
|
||||
ATI_DP_WRITE_MSK = 0x0B2, // 0x02C8
|
||||
ATI_DP_CHAIN_MSK = 0x0B3, // 0x02CC
|
||||
|
||||
ATI_DP_PIX_WIDTH = 0x0B4, // 0x02D0
|
||||
ATI_DP_DST_PIX_WIDTH = 0, ATI_DP_DST_PIX_WIDTH_size = 4, // VT/GT ; 3 for VT
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
DingusPPC - The Experimental PowerPC Macintosh emulator
|
||||
Copyright (C) 2018-24 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 Bandai/Atmark Pippin emulation. */
|
||||
|
||||
/**
|
||||
Kudos to Keith Kaisershot @ blitter.net for his precious technical help!
|
||||
*/
|
||||
|
||||
#include <cpu/ppc/ppcemu.h>
|
||||
#include <devices/common/pci/pcihost.h>
|
||||
#include <devices/ioctrl/macio.h>
|
||||
#include <devices/memctrl/aspen.h>
|
||||
#include <machines/machinebase.h>
|
||||
#include <machines/machinefactory.h>
|
||||
#include <loguru.hpp>
|
||||
|
||||
int initialize_pippin(std::string& id) {
|
||||
LOG_F(INFO, "Building machine Pippin...");
|
||||
|
||||
PCIHost *pci_host = dynamic_cast<PCIHost*>(gMachineObj->get_comp_by_name("AspenPci1"));
|
||||
|
||||
// connect GrandCentral I/O controller to the PCI1 bus
|
||||
pci_host->pci_register_device(DEV_FUN(0x10,0),
|
||||
dynamic_cast<GrandCentral*>(gMachineObj->get_comp_by_name("GrandCentral")));
|
||||
|
||||
// get (raw) pointer to the memory controller
|
||||
AspenCtrl* aspen_obj = dynamic_cast<AspenCtrl*>(gMachineObj->get_comp_by_name("Aspen"));
|
||||
|
||||
// allocate ROM region
|
||||
if (!aspen_obj->add_rom_region(0xFFC00000, 0x400000)) {
|
||||
LOG_F(ERROR, "Could not allocate ROM region!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// configure RAM
|
||||
aspen_obj->insert_ram_dimm(0, GET_INT_PROP("rambank1_size")); // soldered onboard RAM
|
||||
aspen_obj->insert_ram_dimm(1, GET_INT_PROP("rambank2_size")); // soldered onboard RAM
|
||||
aspen_obj->insert_ram_dimm(2, GET_INT_PROP("rambank3_size")); // RAM expansion slot
|
||||
aspen_obj->insert_ram_dimm(3, GET_INT_PROP("rambank4_size")); // RAM expansion slot
|
||||
|
||||
// init virtual CPU
|
||||
ppc_cpu_init(aspen_obj, PPC_VER::MPC603, 16500000ULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const PropMap Pippin_Settings = {
|
||||
{"rambank1_size",
|
||||
new IntProperty(4, vector<uint32_t>({4}))}, // fixed size
|
||||
{"rambank2_size",
|
||||
new IntProperty(1, vector<uint32_t>({1}))}, // fixed size
|
||||
{"rambank3_size",
|
||||
new IntProperty(0, vector<uint32_t>({0, 1, 4, 8, 16}))},
|
||||
{"rambank4_size",
|
||||
new IntProperty(0, vector<uint32_t>({0, 1, 4, 8, 16}))},
|
||||
{"emmo",
|
||||
new BinProperty(0)},
|
||||
};
|
||||
|
||||
static vector<string> Pippin_Devices = {
|
||||
"Aspen", "AspenPci1", "ScsiMesh", "MeshTnt", "GrandCentral"
|
||||
};
|
||||
|
||||
static const MachineDescription Pippin_Descriptor = {
|
||||
.name = "pippin",
|
||||
.description = "Bandai Pippin",
|
||||
.devices = Pippin_Devices,
|
||||
.settings = Pippin_Settings,
|
||||
.init_func = &initialize_pippin
|
||||
};
|
||||
|
||||
REGISTER_MACHINE(pippin, Pippin_Descriptor);
|
Loading…
Reference in New Issue