Files
LibretroAdmin 93ae3b9520 retro_unserialize: validate instead of backing up the running state
Every state load first serialized the running state into a fresh
allocation the size of a state -- 25.5 MB -- so a failed load could be
rolled back. That doubled the cost of every load, and run-ahead and
rewind load every frame.

A current-format state is now checked first and then loaded. Each
current-format loader already parses its chunk into locals and checks
it before touching emulator state; with opera_state_set_validating()
set they stop there, so a state that validates is one the real load
accepts in full and there is nothing to roll back. In that pass:

- the MEM chunk's 25 MB payload is stepped over (sizes and config are
  still checked), not copied;
- the xbus devices are not reset, and the CD-ROM device fields are
  checked against a scratch copy;
- the memory-map sync, pbus reset and other side effects after loading
  are skipped.

An older format, or a state carrying an older CD-ROM chunk, is not
validated and takes the old backup path, as does anything that fails
validation, so a refused state still leaves the running state as it
was.

Measured with a libretro driver over 200 loads: retro_unserialize went
from 9.08 ms to 4.20 ms on average (best 7.80 ms to 3.58 ms). Checked:
save, load and save again is byte-identical; a state loaded and run 60
frames matches the original run byte for byte; and a sweep flipping
each of 9721 bytes (every 64th byte of the first 360 KB, every byte of
the last 4 KB) is accepted or refused exactly as before, with no
refused load ever changing the running state.
2026-10-28 20:21:15 +01:00

2212 lines
51 KiB
C

/*
www.freedo.org
The first working 3DO multiplayer emulator.
The FreeDO licensed under modified GNU LGPL, with following notes:
* The owners and original authors of the FreeDO have full right to
* develop closed source derivative work.
* Any non-commercial uses of the FreeDO sources or any knowledge
* obtained by studying or reverse engineering of the sources, or
* any other material published by FreeDO have to be accompanied
* with full credits.
* Any commercial uses of FreeDO sources or any knowledge obtained
* by studying or reverse engineering of the sources, or any other
* material published by FreeDO is strictly forbidden without
* owners approval.
The above notes are taking precedence over GNU LGPL in conflicting
situations.
Project authors:
* Alexander Troosh
* Maxim Grishin
* Allen Wright
* John Sammons
* Felix Lazarev
*/
#include "boolean.h"
#include "endianness.h"
#include "inline.h"
#include "opera_arm.h"
#include "opera_arm_core.h"
#include "opera_3do.h"
#include "opera_clio.h"
#include "opera_core.h"
#include "opera_diag_port.h"
#include "opera_fixedpoint_math.h"
#include "opera_madam.h"
#include "opera_mem.h"
#include "opera_xbus.h"
#include "opera_sport.h"
#include "opera_state.h"
#include "opera_swi_hle_0x5XXXX.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define ARM_INITIAL_PC 0x00000000
#define ARM_ARRAY_COUNT(A_) ((uint32_t)(sizeof(A_) / sizeof((A_)[0])))
#define ARM_MUL_MASK 0x0fc000f0
#define ARM_MUL_SIGN 0x00000090
#define ARM_SDS_MASK 0x0fb00ff0
#define ARM_SDS_SIGN 0x01000090
#define ARM_UND_MASK 0x0e000010
#define ARM_UND_SIGN 0x06000010
#define ARM_MRS_MASK 0x0fbf0fff
#define ARM_MRS_SIGN 0x010f0000
#define ARM_MSR_MASK 0x0fbffff0
#define ARM_MSR_SIGN 0x0129f000
#define ARM_MSRF_MASK 0x0dbff000
#define ARM_MSRF_SIGN 0x0128f000
#define ARM_DP_IMM_MASK 0xFF
#define ARM_DP_IMM_ROT_AMOUNT_SHIFT 7
#define ARM_DP_IMM_ROT_AMOUNT_MASK 0x1E
#define ARM_SHIFT_TYPE_ROR 3
#define ARM_CPSR_MODE_MASK 0x1F
#define ARM_CPSR_MODE_SVC 0x13
#define ARM_CPSR_IRQ_DISABLE 0x80
#define ARM_CPSR_FIQ_DISABLE 0x40
#define ARM_MODE_USER 0
#define ARM_MODE_FIQ 1
#define ARM_MODE_IRQ 2
#define ARM_MODE_SVC 3
#define ARM_MODE_ABT 4
#define ARM_MODE_UND 5
#define ARM_MODE_UNK 0xff
const static uint8_t arm_mode_table[]=
{
ARM_MODE_UNK, ARM_MODE_UNK, ARM_MODE_UNK, ARM_MODE_UNK,
ARM_MODE_UNK, ARM_MODE_UNK, ARM_MODE_UNK, ARM_MODE_UNK,
ARM_MODE_UNK, ARM_MODE_UNK, ARM_MODE_UNK, ARM_MODE_UNK,
ARM_MODE_UNK, ARM_MODE_UNK, ARM_MODE_UNK, ARM_MODE_UNK,
ARM_MODE_USER, ARM_MODE_FIQ, ARM_MODE_IRQ, ARM_MODE_SVC,
ARM_MODE_UNK, ARM_MODE_UNK, ARM_MODE_UNK, ARM_MODE_ABT,
ARM_MODE_UNK, ARM_MODE_UNK, ARM_MODE_UNK, ARM_MODE_UND,
ARM_MODE_UNK, ARM_MODE_UNK, ARM_MODE_UNK, ARM_MODE_UNK
};
/*
* ARM60 instruction timings are expressed in N/S/I/C cycle classes rather
* than fixed cycle counts. The ARM60 datasheet defines the cycle classes and
* notes that extra N-cycle length is a memory-system property, not an ARM60
* requirement. The ARM toolkit docs bundled with the 3DO SDK use N=2S in an
* uncached ARM example, and 3DO hardware is documented as an uncached ARM60.
*
* References:
* - https://github.com/trapexit/3do-devkit/blob/master/docs/cpu/arm60_datasheet_-_zarlink_semiconductor.pdf
* - https://github.com/trapexit/3do-devkit/blob/master/docs/3dosdk/tktfldr/atsfldr/4atsd.html#L71-L78
* - https://github.com/trapexit/portfolio_os/blob/master/src/audio/audiofolio/dspp_instr.c#L220-L245
*/
#define NCYCLE 2
#define SCYCLE 1
#define ICYCLE 1
//--------------------------Conditions-------------------------------------------
// flags - N Z C V - 31...28
const static uint16_t cond_flags_cross[]=
{
0xf0f0, //EQ - Z set (equal)
0x0f0f, //NE - Z clear (not equal)
0xcccc, //CS - C set (unsigned higher or same)
0x3333, //CC - C clear (unsigned lower)
0xff00, //N set (negative)
0x00ff, //N clear (positive or zero)
0xaaaa, //V set (overflow)
0x5555, //V clear (no overflow)
0x0c0c, //C set and Z clear (unsigned higher)
0xf3f3, //C clear or Z set (unsigned lower or same)
0xaa55, //N set and V set, or N clear and V clear (greater or equal)
0x55aa, //N set and V clear, or N clear and V set (less than)
0x0a05, //Z clear, and either N set and V set, or N clear and V clear (greater than)
0xf5fa, //Z set, or N set and V clear, or N clear and V set (less than or equal)
0xffff, //always
0x0000 //never
};
static int g_SWI_HLE;
static arm_core_t CPU;
static int CYCLES; //cycle counter
static bool g_SOFT_RESET_PENDING = false;
static bool g_ROM_OVERLAY = false;
static uint32_t g_CPU_DIRECT_RAM_LIMIT = 0;
static uint32_t g_ROM_OVERLAY_READ_LIMIT = 0;
static uint32_t g_ROM_OVERLAY_CLEAR_LIMIT = 0;
static uint32_t carry_out = 0;
static uint32_t readusr(uint32_t const rn);
static void loadusr(uint32_t const rn, uint32_t const val);
static uint32_t mreadb(uint32_t const addr);
static void mwriteb(uint32_t const addr, uint8_t const val);
static uint32_t mreadw(uint32_t const addr);
static void mwritew(uint32_t const addr,uint32_t const val);
static void mwritew_internal(uint32_t const addr,
uint32_t const val);
#define ROM_OVERLAY_READ_LIMIT 0x03000000
/*
Encode the overlay state in the limits used by the hot memory paths. While
enabled, low reads select ROM and low DRAM writes clear the overlay. Once
disabled, zero makes the unsigned address comparisons consistently false
until a reset restores the overlay. This avoids a separate overlay-state
branch on every fetch/access and gives the branch predictor a stable path.
*/
static
void
set_rom_overlay(bool const enabled_)
{
g_ROM_OVERLAY = enabled_;
g_CPU_DIRECT_RAM_LIMIT = enabled_ ? 0 : RAM_SIZE;
g_ROM_OVERLAY_READ_LIMIT = enabled_ ? ROM_OVERLAY_READ_LIMIT : 0;
g_ROM_OVERLAY_CLEAR_LIMIT = enabled_ ? DRAM_SIZE : 0;
}
void
opera_arm_sync_memory_map(void)
{
set_rom_overlay(g_ROM_OVERLAY);
}
static
OPERA_FORCEINLINE
uint32_t
mfetchw(uint32_t const addr_)
{
uint32_t index;
uint32_t const addr = (addr_ & ~3);
if(addr < g_CPU_DIRECT_RAM_LIMIT)
return opera_mem_read32(addr);
if(addr < g_ROM_OVERLAY_READ_LIMIT)
return *(uint32_t*)&ROM[addr & ROM1_SIZE_MASK];
index = (addr ^ 0x03000000);
if(!(index & ~ROM1_SIZE_MASK))
return *(uint32_t*)&ROM[index];
index = (addr ^ 0x06000000);
if(!(index & ~ROM1_SIZE_MASK))
return *(uint32_t*)&ROM[index];
return mreadw(addr);
}
static
bool
clio_xbus_access_aborts(uint32_t const index_)
{
if((index_ < 0x540) || (index_ >= 0x600))
return false;
if(opera_clio_xbus_access_aborts(opera_xbus_selected_device_address(),
index_))
return true;
return opera_xbus_selected_device_absent();
}
uint32_t
opera_arm_state_size_v1(void)
{
return opera_state_save_size(sizeof(CPU));
}
static
bool
opera_arm_state_write_payload(opera_state_writer_t *writer_,
arm_core_t const *state_)
{
return (opera_state_write_u32_array(writer_,state_->USER,ARM_ARRAY_COUNT(state_->USER)) &&
opera_state_write_u32_array(writer_,state_->CASH,ARM_ARRAY_COUNT(state_->CASH)) &&
opera_state_write_u32_array(writer_,state_->SVC,ARM_ARRAY_COUNT(state_->SVC)) &&
opera_state_write_u32_array(writer_,state_->ABT,ARM_ARRAY_COUNT(state_->ABT)) &&
opera_state_write_u32_array(writer_,state_->FIQ,ARM_ARRAY_COUNT(state_->FIQ)) &&
opera_state_write_u32_array(writer_,state_->IRQ,ARM_ARRAY_COUNT(state_->IRQ)) &&
opera_state_write_u32_array(writer_,state_->UND,ARM_ARRAY_COUNT(state_->UND)) &&
opera_state_write_u32_array(writer_,state_->SPSR,ARM_ARRAY_COUNT(state_->SPSR)) &&
opera_state_write_u32(writer_,state_->CPSR) &&
opera_state_write_u8(writer_,state_->nFIQ) &&
opera_state_write_u8(writer_,state_->MAS_Access_Exept) &&
opera_state_write_u8(writer_,g_SOFT_RESET_PENDING) &&
opera_state_write_u8(writer_,g_ROM_OVERLAY));
}
static
uint32_t
opera_arm_state_payload_size(void)
{
opera_state_writer_t writer;
opera_state_writer_init(&writer,NULL,UINT32_MAX);
opera_arm_state_write_payload(&writer,&CPU);
return opera_state_writer_used(&writer);
}
uint32_t
opera_arm_state_size(void)
{
return opera_state_chunk_size(opera_arm_state_payload_size());
}
uint32_t
opera_arm_state_save(void *data_)
{
uint32_t payload_size;
opera_state_writer_t writer;
payload_size = opera_arm_state_payload_size();
opera_state_writer_init(&writer,data_,opera_state_chunk_size(payload_size));
opera_state_write_chunk_header(&writer,"ARM",payload_size);
opera_arm_state_write_payload(&writer,&CPU);
return opera_state_writer_ok(&writer) ? opera_state_writer_used(&writer) : 0;
}
uint32_t
opera_arm_state_load_v1(void const *data_,
uint32_t const size_)
{
uint32_t rv;
rv = opera_state_load_sized(&CPU,"ARM",data_,size_,sizeof(CPU));
if(rv != 0)
{
g_SOFT_RESET_PENDING = false;
set_rom_overlay(false);
}
return rv;
}
static
bool
opera_arm_state_read_payload(opera_state_reader_t *reader_,
arm_core_t *state_,
bool *soft_reset_pending_,
bool *rom_overlay_)
{
uint8_t soft_reset_pending;
uint8_t rom_overlay;
soft_reset_pending = false;
rom_overlay = false;
if(!opera_state_read_u32_array(reader_,state_->USER,ARM_ARRAY_COUNT(state_->USER)) ||
!opera_state_read_u32_array(reader_,state_->CASH,ARM_ARRAY_COUNT(state_->CASH)) ||
!opera_state_read_u32_array(reader_,state_->SVC,ARM_ARRAY_COUNT(state_->SVC)) ||
!opera_state_read_u32_array(reader_,state_->ABT,ARM_ARRAY_COUNT(state_->ABT)) ||
!opera_state_read_u32_array(reader_,state_->FIQ,ARM_ARRAY_COUNT(state_->FIQ)) ||
!opera_state_read_u32_array(reader_,state_->IRQ,ARM_ARRAY_COUNT(state_->IRQ)) ||
!opera_state_read_u32_array(reader_,state_->UND,ARM_ARRAY_COUNT(state_->UND)) ||
!opera_state_read_u32_array(reader_,state_->SPSR,ARM_ARRAY_COUNT(state_->SPSR)) ||
!opera_state_read_u32(reader_,&state_->CPSR) ||
!opera_state_read_u8(reader_,&state_->nFIQ) ||
!opera_state_read_u8(reader_,&state_->MAS_Access_Exept) ||
!opera_state_read_u8(reader_,&soft_reset_pending))
return false;
/* The overlay byte was added after the first chunked ARM save format. */
if(opera_state_reader_remaining(reader_) &&
!opera_state_read_u8(reader_,&rom_overlay))
return false;
*soft_reset_pending_ = (soft_reset_pending != 0);
*rom_overlay_ = (rom_overlay != 0);
return true;
}
uint32_t
opera_arm_state_load(void const *data_,
uint32_t const size_)
{
arm_core_t state;
bool soft_reset_pending;
bool rom_overlay;
opera_state_reader_t reader;
opera_state_reader_t payload;
opera_state_reader_init(&reader,data_,size_);
if(!opera_state_read_chunk(&reader,"ARM",&payload) ||
!opera_arm_state_read_payload(&payload,
&state,
&soft_reset_pending,
&rom_overlay) ||
!opera_state_reader_finished(&payload))
return 0;
if(opera_state_validating())
return opera_state_reader_used(&reader);
CPU = state;
g_SOFT_RESET_PENDING = soft_reset_pending;
set_rom_overlay(rom_overlay);
return opera_state_reader_used(&reader);
}
static
void
ARM_RestUserRONS(void)
{
switch(arm_mode_table[CPU.CPSR & 0x1F])
{
case ARM_MODE_USER:
break;
case ARM_MODE_FIQ:
memcpy(CPU.FIQ,&CPU.USER[8],7<<2);
memcpy(&CPU.USER[8],CPU.CASH,7<<2);
break;
case ARM_MODE_IRQ:
CPU.IRQ[0] = CPU.USER[13];
CPU.IRQ[1] = CPU.USER[14];
CPU.USER[13] = CPU.CASH[5];
CPU.USER[14] = CPU.CASH[6];
break;
case ARM_MODE_SVC:
CPU.SVC[0] = CPU.USER[13];
CPU.SVC[1] = CPU.USER[14];
CPU.USER[13] = CPU.CASH[5];
CPU.USER[14] = CPU.CASH[6];
break;
case ARM_MODE_ABT:
CPU.ABT[0] = CPU.USER[13];
CPU.ABT[1] = CPU.USER[14];
CPU.USER[13] = CPU.CASH[5];
CPU.USER[14] = CPU.CASH[6];
break;
case ARM_MODE_UND:
CPU.UND[0] = CPU.USER[13];
CPU.UND[1] = CPU.USER[14];
CPU.USER[13] = CPU.CASH[5];
CPU.USER[14] = CPU.CASH[6];
break;
}
}
static
void
ARM_RestFiqRONS(void)
{
switch(arm_mode_table[CPU.CPSR & 0x1F])
{
case ARM_MODE_USER:
memcpy(CPU.CASH,&CPU.USER[8],7<<2);
memcpy(&CPU.USER[8],CPU.FIQ,7<<2);
break;
case ARM_MODE_FIQ:
break;
case ARM_MODE_IRQ:
memcpy(CPU.CASH,&CPU.USER[8],5<<2);
CPU.IRQ[0] = CPU.USER[13];
CPU.IRQ[1] = CPU.USER[14];
memcpy(&CPU.USER[8],CPU.FIQ,7<<2);
break;
case ARM_MODE_SVC:
memcpy(CPU.CASH,&CPU.USER[8],5<<2);
CPU.SVC[0] = CPU.USER[13];
CPU.SVC[1] = CPU.USER[14];
memcpy(&CPU.USER[8],CPU.FIQ,7<<2);
break;
case ARM_MODE_ABT:
memcpy(CPU.CASH,&CPU.USER[8],5<<2);
CPU.ABT[0] = CPU.USER[13];
CPU.ABT[1] = CPU.USER[14];
memcpy(&CPU.USER[8],CPU.FIQ,7<<2);
break;
case ARM_MODE_UND:
memcpy(CPU.CASH,&CPU.USER[8],5<<2);
CPU.UND[0] = CPU.USER[13];
CPU.UND[1] = CPU.USER[14];
memcpy(&CPU.USER[8],CPU.FIQ,7<<2);
break;
}
}
static
void
ARM_RestIrqRONS(void)
{
switch(arm_mode_table[CPU.CPSR & 0x1F])
{
case ARM_MODE_USER:
CPU.CASH[5] = CPU.USER[13];
CPU.CASH[6] = CPU.USER[14];
CPU.USER[13] = CPU.IRQ[0];
CPU.USER[14] = CPU.IRQ[1];
break;
case ARM_MODE_FIQ:
memcpy(CPU.FIQ,&CPU.USER[8],7<<2);
memcpy(&CPU.USER[8],CPU.CASH,5<<2);
CPU.USER[13] = CPU.IRQ[0];
CPU.USER[14] = CPU.IRQ[1];
break;
case ARM_MODE_IRQ:
break;
case ARM_MODE_SVC:
CPU.SVC[0] = CPU.USER[13];
CPU.SVC[1] = CPU.USER[14];
CPU.USER[13] = CPU.IRQ[0];
CPU.USER[14] = CPU.IRQ[1];
break;
case ARM_MODE_ABT:
CPU.ABT[0] = CPU.USER[13];
CPU.ABT[1] = CPU.USER[14];
CPU.USER[13] = CPU.IRQ[0];
CPU.USER[14] = CPU.IRQ[1];
break;
case ARM_MODE_UND:
CPU.UND[0] = CPU.USER[13];
CPU.UND[1] = CPU.USER[14];
CPU.USER[13] = CPU.IRQ[0];
CPU.USER[14] = CPU.IRQ[1];
break;
}
}
static
void
ARM_RestSvcRONS(void)
{
switch(arm_mode_table[CPU.CPSR & 0x1F])
{
case ARM_MODE_USER:
CPU.CASH[5] = CPU.USER[13];
CPU.CASH[6] = CPU.USER[14];
CPU.USER[13] = CPU.SVC[0];
CPU.USER[14] = CPU.SVC[1];
break;
case ARM_MODE_FIQ:
memcpy(CPU.FIQ,&CPU.USER[8],7<<2);
memcpy(&CPU.USER[8],CPU.CASH,5<<2);
CPU.USER[13] = CPU.SVC[0];
CPU.USER[14] = CPU.SVC[1];
break;
case ARM_MODE_IRQ:
CPU.IRQ[0] = CPU.USER[13];
CPU.IRQ[1] = CPU.USER[14];
CPU.USER[13] = CPU.SVC[0];
CPU.USER[14] = CPU.SVC[1];
break;
case ARM_MODE_SVC:
break;
case ARM_MODE_ABT:
CPU.ABT[0] = CPU.USER[13];
CPU.ABT[1] = CPU.USER[14];
CPU.USER[13] = CPU.SVC[0];
CPU.USER[14] = CPU.SVC[1];
break;
case ARM_MODE_UND:
CPU.UND[0] = CPU.USER[13];
CPU.UND[1] = CPU.USER[14];
CPU.USER[13] = CPU.SVC[0];
CPU.USER[14] = CPU.SVC[1];
break;
}
}
static
void
ARM_RestAbtRONS(void)
{
switch(arm_mode_table[CPU.CPSR & 0x1F])
{
case ARM_MODE_USER:
CPU.CASH[5] = CPU.USER[13];
CPU.CASH[6] = CPU.USER[14];
CPU.USER[13] = CPU.ABT[0];
CPU.USER[14] = CPU.ABT[1];
break;
case ARM_MODE_FIQ:
memcpy(CPU.FIQ,&CPU.USER[8],7<<2);
memcpy(&CPU.USER[8],CPU.CASH,5<<2);
CPU.USER[13] = CPU.ABT[0];
CPU.USER[14] = CPU.ABT[1];
break;
case ARM_MODE_IRQ:
CPU.IRQ[0] = CPU.USER[13];
CPU.IRQ[1] = CPU.USER[14];
CPU.USER[13] = CPU.ABT[0];
CPU.USER[14] = CPU.ABT[1];
break;
case ARM_MODE_SVC:
CPU.SVC[0] = CPU.USER[13];
CPU.SVC[1] = CPU.USER[14];
CPU.USER[13] = CPU.ABT[0];
CPU.USER[14] = CPU.ABT[1];
break;
case ARM_MODE_ABT:
break;
case ARM_MODE_UND:
CPU.UND[0] = CPU.USER[13];
CPU.UND[1] = CPU.USER[14];
CPU.USER[13] = CPU.ABT[0];
CPU.USER[14] = CPU.ABT[1];
break;
}
}
static
void
ARM_RestUndRONS(void)
{
switch(arm_mode_table[CPU.CPSR & 0x1F])
{
case ARM_MODE_USER:
CPU.CASH[5] = CPU.USER[13];
CPU.CASH[6] = CPU.USER[14];
CPU.USER[13] = CPU.UND[0];
CPU.USER[14] = CPU.UND[1];
break;
case ARM_MODE_FIQ:
memcpy(CPU.FIQ,&CPU.USER[8],7<<2);
memcpy(&CPU.USER[8],CPU.CASH,5<<2);
CPU.USER[13] = CPU.UND[0];
CPU.USER[14] = CPU.UND[1];
break;
case ARM_MODE_IRQ:
CPU.IRQ[0] = CPU.USER[13];
CPU.IRQ[1] = CPU.USER[14];
CPU.USER[13] = CPU.UND[0];
CPU.USER[14] = CPU.UND[1];
break;
case ARM_MODE_SVC:
CPU.SVC[0] = CPU.USER[13];
CPU.SVC[1] = CPU.USER[14];
CPU.USER[13] = CPU.UND[0];
CPU.USER[14] = CPU.UND[1];
break;
case ARM_MODE_ABT:
CPU.ABT[0] = CPU.USER[13];
CPU.ABT[1] = CPU.USER[14];
CPU.USER[13] = CPU.UND[0];
CPU.USER[14] = CPU.UND[1];
break;
case ARM_MODE_UND:
break;
}
}
static
void
ARM_Change_ModeSafe(uint32_t mode_)
{
switch(arm_mode_table[mode_ & 0x1F])
{
case ARM_MODE_USER:
ARM_RestUserRONS();
break;
case ARM_MODE_FIQ:
ARM_RestFiqRONS();
break;
case ARM_MODE_IRQ:
ARM_RestIrqRONS();
break;
case ARM_MODE_SVC:
ARM_RestSvcRONS();
break;
case ARM_MODE_ABT:
ARM_RestAbtRONS();
break;
case ARM_MODE_UND:
ARM_RestUndRONS();
break;
}
}
static
INLINE
void
arm_cpsr_set(uint32_t a_)
{
a_ |= 0x10;
ARM_Change_ModeSafe(a_);
CPU.CPSR = (a_ & 0xf00000df);
}
static
INLINE
void
SETM(uint32_t a_)
{
a_ |= 0x10;
ARM_Change_ModeSafe(a_);
CPU.CPSR = ((CPU.CPSR & 0xffffffe0) | (a_ & 0x1F));
}
static
INLINE
void
SETN(int a_)
{
CPU.CPSR = (CPU.CPSR & 0x7fffffff) | ((a_ ? 1 << 31 : 0));
}
static
INLINE
void
SETZ(int a_)
{
CPU.CPSR = (CPU.CPSR & 0xbfffffff) | ((a_ ? 1 << 30 : 0));
}
static
INLINE
void
SETC(int a_)
{
CPU.CPSR = (CPU.CPSR & 0xdfffffff) | ((a_ ? 1 << 29 : 0));
}
static
INLINE
void
SETV(int a_)
{
CPU.CPSR = (CPU.CPSR & 0xefffffff) | ((a_ ? 1 << 28 : 0));
}
static
INLINE
void
SETI(int a_)
{
CPU.CPSR = (CPU.CPSR & 0xffffff7f) | ((a_ ? 1 << 7 : 0));
}
static
INLINE
void
SETF(int a_)
{
CPU.CPSR = (CPU.CPSR & 0xffffffbf) | ((a_ ? 1 << 6 : 0));
}
static
void
arm_data_abort(void)
{
CPU.SPSR[arm_mode_table[0x17]] = CPU.CPSR;
SETI(1);
SETM(0x17);
CPU.USER[14] = (CPU.USER[15] + 4);
CPU.USER[15] = 0x00000010;
CYCLES -= (SCYCLE + NCYCLE);
CPU.MAS_Access_Exept = false;
}
#define MODE ((CPU.CPSR & 0x1F))
#define ISN ((CPU.CPSR >> 31) & 1)
#define ISZ ((CPU.CPSR >> 30) & 1)
#define ISC ((CPU.CPSR >> 29) & 1)
#define ISV ((CPU.CPSR >> 28) & 1)
#define ISI ((CPU.CPSR >> 7) & 1)
#define ISF ((CPU.CPSR >> 6) & 1)
static
INLINE
uint32_t
ROTR(const uint32_t val_,
const uint32_t shift_)
{
return (shift_ ?
((val_ >> shift_) | (val_ << (32 - shift_))) :
val_);
}
void
opera_arm_init(void)
{
int i;
g_SWI_HLE = 0;
g_SOFT_RESET_PENDING = false;
set_rom_overlay(true);
carry_out = 0;
CYCLES = 0;
for(i = 0; i < 16; i++)
CPU.USER[i] = 0;
for(i = 0; i < 2; i++)
{
CPU.SVC[i] = 0;
CPU.ABT[i] = 0;
CPU.IRQ[i] = 0;
CPU.UND[i] = 0;
}
for(i = 0;i < 7; i++)
CPU.CASH[i] = CPU.FIQ[i] = 0;
CPU.nFIQ = false;
CPU.MAS_Access_Exept = false;
CPU.USER[15] = ARM_INITIAL_PC;
arm_cpsr_set(((ARM_CPSR_MODE_SVC | ARM_CPSR_IRQ_DISABLE) |
ARM_CPSR_FIQ_DISABLE));
}
void
opera_arm_destroy(void)
{
}
void
opera_arm_soft_reset(void)
{
uint32_t const return_cpsr = CPU.CPSR;
uint32_t const return_pc = CPU.USER[15];
CYCLES = 0;
g_SOFT_RESET_PENDING = false;
set_rom_overlay(true);
/*
A CLIO soft reset asserts the ARM reset input without making it a cold CPU
reset. Portfolio's DIPIR path relies on the interrupted CPSR and next PC
being visible as SPSR_svc and R14_svc when it chooses to return.
*/
CPU.SPSR[ARM_MODE_SVC] = return_cpsr;
arm_cpsr_set((((return_cpsr & ~ARM_CPSR_MODE_MASK) |
ARM_CPSR_MODE_SVC) |
ARM_CPSR_IRQ_DISABLE) |
ARM_CPSR_FIQ_DISABLE);
CPU.USER[14] = return_pc;
CPU.USER[15] = ARM_INITIAL_PC;
CPU.MAS_Access_Exept = false;
CPU.nFIQ = false;
}
void
opera_arm_swi_hle_set(const int hle_)
{
g_SWI_HLE = !!hle_;
}
int
opera_arm_swi_hle_get(void)
{
return g_SWI_HLE;
}
static
void
ldm_accur(uint32_t opc_,
uint32_t base_,
uint32_t rn_ind_)
{
uint16_t x;
uint16_t list;
uint32_t i;
uint32_t tmp;
uint32_t base_comp;
i = 0;
x = opc_ & 0xFFFF;
list = opc_ & 0xFFFF;
x = ((x & 0x5555) + ((x >> 1) & 0x5555));
x = ((x & 0x3333) + ((x >> 2) & 0x3333));
x = ((x & 0x00ff) + (x >> 8));
x = ((x & 0x000f) + (x >> 4));
switch((opc_>>23)&3)
{
case 0:
base_ -= (x << 2);
base_comp = (base_ + 4);
break;
case 1:
base_comp = base_;
base_ += (x << 2);
break;
case 2:
base_comp = base_ = (base_ - (x << 2));
break;
case 3:
base_comp = (base_ + 4);
base_ += (x<<2);
break;
}
//base_comp&=~3;
//if(opc_&(1<<21))CPU.USER[rn_ind_]=base_;
if((opc_ & (1 << 22)) && !(opc_ & 0x8000))
{
if(opc_ & (1 << 21))
loadusr(rn_ind_,base_);
while(list)
{
if(list & 1)
{
tmp = mreadw(base_comp);
/*
if(MAS_Access_Exept)
{
if(opc_&(1<<21))
CPU.USER[rn_ind_]=base_;
break;
}
*/
loadusr(i,tmp);
base_comp += 4;
}
i++;
list >>= 1;
}
}
else
{
if(opc_ & (1 << 21))
CPU.USER[rn_ind_] = base_;
while(list)
{
if(list & 1)
{
tmp = mreadw(base_comp);
CPU.USER[i] = tmp;
base_comp += 4;
}
i++;
list >>= 1;
}
if((opc_ & (1 << 22)) && arm_mode_table[MODE] /*&& !MAS_Access_Exept*/)
arm_cpsr_set(CPU.SPSR[arm_mode_table[MODE]]);
}
CYCLES -= ((x-1) * SCYCLE + NCYCLE + ICYCLE);
}
static
void
stm_accur(uint32_t opc_,
uint32_t base_,
uint32_t rn_ind_)
{
uint16_t x;
uint16_t list;
uint32_t i;
uint32_t base_comp;
i = 0;
x = opc_ & 0xFFFF;
list = opc_ & 0x7FFF;
x = ((x & 0x5555) + ((x >> 1) & 0x5555));
x = ((x & 0x3333) + ((x >> 2) & 0x3333));
x = ((x & 0x00ff) + (x >> 8));
x = ((x & 0x000f) + (x >> 4));
switch((opc_ >> 23) & 3)
{
case 0:
base_ -= (x << 2);
base_comp = (base_ + 4);
break;
case 1:
base_comp = base_;
base_ += (x << 2);
break;
case 2:
base_comp = base_ = (base_ - (x << 2));
break;
case 3:
base_comp = (base_ + 4);
base_ += (x << 2);
break;
}
if((opc_ & (1 << 22)))
{
if((opc_ & (1 << 21)) && (opc_ & ((1 << rn_ind_) - 1)))
loadusr(rn_ind_,base_);
while(list)
{
if(list & 1)
{
mwritew(base_comp,readusr(i));
if(g_SOFT_RESET_PENDING)
return;
//if(MAS_Access_Exept)break;
base_comp += 4;
}
i++;
list >>= 1;
}
if(opc_ & (1 << 21))
loadusr(rn_ind_,base_);
}
else
{
if((opc_ & (1 << 21)) && (opc_ & ((1 << rn_ind_) - 1)))
CPU.USER[rn_ind_] = base_;
while(list)
{
if(list&1)
{
mwritew(base_comp,CPU.USER[i]);
if(g_SOFT_RESET_PENDING)
return;
base_comp += 4;
}
i++;
list >>= 1;
}
if(opc_ & (1 << 21))
CPU.USER[rn_ind_] = base_;
}
if((opc_ & 0x8000) /*&& !MAS_Access_Exept*/)
{
mwritew(base_comp,CPU.USER[15]+8);
if(g_SOFT_RESET_PENDING)
return;
}
CYCLES -= ((x - 2) * SCYCLE + NCYCLE + NCYCLE);
}
static
void
bdt_core(uint32_t opc_)
{
uint32_t base;
uint32_t rn_ind = ((opc_ >> 16) & 0xF);
if(rn_ind == 0xF)
base = CPU.USER[rn_ind] + 8;
else
base = CPU.USER[rn_ind];
if(opc_ & (1 << 20)) /* memory or register? */
{
if(opc_ & 0x8000)
CYCLES -= (SCYCLE + NCYCLE);
ldm_accur(opc_,base,rn_ind);
}
else
{
stm_accur(opc_,base,rn_ind);
}
}
typedef struct TagArg
{
uint32_t Type;
uint32_t Arg;
} TagItem;
static
void
decode_swi_lle(void)
{
CPU.SPSR[arm_mode_table[0x13]] = CPU.CPSR;
SETI(1);
SETM(0x13);
CPU.USER[14] = CPU.USER[15];
CPU.USER[15] = 0x00000008;
}
static void decode_swi_hle(const uint32_t op_)
{
switch(op_ & 0x000FFFFF)
{
case 0x50000:
opera_swi_hle_0x50000(DRAM,CPU.USER[0],CPU.USER[1],CPU.USER[2]);
return;
case 0x50001:
opera_swi_hle_0x50001(DRAM,CPU.USER[0],CPU.USER[1],CPU.USER[2]);
return;
case 0x50002:
opera_swi_hle_0x50002(DRAM,CPU.USER[0],CPU.USER[1],CPU.USER[2],CPU.USER[3]);
return;
case 0x50003:
break;
case 0x50004:
break;
case 0x50005:
opera_swi_hle_0x50005(DRAM,CPU.USER[0],CPU.USER[1],CPU.USER[2],CPU.USER[3]);
return;
case 0x50006:
opera_swi_hle_0x50006(DRAM,CPU.USER[0],CPU.USER[1],CPU.USER[2],CPU.USER[3]);
return;
case 0x50007:
opera_swi_hle_0x50007(DRAM,CPU.USER[0],CPU.USER[1],CPU.USER[2]);
return;
case 0x50008:
opera_swi_hle_0x50008(DRAM,CPU.USER[0],CPU.USER[1],CPU.USER[2]);
return;
case 0x50009:
opera_swi_hle_0x50009(DRAM,CPU.USER[0],CPU.USER[1],CPU.USER[2],CPU.USER[3]);
return;
case 0x5000A:
break;
case 0x5000B:
break;
case 0x5000C:
CPU.USER[0] = opera_swi_hle_0x5000C(DRAM,CPU.USER[0],CPU.USER[1]);
return;
case 0x5000E:
opera_swi_hle_0x5000E(DRAM,CPU.USER[0],CPU.USER[1],CPU.USER[2]);
return;
case 0x5000F:
CPU.USER[0] = opera_swi_hle_0x5000F(DRAM,CPU.USER[0]);
return;
case 0x50010:
CPU.USER[0] = opera_swi_hle_0x50010(DRAM,CPU.USER[0]);
return;
case 0x50011:
opera_swi_hle_0x50011(DRAM,CPU.USER[0],CPU.USER[1],CPU.USER[2],CPU.USER[3]);
return;
case 0x50012:
opera_swi_hle_0x50012(DRAM,CPU.USER[0]);
return;
}
decode_swi_lle();
}
static void decode_swi(const uint32_t op_)
{
CYCLES -= (SCYCLE + NCYCLE); // +2S+1N
if(g_SWI_HLE)
{
decode_swi_hle(op_);
return;
}
decode_swi_lle();
}
static
INLINE
void
ARM_SET_C(const uint32_t x_)
{
CPU.CPSR = ((CPU.CPSR & 0xdfffffff) | ((x_ & 1) << 29));
}
static
INLINE
void
ARM_SET_Z(const uint32_t x_)
{
CPU.CPSR = ((CPU.CPSR & 0xbfffffff) | (x_ == 0 ? 0x40000000 : 0));
}
static
INLINE
void
ARM_SET_N(const uint32_t x_)
{
CPU.CPSR = ((CPU.CPSR & 0x7fffffff) | (x_ & 0x80000000));
}
static
INLINE
uint32_t
ARM_GET_C(void)
{
return ((CPU.CPSR >> 29) & 1);
}
static
INLINE
void
ARM_SET_ZN(const uint32_t val_)
{
if(val_)
CPU.CPSR = ((CPU.CPSR & 0x3fffffff) | (val_ & 0x80000000));
else
CPU.CPSR = ((CPU.CPSR & 0x3fffffff) | 0x40000000);
}
static
INLINE
void
ARM_SET_CV(const uint32_t rd_,
const uint32_t op1_,
const uint32_t op2_)
{
CPU.CPSR = ((CPU.CPSR & 0xcfffffff) |
((((op1_ & op2_) | ((~rd_) & (op1_ | op2_))) & 0x80000000) >> 2) |
(((((op1_ & (op2_ & (~rd_))) | ((~op1_) & (~op2_) & rd_))) & 0x80000000) >> 3));
}
static
INLINE
void
ARM_SET_CV_sub(uint32_t rd_,
uint32_t op1_,
uint32_t op2_)
{
CPU.CPSR = ((CPU.CPSR & 0xcfffffff) |
((((op1_ & (~op2_)) | ((~rd_) & (op1_ | (~op2_)))) & 0x80000000) >> 2) |
(((((op1_ & ((~op2_) & (~rd_))) | ((~op1_) & op2_ & rd_))) & 0x80000000) >> 3));
}
static
INLINE
int
ARM_ALU_Exec(uint32_t inst_,
uint8_t opc_,
uint32_t op1_,
uint32_t op2_,
uint32_t *rd_)
{
switch(opc_)
{
case 0:
*rd_ = op1_ & op2_;
break;
case 2:
*rd_ = op1_ ^ op2_;
break;
case 4:
*rd_ = op1_ - op2_;
break;
case 6:
*rd_ = op2_ - op1_;
break;
case 8:
*rd_ = op1_ + op2_;
break;
case 10:
*rd_ = op1_ + op2_ + ARM_GET_C();
break;
case 12:
*rd_ = op1_ - op2_ - (ARM_GET_C() ^ 1);
break;
case 14:
*rd_ = op2_ - op1_ - (ARM_GET_C() ^ 1);
break;
case 16:
case 20:
if((inst_ >> 22) & 1)
CPU.USER[(inst_ >> 12) & 0xF] = CPU.SPSR[arm_mode_table[CPU.CPSR & 0x1F]];
else
CPU.USER[(inst_ >> 12) & 0xF] = CPU.CPSR;
return true;
case 18:
case 22:
if(!((inst_ >> 16) & 0x1) || !(arm_mode_table[MODE]))
{
if((inst_ >> 22) & 1)
CPU.SPSR[arm_mode_table[MODE]] = (CPU.SPSR[arm_mode_table[MODE]] & 0x0fffffff) | (op2_ & 0xf0000000);
else
CPU.CPSR = (CPU.CPSR & 0x0fffffff) | (op2_ & 0xf0000000);
}
else
{
if((inst_ >> 22) & 1)
CPU.SPSR[arm_mode_table[MODE]] = op2_ & 0xf00000df;
else
arm_cpsr_set(op2_);
}
return true;
case 24:
*rd_ = op1_ | op2_;
break;
case 26:
*rd_ = op2_;
break;
case 28:
*rd_ = op1_ & ~op2_;
break;
case 30:
*rd_ = ~op2_;
break;
case 1:
*rd_ = op1_ & op2_;
ARM_SET_ZN(*rd_);
break;
case 3:
*rd_ = op1_ ^ op2_;
ARM_SET_ZN(*rd_);
break;
case 5:
*rd_ = op1_ - op2_;
ARM_SET_ZN(*rd_);
ARM_SET_CV_sub(*rd_,op1_,op2_);
break;
case 7:
*rd_ = op2_ - op1_;
ARM_SET_ZN(*rd_);
ARM_SET_CV_sub(*rd_,op2_,op1_);
break;
case 9:
*rd_ = op1_ + op2_;
ARM_SET_ZN(*rd_);
ARM_SET_CV(*rd_,op1_,op2_);
break;
case 11:
*rd_ = op1_ + op2_ + ARM_GET_C();
ARM_SET_ZN(*rd_);
ARM_SET_CV(*rd_,op1_,op2_);
break;
case 13:
*rd_ = op1_ - op2_ - (ARM_GET_C()^1);
ARM_SET_ZN(*rd_);
ARM_SET_CV_sub(*rd_,op1_,op2_);
break;
case 15:
*rd_ = op2_ - op1_ - (ARM_GET_C()^1);
ARM_SET_ZN(*rd_);
ARM_SET_CV_sub(*rd_,op2_,op1_);
break;//*/
case 17:
op1_ &= op2_;
ARM_SET_ZN(op1_);
return true;
case 19:
op1_ ^= op2_;
ARM_SET_ZN(op1_);
return true;
case 21:
ARM_SET_CV_sub(op1_ - op2_,op1_,op2_);
ARM_SET_ZN(op1_ - op2_);
return true;
case 23:
ARM_SET_CV(op1_ + op2_,op1_,op2_);
ARM_SET_ZN(op1_ + op2_);
return true;
case 25:
*rd_ = op1_ | op2_;
ARM_SET_ZN(*rd_);
break;
case 27:
*rd_ = op2_;
ARM_SET_ZN(*rd_);
break;
case 29:
*rd_ = op1_ & ~op2_;
ARM_SET_ZN(*rd_);
break;
case 31:
*rd_ = ~op2_;
ARM_SET_ZN(*rd_);
break;
};
return false;
}
static
uint32_t
ARM_SHIFT_NSC(uint32_t value_,
uint8_t shift_,
uint8_t type_)
{
switch(type_)
{
case 0:
if(shift_)
{
if(shift_ > 32)
carry_out = 0;
else
carry_out = (((value_ << (shift_ - 1)) & 0x80000000) >> 31);
}
else
{
carry_out = ARM_GET_C();
}
if(shift_ == 0)
return value_;
if(shift_ > 31)
return 0;
return (value_ << shift_);
case 1:
if(shift_)
{
if(shift_ > 32)
carry_out = 0;
else
carry_out = ((value_ >> (shift_ - 1)) & 1);
}
else
{
carry_out = ARM_GET_C();
}
if(shift_ == 0)
return value_;
if(shift_ > 31)
return 0;
return (value_ >> shift_);
case 2:
if(shift_)
{
if(shift_ > 32)
carry_out = ((((int32_t)value_) >> 31) & 1);
else
carry_out = ((((int32_t)value_) >> (shift_ - 1)) & 1);
}
else
{
carry_out = ARM_GET_C();
}
if(shift_ == 0)
return value_;
if(shift_ > 31)
return (((int32_t)value_) >> 31);
return (((int32_t)value_) >> shift_);
case 3:
if(shift_)
{
if(shift_&31)
carry_out = ((value_ >> (shift_ - 1)) & 1);
else
carry_out = ((value_ >> 31) & 1);
}
else
{
carry_out = ARM_GET_C();
}
shift_ &= 31;
if(shift_ == 0)
return value_;
return ROTR(value_,shift_);
case 4:
carry_out = value_ & 1;
return ((value_ >> 1) | (ARM_GET_C() << 31));
}
return 0;
}
static
uint32_t
ARM_SHIFT_SC(uint32_t value_,
uint8_t shift_,
uint8_t type_)
{
uint32_t tmp;
switch(type_)
{
case 0:
if(shift_)
{
if(shift_ > 32)
ARM_SET_C(0);
else
ARM_SET_C(((value_ << (shift_ - 1)) & 0x80000000) >> 31);
}
else
{
return value_;
}
if(shift_ > 31)
return 0;
return (value_ << shift_);
case 1:
if(shift_)
{
if(shift_ > 32)
ARM_SET_C(0);
else
ARM_SET_C((value_ >> (shift_ - 1)) & 1);
}
else
{
return value_;
}
if(shift_ > 31)
return 0;
return (value_ >> shift_);
case 2:
if(shift_)
{
if(shift_ > 32)
ARM_SET_C((((int32_t)value_) >> 31) & 1);
else
ARM_SET_C((((int32_t)value_) >> (shift_ - 1)) & 1);
}
else
{
return value_;
}
if(shift_ > 31)
return (((int32_t)value_) >> 31);
return ((int32_t)value_) >> shift_;
case 3:
if(shift_)
{
shift_ = (shift_ & 31);
if(shift_)
ARM_SET_C((value_ >> (shift_ - 1)) & 1);
else
ARM_SET_C((value_ >> 31) & 1);
}
else
{
return value_;
}
return ROTR(value_,shift_);
case 4:
tmp = ARM_GET_C() << 31;
ARM_SET_C(value_ & 1);
return ((value_ >> 1) | (tmp));
}
return 0;
}
static
void
ARM_SWAP(uint32_t cmd_)
{
uint32_t tmp;
uint32_t addr;
uint32_t value;
CPU.USER[15] += 4;
addr = CPU.USER[(cmd_ >> 16) & 0xF];
CPU.USER[15] += 4;
value = CPU.USER[cmd_ & 0xF];
// Preserve PC operands, but expose the execution PC to memory-triggered resets.
CPU.USER[15] -= 8;
if(cmd_ & (1 << 22))
{
tmp = mreadb(addr);
if(CPU.MAS_Access_Exept)
return;
mwriteb(addr,value);
if(g_SOFT_RESET_PENDING)
return;
if(CPU.MAS_Access_Exept)
return;
CPU.USER[(cmd_ >> 12) & 0xF] = tmp;
}
else
{
tmp = mreadw(addr);
if(CPU.MAS_Access_Exept)
return;
mwritew(addr,value);
if(g_SOFT_RESET_PENDING)
return;
if(CPU.MAS_Access_Exept)
return;
if(addr & 3)
tmp = ((tmp >> ((addr & 3) << 3)) | (tmp << (32 - ((addr & 3) << 3))));
CPU.USER[(cmd_ >> 12) & 0xF] = tmp;
}
}
static
INLINE
uint32_t
calcbits(uint32_t num_)
{
uint32_t rv;
if(!num_)
return 1;
if(num_ >> 16)
{
num_ >>= 16;
rv = 16;
}
else
{
rv = 0;
}
if(num_ >> 8)
{
num_ >>= 8;
rv += 8;
}
if(num_ >> 4)
{
num_ >>= 4;
rv += 4;
}
if(num_ >> 2)
{
num_ >>= 2;
rv += 2;
}
if(num_ >> 1)
{
num_ >>= 1;
rv += 2;
}
else if(num_)
{
rv++;
}
return rv;
}
static const int is_logic[] =
{
true,true,false,false,
false,false,false,false,
true,true,false,false,
true,true,true,true
};
int32_t
opera_arm_execute(void)
{
uint32_t op1;
uint32_t op2;
uint8_t shift;
uint8_t shtype;
uint32_t cmd;
uint32_t pc_tmp;
int isexeption;
isexeption = false;
cmd = mfetchw(CPU.USER[15]);
CPU.USER[15] += 4;
CYCLES = -SCYCLE;
if((cmd == 0xE5101810) && (CPU.CPSR == 0x80000093))
isexeption = true;
if(((cond_flags_cross[cmd >> 28] >> (CPU.CPSR >> 28)) & 1) &&
(isexeption == false))
{
switch((cmd >> 24) & 0xF)
{
case 0x0: //Multiply
if((cmd & ARM_MUL_MASK) == ARM_MUL_SIGN)
{
uint32_t res = ((calcbits(CPU.USER[(cmd>>8)&0xf])+5)>>1)-1;
if(res > 16)
CYCLES -= 16;
else
CYCLES -= res;
if(((cmd >> 16) & 0xF) == (cmd & 0xF))
{
if(cmd & (1 << 21))
{
CPU.USER[15] += 8;
res=CPU.USER[(cmd >> 12) & 0xF];
CPU.USER[15] -= 8;
}
else
{
res = 0;
}
}
else
{
if(cmd & (1 << 21))
{
res = CPU.USER[cmd & 0xF] * CPU.USER[(cmd >> 8) & 0xF];
CPU.USER[15] += 8;
res += CPU.USER[(cmd >> 12) & 0xF];
CPU.USER[15] -= 8;
}
else
{
res = CPU.USER[cmd & 0xF] * CPU.USER[(cmd >> 8) & 0xF];
}
}
if(cmd & (1 << 20))
ARM_SET_ZN(res);
CPU.USER[(cmd >> 16) & 0xF] = res;
break;
}
case 0x1: //Single Data Swap
if((cmd & ARM_SDS_MASK) == ARM_SDS_SIGN)
{
ARM_SWAP(cmd);
CYCLES -= ((2 * NCYCLE) + ICYCLE);
if(g_SOFT_RESET_PENDING)
break;
if(CPU.MAS_Access_Exept)
arm_data_abort();
break;
}
case 0x2: //ALU
case 0x3:
{
if((cmd & 0x2000090) != 0x90)
{
/* SHIFT */
pc_tmp = CPU.USER[15];
CPU.USER[15] += 4;
if(cmd & (1 << 25))
{
op2 = cmd & ARM_DP_IMM_MASK;
op2 = ARM_SHIFT_NSC(op2,
((cmd >> ARM_DP_IMM_ROT_AMOUNT_SHIFT) &
ARM_DP_IMM_ROT_AMOUNT_MASK),
ARM_SHIFT_TYPE_ROR);
op1 = CPU.USER[(cmd >> 16) & 0xF];
}
else
{
shtype = ((cmd >> 5) & 0x3);
if(cmd & (1 << 4))
{
shift = ((cmd >> 8) & 0xF);
shift = (CPU.USER[shift] & 0xFF);
CPU.USER[15] += 4;
op2 = CPU.USER[cmd & 0xF];
op1 = CPU.USER[(cmd >> 16) & 0xF];
CYCLES -= ICYCLE;
}
else
{
shift = ((cmd >> 7) & 0x1F);
if(!shift)
{
if(shtype)
{
if(shtype == 3)
shtype++;
else
shift=32;
}
}
op2 = CPU.USER[cmd & 0xF];
op1 = CPU.USER[(cmd >> 16) & 0xF];
}
//if((cmd&(1<<20)) && is_logic[((cmd>>21)&0xf)] ) op2=ARM_SHIFT_SC(op2, shift, shtype);
//else
op2 = ARM_SHIFT_NSC(op2,shift,shtype);
}
CPU.USER[15] = pc_tmp;
if((cmd & (1 << 20)) && is_logic[((cmd >> 21) & 0xF)])
ARM_SET_C(carry_out);
if(ARM_ALU_Exec(cmd,((cmd >> 20) & 0x1F),op1,op2,&CPU.USER[(cmd >> 12) & 0xF]))
break;
if(((cmd >> 12) & 0xF) == 0xF) //destination = pc, take care of cpsr
{
if(cmd & (1 << 20))
arm_cpsr_set(CPU.SPSR[arm_mode_table[MODE]]);
CYCLES -= (ICYCLE + NCYCLE);
}
break;
}
}
case 0x6: //Undefined
case 0x7:
Undefine:
if((cmd & ARM_UND_MASK) == ARM_UND_SIGN)
{
CPU.SPSR[arm_mode_table[0x1b]] = CPU.CPSR;
SETI(1);
SETM(0x1b);
CPU.USER[14] = CPU.USER[15];
CPU.USER[15] = 0x00000004;
CYCLES -= (SCYCLE + NCYCLE); // +2S+1N
break;
}
case 0x4: //Single Data Transfer
case 0x5:
if((cmd & 0x2000090) != 0x2000090)
{
uint32_t base;
uint32_t tbas;
uint32_t oper2;
uint32_t val;
uint32_t rora;
pc_tmp = CPU.USER[15];
CPU.USER[15] += 4;
if(cmd & (1 << 25))
{
shtype = ((cmd >> 5) & 0x3);
if(cmd & (1 << 4))
{
shift = ((cmd >> 8) & 0xF);
shift = (CPU.USER[shift] & 0xFF);
CPU.USER[15] += 4;
}
else
{
shift = ((cmd >> 7) & 0x1F);
if(!shift)
{
if(shtype)
{
if(shtype == 3)
shtype++;
else
shift = 32;
}
}
}
oper2 = ARM_SHIFT_NSC(CPU.USER[cmd & 0xF],shift,shtype);
}
else
{
oper2 = (cmd & 0x0FFF);
}
tbas = base = CPU.USER[((cmd >> 16) & 0xF)];
if(!(cmd & (1 << 23)))
oper2 = (0 - oper2);
if(cmd & (1 << 24))
tbas = base = (base + oper2);
else
base = (base + oper2);
if(cmd & (1 << 20)) //load
{
if(cmd & (1 << 22)) //bytes
{
val = mreadb(tbas);
}
else //words/halfwords
{
rora = tbas & 3;
val = mreadw(tbas);
if(rora && !CPU.MAS_Access_Exept)
val = ROTR(val,rora*8);
}
CPU.USER[15] = pc_tmp;
if(CPU.MAS_Access_Exept)
{
arm_data_abort();
break;
}
if(((cmd >> 12) & 0xF) == 0xF)
CYCLES -= (SCYCLE + NCYCLE); // +1S+1N ifR15 load
CYCLES -= (NCYCLE + ICYCLE); // +1N+1I
if((cmd & (1 << 21)) || (!(cmd & (1 << 24))))
CPU.USER[(cmd >> 16) & 0xF] = base;
if((cmd & (1 << 21)) && !(cmd & (1 << 24)))
loadusr((cmd >> 12) & 0xF,val);
else
CPU.USER[(cmd >> 12) & 0xF] = val;
}
else // store
{
if((cmd & (1 << 21)) && !(cmd & (1 << 24)))
val = readusr((cmd >> 12) & 0xF);
else
val = CPU.USER[(cmd >> 12) & 0xF];
CPU.USER[15] = pc_tmp;
CYCLES -= (-SCYCLE + 2 * NCYCLE); // 2N
if(cmd & (1 << 22)) //bytes/words
mwriteb(tbas,val);
else //words/halfwords
mwritew(tbas,val);
if(g_SOFT_RESET_PENDING)
break;
if(CPU.MAS_Access_Exept)
{
arm_data_abort();
break;
}
if((cmd & (1 << 21)) || !(cmd & (1 << 24)))
CPU.USER[(cmd >> 16) & 0xF] = base;
}
break;
}
else
{
goto Undefine;
}
case 0x8: //Block Data Transfer
case 0x9:
bdt_core(cmd);
if(CPU.MAS_Access_Exept)
{
arm_data_abort();
break;
}
break;
case 0xa: //BRANCH
case 0xb:
if(cmd & (1 << 24))
CPU.USER[14] = CPU.USER[15];
CPU.USER[15] += ((((cmd & 0x00FFFFFF) | ((cmd & 0x00800000) ? 0xFF000000 : 0)) << 2) + 4);
CYCLES -= (SCYCLE + NCYCLE); //2S+1N
break;
case 0xf: //SWI
decode_swi(cmd);
break;
default: //coprocessor
CPU.SPSR[arm_mode_table[0x1b]] = CPU.CPSR;
SETI(1);
SETM(0x1b);
CPU.USER[14] = CPU.USER[15];
CPU.USER[15] = 0x00000004;
CYCLES -= (SCYCLE + NCYCLE);
break;
}
}
if(g_SOFT_RESET_PENDING)
{
g_SOFT_RESET_PENDING = false;
return -CYCLES;
}
if(!ISF && opera_clio_fiq_needed()/*CPU.nFIQ*/)
{
//Set_madam_FSM(FSM_SUSPENDED);
CPU.nFIQ = false;
CPU.SPSR[arm_mode_table[0x11]] = CPU.CPSR;
SETF(1);
SETI(1);
SETM(0x11);
CPU.USER[14] = (CPU.USER[15] + 4);
CPU.USER[15] = 0x0000001C;
}
return -CYCLES;
}
static
void
mwritew(uint32_t const addr_,
uint32_t const val_)
{
uint32_t const addr = (addr_ & ~3);
if(addr < g_ROM_OVERLAY_CLEAR_LIMIT)
set_rom_overlay(false);
mwritew_internal(addr,val_);
}
static
void
mwritew_internal(uint32_t const addr_,
uint32_t const val_)
{
uint32_t index;
uint32_t const addr = (addr_ & ~3);
if(addr < RAM_SIZE)
{
opera_mem_write32(addr,val_);
return;
}
index = (addr ^ 0x03300000);
if(!(index & ~0x7FF))
{
opera_madam_poke(index,val_);
return;
}
index = (addr ^ 0x03400000);
if(!(index & ~0xFFFF))
{
int reset_kind;
if(clio_xbus_access_aborts(index))
{
CPU.MAS_Access_Exept = true;
return;
}
reset_kind = opera_clio_poke(index,val_);
if(reset_kind)
{
if(reset_kind & OPERA_CLIO_RESET_CLEAR_DIPIR)
opera_3do_dipir_reset();
else
opera_3do_soft_reset();
g_SOFT_RESET_PENDING = true;
}
return;
}
index = (addr ^ 0x03200000);
if(!(index & ~0xFFFFF))
{
opera_sport_write_access(index,val_);
return;
}
index = (addr ^ 0x03100000);
if(!(index & ~0xFFFFF))
{
if(index & 0x80000)
opera_diag_port_send(val_);
else if(index & 0x40000)
NVRAM[(index >> 2) & NVRAM_SIZE_MASK] = (uint8_t)val_;
}
}
static
uint32_t
mreadw(uint32_t const addr_)
{
int32_t index;
uint32_t const addr = (addr_ & ~3);
if(addr < g_CPU_DIRECT_RAM_LIMIT)
return opera_mem_read32(addr);
if(addr < g_ROM_OVERLAY_READ_LIMIT)
return *(uint32_t*)&ROM[addr & ROM1_SIZE_MASK];
index = (addr ^ 0x03300000);
if(!(index & ~0xFFFFF))
return opera_madam_peek(index);
index = (addr ^ 0x03400000);
if(!(index & ~0xFFFFF))
{
if(clio_xbus_access_aborts(index))
{
CPU.MAS_Access_Exept = true;
return 0;
}
return opera_clio_peek(index);
}
index = (addr ^ 0x03200000);
if(!(index & ~0xFFFFF))
{
if(!(index & ~0x1FFF))
return (opera_sport_set_source(index),0);
return 0xBADACCE5;
}
/* Standard ROM */
index = (addr ^ 0x03000000);
if(!(index & ~ROM1_SIZE_MASK))
return *(uint32_t*)&ROM[index];
/* ANVIL ROM */
index = (addr ^ 0x06000000);
if(!(index & ~ROM1_SIZE_MASK))
return *(uint32_t*)&ROM[index];
index = (addr ^ 0x03100000);
if(!(index & ~0xFFFFF))
{
if(index & 0x80000)
return opera_diag_port_get();
else if(index & 0x40000)
return NVRAM[(index >> 2) & NVRAM_SIZE_MASK];
}
/* MAS_Access_Exept = true; */
return 0xBADACCE5;
}
static
void
mwriteb(uint32_t const addr_,
uint8_t const val_)
{
int32_t index;
if(addr_ < g_ROM_OVERLAY_CLEAR_LIMIT)
set_rom_overlay(false);
if(addr_ < RAM_SIZE)
{
opera_mem_write8(addr_,val_);
return;
}
index = (addr_ ^ 0x03100003);
if(!(index & ~0xFFFFF))
{
if((index & 0x40000) == 0x40000)
{
NVRAM[(index >> 2) & NVRAM_SIZE_MASK] = val_;
return;
}
}
}
static
uint32_t
mreadb(uint32_t const addr_)
{
int32_t index;
if(addr_ < g_CPU_DIRECT_RAM_LIMIT)
return opera_mem_read8(addr_);
if(addr_ < g_ROM_OVERLAY_READ_LIMIT)
{
#if IS_BIG_ENDIAN
return ROM[addr_ & ROM1_SIZE_MASK];
#else
return ROM[(addr_ ^ 3) & ROM1_SIZE_MASK];
#endif
}
/* Standard ROM */
index = (addr_ ^ 0x03000003);
if(!(index & ~ROM1_SIZE_MASK))
return ROM[index];
/* ANVIL ROM */
index = (addr_ ^ 0x06000003);
if(!(index & ~ROM1_SIZE_MASK))
return ROM[index];
index = (addr_ ^ 0x03100003);
if(!(index & ~0xFFFFF))
{
if((index & 0x40000) == 0x40000)
return NVRAM[(index >> 2) & NVRAM_SIZE_MASK];
}
/* MAS_Access_Exept = true; */
return 0xBADACCE5;
}
static
void
loadusr(uint32_t const n_,
uint32_t const val_)
{
if(n_ == 15)
{
CPU.USER[15] = val_;
return;
}
switch(arm_mode_table[(CPU.CPSR & 0x1F) | 0x10])
{
case ARM_MODE_USER:
CPU.USER[n_] = val_;
break;
case ARM_MODE_FIQ:
if(n_ > 7)
CPU.CASH[n_ - 8] = val_;
else
CPU.USER[n_] = val_;
break;
case ARM_MODE_IRQ:
case ARM_MODE_ABT:
case ARM_MODE_UND:
case ARM_MODE_SVC:
if(n_ > 12)
CPU.CASH[n_ - 8] = val_;
else
CPU.USER[n_] = val_;
break;
}
}
static
uint32_t
readusr(uint32_t const n_)
{
if(n_ == 15)
return CPU.USER[15];
switch(arm_mode_table[CPU.CPSR & 0x1F])
{
case ARM_MODE_USER:
return CPU.USER[n_];
case ARM_MODE_FIQ:
if(n_ > 7)
return CPU.CASH[n_ - 8];
return CPU.USER[n_];
case ARM_MODE_IRQ:
case ARM_MODE_ABT:
case ARM_MODE_UND:
case ARM_MODE_SVC:
if(n_ > 12)
return CPU.CASH[n_ - 8];
return CPU.USER[n_];
}
return 0;
}
void
opera_io_write(uint32_t const addr_,
uint32_t const val_)
{
/*
External (non-ARM) writers bypass the ROM overlay handling: the overlay
is unmapped by CPU writes only, and external writers are expected to
target registers or DRAM beneath the overlay.
*/
mwritew_internal(addr_,val_);
}