Skip to content

Commit 1b54e2d

Browse files
committed
cpu/powerpc: More support for the 601's POWER/PPC dual nature, including several POWER instructions. [R. Belmont]
apple/macpdm.cpp: Implemented audio DMA IRQs and some minor cleanup. [R. Belmont]
1 parent 8a17e0e commit 1b54e2d

File tree

6 files changed

+381
-60
lines changed

6 files changed

+381
-60
lines changed

src/devices/cpu/powerpc/ppc.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,18 @@ enum
7979
PPC_R30,
8080
PPC_R31,
8181
PPC_CR,
82+
PPC_MQ,
8283
PPC_LR,
8384
PPC_CTR,
8485
PPC_XER,
86+
PPC_BAT0U,
87+
PPC_BAT0L,
88+
PPC_BAT1U,
89+
PPC_BAT1L,
90+
PPC_BAT2U,
91+
PPC_BAT2L,
92+
PPC_BAT3U,
93+
PPC_BAT3L,
8594

8695
PPC_F0,
8796
PPC_F1,
@@ -151,7 +160,6 @@ enum
151160
PPC_SR15
152161
};
153162

154-
155163
/* compiler-specific options */
156164
#define PPCDRC_STRICT_VERIFY 0x0001 /* verify all instructions */
157165
#define PPCDRC_FLUSH_PC 0x0002 /* flush the PC value before each memory access */
@@ -515,6 +523,7 @@ class ppc_device : public cpu_device, public device_vtlb_interface
515523
uint32_t m_serial_clock;
516524
uint64_t m_tb_zero_cycles;
517525
uint64_t m_dec_zero_cycles;
526+
uint64_t m_rtc_zero_cycles;
518527
emu_timer * m_decrementer_int_timer;
519528

520529

src/devices/cpu/powerpc/ppccom.cpp

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ mpc8240_device::mpc8240_device(const machine_config &mconfig, const char *tag, d
281281
}
282282

283283
ppc601_device::ppc601_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
284-
: ppc_device(mconfig, PPC601, tag, owner, clock, 32, 64, PPC_MODEL_601, PPCCAP_OEA | PPCCAP_VEA | PPCCAP_FPU | PPCCAP_MISALIGNED | PPCCAP_MFIOC | PPCCAP_601BAT, 0/* no TB */, address_map_constructor())
284+
: ppc_device(mconfig, PPC601, tag, owner, clock, 32, 64, PPC_MODEL_601, PPCCAP_OEA | PPCCAP_VEA | PPCCAP_FPU | PPCCAP_MISALIGNED | PPCCAP_MFIOC | PPCCAP_601BAT | PPCCAP_LEGACY_POWER, 0 /* no TB */, address_map_constructor())
285285
{
286286
}
287287

@@ -727,6 +727,7 @@ void ppc_device::device_start()
727727
m_cpu_clock = 0;
728728
m_tb_zero_cycles = 0;
729729
m_dec_zero_cycles = 0;
730+
m_rtc_zero_cycles = 0;
730731

731732
m_arg1 = 0;
732733
m_fastram_select = 0;
@@ -837,6 +838,11 @@ void ppc_device::device_start()
837838
state_add(PPC_PC, "PC", m_core->pc).formatstr("%08X");
838839
state_add(PPC_MSR, "MSR", m_core->msr).formatstr("%08X");
839840
state_add(PPC_CR, "CR", m_debugger_temp).callimport().callexport().formatstr("%08X");
841+
// If the legacy POWER instructions exist, that implies MQ is used and should be shown
842+
if (m_cap & PPCCAP_LEGACY_POWER)
843+
{
844+
state_add(PPC_MQ, "MQ", m_core->spr[SPR601_MQ]).formatstr("%08X");
845+
}
840846
state_add(PPC_LR, "LR", m_core->spr[SPR_LR]).formatstr("%08X");
841847
state_add(PPC_CTR, "CTR", m_core->spr[SPR_CTR]).formatstr("%08X");
842848
state_add(PPC_XER, "XER", m_debugger_temp).callimport().callexport().formatstr("%08X");
@@ -1301,8 +1307,6 @@ uint32_t ppc_device::ppccom_translate_address_internal(int intention, bool debug
13011307
uint32_t lower = m_core->spr[SPROEA_IBAT0U + 2*batnum + 1];
13021308
int privbit = ((intention & TR_USER) == 0) ? 3 : 2;
13031309

1304-
// printf("bat %d upper = %08x privbit %d\n", batnum, upper, privbit);
1305-
13061310
// is this pair valid?
13071311
if (lower & 0x40)
13081312
{
@@ -1639,6 +1643,29 @@ void ppc_device::ppccom_execute_mfspr()
16391643
}
16401644
}
16411645

1646+
/* handle 601 specific SPRs (POWER holdovers) */
1647+
if (m_flavor == PPC_MODEL_601)
1648+
{
1649+
switch (m_core->param0)
1650+
{
1651+
case SPR601_PWRDEC:
1652+
m_core->param1 = get_decrementer();
1653+
return;
1654+
1655+
case SPR601_RTCUR_PWR:
1656+
m_core->param1 = (total_cycles() - m_rtc_zero_cycles) / clock();
1657+
return;
1658+
1659+
case SPR601_RTCLR_PWR:
1660+
{
1661+
const uint64_t remainder = (total_cycles() - m_rtc_zero_cycles) % clock();
1662+
const double seconds = remainder / clock(); // get fractional seconds
1663+
m_core->param1 = (uint64_t)(seconds * 1'000'000'000); // and convert to nanoseconds
1664+
}
1665+
return;
1666+
}
1667+
}
1668+
16421669
/* handle 602 SPRs */
16431670
if (m_flavor == PPC_MODEL_602)
16441671
{ // TODO: Which are read/write only?
@@ -1782,6 +1809,21 @@ void ppc_device::ppccom_execute_mtspr()
17821809
}
17831810
}
17841811

1812+
/* handle 601 specific POWER-holdover SPRs */
1813+
if (m_flavor == PPC_MODEL_601)
1814+
{
1815+
switch (m_core->param0)
1816+
{
1817+
case SPR601_MQ:
1818+
m_core->spr[m_core->param0] = m_core->param1;
1819+
return;
1820+
1821+
case SPR601_RTCUW_PWR:
1822+
m_rtc_zero_cycles = total_cycles();
1823+
break;
1824+
}
1825+
}
1826+
17851827
/* handle 602 SPRs */
17861828
if (m_flavor == PPC_MODEL_602)
17871829
{

src/devices/cpu/powerpc/ppccom.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
#define PPCCAP_601BAT 0x80 /* TRUE if we're doing 601-style BATs (unified I/D, different bit layout) */
6666
#define PPCCAP_604_MMU 0x100 /* TRUE if we have 604-class MMU features */
6767
#define PPCCAP_750_TLB 0x200 /* TRUE if we have the extended 740/750 series TLB */
68+
#define PPCCAP_LEGACY_POWER 0x400 /* TRUE if we support the legacy POWER instructions */
6869

6970
/* exception types */
7071
enum
@@ -171,6 +172,14 @@ enum
171172
SPR4XX_PBL2 = 0x3fe, /* R/W 403GA 406GA Protection Bound Lower 2 */
172173
SPR4XX_PBU2 = 0x3ff, /* R/W 403GA 406GA Protection Bound Upper 2 */
173174

175+
/* PowerPC 601 POWER back compatibility SPR indexes */
176+
SPR601_MQ = 0x000, /* R/W Muliplicand/Quotient for 601 POWER instructions */
177+
SPR601_RTCUR_PWR = 0x004, /* R Counts up number set in SPR 20 once per second, POWER only */
178+
SPR601_RTCLR_PWR = 0x005, /* R Number of nanoseconds between the seconds counted in SPR 4 */
179+
SPR601_PWRDEC = 0x006, /* R Decrementer register mirror for POWER compatibilty */
180+
SPR601_RTCUW_PWR = 0x014, /* W Seconds counter, set here and read SPR 4 */
181+
SPR601_RTCLW_PWR = 0x015, /* W Nanoseconds counter, not clear what writing here does */
182+
174183
/* PowerPC 602 SPR register indexes */
175184
SPR602_TCR = 0x3d8, /* 602 */
176185
SPR602_IBR = 0x3da, /* 602 */
@@ -198,7 +207,6 @@ enum
198207
SPR603_HID2 = 0x3f3 /* R/W 603 */
199208
};
200209

201-
202210
/* PowerPC 4XX DCR register indexes */
203211
enum
204212
{

src/devices/cpu/powerpc/ppcdrc.cpp

Lines changed: 157 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// copyright-holders:Aaron Giles
33
/***************************************************************************
44
5-
ppcdrc.c
5+
ppcdrc.cpp
66
77
Universal machine language-based PowerPC emulator.
88
@@ -1746,12 +1746,12 @@ void ppc_device::generate_sequence_instruction(drcuml_block &block, compiler_sta
17461746
UML_EXH(block, *m_tlb_mismatch, 0); // exh tlb_mismatch,0
17471747
}
17481748

1749-
/* validate our TLB entry at this PC; if we fail, we need to handle it */
1750-
if ((desc->flags & OPFLAG_VALIDATE_TLB) && (m_core->mode & MODE_DATA_TRANSLATION))
1749+
// validate our TLB entry at this PC; if we fail, we need to handle it
1750+
// TODO: this code is highly sus based on the PPC architecture manual, but I'll only disable for 601 for now
1751+
if ((desc->flags & OPFLAG_VALIDATE_TLB) && (m_core->mode & MODE_DATA_TRANSLATION) && !(m_cap & PPCCAP_601BAT))
17511752
{
17521753
const vtlb_entry *tlbtable = vtlb_table();
17531754

1754-
/* if we currently have a valid TLB read entry, we just verify */
17551755
if (tlbtable[desc->pc >> 12] != 0)
17561756
{
17571757
if (PRINTF_MMU)
@@ -2024,6 +2024,23 @@ bool ppc_device::generate_opcode(drcuml_block &block, compiler_state *compiler,
20242024
// muls rd,rd,ra,simm
20252025
return true;
20262026

2027+
case 0x9: /* DOZI (POWER) */
2028+
assert(m_cap & PPCCAP_LEGACY_POWER);
2029+
2030+
UML_AND(block, I0, op, 0xffff);
2031+
UML_CMP(block, R32(G_RA(op)), I0); // cmp ra, I0
2032+
UML_JMPc(block, COND_B, compiler->labelnum); // bae 0:
2033+
2034+
UML_XOR(block, R32(G_RD(op)), R32(G_RD(op)), R32(G_RD(op))); // xor rd, rd, rd (rd = 0)
2035+
UML_JMP(block, compiler->labelnum + 1); // jmp 1:
2036+
2037+
UML_LABEL(block, compiler->labelnum++); // 0:
2038+
UML_ADD(block, R32(G_RD(op)), R32(G_RA(op)), I0);
2039+
UML_ADD(block, R32(G_RD(op)), R32(G_RD(op)), 0x1);
2040+
2041+
UML_LABEL(block, compiler->labelnum++); // 1:
2042+
return true;
2043+
20272044
case 0x0e: /* ADDI */
20282045
UML_ADD(block, R32(G_RD(op)), R32Z(G_RA(op)), (int16_t)G_SIMM(op)); // add rd,ra,simm
20292046
return true;
@@ -2687,6 +2704,13 @@ bool ppc_device::generate_instruction_1f(drcuml_block &block, compiler_state *co
26872704
generate_compute_flags(block, desc, op & M_RC, ((op & M_OE) ? XER_OV : 0), false);// <update flags>
26882705
return true;
26892706

2707+
case 0x6b: /* MUL (POWER) */
2708+
assert(m_cap & PPCCAP_LEGACY_POWER);
2709+
2710+
UML_MULU(block, SPR32(SPR601_MQ), R32(G_RD(op)), R32(G_RA(op)), R32(G_RB(op))); // mulu mq, rd, ra, rb
2711+
generate_compute_flags(block, desc, op & M_RC, ((op & M_OE) ? XER_OV | XER_SO : 0), false); // <update flags>
2712+
return true;
2713+
26902714
case 0x1cb: /* DIVWUx */
26912715
case 0x3cb: /* DIVWUOx */
26922716
UML_CMP(block, R32(G_RB(op)), 0x0); // cmp rb, #0
@@ -2714,6 +2738,63 @@ bool ppc_device::generate_instruction_1f(drcuml_block &block, compiler_state *co
27142738
UML_LABEL(block, compiler->labelnum++); // 1:
27152739
return true;
27162740

2741+
case 0x14b: /* DIV (POWER) */
2742+
assert(m_cap & PPCCAP_LEGACY_POWER);
2743+
2744+
UML_SHL(block, I0, R32(G_RB(op)), 32); // I0 = RA << 32
2745+
UML_OR(block, I0, I0, SPR32(SPR601_MQ)); // I0 |= MQ
2746+
UML_CMP(block, I0, 0x0); // cmp I0, #0
2747+
UML_JMPc(block, COND_NZ, compiler->labelnum); // bne 0:
2748+
2749+
UML_MOV(block, R32(G_RD(op)), 0x0); // mov rd, #0
2750+
if (op & M_OE)
2751+
{
2752+
UML_OR(block, XERSO32, XERSO32, 0x1); // SO |= 1
2753+
UML_OR(block, SPR32(SPR_XER), SPR32(SPR_XER), XER_OV); // OV |= 1
2754+
}
2755+
if (op & M_RC)
2756+
{
2757+
UML_MOV(block, CR32(0), 0x2); // CR = EQ
2758+
UML_AND(block, CR32(0), CR32(0), ~0x1);
2759+
UML_OR(block, CR32(0), CR32(0), XERSO32);
2760+
}
2761+
2762+
UML_JMP(block, compiler->labelnum + 1); // jmp 1:
2763+
2764+
UML_LABEL(block, compiler->labelnum++); // 0:
2765+
UML_DIVS(block, R32(G_RD(op)), SPR32(SPR601_MQ), I0, R32(G_RB(op))); // divs rd,mq,I0,rb
2766+
generate_compute_flags(block, desc, op & M_RC, ((op & M_OE) ? XER_OV | XER_SO : 0), false); // <update flags>
2767+
UML_LABEL(block, compiler->labelnum++); // 1:
2768+
return true;
2769+
2770+
case 0x16b: /* DIVS (POWER) */
2771+
assert(m_cap & PPCCAP_LEGACY_POWER);
2772+
2773+
UML_CMP(block, R32(G_RB(op)), 0x0); // cmp rb, #0
2774+
UML_JMPc(block, COND_NZ, compiler->labelnum); // bne 0:
2775+
2776+
UML_MOV(block, R32(G_RD(op)), 0x0); // mov rd, #0
2777+
if (op & M_OE)
2778+
{
2779+
UML_OR(block, XERSO32, XERSO32, 0x1); // SO |= 1
2780+
UML_OR(block, SPR32(SPR_XER), SPR32(SPR_XER), XER_OV); // OV |= 1
2781+
}
2782+
if (op & M_RC)
2783+
{
2784+
UML_MOV(block, CR32(0), 0x2); // CR = EQ
2785+
UML_AND(block, CR32(0), CR32(0), ~0x1);
2786+
UML_OR(block, CR32(0), CR32(0), XERSO32);
2787+
}
2788+
2789+
UML_JMP(block, compiler->labelnum + 1); // jmp 1:
2790+
2791+
UML_LABEL(block, compiler->labelnum++); // 0:
2792+
UML_DIVS(block, R32(G_RD(op)), SPR32(SPR601_MQ), R32(G_RA(op)), R32(G_RB(op))); // divs rd,mq,ra,rb
2793+
generate_compute_flags(block, desc, op & M_RC, ((op & M_OE) ? XER_OV : 0), false); // <update flags>
2794+
2795+
UML_LABEL(block, compiler->labelnum++); // 1:
2796+
return true;
2797+
27172798
case 0x1eb: /* DIVWx */
27182799
case 0x3eb: /* DIVWOx */
27192800
UML_CMP(block, R32(G_RB(op)), 0x0); // cmp rb, #0
@@ -2767,6 +2848,78 @@ bool ppc_device::generate_instruction_1f(drcuml_block &block, compiler_state *co
27672848
UML_LABEL(block, compiler->labelnum++); // 3:
27682849
return true;
27692850

2851+
case 0x108: /* DOZ (POWER) */
2852+
assert(m_cap & PPCCAP_LEGACY_POWER);
2853+
2854+
UML_CMP(block, R32(G_RA(op)), R32(G_RB(op))); // cmp ra, rb
2855+
UML_JMPc(block, COND_B, compiler->labelnum); // bae 0:
2856+
2857+
UML_XOR(block, R32(G_RD(op)), R32(G_RD(op)), R32(G_RD(op))); // xor rd, rd, rd (rd = 0)
2858+
UML_JMP(block, compiler->labelnum+1); // jmp 1:
2859+
2860+
UML_LABEL(block, compiler->labelnum++); // 0:
2861+
UML_ADD(block, R32(G_RD(op)), R32(G_RA(op)), R32(G_RB(op)));
2862+
UML_ADD(block, R32(G_RD(op)), R32(G_RD(op)), 0x1);
2863+
2864+
UML_LABEL(block, compiler->labelnum++); // 1:
2865+
if (op & M_OE)
2866+
{
2867+
UML_OR(block, XERSO32, XERSO32, 0x1); // SO |= 1
2868+
UML_OR(block, SPR32(SPR_XER), SPR32(SPR_XER), XER_OV); // OV |= 1
2869+
}
2870+
if (op & M_RC)
2871+
{
2872+
UML_TEST(block, R32(G_RD(op)), ~0); // test rd,~0
2873+
generate_compute_flags(block, desc, op & M_RC, 0, false); // <update flags>
2874+
}
2875+
return true;
2876+
2877+
case 0x168: /* ABS (POWER) */
2878+
case 0x1e8: /* NABS (POWER) */
2879+
assert(m_cap & PPCCAP_LEGACY_POWER);
2880+
2881+
// is rA already the correct sign (positive for ABS, negative for NABS)?
2882+
UML_CMP(block, R32(G_RA(op)), 0);
2883+
if (op & 0x080)
2884+
{
2885+
UML_JMPc(block, COND_L, compiler->labelnum); // bl 0:
2886+
}
2887+
else
2888+
{
2889+
UML_JMPc(block, COND_GE, compiler->labelnum); // bge 0:
2890+
}
2891+
2892+
UML_SUB(block, I0, 0, R32(G_RA(op))); // sub 0, ra (make positive)
2893+
UML_JMP(block, compiler->labelnum + 1); // jmp 1:
2894+
2895+
UML_LABEL(block, compiler->labelnum++); // 0:
2896+
UML_MOV(block, I0, R32(G_RA(op)));
2897+
2898+
UML_LABEL(block, compiler->labelnum++); // 1:
2899+
UML_MOV(block, R32(G_RD(op)), I0); // mov rd, I0
2900+
if (op & M_RC)
2901+
{
2902+
UML_GETFLGS(block, I0, FLAG_Z | FLAG_V | FLAG_C | FLAG_S); // getflgs i0,zvcs
2903+
UML_LOAD(block, I0, m_cmp_cr_table, I0, SIZE_DWORD, SCALE_x4); // load i0,cmp_cr_table,i0,dword
2904+
}
2905+
if (op & M_OE)
2906+
{
2907+
UML_OR(block, CR32(G_CRFD(op)), I0, XERSO32); // or [crn],i0,[xerso]
2908+
}
2909+
return true;
2910+
2911+
case 0x21d: /* MASKIR (POWER) */
2912+
UML_AND(block, I0, R32(G_RS(op)), R32(G_RB(op))); // and i0, rs, rb
2913+
UML_XOR(block, I1, R32(G_RB(op)), 0xffffffff); // xor i1, rb, 0xffffffff
2914+
UML_AND(block, I1, I1, R32(G_RA(op))); // and i1, i1, ra
2915+
UML_OR(block, R32(G_RA(op)), I0, I1); // or ra, i0, i1
2916+
if (op & M_RC)
2917+
{
2918+
UML_GETFLGS(block, R32(G_RA(op)), FLAG_Z | FLAG_V | FLAG_C | FLAG_S); // getflgs i0,zvcs
2919+
UML_LOAD(block, I0, m_cmp_cr_table, I0, SIZE_DWORD, SCALE_x4); // load i0,cmp_cr_table,i0,dword
2920+
}
2921+
return true;
2922+
27702923
case 0x01c: /* ANDx */
27712924
UML_AND(block, R32(G_RA(op)), R32(G_RS(op)), R32(G_RB(op))); // and ra,rs,rb
27722925
generate_compute_flags(block, desc, op & M_RC, 0, false); // <update flags>

0 commit comments

Comments
 (0)