mirror of
https://github.com/libretro/opera-libretro.git
synced 2026-11-07 12:37:52 -10:00
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.
2212 lines
51 KiB
C
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_);
|
|
}
|