Compare commits

...

28 Commits

Author SHA1 Message Date
joevt c6ea3a374e control: Fix PAL/NTSC interlaced display. 2024-04-10 07:32:35 -07:00
joevt e8ce805f2a poweropcodes: Remove power_setsoov.
None of the POWER opcodes uses it now, plus it is a duplicate of ppc_setsoov (though ppc_setsoov is inline so it would have to be moved to be able to use it in poweropcodes.cpp?
2024-04-10 07:30:06 -07:00
joevt 3d898ebdf3 poweropcodes: Cleanup power_rot_mask.
Use U instead of UL. U will use the smallest size that can fit all the unsigned bytes. Since 0xFFFFFFFF fits in 32 bits, the 0xFFFFFFFFU is a uint32_t.
2024-04-10 07:29:56 -07:00
joevt f45b7c47c8 poweropcodes: Fix srq.
Test bit 26 of rB instead of testing for >= 0x20 to determine which operation to perform.
2024-04-10 07:29:46 -07:00
joevt 916cb47b9d poweropcodes: Fix srlq.
Test bit 26 of rB instead of testing for >= 0x20 to determine which operation to perform.
2024-04-10 07:29:37 -07:00
joevt bce816139b poweropcodes: Fix sreq.
Including bits of rot_sh in the rA and MQ calculations is nonsensical since it is a rotation count and not a source of bits to be extracted or rotated.
The mask is not complicated, so we don't need to use power_rot_mask.
2024-04-10 07:29:28 -07:00
joevt 24bce16c4d poweropcodes: Fix srea.
Fix carry flag calculation. Anding with the rotation count (n = rB) is nonsensical.
(r & ~mask) is the rotated word ANDed with the complement of the generated mask of n zeros followed by 32 - n ones.
The manual says this 32-bit result is ORed together. This means all the bits are ORed together which is equivalent to saying 0 if all zeros and 1 if any ones. In other words: (r & ~mask) != 0.
This boolean is ANDed with bit 0 of rS to produce the carry. int32_t(rS) < 0 will test bit 0. The && operator will treat each side as a boolean so you can exclude "!= 0" tests.
2024-04-10 07:29:20 -07:00
joevt a928c67913 poweropcodes: Fix sraq.
If bit 26 of rB is set then the mask should be all ones.
If bit 26 of rB is set then rA should be all ones or all zeros (depending on the sign bit of rA).
2024-04-10 07:29:11 -07:00
joevt 2b8f510603 poweropcodes: Fix slq.
Test bit 26 of rB instead of using >= 0x20 to determine which operation to perform.
The two operations need to be switched such that rA is cleared when bit 26 is set.
Don't forget to store the result in rA.
2024-04-10 07:28:58 -07:00
joevt e8273ecc61 poweropcodes: Fix sllq.
Test bit 26 of rB instead of using >= 0x20 to determine which operation to perform.
Since the mask is not complicated, we don't need to use power_rot_mask.
2024-04-10 07:28:46 -07:00
joevt e1f31a2da3 poweropcodes: Fix rrib.
It is redundant to test bit 0 of rS and then use bit 0 of rS in the case when bit 0 of rS is set.
In the case when bit 0 of rS is not set, using bit 0 or rS is incorrect since it results in no change of rA.
2024-04-10 07:26:52 -07:00
joevt d897acfd3c poweropcodes: Fix nabs.
Calculate overflow first before calculating condition codes because the overflow condition is copied from XER.
2024-04-10 07:26:32 -07:00
joevt 1e57ac408a poweropcodes: Fix mul.
Operands are supposed to be twos complement numbers.
Calculate overflow first before calculating condition codes because the overflow condition is copied from XER.
Fix OV calculation. Previously, it was using power_setsoov which I think is only for add and subtract operations.
Fix CR calcalation. It's supposed to depend on the low order 32 bits that are placed into MQ.
2024-04-10 07:26:24 -07:00
joevt ef8522e101 poweropcodes: Fix maskg.
The condition code register depends on rA, not rD since rA contains the result.
2024-04-10 07:26:16 -07:00
joevt c71d856a08 poweropcodes: Fix lscbx.
- Fix CR calculation. It depends on whether a match occurred and only the EQ flag is affected.
- Remove bytes_copied. We can subtract bytes_remaining from bytes_to_load to calculate that.
- Initialize ppc_result_d to zero so that bitmask is not needed to add new bytes to it. This is ok since the manual says that bytes that are not loaded are undefined.
2024-04-10 07:26:07 -07:00
joevt df7ff76404 poweropcodes: Fix doz.
Calculate overflow first before calculating condition codes because the overflow condition is copied from XER.
Fix OV calculation. Previously, it was using power_setsoov which I think is only for add and subtract operations. doz does a subtract but only if the result is supposed to be positive, therefore a negative result indicates an overflow.
2024-04-10 07:22:50 -07:00
joevt 0d1ce68d19 poweropcodes: Fix divs.
dividend and divisor are supposed to be a twos compliment numbers.
Fix OV calculation. Previously, it was using power_setsoov which I think is only for add and subtract operations.
Fix CR calculation. It depends on the remainder, not the quotient.
2024-04-10 07:22:41 -07:00
joevt 88aa249ce1 poweropcodes: Fix div.
dividend is supposed to be a twos compliment number.
Fix test for dividend = -0x80000000 and divisor = -1. Previously, the test was assuming dividend was a 32-bit value from rA.
Fix OV calculation. Previously, it was using power_setsoov which I think is only for add and subtract operations.
Fix CR calculation. It depends on the remainder, not the quotient.
2024-04-10 07:22:34 -07:00
joevt ff626ae0b5 poweropcodes: Fix clcs.
For MPC601 CPUs, all values of rA return 64 though the manual says undefined values of rA produce undefined results.
For non-MPC601 CPUs, if this instruction is included (such as for risu DPPC) then return results that are obtained from a G4 running Mac OS 9.2.2.
2024-04-10 07:22:26 -07:00
joevt 529f23d836 poweropcodes: Fix abs.
Making a negative value positive requires unary negate operator rather than binary and operator since negative numbers are stored using twos compliment.
If ov is set then clear overflow when overflow doesn't happen.
2024-04-10 07:22:18 -07:00
joevt cb88bab67d ppcopcodes: Fixes for SPRs.
- Rename DEC to DEC_S and add DEC_U.
- MQ, RTCL_U, RTCU_U, and DEC_U should cause an illegal instruction program exception for non-MPC601 CPUs. The exception handler of classic Mac OS uses this to emulate the instruction.
- For mtspr, the SPRs RTCL_U, RTCU_U, and DEC_U are treated as no-op on MPC601.
- For debugging, use the supervisor instead of the user SPR number as the index for storing the values for RTC, TB, and DEC.
- For debugging, RTC, TB, and DEC should be updated after each access. Previously, mfspr and mtspr would only update the half of RTC and TB that was being accessed instead of both halves.
2024-04-10 07:21:54 -07:00
joevt 67a5c39b1c ppcopcodes: Add Privileged exception for SPRs.
Accessing an SPR with bit 4 set (> 15) requires supervisor privilege and should cause a supervisor-level instruction exception (privileged instruction type program exception).
2024-04-10 07:21:23 -07:00
joevt 0273867c49 ppcopcodes: Cleanup ppc_changecrf0.
- Use one assignment to set ppc_state.cr.
- Use enums for CR and XER bits.
- Use < to check sign bit.
2024-04-10 06:47:10 -07:00
joevt 1e50d88183 ppcopcodes: Use macro to grab instruction fields. 2024-04-10 06:46:46 -07:00
joevt 29a832c68d ppcopcodes: Use < 0 instead of & 0x8000000. 2024-04-10 06:45:31 -07:00
joevt cb05bd05eb cpu: Add ppc_grab_regssash macro.
This macro is like ppc_grab_regssa but includes rot_sh = (ppc_cur_instruction >> 11) & 0x1F;
2024-04-10 06:43:34 -07:00
joevt 4f45d7de35 cpu: Add cpu options to ppc_cpu_init.
The first option is a flag that enables MPC601 (POWER) instructions for CPUs that are not MPC601.
This can be useful for the following reasons:
1) To produce results similar to classic Mac OS which emulates MPC601 instructions on CPUs that don't implement MPC601 instructions. This option is used to compare the risu traces produced in Mac OS 9 on a G3 or G4 with DPPC.
2) May increase performance in apps that use POWER instructions on emulated machines with CPUs that are not MPC601. It is not known if any such apps exist but there could be since Apple included MPC601 emulation in classic Mac OS.
2024-04-10 06:43:18 -07:00
joevt a6ba9a0554 memaccess: Add addr type cast. 2024-04-09 21:19:04 -07:00
15 changed files with 353 additions and 237 deletions

View File

@ -27,20 +27,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "ppcmmu.h"
#include <stdint.h>
// Affects the XER register's SO and OV Bits
inline void power_setsoov(uint32_t a, uint32_t b, uint32_t d) {
if ((a ^ b) & (a ^ d) & 0x80000000UL) {
ppc_state.spr[SPR::XER] |= XER::SO | XER::OV;
} else {
ppc_state.spr[SPR::XER] &= ~XER::OV;
}
}
/** mask generator for rotate and shift instructions (§ 4.2.1.4 PowerpC PEM) */
static inline uint32_t power_rot_mask(unsigned rot_mb, unsigned rot_me) {
uint32_t m1 = 0xFFFFFFFFUL >> rot_mb;
uint32_t m2 = uint32_t(0xFFFFFFFFUL << (31 - rot_me));
uint32_t m1 = 0xFFFFFFFFU >> rot_mb;
uint32_t m2 = 0xFFFFFFFFU << (31 - rot_me);
return ((rot_mb <= rot_me) ? m2 & m1 : m1 | m2);
}
@ -52,9 +42,10 @@ void dppc_interpreter::power_abs() {
ppc_result_d = ppc_result_a;
if (ov)
ppc_state.spr[SPR::XER] |= XER::SO | XER::OV;
} else {
ppc_result_d = ppc_result_a & 0x7FFFFFFF;
ppc_result_d = (int32_t(ppc_result_a) < 0) ? -ppc_result_a : ppc_result_a;
if (ov)
ppc_state.spr[SPR::XER] &= ~XER::OV;
}
if (rec)
@ -76,10 +67,17 @@ void dppc_interpreter::power_clcs() {
case 13: //data cache line size
case 14: //minimum line size
case 15: //maximum line size
ppc_result_d = 64;
break;
default:
ppc_result_d = 0;
default: ppc_result_d = is_601 ? 64 : 32; break;
case 7:
case 23: ppc_result_d = is_601 ? 64 : 0; break;
case 8:
case 9:
case 24:
case 25: ppc_result_d = is_601 ? 64 : 4; break;
case 10:
case 11:
case 26:
case 27: ppc_result_d = is_601 ? 64 : 0x4000; break;
}
ppc_store_iresult_reg(reg_d, ppc_result_d);
@ -90,23 +88,39 @@ void dppc_interpreter::power_div() {
uint32_t ppc_result_d;
ppc_grab_regsdab(ppc_cur_instruction);
uint64_t dividend = ((uint64_t)ppc_result_a << 32) | ppc_state.spr[SPR::MQ];
int32_t divisor = ppc_result_b;
int64_t dividend = (uint64_t(ppc_result_a) << 32) | ppc_state.spr[SPR::MQ];
int32_t divisor = ppc_result_b;
int64_t quotient;
int32_t remainder;
if ((ppc_result_a == 0x80000000UL && divisor == -1) || !divisor) {
ppc_state.spr[SPR::MQ] = 0;
ppc_result_d = 0x80000000UL; // -2^31 aka INT32_MIN
if (dividend == -0x80000000 && divisor == -1) {
remainder = 0;
ppc_result_d = 0x80000000U; // -2^31 aka INT32_MIN
if (ov)
ppc_state.spr[SPR::XER] |= XER::SO | XER::OV;
} else if (!divisor) {
remainder = 0;
ppc_result_d = 0x80000000U; // -2^31 aka INT32_MIN
if (ov)
ppc_state.spr[SPR::XER] |= XER::SO | XER::OV;
} else {
ppc_result_d = uint32_t(dividend / divisor);
ppc_state.spr[SPR::MQ] = dividend % divisor;
quotient = dividend / divisor;
remainder = dividend % divisor;
ppc_result_d = uint32_t(quotient);
if (ov) {
if (((quotient >> 31) + 1) & ~1) {
ppc_state.spr[SPR::XER] |= XER::SO | XER::OV;
} else {
ppc_state.spr[SPR::XER] &= ~XER::OV;
}
}
}
if (ov)
power_setsoov(ppc_result_b, ppc_result_a, ppc_result_d);
if (rec)
ppc_changecrf0(ppc_result_d);
ppc_changecrf0(remainder);
ppc_store_iresult_reg(reg_d, ppc_result_d);
ppc_state.spr[SPR::MQ] = remainder;
}
template void dppc_interpreter::power_div<RC0, OV0>();
@ -116,16 +130,31 @@ template void dppc_interpreter::power_div<RC1, OV1>();
template <field_rc rec, field_ov ov>
void dppc_interpreter::power_divs() {
uint32_t ppc_result_d;
int32_t remainder;
ppc_grab_regsdab(ppc_cur_instruction);
uint32_t ppc_result_d = ppc_result_a / ppc_result_b;
ppc_state.spr[SPR::MQ] = (ppc_result_a % ppc_result_b);
if (ov)
power_setsoov(ppc_result_b, ppc_result_a, ppc_result_d);
if (!ppc_result_b) { // handle the "anything / 0" case
ppc_result_d = -1;
remainder = ppc_result_a;
if (ov)
ppc_state.spr[SPR::XER] |= XER::SO | XER::OV;
} else if (ppc_result_a == 0x80000000U && ppc_result_b == 0xFFFFFFFFU) {
ppc_result_d = 0x80000000U;
remainder = 0;
if (ov)
ppc_state.spr[SPR::XER] |= XER::SO | XER::OV;
} else { // normal signed devision
ppc_result_d = int32_t(ppc_result_a) / int32_t(ppc_result_b);
remainder = (int32_t(ppc_result_a) % int32_t(ppc_result_b));
if (ov)
ppc_state.spr[SPR::XER] &= ~XER::OV;
}
if (rec)
ppc_changecrf0(ppc_result_d);
ppc_changecrf0(remainder);
ppc_store_iresult_reg(reg_d, ppc_result_d);
ppc_state.spr[SPR::MQ] = remainder;
}
template void dppc_interpreter::power_divs<RC0, OV0>();
@ -136,13 +165,18 @@ template void dppc_interpreter::power_divs<RC1, OV1>();
template <field_rc rec, field_ov ov>
void dppc_interpreter::power_doz() {
ppc_grab_regsdab(ppc_cur_instruction);
uint32_t ppc_result_d = (int32_t(ppc_result_a) >= int32_t(ppc_result_b)) ? 0 :
ppc_result_b - ppc_result_a;
uint32_t ppc_result_d = (int32_t(ppc_result_a) < int32_t(ppc_result_b)) ?
ppc_result_b - ppc_result_a : 0;
if (ov) {
if (int32_t(ppc_result_d) < 0) {
ppc_state.spr[SPR::XER] |= XER::SO | XER::OV;
} else {
ppc_state.spr[SPR::XER] &= ~XER::OV;
}
}
if (rec)
ppc_changecrf0(ppc_result_d);
if (ov)
power_setsoov(ppc_result_a, ppc_result_b, ppc_result_d);
ppc_store_iresult_reg(reg_d, ppc_result_d);
}
@ -168,47 +202,50 @@ void dppc_interpreter::power_lscbx() {
ppc_grab_regsdab(ppc_cur_instruction);
ppc_effective_address = ppc_result_b + (reg_a ? ppc_result_a : 0);
uint8_t return_value = 0;
uint32_t bytes_to_load = (ppc_state.spr[SPR::XER] & 0x7F);
uint32_t bytes_copied = 0;
uint32_t bytes_remaining = bytes_to_load;
uint8_t matching_byte = (uint8_t)(ppc_state.spr[SPR::XER] >> 8);
uint32_t ppc_result_d = 0;
uint32_t ppc_result_d = 0;
bool is_match = false;
// for storing each byte
uint32_t bitmask = 0xFF000000;
uint8_t shift_amount = 24;
while (bytes_to_load > 0) {
return_value = mmu_read_vmem<uint8_t>(ppc_effective_address);
while (bytes_remaining > 0) {
uint8_t return_value = mmu_read_vmem<uint8_t>(ppc_effective_address);
ppc_result_d = (ppc_result_d & ~bitmask) | (return_value << shift_amount);
ppc_result_d |= return_value << shift_amount;
if (!shift_amount) {
if (reg_d != reg_a && reg_d != reg_b)
ppc_store_iresult_reg(reg_d, ppc_result_d);
reg_d = (reg_d + 1) & 0x1F;
bitmask = 0xFF000000;
ppc_result_d = 0;
shift_amount = 24;
} else {
bitmask >>= 8;
shift_amount -= 8;
}
ppc_effective_address++;
bytes_copied++;
bytes_to_load--;
bytes_remaining--;
if (return_value == matching_byte)
if (return_value == matching_byte) {
is_match = true;
break;
}
}
// store partiallly loaded register if any
// store partially loaded register if any
if (shift_amount != 24 && reg_d != reg_a && reg_d != reg_b)
ppc_store_iresult_reg(reg_d, ppc_result_d);
ppc_state.spr[SPR::XER] = (ppc_state.spr[SPR::XER] & ~0x7F) | bytes_copied;
ppc_state.spr[SPR::XER] = (ppc_state.spr[SPR::XER] & ~0x7F) | (bytes_to_load - bytes_remaining);
if (rec)
ppc_changecrf0(ppc_result_d);
if (rec) {
ppc_state.cr =
(ppc_state.cr & 0x0FFFFFFFUL) |
(is_match ? CRx_bit::CR_EQ : 0) |
((ppc_state.spr[SPR::XER] & XER::SO) >> 3);
}
}
template void dppc_interpreter::power_lscbx<RC0>();
@ -234,7 +271,7 @@ void dppc_interpreter::power_maskg() {
ppc_result_a = insert_mask;
if (rec)
ppc_changecrf0(ppc_result_d);
ppc_changecrf0(ppc_result_a);
ppc_store_iresult_reg(reg_a, ppc_result_a);
}
@ -259,17 +296,19 @@ template void dppc_interpreter::power_maskir<RC1>();
template <field_rc rec, field_ov ov>
void dppc_interpreter::power_mul() {
ppc_grab_regsdab(ppc_cur_instruction);
uint64_t product;
product = ((uint64_t)ppc_result_a) * ((uint64_t)ppc_result_b);
uint32_t ppc_result_d = ((uint32_t)(product >> 32));
ppc_state.spr[SPR::MQ] = ((uint32_t)(product));
int64_t product = int64_t(int32_t(ppc_result_a)) * int32_t(ppc_result_b);
uint32_t ppc_result_d = uint32_t(uint64_t(product) >> 32);
ppc_state.spr[SPR::MQ] = uint32_t(product);
if (ov) {
if (uint64_t(product >> 31) + 1 & ~1) {
ppc_state.spr[SPR::XER] |= XER::SO | XER::OV;
} else {
ppc_state.spr[SPR::XER] &= ~XER::OV;
}
}
if (rec)
ppc_changecrf0(ppc_result_d);
if (ov)
power_setsoov(ppc_result_a, ppc_result_b, ppc_result_d);
ppc_changecrf0(uint32_t(product));
ppc_store_iresult_reg(reg_d, ppc_result_d);
}
@ -281,12 +320,12 @@ template void dppc_interpreter::power_mul<RC1, OV1>();
template <field_rc rec, field_ov ov>
void dppc_interpreter::power_nabs() {
ppc_grab_regsda(ppc_cur_instruction);
uint32_t ppc_result_d = ppc_result_a & 0x80000000 ? ppc_result_a : -ppc_result_a;
uint32_t ppc_result_d = (int32_t(ppc_result_a) < 0) ? ppc_result_a : -ppc_result_a;
if (rec)
ppc_changecrf0(ppc_result_d);
if (ov)
ppc_state.spr[SPR::XER] &= ~XER::OV;
if (rec)
ppc_changecrf0(ppc_result_d);
ppc_store_iresult_reg(reg_d, ppc_result_d);
}
@ -316,11 +355,12 @@ void dppc_interpreter::power_rlmi() {
template <field_rc rec>
void dppc_interpreter::power_rrib() {
ppc_grab_regssab(ppc_cur_instruction);
unsigned rot_sh = ppc_result_b & 0x1F;
if (ppc_result_d & 0x80000000) {
ppc_result_a |= ((ppc_result_d & 0x80000000) >> ppc_result_b);
if (int32_t(ppc_result_d) < 0) {
ppc_result_a |= (0x80000000U >> rot_sh);
} else {
ppc_result_a &= ~((ppc_result_d & 0x80000000) >> ppc_result_b);
ppc_result_a &= ~(0x80000000U >> rot_sh);
}
if (rec)
@ -372,8 +412,7 @@ template void dppc_interpreter::power_sleq<RC1>();
template <field_rc rec>
void dppc_interpreter::power_sliq() {
ppc_grab_regssa(ppc_cur_instruction);
unsigned rot_sh = (ppc_cur_instruction >> 11) & 0x1F;
ppc_grab_regssash(ppc_cur_instruction);
ppc_result_a = ppc_result_d << rot_sh;
ppc_state.spr[SPR::MQ] = ((ppc_result_d << rot_sh) | (ppc_result_d >> (32 - rot_sh)));
@ -389,8 +428,7 @@ template void dppc_interpreter::power_sliq<RC1>();
template <field_rc rec>
void dppc_interpreter::power_slliq() {
ppc_grab_regssa(ppc_cur_instruction);
unsigned rot_sh = (ppc_cur_instruction >> 11) & 0x1F;
ppc_grab_regssash(ppc_cur_instruction);
uint32_t r = ((ppc_result_d << rot_sh) | (ppc_result_d >> (32 - rot_sh)));
uint32_t mask = power_rot_mask(0, 31 - rot_sh);
@ -410,14 +448,11 @@ template <field_rc rec>
void dppc_interpreter::power_sllq() {
ppc_grab_regssab(ppc_cur_instruction);
unsigned rot_sh = ppc_result_b & 0x1F;
uint32_t r = ((ppc_result_d << rot_sh) | (ppc_result_d >> (32 - rot_sh)));
uint32_t mask = power_rot_mask(0, 31 - rot_sh);
if (ppc_result_b >= 0x20) {
ppc_result_a = (ppc_state.spr[SPR::MQ] & mask);
}
else {
ppc_result_a = ((r & mask) | (ppc_state.spr[SPR::MQ] & ~mask));
if (ppc_result_b & 0x20) {
ppc_result_a = ppc_state.spr[SPR::MQ] & (-1U << rot_sh);
} else {
ppc_result_a = ((ppc_result_d << rot_sh) | (ppc_state.spr[SPR::MQ] & ((1 << rot_sh) - 1)));
}
if (rec)
@ -434,16 +469,17 @@ void dppc_interpreter::power_slq() {
ppc_grab_regssab(ppc_cur_instruction);
unsigned rot_sh = ppc_result_b & 0x1F;
if (ppc_result_b >= 0x20) {
ppc_result_a = ppc_result_d << rot_sh;
} else {
if (ppc_result_b & 0x20) {
ppc_result_a = 0;
} else {
ppc_result_a = ppc_result_d << rot_sh;
}
if (rec)
ppc_changecrf0(ppc_result_a);
ppc_state.spr[SPR::MQ] = ((ppc_result_d << rot_sh) | (ppc_result_d >> (32 - rot_sh)));
ppc_store_iresult_reg(reg_a, ppc_result_a);
}
template void dppc_interpreter::power_slq<RC0>();
@ -451,13 +487,12 @@ template void dppc_interpreter::power_slq<RC1>();
template <field_rc rec>
void dppc_interpreter::power_sraiq() {
ppc_grab_regssa(ppc_cur_instruction);
unsigned rot_sh = (ppc_cur_instruction >> 11) & 0x1F;
ppc_grab_regssash(ppc_cur_instruction);
uint32_t mask = (1 << rot_sh) - 1;
ppc_result_a = (int32_t)ppc_result_d >> rot_sh;
ppc_state.spr[SPR::MQ] = (ppc_result_d >> rot_sh) | (ppc_result_d << (32 - rot_sh));
if ((ppc_result_d & 0x80000000UL) && (ppc_result_d & mask)) {
if ((int32_t(ppc_result_d) < 0) && (ppc_result_d & mask)) {
ppc_state.spr[SPR::XER] |= XER::CA;
} else {
ppc_state.spr[SPR::XER] &= ~XER::CA;
@ -476,11 +511,11 @@ template <field_rc rec>
void dppc_interpreter::power_sraq() {
ppc_grab_regssab(ppc_cur_instruction);
unsigned rot_sh = ppc_result_b & 0x1F;
uint32_t mask = (1 << rot_sh) - 1;
ppc_result_a = (int32_t)ppc_result_d >> rot_sh;
uint32_t mask = (ppc_result_b & 0x20) ? -1 : (1 << rot_sh) - 1;
ppc_result_a = (int32_t)ppc_result_d >> ((ppc_result_b & 0x20) ? 31 : rot_sh);
ppc_state.spr[SPR::MQ] = ((ppc_result_d << rot_sh) | (ppc_result_d >> (32 - rot_sh)));
if ((ppc_result_d & 0x80000000UL) && (ppc_result_d & mask)) {
if ((int32_t(ppc_result_d) < 0) && (ppc_result_d & mask)) {
ppc_state.spr[SPR::XER] |= XER::CA;
} else {
ppc_state.spr[SPR::XER] &= ~XER::CA;
@ -520,9 +555,10 @@ void dppc_interpreter::power_srea() {
ppc_grab_regssab(ppc_cur_instruction);
unsigned rot_sh = ppc_result_b & 0x1F;
ppc_result_a = (int32_t)ppc_result_d >> rot_sh;
ppc_state.spr[SPR::MQ] = ((ppc_result_d << rot_sh) | (ppc_result_d >> (32 - rot_sh)));
uint32_t r = ((ppc_result_d >> rot_sh) | (ppc_result_d << (32 - rot_sh)));
uint32_t mask = -1U >> rot_sh;
if ((ppc_result_d & 0x80000000UL) && (ppc_result_d & rot_sh)) {
if ((int32_t(ppc_result_d) < 0) && (r & ~mask)) {
ppc_state.spr[SPR::XER] |= XER::CA;
} else {
ppc_state.spr[SPR::XER] &= ~XER::CA;
@ -532,6 +568,7 @@ void dppc_interpreter::power_srea() {
ppc_changecrf0(ppc_result_a);
ppc_store_iresult_reg(reg_a, ppc_result_a);
ppc_state.spr[SPR::MQ] = r;
}
template void dppc_interpreter::power_srea<RC0>();
@ -541,10 +578,10 @@ template <field_rc rec>
void dppc_interpreter::power_sreq() {
ppc_grab_regssab(ppc_cur_instruction);
unsigned rot_sh = ppc_result_b & 0x1F;
unsigned mask = power_rot_mask(rot_sh, 31);
uint32_t mask = -1U >> rot_sh;
ppc_result_a = ((rot_sh & mask) | (ppc_state.spr[SPR::MQ] & ~mask));
ppc_state.spr[SPR::MQ] = rot_sh;
ppc_result_a = (ppc_result_d >> rot_sh) | (ppc_state.spr[SPR::MQ] & ~mask);
ppc_state.spr[SPR::MQ] = (ppc_result_d >> rot_sh) | (ppc_result_d << (32 - rot_sh));
if (rec)
ppc_changecrf0(ppc_result_a);
@ -557,8 +594,7 @@ template void dppc_interpreter::power_sreq<RC1>();
template <field_rc rec>
void dppc_interpreter::power_sriq() {
ppc_grab_regssa(ppc_cur_instruction);
unsigned rot_sh = (ppc_cur_instruction >> 11) & 0x1F;
ppc_grab_regssash(ppc_cur_instruction);
ppc_result_a = ppc_result_d >> rot_sh;
ppc_state.spr[SPR::MQ] = (ppc_result_d >> rot_sh) | (ppc_result_d << (32 - rot_sh));
@ -573,8 +609,7 @@ template void dppc_interpreter::power_sriq<RC1>();
template <field_rc rec>
void dppc_interpreter::power_srliq() {
ppc_grab_regssa(ppc_cur_instruction);
unsigned rot_sh = (ppc_cur_instruction >> 11) & 0x1F;
ppc_grab_regssash(ppc_cur_instruction);
uint32_t r = (ppc_result_d >> rot_sh) | (ppc_result_d << (32 - rot_sh));
unsigned mask = power_rot_mask(rot_sh, 31);
@ -597,10 +632,9 @@ void dppc_interpreter::power_srlq() {
uint32_t r = (ppc_result_d >> rot_sh) | (ppc_result_d << (32 - rot_sh));
unsigned mask = power_rot_mask(rot_sh, 31);
if (ppc_result_b >= 0x20) {
if (ppc_result_b & 0x20) {
ppc_result_a = (ppc_state.spr[SPR::MQ] & mask);
}
else {
} else {
ppc_result_a = ((r & mask) | (ppc_state.spr[SPR::MQ] & ~mask));
}
@ -618,7 +652,7 @@ void dppc_interpreter::power_srq() {
ppc_grab_regssab(ppc_cur_instruction);
unsigned rot_sh = ppc_result_b & 0x1F;
if (ppc_result_b >= 0x20) {
if (ppc_result_b & 0x20) {
ppc_result_a = 0;
} else {
ppc_result_a = ppc_result_d >> rot_sh;

View File

@ -85,17 +85,18 @@ extern SetPRS ppc_state;
/** symbolic names for frequently used SPRs */
enum SPR : int {
MQ = 0,
MQ = 0, // MQ (601)
XER = 1,
RTCU_U = 4, // user RTCU
RTCL_U = 5, // user RTCL
RTCU_U = 4, // user mode RTCU (601)
RTCL_U = 5, // user mode RTCL (601)
DEC_U = 6, // user mode decrementer (601)
LR = 8,
CTR = 9,
DSISR = 18,
DAR = 19,
RTCU_S = 20, // supervisor RTCU
RTCL_S = 21, // supervisor RTCL
DEC = 22, // decrementer
RTCU_S = 20, // supervisor RTCU (601)
RTCL_S = 21, // supervisor RTCL (601)
DEC_S = 22, // supervisor decrementer
SDR1 = 25,
SRR0 = 26,
SRR1 = 27,
@ -398,7 +399,7 @@ typedef enum {
} field_601;
// Function prototypes
extern void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, uint64_t tb_freq);
extern void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, bool include_601, uint64_t tb_freq);
extern void ppc_mmu_init();
void ppc_illegalop();
@ -414,7 +415,7 @@ void ppc_opcode31();
void ppc_opcode59();
void ppc_opcode63();
void initialize_ppc_opcode_tables();
void initialize_ppc_opcode_tables(bool include_601);
extern double fp_return_double(uint32_t reg);
extern uint64_t fp_return_uint64(uint32_t reg);

View File

@ -586,13 +586,13 @@ do { \
#define OP63d(subopcode, fn) OPXd(SubOpcode63, subopcode, fn)
#define OP63dc(subopcode, fn, carry) OPXdc(SubOpcode63, subopcode, fn, carry)
void initialize_ppc_opcode_tables() {
void initialize_ppc_opcode_tables(bool include_601) {
std::fill_n(OpcodeGrabber, 64, ppc_illegalop);
OP(3, ppc_twi);
//OP(4, ppc_opcode4); - Altivec instructions not emulated yet. Uncomment once they're implemented.
OP(7, ppc_mulli);
OP(8, ppc_subfic);
if (is_601) OP(9, power_dozi);
if (is_601 || include_601) OP(9, power_dozi);
OP(10, ppc_cmpli);
OP(11, ppc_cmpi);
OP(12, ppc_addic<RC0>);
@ -605,7 +605,7 @@ void initialize_ppc_opcode_tables() {
if (is_601) OP(19, ppc_opcode19<IS601>); else OP(19, ppc_opcode19<NOT601>);
OP(20, ppc_rlwimi);
OP(21, ppc_rlwinm);
if (is_601) OP(22, power_rlmi);
if (is_601 || include_601) OP(22, power_rlmi);
OP(23, ppc_rlwnm);
OP(24, ppc_ori<SHFT0>);
OP(25, ppc_ori<SHFT1>);
@ -741,7 +741,7 @@ void initialize_ppc_opcode_tables() {
OP31(470, ppc_dcbi);
OP31(1014, ppc_dcbz);
if (is_601) {
if (is_601 || include_601) {
OP31d(29, power_maskg);
OP31od(107, power_mul);
OP31d(152, power_slq);
@ -822,7 +822,7 @@ void initialize_ppc_opcode_tables() {
}
}
void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, uint64_t tb_freq)
void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, bool include_601, uint64_t tb_freq)
{
int i;
@ -834,7 +834,7 @@ void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, uint64_t tb_freq)
ppc_state.spr[SPR::PVR] = cpu_version;
is_601 = (cpu_version >> 16) == 1;
initialize_ppc_opcode_tables();
initialize_ppc_opcode_tables(include_601);
// initialize emulator timers
TimerManager::get_instance()->set_time_now_cb(&get_virt_time_ns);
@ -864,8 +864,8 @@ void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, uint64_t tb_freq)
/* MPC601 sets MSR[ME] bit during hard reset / Power-On */
ppc_state.msr = (MSR::ME + MSR::IP);
} else {
ppc_state.msr = MSR::IP;
ppc_state.spr[SPR::DEC] = 0xFFFFFFFFUL;
ppc_state.msr = MSR::IP;
ppc_state.spr[SPR::DEC_S] = 0xFFFFFFFFUL;
}
ppc_mmu_init();
@ -885,10 +885,10 @@ void print_fprs() {
}
static map<string, int> SPRName2Num = {
{"XER", SPR::XER}, {"LR", SPR::LR}, {"CTR", SPR::CTR},
{"DEC", SPR::DEC}, {"PVR", SPR::PVR}, {"SPRG0", SPR::SPRG0},
{"SPRG1", SPR::SPRG1}, {"SPRG2", SPR::SPRG2}, {"SPRG3", SPR::SPRG3},
{"SRR0", SPR::SRR0}, {"SRR1", SPR::SRR1}, {"IBAT0U", 528},
{"XER", SPR::XER}, {"LR", SPR::LR}, {"CTR", SPR::CTR},
{"DEC", SPR::DEC_S}, {"PVR", SPR::PVR}, {"SPRG0", SPR::SPRG0},
{"SPRG1", SPR::SPRG1}, {"SPRG2", SPR::SPRG2}, {"SPRG3", SPR::SPRG3},
{"SRR0", SPR::SRR0}, {"SRR1", SPR::SRR1}, {"IBAT0U", 528},
{"IBAT0L", 529}, {"IBAT1U", 530}, {"IBAT1L", 531},
{"IBAT2U", 532}, {"IBAT2L", 533}, {"IBAT3U", 534},
{"IBAT3L", 535}, {"DBAT0U", 536}, {"DBAT0L", 537},
@ -897,9 +897,9 @@ static map<string, int> SPRName2Num = {
{"HID0", SPR::HID0}, {"HID1", SPR::HID1}, {"IABR", 1010},
{"DABR", 1013}, {"L2CR", 1017}, {"ICTC", 1019},
{"THRM1", 1020}, {"THRM2", 1021}, {"THRM3", 1022},
{"PIR", 1023}, {"TBL", SPR::TBL_U}, {"TBU", SPR::TBU_U},
{"SDR1", SPR::SDR1}, {"MQ", SPR::MQ}, {"RTCU", SPR::RTCU_U},
{"RTCL", SPR::RTCL_U}, {"DSISR", SPR::DSISR}, {"DAR", SPR::DAR},
{"PIR", 1023}, {"TBL", SPR::TBL_S}, {"TBU", SPR::TBU_S},
{"SDR1", SPR::SDR1}, {"MQ", SPR::MQ}, {"RTCU", SPR::RTCU_S},
{"RTCL", SPR::RTCL_S}, {"DSISR", SPR::DSISR}, {"DAR", SPR::DAR},
{"MMCR0", SPR::MMCR0}, {"PMC1", SPR::PMC1}, {"PMC2", SPR::PMC2},
{"SDA", SPR::SDA}, {"SIA", SPR::SIA}, {"MMCR1", SPR::MMCR1}
};
@ -974,6 +974,13 @@ uint64_t reg_op(string& reg_name, uint64_t val, bool is_write) {
reg_num_str = reg_name_u.substr(3);
reg_num = (unsigned)stoul(reg_num_str, NULL, 0);
if (reg_num < 1024) {
switch (reg_num) {
case SPR::DEC_U : reg_num = SPR::DEC_S ; break;
case SPR::RTCL_U : reg_num = SPR::RTCL_S ; break;
case SPR::RTCU_U : reg_num = SPR::RTCU_S ; break;
case SPR::TBL_U : reg_num = SPR::TBL_S ; break;
case SPR::TBU_U : reg_num = SPR::TBU_S ; break;
}
if (is_write)
ppc_state.spr[reg_num] = (uint32_t)val;
return ppc_state.spr[reg_num];

View File

@ -74,6 +74,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
uint32_t ppc_result_d = ppc_state.gpr[reg_s]; \
uint32_t ppc_result_a = ppc_state.gpr[reg_a];
#define ppc_grab_regssash(opcode) \
uint32_t reg_s = (opcode >> 21) & 31; \
uint32_t reg_a = (opcode >> 16) & 31; \
uint32_t rot_sh = (opcode >> 11) & 31; \
uint32_t ppc_result_d = ppc_state.gpr[reg_s]; \
uint32_t ppc_result_a = ppc_state.gpr[reg_a];
#define ppc_grab_regssb(opcode) \
uint32_t reg_s = (opcode >> 21) & 31; \
@ -169,4 +175,4 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
double val_reg_b = GET_FPR(reg_b); \
double val_reg_c = GET_FPR(reg_c);
#endif // PPC_MACROS_H
#endif // PPC_MACROS_H

View File

@ -33,20 +33,17 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
// Affects CR Field 0 - For integer operations
void ppc_changecrf0(uint32_t set_result) {
ppc_state.cr &= 0x0FFFFFFFUL;
if (set_result == 0) {
ppc_state.cr |= 0x20000000UL;
} else {
if (set_result & 0x80000000) {
ppc_state.cr |= 0x80000000UL;
} else {
ppc_state.cr |= 0x40000000UL;
}
}
/* copy XER[SO] into CR0[SO]. */
ppc_state.cr |= (ppc_state.spr[SPR::XER] >> 3) & 0x10000000UL;
ppc_state.cr =
(ppc_state.cr & 0x0FFFFFFFU) // clear CR0
| (
(set_result == 0) ?
CRx_bit::CR_EQ
: (int32_t(set_result) < 0) ?
CRx_bit::CR_LT
:
CRx_bit::CR_GT
)
| ((ppc_state.spr[SPR::XER] & XER::SO) >> 3); // copy XER[SO] into CR0[SO].
}
// Affects the XER register's Carry Bit
@ -69,7 +66,7 @@ inline void ppc_carry_sub(uint32_t a, uint32_t b) {
// Affects the XER register's SO and OV Bits
inline void ppc_setsoov(uint32_t a, uint32_t b, uint32_t d) {
if ((a ^ b) & (a ^ d) & 0x80000000UL) {
if (int32_t((a ^ b) & (a ^ d)) < 0) {
ppc_state.spr[SPR::XER] |= XER::SO | XER::OV;
} else {
ppc_state.spr[SPR::XER] &= ~XER::OV;
@ -533,7 +530,7 @@ void dppc_interpreter::ppc_divw() {
if (!ppc_result_b) { // handle the "anything / 0" case
ppc_result_d = 0; // tested on G4 in Mac OS X 10.4 and Open Firmware.
// ppc_result_d = (ppc_result_a & 0x80000000) ? -1 : 0; /* UNDOCUMENTED! */
// ppc_result_d = (int32_t(ppc_result_a) < 0) ? -1 : 0; /* UNDOCUMENTED! */
if (ov)
ppc_state.spr[SPR::XER] |= XER::SO | XER::OV;
@ -632,7 +629,7 @@ void dppc_interpreter::ppc_sraw() {
} else {
uint32_t shift = ppc_result_b & 0x1F;
ppc_result_a = int32_t(ppc_result_d) >> shift;
if ((ppc_result_d & 0x80000000UL) && (ppc_result_d & ((1U << shift) - 1)))
if ((int32_t(ppc_result_d) < 0) && (ppc_result_d & ((1U << shift) - 1)))
ppc_state.spr[SPR::XER] |= XER::CA;
}
@ -647,16 +644,15 @@ template void dppc_interpreter::ppc_sraw<RC1>();
template <field_rc rec>
void dppc_interpreter::ppc_srawi() {
ppc_grab_regssa(ppc_cur_instruction);
uint32_t shift = (ppc_cur_instruction >> 11) & 0x1F;
ppc_grab_regssash(ppc_cur_instruction);
// clear XER[CA] by default
ppc_state.spr[SPR::XER] &= ~XER::CA;
if ((ppc_result_d & 0x80000000UL) && (ppc_result_d & ((1U << shift) - 1)))
if ((int32_t(ppc_result_d) < 0) && (ppc_result_d & ((1U << rot_sh) - 1)))
ppc_state.spr[SPR::XER] |= XER::CA;
ppc_result_a = int32_t(ppc_result_d) >> shift;
ppc_result_a = int32_t(ppc_result_d) >> rot_sh;
if (rec)
ppc_changecrf0(ppc_result_a);
@ -675,8 +671,7 @@ static inline uint32_t rot_mask(unsigned rot_mb, unsigned rot_me) {
}
void dppc_interpreter::ppc_rlwimi() {
ppc_grab_regssa(ppc_cur_instruction);
unsigned rot_sh = (ppc_cur_instruction >> 11) & 0x1F;
ppc_grab_regssash(ppc_cur_instruction);
unsigned rot_mb = (ppc_cur_instruction >> 6) & 0x1F;
unsigned rot_me = (ppc_cur_instruction >> 1) & 0x1F;
uint32_t mask = rot_mask(rot_mb, rot_me);
@ -690,8 +685,7 @@ void dppc_interpreter::ppc_rlwimi() {
}
void dppc_interpreter::ppc_rlwinm() {
ppc_grab_regssa(ppc_cur_instruction);
unsigned rot_sh = (ppc_cur_instruction >> 11) & 0x1F;
ppc_grab_regssash(ppc_cur_instruction);
unsigned rot_mb = (ppc_cur_instruction >> 6) & 0x1F;
unsigned rot_me = (ppc_cur_instruction >> 1) & 0x1F;
uint32_t mask = rot_mask(rot_mb, rot_me);
@ -891,74 +885,118 @@ static void update_decrementer(uint32_t val) {
}
void dppc_interpreter::ppc_mfspr() {
uint32_t ref_spr = (((ppc_cur_instruction >> 11) & 0x1F) << 5) |
((ppc_cur_instruction >> 16) & 0x1F);
ppc_grab_dab(ppc_cur_instruction);
uint32_t ref_spr = (reg_b << 5) | reg_a;
if (ref_spr & 0x10) {
#ifdef CPU_PROFILING
if (ref_spr > 31) {
num_supervisor_instrs++;
}
#endif
if (ppc_state.msr & MSR::PR) {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::NOT_ALLOWED);
}
}
switch (ref_spr) {
case SPR::MQ:
if (!is_601) {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
}
ppc_state.gpr[reg_d] = ppc_state.spr[ref_spr];
break;
case SPR::RTCL_U:
if (!is_601) {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
}
calc_rtcl_value();
ppc_state.spr[SPR::RTCL_U] = rtc_lo & 0x3FFFFF80UL;
ppc_state.gpr[reg_d] =
ppc_state.spr[SPR::RTCL_S] = rtc_lo & 0x3FFFFF80UL;
ppc_state.spr[SPR::RTCU_S] = rtc_hi;
break;
case SPR::RTCU_U:
if (!is_601) {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
}
calc_rtcl_value();
ppc_state.spr[SPR::RTCU_U] = rtc_hi;
ppc_state.gpr[reg_d] =
ppc_state.spr[SPR::RTCU_S] = rtc_hi;
ppc_state.spr[SPR::RTCL_S] = rtc_lo;
break;
case SPR::DEC:
ppc_state.spr[SPR::DEC] = calc_dec_value();
case SPR::DEC_U:
if (!is_601) {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
}
// fallthrough
case SPR::DEC_S:
ppc_state.gpr[reg_d] = ppc_state.spr[SPR::DEC_S] = calc_dec_value();
break;
default:
// FIXME: Unknown SPR should be noop or illegal instruction.
ppc_state.gpr[reg_d] = ppc_state.spr[ref_spr];
}
ppc_state.gpr[(ppc_cur_instruction >> 21) & 0x1F] = ppc_state.spr[ref_spr];
}
void dppc_interpreter::ppc_mtspr() {
uint32_t ref_spr = (((ppc_cur_instruction >> 11) & 0x1F) << 5) |
((ppc_cur_instruction >> 16) & 0x1F);
ppc_grab_dab(ppc_cur_instruction);
uint32_t ref_spr = (reg_b << 5) | reg_a;
if (ref_spr & 0x10) {
#ifdef CPU_PROFILING
if (ref_spr > 31) {
num_supervisor_instrs++;
}
#endif
if (ref_spr == SPR::PVR || (
ref_spr == SPR::MQ && !is_601
)) { // prevent writes to the read-only registers
return;
if (ppc_state.msr & MSR::PR) {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::NOT_ALLOWED);
}
}
uint32_t val = ppc_state.gpr[(ppc_cur_instruction >> 21) & 0x1F];
ppc_state.spr[ref_spr] = val;
uint32_t val = ppc_state.gpr[reg_d];
switch (ref_spr) {
case SPR::MQ:
if (!is_601) {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
}
ppc_state.spr[ref_spr] = val;
break;
case SPR::RTCL_U:
case SPR::RTCU_U:
case SPR::DEC_U:
if (!is_601) {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
}
break;
case SPR::XER:
ppc_state.spr[ref_spr] = val & 0xe000ff7f;
break;
case SPR::SDR1:
ppc_state.spr[ref_spr] = val;
mmu_pat_ctx_changed(); // adapt to SDR1 changes
break;
case SPR::RTCL_S:
calc_rtcl_value();
rtc_lo = val & 0x3FFFFF80UL;
ppc_state.spr[RTCL_S] = rtc_lo = val & 0x3FFFFF80UL;
ppc_state.spr[RTCU_S] = rtc_hi;
break;
case SPR::RTCU_S:
calc_rtcl_value();
rtc_hi = val;
ppc_state.spr[RTCL_S] = rtc_lo;
ppc_state.spr[RTCU_S] = rtc_hi = val;
break;
case SPR::DEC:
case SPR::DEC_S:
ppc_state.spr[DEC_S] = val;
update_decrementer(val);
break;
case SPR::TBL_S:
update_timebase(0xFFFFFFFF00000000ULL, val);
ppc_state.spr[TBL_S] = val;
ppc_state.spr[TBU_S] = tbr_wr_value >> 32;
break;
case SPR::TBU_S:
update_timebase(0x00000000FFFFFFFFULL, uint64_t(val) << 32);
ppc_state.spr[TBL_S] = (uint32_t)tbr_wr_value;
ppc_state.spr[TBU_S] = val;
break;
case SPR::PVR:
break;
case 528:
case 529:
@ -968,6 +1006,7 @@ void dppc_interpreter::ppc_mtspr() {
case 533:
case 534:
case 535:
ppc_state.spr[ref_spr] = val;
ibat_update(ref_spr);
break;
case 536:
@ -978,23 +1017,30 @@ void dppc_interpreter::ppc_mtspr() {
case 541:
case 542:
case 543:
ppc_state.spr[ref_spr] = val;
dbat_update(ref_spr);
default:
// FIXME: Unknown SPR should be noop or illegal instruction.
ppc_state.spr[ref_spr] = val;
}
}
void dppc_interpreter::ppc_mftb() {
uint32_t ref_spr = (((ppc_cur_instruction >> 11) & 0x1F) << 5) |
((ppc_cur_instruction >> 16) & 0x1F);
int reg_d = (ppc_cur_instruction >> 21) & 0x1F;
ppc_grab_dab(ppc_cur_instruction);
uint32_t ref_spr = (reg_b << 5) | reg_a;
uint64_t tbr_value = calc_tbr_value();
switch (ref_spr) {
case SPR::TBL_U:
ppc_state.gpr[reg_d] = uint32_t(tbr_value);
ppc_state.gpr[reg_d] =
ppc_state.spr[TBL_S] = uint32_t(tbr_value);
ppc_state.spr[TBU_S] = uint32_t(tbr_value >> 32);
break;
case SPR::TBU_U:
ppc_state.gpr[reg_d] = uint32_t(tbr_value >> 32);
ppc_state.gpr[reg_d] =
ppc_state.spr[TBU_S] = uint32_t(tbr_value >> 32);
ppc_state.spr[TBL_S] = uint32_t(tbr_value);
break;
default:
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
@ -1820,10 +1866,9 @@ void dppc_interpreter::ppc_stswi() {
#ifdef CPU_PROFILING
num_int_stores++;
#endif
ppc_grab_regssa(ppc_cur_instruction);
ppc_grab_regssash(ppc_cur_instruction);
ppc_effective_address = reg_a ? ppc_result_a : 0;
uint32_t grab_inb = (ppc_cur_instruction >> 11) & 0x1F;
grab_inb = grab_inb ? grab_inb : 32;
uint32_t grab_inb = rot_sh ? rot_sh : 32;
while (grab_inb >= 4) {
mmu_write_vmem<uint32_t>(ppc_effective_address, ppc_state.gpr[reg_s]);

View File

@ -315,7 +315,7 @@ static void read_test_float_data() {
}
int main() {
initialize_ppc_opcode_tables(); //kludge
initialize_ppc_opcode_tables(true); //kludge
cout << "Running DingusPPC emulator tests..." << endl << endl;

View File

@ -586,6 +586,9 @@ void ControlVideo::enable_display()
// set framebuffer parameters
this->fb_ptr = &this->vram_ptr[this->fb_base];
this->fb_pitch = this->row_words;
if (~this->enables & SCAN_CONTROL) {
this->fb_pitch >>= 1;
}
this->pixel_depth = this->radacal->get_pix_width();
if (swatch_params[ControlRegs::HAL-1] != swatch_params[ControlRegs::PIPE_DELAY-1] + 1 || this->pixel_depth == 32) {
@ -650,7 +653,10 @@ void ControlVideo::enable_display()
// set up periodic timer for display updates
if (this->active_width > 0 && this->active_height > 0 && this->pixel_clock > 0) {
this->refresh_rate = (double)(this->pixel_clock) / (this->hori_total * this->vert_total);
LOG_F(INFO, "Control: refresh rate set to %f Hz", this->refresh_rate);
if (~this->enables & SCAN_CONTROL) {
this->refresh_rate *= 2;
}
LOG_F(INFO, "%s: refresh rate set to %f Hz", this->name.c_str(), this->refresh_rate);
this->start_refresh_task();

View File

@ -79,7 +79,7 @@ int initialize_catalyst(std::string& id)
std::string cpu = GET_STR_PROP("cpu");
if (cpu == "601") {
// init virtual CPU and request MPC601
ppc_cpu_init(platinum_obj, PPC_VER::MPC601, 7833600ULL);
ppc_cpu_init(platinum_obj, PPC_VER::MPC601, true, 7833600ULL);
}
else if (cpu == "750") {
// configure CPU clocks
@ -87,7 +87,7 @@ int initialize_catalyst(std::string& id)
uint64_t timebase_freq = bus_freq / 4;
// initialize virtual CPU and request MPC750 CPU aka G3
ppc_cpu_init(platinum_obj, PPC_VER::MPC750, timebase_freq);
ppc_cpu_init(platinum_obj, PPC_VER::MPC750, false, timebase_freq);
// set CPU PLL ratio to 3.5
ppc_state.spr[SPR::HID1] = 0xE << 28;

View File

@ -77,7 +77,7 @@ int initialize_gazelle(std::string& id)
uint64_t timebase_freq = bus_freq / 4;
// init virtual CPU and request MPC603ev
ppc_cpu_init(psx_obj, PPC_VER::MPC603EV, timebase_freq);
ppc_cpu_init(psx_obj, PPC_VER::MPC603EV, false, timebase_freq);
// CPU frequency is hardcoded to 225 MHz for now
ppc_state.spr[SPR::HID1] = get_cpu_pll_value(225000000) << 28;

View File

@ -146,7 +146,7 @@ int initialize_gossamer(std::string& id)
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);
ppc_cpu_init(grackle_obj, PPC_VER::MPC750, false, timebase_freq);
// set CPU PLL ratio to 3.5
ppc_state.spr[SPR::HID1] = 0xE << 28;

View File

@ -90,7 +90,7 @@ int initialize_pdm(std::string& id)
}
// Init virtual CPU and request MPC601
ppc_cpu_init(hmc_obj, PPC_VER::MPC601, 7812500ULL);
ppc_cpu_init(hmc_obj, PPC_VER::MPC601, true, 7812500ULL);
return 0;
}

View File

@ -58,7 +58,7 @@ int initialize_pippin(std::string& id) {
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);
ppc_cpu_init(aspen_obj, PPC_VER::MPC603, false, 16500000ULL);
return 0;
}

View File

@ -101,16 +101,16 @@ int initialize_tnt(std::string& id)
// init virtual CPU
std::string cpu = GET_STR_PROP("cpu");
if (cpu == "604e")
ppc_cpu_init(memctrl_obj, PPC_VER::MPC604E, 12500000ULL);
ppc_cpu_init(memctrl_obj, PPC_VER::MPC604E, false, 12500000ULL);
else if (cpu == "601")
ppc_cpu_init(memctrl_obj, PPC_VER::MPC601, 7833600ULL);
ppc_cpu_init(memctrl_obj, PPC_VER::MPC601, true, 7833600ULL);
else if (cpu == "750") {
// configure CPU clocks
uint64_t bus_freq = 50000000ULL;
uint64_t timebase_freq = bus_freq / 4;
// initialize virtual CPU and request MPC750 CPU aka G3
ppc_cpu_init(memctrl_obj, PPC_VER::MPC750, timebase_freq);
ppc_cpu_init(memctrl_obj, PPC_VER::MPC750, false, timebase_freq);
// set CPU PLL ratio to 3.5
ppc_state.spr[SPR::HID1] = 0xE << 28;

View File

@ -76,7 +76,7 @@ int initialize_yosemite(std::string& id)
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);
ppc_cpu_init(grackle_obj, PPC_VER::MPC750, false, timebase_freq);
// set CPU PLL ratio to 3.5
ppc_state.spr[SPR::HID1] = 0xE << 28;

View File

@ -31,110 +31,127 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <loguru.hpp>
/* read an aligned big-endian WORD (16bit) */
#define READ_WORD_BE_A(addr) (BYTESWAP_16(*((uint16_t*)((addr)))))
#define READ_WORD_BE_A( addr) (BYTESWAP_16(*((uint16_t*)(addr))))
/* read an aligned big-endian DWORD (32bit) */
#define READ_DWORD_BE_A(addr) (BYTESWAP_32(*((uint32_t*)((addr)))))
#define READ_DWORD_BE_A(addr) (BYTESWAP_32(*((uint32_t*)(addr))))
/* read an aligned big-endian QWORD (64bit) */
#define READ_QWORD_BE_A(addr) (BYTESWAP_64(*((uint64_t*)((addr)))))
#define READ_QWORD_BE_A(addr) (BYTESWAP_64(*((uint64_t*)(addr))))
/* read an aligned little-endian WORD (16bit) */
#define READ_WORD_LE_A(addr) (*(uint16_t*)((addr)))
#define READ_WORD_LE_A( addr) (*(uint16_t*)(addr))
/* read an aligned little-endian DWORD (32bit) */
#define READ_DWORD_LE_A(addr) (*(uint32_t*)((addr)))
#define READ_DWORD_LE_A(addr) (*(uint32_t*)(addr))
/* read an aligned little-endian QWORD (64bit) */
#define READ_QWORD_LE_A(addr) (*(uint64_t*)((addr)))
#define READ_QWORD_LE_A(addr) (*(uint64_t*)(addr))
/* read an unaligned big-endian WORD (16bit) */
#define READ_WORD_BE_U(addr) (((addr)[0] << 8) | (addr)[1])
#define READ_WORD_BE_U( addr) ((((uint8_t*)(addr))[0] << 8) | ((uint8_t*)(addr))[1])
/* read an unaligned big-endian DWORD (32bit) */
#define READ_DWORD_BE_U(addr) \
(((addr)[0] << 24) | ((addr)[1] << 16) | ((addr)[2] << 8) | (addr)[3])
((((uint8_t*)(addr))[0] << 24) | (((uint8_t*)(addr))[1] << 16) | \
(((uint8_t*)(addr))[2] << 8) | ((uint8_t*)(addr))[3] )
/* read an unaligned big-endian QWORD (32bit) */
#define READ_QWORD_BE_U(addr) \
((uint64_t((addr)[0]) << 56) | (uint64_t((addr)[1]) << 48) | \
(uint64_t((addr)[2]) << 40) | (uint64_t((addr)[3]) << 32) | \
(uint64_t((addr)[4]) << 24) | ((addr)[5] << 16) | ((addr)[6] << 8) | (addr)[7])
((uint64_t(((uint8_t*)(addr))[0]) << 56) | (uint64_t(((uint8_t*)(addr))[1]) << 48) | \
(uint64_t(((uint8_t*)(addr))[2]) << 40) | (uint64_t(((uint8_t*)(addr))[3]) << 32) | \
(uint64_t(((uint8_t*)(addr))[4]) << 24) | ( ((uint8_t*)(addr))[5] << 16) | \
( ((uint8_t*)(addr))[6] << 8) | ((uint8_t*)(addr))[7] )
/* read an unaligned little-endian WORD (16bit) */
#define READ_WORD_LE_U(addr) (((addr)[1] << 8) | (addr)[0])
#define READ_WORD_LE_U( addr) ((((uint8_t*)(addr))[1] << 8) | ((uint8_t*)(addr))[0])
/* read an unaligned little-endian DWORD (32bit) */
#define READ_DWORD_LE_U(addr) \
(((addr)[3] << 24) | ((addr)[2] << 16) | ((addr)[1] << 8) | (addr)[0])
((((uint8_t*)(addr))[3] << 24) | (((uint8_t*)(addr))[2] << 16) | \
(((uint8_t*)(addr))[1] << 8) | ((uint8_t*)(addr))[0] )
/* read an unaligned little-endian DWORD (64bit) */
#define READ_QWORD_LE_U(addr) \
((uint64_t((addr)[7]) << 56) | (uint64_t((addr)[6]) << 48) | \
(uint64_t((addr)[5]) << 40) | (uint64_t((addr)[4]) << 32) | \
(uint64_t((addr)[3]) << 24) | ((addr)[2] << 16) | ((addr)[1] << 8) | (addr)[0])
((uint64_t(((uint8_t*)(addr))[7]) << 56) | (uint64_t(((uint8_t*)(addr))[6]) << 48) | \
(uint64_t(((uint8_t*)(addr))[5]) << 40) | (uint64_t(((uint8_t*)(addr))[4]) << 32) | \
(uint64_t(((uint8_t*)(addr))[3]) << 24) | ( ((uint8_t*)(addr))[2] << 16) | \
( ((uint8_t*)(addr))[1] << 8) | ((uint8_t*)(addr))[0] )
/* write an aligned big-endian WORD (16bit) */
#define WRITE_WORD_BE_A(addr, val) (*((uint16_t*)((addr))) = BYTESWAP_16(val))
#define WRITE_WORD_BE_A( addr, val) (*((uint16_t*)(addr)) = BYTESWAP_16(val))
/* write an aligned big-endian DWORD (32bit) */
#define WRITE_DWORD_BE_A(addr, val) (*((uint32_t*)((addr))) = BYTESWAP_32(val))
#define WRITE_DWORD_BE_A(addr, val) (*((uint32_t*)(addr)) = BYTESWAP_32(val))
/* write an aligned big-endian QWORD (64bit) */
#define WRITE_QWORD_BE_A(addr, val) (*((uint64_t*)((addr))) = BYTESWAP_64(val))
#define WRITE_QWORD_BE_A(addr, val) (*((uint64_t*)(addr)) = BYTESWAP_64(val))
/* write an unaligned big-endian WORD (16bit) */
#define WRITE_WORD_BE_U(addr, val) \
do { \
(addr)[0] = ((val) >> 8) & 0xFF; \
(addr)[1] = (val) & 0xFF; \
((uint8_t*)(addr))[0] = ((val) >> 8); \
((uint8_t*)(addr))[1] = (uint8_t)(val); \
} while (0)
/* write an unaligned big-endian DWORD (32bit) */
#define WRITE_DWORD_BE_U(addr, val) \
do { \
(addr)[0] = ((val) >> 24) & 0xFF; \
(addr)[1] = ((val) >> 16) & 0xFF; \
(addr)[2] = ((val) >> 8) & 0xFF; \
(addr)[3] = (val) & 0xFF; \
((uint8_t*)(addr))[0] = ((val) >> 24); \
((uint8_t*)(addr))[1] = ((val) >> 16); \
((uint8_t*)(addr))[2] = ((val) >> 8); \
((uint8_t*)(addr))[3] = (uint8_t)(val); \
} while (0)
/* write an unaligned big-endian DWORD (64bit) */
#define WRITE_QWORD_BE_U(addr, val) \
do { \
(addr)[0] = ((uint64_t)(val) >> 56) & 0xFF; \
(addr)[1] = ((uint64_t)(val) >> 48) & 0xFF; \
(addr)[2] = ((uint64_t)(val) >> 40) & 0xFF; \
(addr)[3] = ((uint64_t)(val) >> 32) & 0xFF; \
(addr)[4] = ((uint64_t)(val) >> 24) & 0xFF; \
(addr)[5] = ((val) >> 16) & 0xFF; \
(addr)[6] = ((val) >> 8) & 0xFF; \
(addr)[7] = (val) & 0xFF; \
((uint8_t*)(addr))[0] = ((uint64_t)(val) >> 56); \
((uint8_t*)(addr))[1] = ((uint64_t)(val) >> 48); \
((uint8_t*)(addr))[2] = ((uint64_t)(val) >> 40); \
((uint8_t*)(addr))[3] = ((uint64_t)(val) >> 32); \
((uint8_t*)(addr))[4] = ( (val) >> 24); \
((uint8_t*)(addr))[5] = ( (val) >> 16); \
((uint8_t*)(addr))[6] = ( (val) >> 8); \
((uint8_t*)(addr))[7] = (uint8_t)(val) ; \
} while (0)
/* write an aligned little-endian WORD (16bit) */
#define WRITE_WORD_LE_A(addr, val) (*((uint16_t*)((addr))) = (val))
#define WRITE_WORD_LE_A( addr, val) (*((uint16_t*)(addr)) = (val))
/* write an aligned little-endian DWORD (32bit) */
#define WRITE_DWORD_LE_A(addr, val) (*((uint32_t*)((addr))) = (val))
#define WRITE_DWORD_LE_A(addr, val) (*((uint32_t*)(addr)) = (val))
/* write an aligned little-endian QWORD (64bit) */
#define WRITE_QWORD_LE_A(addr, val) (*((uint64_t*)((addr))) = (val))
#define WRITE_QWORD_LE_A(addr, val) (*((uint64_t*)(addr)) = (val))
/* write an unaligned little-endian WORD (16bit) */
#define WRITE_WORD_LE_U(addr, val) \
do { \
(addr)[0] = (val)&0xFF; \
(addr)[1] = ((val) >> 8) & 0xFF; \
((uint8_t*)(addr))[0] = (uint8_t)(val); \
((uint8_t*)(addr))[1] = ((val) >> 8); \
} while (0)
/* write an unaligned little-endian DWORD (32bit) */
#define WRITE_DWORD_LE_U(addr, val) \
do { \
(addr)[0] = (val)&0xFF; \
(addr)[1] = ((val) >> 8) & 0xFF; \
(addr)[2] = ((val) >> 16) & 0xFF; \
(addr)[3] = ((val) >> 24) & 0xFF; \
((uint8_t*)(addr))[0] = (uint8_t)(val); \
((uint8_t*)(addr))[1] = ((val) >> 8); \
((uint8_t*)(addr))[2] = ((val) >> 16); \
((uint8_t*)(addr))[3] = ((val) >> 24); \
} while (0)
/* write an unaligned little-endian DWORD (64bit) */
#define WRITE_QWORD_LE_U(addr, val) \
do { \
((uint8_t*)(addr))[0] = (uint8_t)(val) ; \
((uint8_t*)(addr))[1] = ( (val) >> 8); \
((uint8_t*)(addr))[2] = ( (val) >> 16); \
((uint8_t*)(addr))[3] = ( (val) >> 24); \
((uint8_t*)(addr))[4] = ((uint64_t)(val) >> 32); \
((uint8_t*)(addr))[5] = ((uint64_t)(val) >> 40); \
((uint8_t*)(addr))[6] = ((uint64_t)(val) >> 48); \
((uint8_t*)(addr))[7] = ((uint64_t)(val) >> 56); \
} while (0)
/* read value of the specified size from memory starting at addr,