exmemwb_mem.c 12.92 KiB
#include "decode.h"
#include "exmemwb.h"
#include <stdlib.h>
///--- Load/store multiple operations
///--------------------------------------------///
// LDM - Load multiple registers from the stack
u32 ldm() {
diss_printf("ldm r%u!, {0x%X}\n", decoded.rN, decoded.reg_list);
u32 numLoaded = 0;
u32 rNWritten = (1 << decoded.rN) & decoded.reg_list;
u32 address = cpu_get_gpr(decoded.rN);
for (int i = 0; i < 8; ++i) {
int mask = 1 << i;
if (decoded.reg_list & mask) {
u32 data = 0;
simLoadData(address, &data);
cpu_set_gpr(i, data);
address += 4;
++numLoaded;
}
}
if (rNWritten == 0)
cpu_set_gpr(decoded.rN, address);
return 1 + numLoaded;
}
// STM - Store multiple registers to the stack
u32 stm() {
diss_printf("stm r%u!, {0x%X}\n", decoded.rN, decoded.reg_list);
u32 numStored = 0;
u32 address = cpu_get_gpr(decoded.rN);
for (int i = 0; i < 8; ++i) {
int mask = 1 << i;
if (decoded.reg_list & mask) {
if (i == decoded.rN && numStored == 0) {
fprintf(stderr, "Error: Malformed instruction!\n");
sim_exit(1);
}
u32 data = cpu_get_gpr(i);
simStoreData(address, data);
address += 4;
++numStored;
}
}
cpu_set_gpr(decoded.rN, address);
return 1 + numStored;
}
///--- Stack operations --------------------------------------------///
// Pop multiple reg values from the stack and update SP
u32 pop() {
diss_printf("pop {0x%X}\n", decoded.reg_list);
u32 numLoaded = 0;
u32 address = cpu_get_sp();
for (int i = 0; i < 16; ++i) {
int mask = 1 << i;
if (decoded.reg_list & mask) {
u32 data = 0;
simLoadData(address, &data);
cpu_set_gpr(i, data);
++numLoaded;
if (i == 15)
takenBranch = 1;
address += 4;
}
// Skip constant 0s
if (i == 7)
i = 14;
}
cpu_set_sp(address);
return 1 + numLoaded + takenBranch ? TIMING_PC_UPDATE : 0;
}
// Push multiple reg values to the stack and update SP
u32 push() {
diss_printf("push {0x%4.4X}\n", decoded.reg_list);
u32 numStored = 0;
u32 address = cpu_get_sp();
for (int i = 14; i >= 0; --i) {
int mask = 1 << i;
if (decoded.reg_list & mask) {
address -= 4;
u32 data = cpu_get_gpr(i);
simStoreData(address, data);
++numStored;
}
// Skip constant 0s
if (i == 14)
i = 8;
}
cpu_set_sp(address);
return 1 + numStored;
}
///--- Single load operations --------------------------------------------///
// LDR - Load from offset from register
u32 ldr_i() {
diss_printf("ldr r%u, [r%u, #0x%X]\n", decoded.rD, decoded.rN,
decoded.imm << 2);
u32 base = cpu_get_gpr(decoded.rN);
u32 offset = zeroExtend32(decoded.imm << 2);
u32 effectiveAddress = base + offset;
u32 result = 0;
simLoadData(effectiveAddress, &result);
cpu_set_gpr(decoded.rD, result);
return TIMING_MEM;
}
// LDR - Load from offset from SP
u32 ldr_sp() {
diss_printf("ldr r%u, [SP, #0x%X]\n", decoded.rD, decoded.imm << 2);
u32 base = cpu_get_sp();
u32 offset = zeroExtend32(decoded.imm << 2);
u32 effectiveAddress = base + offset;
u32 result = 0;
simLoadData(effectiveAddress, &result);
cpu_set_gpr(decoded.rD, result);
return TIMING_MEM;
}
// LDR - Load from offset from PC
u32 ldr_lit() {
diss_printf("ldr r%u, [PC, #%d]\n", decoded.rD, decoded.imm << 2);
u32 base = cpu_get_pc() & 0xFFFFFFFC;
u32 offset = zeroExtend32(decoded.imm << 2);
u32 effectiveAddress = base + offset;
u32 result = 0;
simLoadData(effectiveAddress, &result);
cpu_set_gpr(decoded.rD, result);
return TIMING_MEM;
}
// LDR - Load from an offset from a reg based on another reg value
u32 ldr_r() {
diss_printf("ldr r%u, [r%u, r%u]\n", decoded.rD, decoded.rN, decoded.rM);
u32 base = cpu_get_gpr(decoded.rN);
u32 offset = cpu_get_gpr(decoded.rM);
u32 effectiveAddress = base + offset;
u32 result = 0;
simLoadData(effectiveAddress, &result);
cpu_set_gpr(decoded.rD, result);
return TIMING_MEM;
}
// LDRB - Load byte from offset from register
u32 ldrb_i() {
diss_printf("ldrb r%u, [r%u, #0x%X]\n", decoded.rD, decoded.rN, decoded.imm);
u32 base = cpu_get_gpr(decoded.rN);
u32 offset = zeroExtend32(decoded.imm);
u32 effectiveAddress = base + offset;
u32 effectiveAddressWordAligned = effectiveAddress & ~0x3;
u32 result = 0;
simLoadData(effectiveAddressWordAligned, &result);
// Select the correct byte
switch (effectiveAddress & 0x3) {
case 0:
break;
case 1:
result >>= 8;
break;
case 2:
result >>= 16;
break;
case 3:
result >>= 24;
}
result = zeroExtend32(result & 0xFF);
cpu_set_gpr(decoded.rD, result);
return TIMING_MEM;
}
// LDRB - Load byte from an offset from a reg based on another reg value
u32 ldrb_r() {
diss_printf("ldrb r%u, [r%u, r%u]\n", decoded.rD, decoded.rN, decoded.rM);
u32 base = cpu_get_gpr(decoded.rN);
u32 offset = cpu_get_gpr(decoded.rM);
u32 effectiveAddress = base + offset;
u32 effectiveAddressWordAligned = effectiveAddress & ~0x3;
u32 result = 0;
simLoadData(effectiveAddressWordAligned, &result);
// Select the correct byte
switch (effectiveAddress & 0x3) {
case 0:
break;
case 1:
result >>= 8;
break;
case 2:
result >>= 16;
break;
case 3:
result >>= 24;
}
result = zeroExtend32(result & 0xFF);
cpu_set_gpr(decoded.rD, result);
return TIMING_MEM;
}
// LDRH - Load halfword from offset from register
u32 ldrh_i() {
diss_printf("ldrh r%u, [r%u, #0x%X]\n", decoded.rD, decoded.rN, decoded.imm);
u32 base = cpu_get_gpr(decoded.rN);
u32 offset = zeroExtend32(decoded.imm << 1);
u32 effectiveAddress = base + offset;
u32 effectiveAddressWordAligned = effectiveAddress & ~0x3;
u32 result = 0;
simLoadData(effectiveAddressWordAligned, &result);
// Select the correct halfword
switch (effectiveAddress & 0x2) {
case 0:
break;
default:
result >>= 16;
break;
}
result = zeroExtend32(result & 0xFFFF);
cpu_set_gpr(decoded.rD, result);
return TIMING_MEM;
}
// LDRH - Load halfword from an offset from a reg based on another reg value
u32 ldrh_r() {
diss_printf("ldrh r%u, [r%u, r%u]\n", decoded.rD, decoded.rN, decoded.rM);
u32 base = cpu_get_gpr(decoded.rN);
u32 offset = cpu_get_gpr(decoded.rM);
u32 effectiveAddress = base + offset;
u32 effectiveAddressWordAligned = effectiveAddress & ~0x3;
u32 result = 0;
simLoadData(effectiveAddressWordAligned, &result);
// Select the correct halfword
switch (effectiveAddress & 0x2) {
case 0:
break;
default:
result >>= 16;
break;
}
result = zeroExtend32(result & 0xFFFF);
cpu_set_gpr(decoded.rD, result);
return TIMING_MEM;
}
// LDRSB - Load signed byte from an offset from a reg based on another reg value
u32 ldrsb_r() {
diss_printf("ldrsb r%u, [r%u, r%u]\n", decoded.rD, decoded.rN, decoded.rM);
u32 base = cpu_get_gpr(decoded.rN);
u32 offset = cpu_get_gpr(decoded.rM);
u32 effectiveAddress = base + offset;
u32 effectiveAddressWordAligned = effectiveAddress & ~0x3;
u32 result = 0;
simLoadData(effectiveAddressWordAligned, &result);
// Select the correct byte
switch (effectiveAddress & 0x3) {
case 0:
break;
case 1:
result >>= 8;
break;
case 2:
result >>= 16;
break;
case 3:
result >>= 24;
}
result = signExtend32(result & 0xFF, 8);
cpu_set_gpr(decoded.rD, result);
return TIMING_MEM;
}
// LDRSH - Load signed halfword from an offset from a reg based on another reg
// value
u32 ldrsh_r() {
diss_printf("ldrsh r%u, [r%u, r%u]\n", decoded.rD, decoded.rN, decoded.rM);
u32 base = cpu_get_gpr(decoded.rN);
u32 offset = cpu_get_gpr(decoded.rM);
u32 effectiveAddress = base + offset;
u32 effectiveAddressWordAligned = effectiveAddress & ~0x3;
u32 result = 0;
simLoadData(effectiveAddressWordAligned, &result);
// Select the correct halfword
switch (effectiveAddress & 0x2) {
case 0:
break;
default:
result >>= 16;
}
result = signExtend32(result & 0xFFFF, 16);
cpu_set_gpr(decoded.rD, result);
return TIMING_MEM;
}
///--- Single store operations --------------------------------------------///
// STR - Store to offset from register
u32 str_i() {
diss_printf("str r%u, [r%u, #%d]\n", decoded.rD, decoded.rN,
decoded.imm << 2);
u32 base = cpu_get_gpr(decoded.rN);
u32 offset = zeroExtend32(decoded.imm << 2);
u32 effectiveAddress = base + offset;
simStoreData(effectiveAddress, cpu_get_gpr(decoded.rD));
#if PRINT_STORES_WITH_STATE
printf("write: %08X %08X\n", effectiveAddress, cpu_get_gpr(decoded.rD));
#endif
return TIMING_MEM;
}
// STR - Store to offset from SP
u32 str_sp() {
diss_printf("str r%u, [SP, #%d]\n", decoded.rD, decoded.imm << 2);
u32 base = cpu_get_sp();
u32 offset = zeroExtend32(decoded.imm << 2);
u32 effectiveAddress = base + offset;
simStoreData(effectiveAddress, cpu_get_gpr(decoded.rD));
#if PRINT_STORES_WITH_STATE
printf("write: %08X %08X\n", effectiveAddress, cpu_get_gpr(decoded.rD));
#endif
return TIMING_MEM;
}
// STR - Store to an offset from a reg based on another reg value
u32 str_r() {
diss_printf("str r%u, [r%u, r%u]\n", decoded.rD, decoded.rN, decoded.rM);
u32 base = cpu_get_gpr(decoded.rN);
u32 offset = cpu_get_gpr(decoded.rM);
u32 effectiveAddress = base + offset;
simStoreData(effectiveAddress, cpu_get_gpr(decoded.rD));
#if PRINT_STORES_WITH_STATE
printf("write: %08X %08X\n", effectiveAddress, cpu_get_gpr(decoded.rD));
#endif
return TIMING_MEM;
}
// STRB - Store byte to offset from register
u32 strb_i() {
diss_printf("strb r%u, [r%u, #0x%X]\n", decoded.rD, decoded.rN, decoded.imm);
u32 base = cpu_get_gpr(decoded.rN);
u32 offset = zeroExtend32(decoded.imm);
u32 effectiveAddress = base + offset;
u32 effectiveAddressWordAligned = effectiveAddress & ~0x3;
u32 data = cpu_get_gpr(decoded.rD) & 0xFF;
u32 orig;
simLoadData_internal(effectiveAddressWordAligned, &orig, 1);
// Select the correct byte
switch (effectiveAddress & 0x3) {
case 0:
orig = (orig & 0xFFFFFF00) | (data << 0);
break;
case 1:
orig = (orig & 0xFFFF00FF) | (data << 8);
break;
case 2:
orig = (orig & 0xFF00FFFF) | (data << 16);
break;
case 3:
orig = (orig & 0x00FFFFFF) | (data << 24);
}
simStoreData(effectiveAddressWordAligned, orig);
#if PRINT_STORES_WITH_STATE
printf("write: %08X %08X\n", effectiveAddressWordAligned, orig);
#endif
return TIMING_MEM;
}
// STRB - Store byte to an offset from a reg based on another reg value
u32 strb_r() {
diss_printf("strb r%u, [r%u, r%u]\n", decoded.rD, decoded.rN, decoded.rM);
u32 base = cpu_get_gpr(decoded.rN);
u32 offset = cpu_get_gpr(decoded.rM);
u32 effectiveAddress = base + offset;
u32 effectiveAddressWordAligned = effectiveAddress & ~0x3;
u32 data = cpu_get_gpr(decoded.rD) & 0xFF;
u32 orig;
simLoadData_internal(effectiveAddressWordAligned, &orig, 1);
// Select the correct byte
switch (effectiveAddress & 0x3) {
case 0:
orig = (orig & 0xFFFFFF00) | (data << 0);
break;
case 1:
orig = (orig & 0xFFFF00FF) | (data << 8);
break;
case 2:
orig = (orig & 0xFF00FFFF) | (data << 16);
break;
case 3:
orig = (orig & 0x00FFFFFF) | (data << 24);
}
simStoreData(effectiveAddressWordAligned, orig);
#if PRINT_STORES_WITH_STATE
printf("write: %08X %08X\n", effectiveAddressWordAligned, orig);
#endif
return TIMING_MEM;
}
// STRH - Store halfword to offset from register
u32 strh_i() {
diss_printf("strh r%u, [r%u, #0x%X]\n", decoded.rD, decoded.rN, decoded.imm);
u32 base = cpu_get_gpr(decoded.rN);
u32 offset = zeroExtend32(decoded.imm << 1);
u32 effectiveAddress = base + offset;
u32 effectiveAddressWordAligned = effectiveAddress & ~0x3;
u32 data = cpu_get_gpr(decoded.rD) & 0xFFFF;
u32 orig;
simLoadData_internal(effectiveAddressWordAligned, &orig, 1);
// Select the correct byte
switch (effectiveAddress & 0x2) {
case 0:
orig = (orig & 0xFFFF0000) | (data << 0);
break;
default:
orig = (orig & 0x0000FFFF) | (data << 16);
}
simStoreData(effectiveAddressWordAligned, orig);
#if PRINT_STORES_WITH_STATE
printf("write: %08X %08X\n", effectiveAddressWordAligned, orig);
#endif
return TIMING_MEM;
}
// STRH - Store halfword to an offset from a reg based on another reg value
u32 strh_r() {
diss_printf("strh r%u, [r%u, r%u]\n", decoded.rD, decoded.rN, decoded.rM);
u32 base = cpu_get_gpr(decoded.rN);
u32 offset = cpu_get_gpr(decoded.rM);
u32 effectiveAddress = base + offset;
u32 effectiveAddressWordAligned = effectiveAddress & ~0x3;
u32 data = cpu_get_gpr(decoded.rD) & 0xFFFF;
u32 orig;
simLoadData_internal(effectiveAddressWordAligned, &orig, 1);
// Select the correct byte
switch (effectiveAddress & 0x2) {
case 0:
orig = (orig & 0xFFFF0000) | (data << 0);
break;
default:
orig = (orig & 0x0000FFFF) | (data << 16);
}
simStoreData(effectiveAddressWordAligned, orig);
#if PRINT_STORES_WITH_STATE
printf("write: %08X %08X\n", effectiveAddressWordAligned, orig);
#endif
return TIMING_MEM;
}