mirror of
https://github.com/libretro/Mu.git
synced 2026-02-13 21:24:19 +00:00
Compare commits
39 Commits
removeOldA
...
tungstenCS
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3de2c67cae | ||
|
|
1d693ae10d | ||
|
|
1b3336c9af | ||
|
|
4d88e893e0 | ||
|
|
2238274f51 | ||
|
|
5b93a54b2b | ||
|
|
c89da48739 | ||
|
|
b6093dc8ca | ||
|
|
48ba518086 | ||
|
|
372d23e534 | ||
|
|
125e85d73b | ||
|
|
d70e2b02b9 | ||
|
|
a2058e2d73 | ||
|
|
c7f8816541 | ||
|
|
6c53ade08b | ||
|
|
d8abf3643d | ||
|
|
a80b783c71 | ||
|
|
06acab1345 | ||
|
|
8482c98010 | ||
|
|
3c645bd0bd | ||
|
|
368757023c | ||
|
|
4d803c6fc0 | ||
|
|
cb9093c5f4 | ||
|
|
fa5f85e77c | ||
|
|
1b5f4d215f | ||
|
|
55a2e97bc5 | ||
|
|
0a58122f4b | ||
|
|
5a7191cd47 | ||
|
|
cee653b1a0 | ||
|
|
0bf5f9f981 | ||
|
|
23f943ce8b | ||
|
|
9e98f29215 | ||
|
|
ee7fd8e676 | ||
|
|
34e4b9ce7c | ||
|
|
4e7a08e6df | ||
|
|
ae6efb6b7b | ||
|
|
f1d1c3f051 | ||
|
|
03101a5214 | ||
|
|
f4a6d6c1b0 |
15
.circleci/config.yml
Normal file
15
.circleci/config.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
# Use the latest 2.1 version of CircleCI pipeline processing engine, see https://circleci.com/docs/2.0/configuration-reference/
|
||||
version: 2.1
|
||||
|
||||
# Use a package of configuration called an orb, see https://circleci.com/docs/2.0/orb-intro/
|
||||
orbs:
|
||||
# Declare a dependency on the welcome-orb
|
||||
welcome: circleci/welcome-orb@0.3.1
|
||||
|
||||
# Orchestrate or schedule a set of jobs, see https://circleci.com/docs/2.0/workflows/
|
||||
workflows:
|
||||
# Name the workflow "Welcome"
|
||||
Welcome:
|
||||
# Run the welcome/run job in its own container
|
||||
jobs:
|
||||
- welcome/run
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -3,7 +3,7 @@
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
*.pro.user
|
||||
*.pro.user*
|
||||
*_emscripten.bc
|
||||
/libretroBuildSystem/obj
|
||||
/libretroBuildSystem/libs
|
||||
@@ -14,7 +14,6 @@
|
||||
/tools/palm/hwTestSuite/TstSuite
|
||||
/tools/palm/hwTestSuite/TstSuite-sections.s
|
||||
/tools/palm/hwTestSuite/TstSuite-sections.ld
|
||||
/tools/palm/muExpansionDriver/devStuff/*
|
||||
/tools/palm/muExpansionDriver/*.bin
|
||||
/tools/palm/muExpansionDriver/*.grc
|
||||
/tools/palm/muExpansionDriver/*.prc
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Cinco De Mayo(May 5th), 17:00 GMT, hopefully with some good ARM stuff
|
||||
Cinco De Mayo(May 5th), 17:00 GMT, hopefully with some good ARM stuff(delayed again)
|
||||
Easter release, 17:00 GMT, hopefully with some good ARM stuff(delayed)
|
||||
|
||||
Done:
|
||||
|
||||
BIN
bugs/shadowTheifBug/Screenshot_20190426-220702.png
Normal file
BIN
bugs/shadowTheifBug/Screenshot_20190426-220702.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 123 KiB |
4
bugs/shadowTheifBug/text.txt
Normal file
4
bugs/shadowTheifBug/text.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
Whenever I register ShadowThief, this happens:
|
||||
|
||||
It happens on any version of Mu
|
||||
PHEM/ST is fine
|
||||
@@ -1,6 +1,4 @@
|
||||
CPU:
|
||||
sysTrapPceNativeCall, ARM Emulator
|
||||
VFP(floating point) coprocessor for ARM
|
||||
Test what bits of IMR are writable
|
||||
sandbox isnt notified about switches to Thumb mode
|
||||
function name finders for ARMv5 and Thumb are unimplemented
|
||||
@@ -22,10 +20,9 @@ ICR POL(1,2,3,6) may flip the pin value as well as the interrupt, POL5 does not
|
||||
PWM2 is not implemented
|
||||
while it is stated that the PLL is turned off "30 clocks" after the DISPLL bit is set, it doesnt state where the clocks come from, CLK32 or SYSCLK
|
||||
UART1 USTCNT1
|
||||
PLLFSR has a hack that makes busy wait loops finish faster by toggling the CLK32 bit on read, for power button issue
|
||||
should also not transfer data to SD card when MOSI, MISO or SPICLK1 are disabled
|
||||
port d data register INT* bits seem to have there data bits cleared when an edge triggered interrupt is cleared(according to MC68VZ328UM.pdf Page 10-15)
|
||||
sound plays too long when the category's menu is selected on the home screen and the pen is pressed and held in the middle of the screen(only happens there, other views of the same type don't have this issue)(effected by CPU speed, turning it up reduces the duration of the excess squeal)
|
||||
sound plays too long when the category's menu is selected on the home screen and the pen is pressed and held in the middle of the screen(only happens there, other views of the same type don't have this issue)(effected by CPU speed, turning it up reduces the duration of the excess squeal)(the home menu may be changing the clock speed when this menu is released since it refreshes a bunch of icons which is CPU intensive, if it is it would distort the audio since sysclks are only calculated at the begining of a frame the new value would not take effect until a new frame began, and since it works properly sometimes that is probably when the change occurs just at the end of the last frame and the sysclk duration refresh happens at the intended time due to the new frame starting)
|
||||
trying to beam anything will lock up the OS
|
||||
|
||||
Memory:
|
||||
@@ -46,6 +43,7 @@ SD Card:
|
||||
block specific write protect bits don't work
|
||||
IRQ2 may actually be attached to the SD Card data out pin, triggering an interrupt when a 0 is received to process the return data
|
||||
need to test if Port J Pin 3(SD card chip select) is attached to Port D Pin 5(SD Card Status(IRQ2))(pinouts.ru says chip select and card detect are the same line)
|
||||
This thing: case SEND_STATUS://HACK, need to add real write protection, this command is also how the host reads the value of the little switch on the side
|
||||
|
||||
Debug tools:
|
||||
ADS7846 channels can't be read in single reference mode in hwTestSuite
|
||||
@@ -54,9 +52,16 @@ MakePalmBitmap:
|
||||
|
||||
Other:
|
||||
Support for Silkyboard 2 silkscreen:https://archive.org/details/tucows_228076_SILKYBOARD_II
|
||||
|
||||
Qt port dosent support Windows touchscreen input
|
||||
Qt GUI dosent resize properly with 320x320 framebuffer
|
||||
File Installer isnt working yet
|
||||
Qt dosent have a hybrid file/folder selector so apps will always have to be launched from folders for now
|
||||
|
||||
Fixed:
|
||||
PLLFSR has a hack that makes busy wait loops finish faster by toggling the CLK32 bit on read, for power button issue(removed, power button works as expected if you wait at least 2 seconds before pushing it again(makes sence that it cnat read the new press while its turning off the CPU))
|
||||
(CPU)VFP(floating point) coprocessor for ARM(dont think the ARM Palms even used VFP)
|
||||
(Other)need to get rid of buffer_t, its not used much anyway
|
||||
ARM dynarec SIGSEGVs on exit(pushing play in the debugger still lets it continue, dont think this is a bug)
|
||||
(Feature)need to add FEATURE_DURABLE which will ignore CPU errors, increasing stability at the cost of bugginess
|
||||
(RetroArch port)set the alarm LED using retro_set_led_state_t on RetroArch
|
||||
(CPU I/O)SPI1 SPISPC register(this is just a clock divider used to slow transfers to allow the voltages to settle, since the emu has no voltages all transfers are just instant)
|
||||
|
||||
@@ -463,7 +463,7 @@ endif
|
||||
|
||||
include $(BUILD_DIR)/Makefile.common
|
||||
|
||||
OBJECTS := $(SOURCES_C:.c=.o) $(SOURCES_ASM:.S=.o)
|
||||
OBJECTS := $(SOURCES_C:.c=.o) $(SOURCES_CXX:.cpp=.o) $(SOURCES_ASM:.S=.o)
|
||||
|
||||
DEFINES = $(COREDEFINES)
|
||||
|
||||
@@ -507,7 +507,7 @@ endif
|
||||
ifeq ($(platform), theos_ios)
|
||||
COMMON_FLAGS := -DIOS -DARM $(COMMON_DEFINES) $(INCFLAGS) -I$(THEOS_INCLUDE_PATH) -Wno-error
|
||||
$(LIBRARY_NAME)_CFLAGS += $(COMMON_FLAGS)
|
||||
${LIBRARY_NAME}_FILES = $(SOURCES_C) $(SOURCES_ASM)
|
||||
${LIBRARY_NAME}_FILES = $(SOURCES_C) $(SOURCES_CXX) $(SOURCES_ASM)
|
||||
include $(THEOS_MAKE_PATH)/library.mk
|
||||
else
|
||||
all: $(TARGET)
|
||||
|
||||
@@ -20,6 +20,31 @@ ifneq (,$(findstring msvc20,$(platform)))
|
||||
COREDEFINES += -Dinline=_inline -DINLINE=_inline
|
||||
endif
|
||||
|
||||
ifneq (,$(findstring windows,$(platform)))
|
||||
# Windows
|
||||
EMU_SUPPORT_PALM_OS5 = 1
|
||||
EMU_OS := windows
|
||||
ifneq (,$(findstring x64,$(platform)))
|
||||
EMU_ARCH := x86_64
|
||||
else
|
||||
EMU_ARCH := x86_32
|
||||
endif
|
||||
else ifneq (,$(findstring armv,$(platform)))
|
||||
# ARM Linux
|
||||
EMU_SUPPORT_PALM_OS5 = 1
|
||||
EMU_ARCH := armv7
|
||||
EMU_OS := linux
|
||||
else ifneq (,$(filter unix osx osx_x86 osx_x86_64 linux_x86 linux_x86_64 linux-portable_x86 linux-portable_x86_64,$(platform)))
|
||||
# x86_* Linux
|
||||
EMU_SUPPORT_PALM_OS5 = 1
|
||||
ifneq (,$(filter osx osx_x86_64 linux_x86_64 linux-portable_x86_64,$(platform)))
|
||||
EMU_ARCH := x86_64
|
||||
else
|
||||
EMU_ARCH := x86_32
|
||||
endif
|
||||
EMU_OS := linux
|
||||
endif
|
||||
|
||||
ifneq (,$(filter ps3 sncps3 psl1ght ngc wii wiiu,$(platform)))
|
||||
COREDEFINES += -DEMU_BIG_ENDIAN
|
||||
else ifeq ($(platform), osx)
|
||||
@@ -28,6 +53,12 @@ else ifeq ($(platform), osx)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(EMU_SUPPORT_PALM_OS5), 1)
|
||||
ifneq ($(platform), android_jni)
|
||||
CXXFLAGS += -std=c++11
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(EMU_PATH)/makefile.all
|
||||
|
||||
COREDEFINES += $(EMU_DEFINES)
|
||||
@@ -35,8 +66,8 @@ COREDEFINES += $(EMU_DEFINES)
|
||||
SOURCES_C := $(CORE_DIR)/libretro.c \
|
||||
$(CORE_DIR)/cursors.c \
|
||||
$(EMU_SOURCES_C)
|
||||
|
||||
SOURCES_ASM :=
|
||||
SOURCES_CXX := $(EMU_SOURCES_CXX)
|
||||
SOURCES_ASM := $(EMU_SOURCES_ASM)
|
||||
|
||||
ifneq ($(STATIC_LINKING), 1)
|
||||
SOURCES_C += $(LIBRETRO_COMM_DIR)/compat/compat_strl.c \
|
||||
|
||||
@@ -4,6 +4,21 @@ CORE_DIR := $(LOCAL_PATH)/..
|
||||
|
||||
GIT_VERSION := " $(shell git rev-parse --short HEAD || echo unknown)"
|
||||
|
||||
platform = android_jni
|
||||
|
||||
# Palm OS 5 support
|
||||
EMU_SUPPORT_PALM_OS5 = 1
|
||||
EMU_OS := linux
|
||||
ifeq ($(TARGET_ARCH_ABI), x86)
|
||||
EMU_ARCH := x86_32
|
||||
else ifeq ($(TARGET_ARCH_ABI), x86_64)
|
||||
EMU_ARCH := x86_64
|
||||
else ifeq ($(TARGET_ARCH_ABI), armeabi-v7a)
|
||||
EMU_ARCH := armv7
|
||||
else ifeq ($(TARGET_ARCH_ABI), arm64-v8a)
|
||||
EMU_ARCH := armv8
|
||||
endif
|
||||
|
||||
include $(CORE_DIR)/build/Makefile.common
|
||||
|
||||
COREFLAGS := -ffast-math -funroll-loops -D__LIBRETRO__ -DINLINE=inline -DFRONTEND_SUPPORTS_RGB565 $(INCFLAGS) $(COREDEFINES)
|
||||
@@ -14,7 +29,7 @@ endif
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := retro
|
||||
LOCAL_SRC_FILES := $(SOURCES_C) $(SOURCES_ASM)
|
||||
LOCAL_SRC_FILES := $(SOURCES_C) $(SOURCES_CXX) $(SOURCES_ASM)
|
||||
LOCAL_CFLAGS := $(COREFLAGS)
|
||||
LOCAL_LDFLAGS := -Wl,-version-script=$(CORE_DIR)/build/link.T
|
||||
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
APP_ABI := all
|
||||
APP_PLATFORM := android-18
|
||||
APP_CPPFLAGS += -std=c++11
|
||||
|
||||
@@ -284,8 +284,10 @@ void retro_run(void){
|
||||
}
|
||||
|
||||
bool retro_load_game(const struct retro_game_info *info){
|
||||
buffer_t rom;
|
||||
buffer_t bootloader;
|
||||
uint8_t* romData;
|
||||
uint32_t romSize;
|
||||
uint8_t* bootloaderData;
|
||||
uint32_t bootloaderSize;
|
||||
char bootloaderPath[PATH_MAX_LENGTH];
|
||||
char saveRamPath[PATH_MAX_LENGTH];
|
||||
char sdImgPath[PATH_MAX_LENGTH];
|
||||
@@ -304,37 +306,37 @@ bool retro_load_game(const struct retro_game_info *info){
|
||||
environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &systemDir);
|
||||
environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &saveDir);
|
||||
|
||||
rom.data = info->data;
|
||||
rom.size = info->size;
|
||||
romData = info->data;
|
||||
romSize = info->size;
|
||||
|
||||
//bootloader
|
||||
strlcpy(bootloaderPath, systemDir, PATH_MAX_LENGTH);
|
||||
strlcat(bootloaderPath, "/bootloader-en-m515.rom", PATH_MAX_LENGTH);
|
||||
bootloaderFile = filestream_open(bootloaderPath, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
if(bootloaderFile){
|
||||
bootloader.size = filestream_get_size(bootloaderFile);
|
||||
bootloader.data = malloc(bootloader.size);
|
||||
bootloaderSize = filestream_get_size(bootloaderFile);
|
||||
bootloaderData = malloc(bootloaderSize);
|
||||
|
||||
if(bootloader.data)
|
||||
filestream_read(bootloaderFile, bootloader.data, bootloader.size);
|
||||
if(bootloaderData)
|
||||
filestream_read(bootloaderFile, bootloaderData, bootloaderSize);
|
||||
else
|
||||
bootloader.size = 0;
|
||||
bootloaderSize = 0;
|
||||
filestream_close(bootloaderFile);
|
||||
}
|
||||
else{
|
||||
bootloader.data = NULL;
|
||||
bootloader.size = 0;
|
||||
bootloaderData = NULL;
|
||||
bootloaderSize = 0;
|
||||
}
|
||||
|
||||
//updates the emulator configuration
|
||||
check_variables(true);
|
||||
|
||||
error = emulatorInit(rom, bootloader, emuFeatures);
|
||||
error = emulatorInit(romData, romSize, bootloaderData, bootloaderSize, emuFeatures);
|
||||
if(error != EMU_ERROR_NONE)
|
||||
return false;
|
||||
|
||||
if(bootloader.data)
|
||||
free(bootloader.data);
|
||||
if(bootloaderData)
|
||||
free(bootloaderData);
|
||||
|
||||
//save RAM
|
||||
strlcpy(saveRamPath, saveDir, PATH_MAX_LENGTH);
|
||||
@@ -353,18 +355,13 @@ bool retro_load_game(const struct retro_game_info *info){
|
||||
strlcat(sdImgPath, "/sd-en-m515.img", PATH_MAX_LENGTH);
|
||||
sdImgFile = filestream_open(sdImgPath, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
if(sdImgFile){
|
||||
buffer_t sdCard;
|
||||
uint32_t sdImgSize = filestream_get_size(sdImgFile);
|
||||
|
||||
//use the NULL, size method because it takes less RAM
|
||||
sdCard.data = NULL;
|
||||
sdCard.size = sdImgSize;
|
||||
//use the NULL, size, NULL method because it takes less RAM
|
||||
|
||||
error = emulatorInsertSdCard(sdCard, false);
|
||||
if(error == EMU_ERROR_NONE){
|
||||
sdCard = emulatorGetSdCardBuffer();
|
||||
filestream_read(sdImgFile, sdCard.data, sdImgSize);
|
||||
}
|
||||
error = emulatorInsertSdCard(NULL, sdImgSize, NULL);
|
||||
if(error == EMU_ERROR_NONE)
|
||||
filestream_read(sdImgFile, palmSdCard.flashChipData, sdImgSize);
|
||||
|
||||
filestream_close(sdImgFile);
|
||||
}
|
||||
@@ -387,7 +384,6 @@ void retro_unload_game(void){
|
||||
char sdImgPath[PATH_MAX_LENGTH];
|
||||
struct RFILE* saveRamFile;
|
||||
struct RFILE* sdImgFile;
|
||||
buffer_t sdCard = emulatorGetSdCardBuffer();
|
||||
|
||||
environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &saveDir);
|
||||
|
||||
@@ -402,12 +398,12 @@ void retro_unload_game(void){
|
||||
}
|
||||
|
||||
//SD card
|
||||
if(sdCard.data){
|
||||
if(palmSdCard.flashChipData){
|
||||
strlcpy(sdImgPath, saveDir, PATH_MAX_LENGTH);
|
||||
strlcat(sdImgPath, "/sd-en-m515.img", PATH_MAX_LENGTH);
|
||||
sdImgFile = filestream_open(sdImgPath, RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
if(sdImgFile){
|
||||
filestream_write(sdImgFile, sdCard.data, sdCard.size);
|
||||
filestream_write(sdImgFile, palmSdCard.flashChipData, palmSdCard.flashChipSize);
|
||||
filestream_close(sdImgFile);
|
||||
}
|
||||
}
|
||||
@@ -431,21 +427,11 @@ size_t retro_serialize_size(void){
|
||||
}
|
||||
|
||||
bool retro_serialize(void *data, size_t size){
|
||||
buffer_t saveBuffer;
|
||||
|
||||
saveBuffer.data = (uint8_t*)data;
|
||||
saveBuffer.size = size;
|
||||
|
||||
return emulatorSaveState(saveBuffer);
|
||||
return emulatorSaveState(data, size);
|
||||
}
|
||||
|
||||
bool retro_unserialize(const void *data, size_t size){
|
||||
buffer_t saveBuffer;
|
||||
|
||||
saveBuffer.data = (uint8_t*)data;
|
||||
saveBuffer.size = size;
|
||||
|
||||
return emulatorLoadState(saveBuffer);
|
||||
return emulatorLoadState(data, size);
|
||||
}
|
||||
|
||||
void* retro_get_memory_data(unsigned id){
|
||||
|
||||
@@ -36,28 +36,32 @@ windows{
|
||||
QMAKE_CXXFLAGS += -fopenmp
|
||||
QMAKE_LFLAGS += -fopenmp
|
||||
}
|
||||
DEFINES += EMU_MULTITHREADED
|
||||
DEFINES += EMU_MULTITHREADED EMU_MANAGE_HOST_CPU_PIPELINE
|
||||
CONFIG += cpu_x86_32 # this should be auto detected in the future
|
||||
}
|
||||
|
||||
macx{
|
||||
QMAKE_CFLAGS += -std=c89 -D__STDBOOL_H -Dinline= -Dbool=char -Dtrue=1 -Dfalse=0 # tests C89 mode
|
||||
ICON = macos/Mu.icns
|
||||
QMAKE_INFO_PLIST = macos/Info.plist
|
||||
DEFINES += EMU_MULTITHREADED
|
||||
DEFINES += EMU_MULTITHREADED EMU_MANAGE_HOST_CPU_PIPELINE
|
||||
CONFIG += cpu_x86_64 # Mac OS is only x86_64
|
||||
}
|
||||
|
||||
linux-g++{
|
||||
QMAKE_CFLAGS += -fopenmp
|
||||
QMAKE_CXXFLAGS += -fopenmp
|
||||
QMAKE_LFLAGS += -fopenmp
|
||||
DEFINES += EMU_MULTITHREADED
|
||||
DEFINES += EMU_MULTITHREADED EMU_MANAGE_HOST_CPU_PIPELINE
|
||||
CONFIG += cpu_x86_64 # this should be auto detected in the future
|
||||
}
|
||||
|
||||
android{
|
||||
QMAKE_CFLAGS += -fopenmp
|
||||
QMAKE_CXXFLAGS += -fopenmp
|
||||
QMAKE_LFLAGS += -fopenmp
|
||||
DEFINES += EMU_MULTITHREADED
|
||||
DEFINES += EMU_MULTITHREADED EMU_MANAGE_HOST_CPU_PIPELINE
|
||||
CONFIG += cpu_armv7 # this should be auto detected in the future
|
||||
}
|
||||
|
||||
|
||||
@@ -65,9 +69,10 @@ CONFIG(debug, debug|release){
|
||||
# debug build, be accurate, fail hard, and add logging
|
||||
DEFINES += EMU_DEBUG EMU_CUSTOM_DEBUG_LOG_HANDLER EMU_SANDBOX
|
||||
# DEFINES += EMU_SANDBOX_LOG_MEMORY_ACCESSES # checks all reads and writes to memory and logs certain events
|
||||
DEFINES += EMU_SANDBOX_OPCODE_LEVEL_DEBUG # for breakpoints
|
||||
DEFINES += EMU_SANDBOX_LOG_JUMPS # log large jumps
|
||||
DEFINES += EMU_SANDBOX_LOG_APIS # for printing sysTrap* calls, EMU_SANDBOX_OPCODE_LEVEL_DEBUG must be on too
|
||||
# DEFINES += EMU_SANDBOX_OPCODE_LEVEL_DEBUG # for breakpoints
|
||||
# DEFINES += EMU_SANDBOX_LOG_JUMPS # log large jumps
|
||||
# DEFINES += EMU_SANDBOX_LOG_APIS # for printing sysTrap* calls, EMU_SANDBOX_OPCODE_LEVEL_DEBUG must be on too
|
||||
CONFIG += no_dynarec # easier to debug with
|
||||
macx|linux-g++{
|
||||
# also check for any buffer overflows and memory leaks
|
||||
# -fsanitize=undefined,leak
|
||||
@@ -82,7 +87,107 @@ CONFIG(debug, debug|release){
|
||||
|
||||
support_palm_os5{
|
||||
DEFINES += EMU_SUPPORT_PALM_OS5 # the Qt build will not be supporting anything too slow to run OS 5
|
||||
# SOURCES +=
|
||||
DEFINES += SUPPORT_LINUX # forces the dynarec to use accurate mode and disable Nspire OS hacks
|
||||
|
||||
!no_dynarec{
|
||||
# Windows is only supported in 32 bit mode right now(this is a limitation of the dynarec)
|
||||
# iOS needs IS_IOS_BUILD set, but the Qt port does not support iOS currently
|
||||
|
||||
cpu_x86_32{
|
||||
SOURCES += \
|
||||
../../src/armv5te/translate_x86.c \
|
||||
../../src/armv5te/asmcode_x86.S
|
||||
}
|
||||
else{
|
||||
# x86 has this implemented in asmcode_x86.S
|
||||
SOURCES += \
|
||||
../../src/armv5te/asmcode.c
|
||||
}
|
||||
|
||||
cpu_x86_64{
|
||||
SOURCES += \
|
||||
../../src/armv5te/translate_x86_64.c \
|
||||
../../src/armv5te/asmcode_x86_64.S
|
||||
}
|
||||
|
||||
cpu_armv7{
|
||||
SOURCES += \
|
||||
../../src/armv5te/translate_arm.cpp \
|
||||
../../src/armv5te/asmcode_arm.S
|
||||
}
|
||||
|
||||
cpu_armv8{
|
||||
SOURCES += \
|
||||
../../src/armv5te/translate_aarch64.cpp \
|
||||
../../src/armv5te/asmcode_aarch64.S
|
||||
}
|
||||
}
|
||||
else{
|
||||
# use platform independant C with no dynarec
|
||||
SOURCES += \
|
||||
../../src/armv5te/asmcode.c
|
||||
DEFINES += NO_TRANSLATION
|
||||
}
|
||||
|
||||
windows{
|
||||
SOURCES += \
|
||||
../../src/armv5te/os/os-win32.c
|
||||
}
|
||||
|
||||
macx|linux-g++|android{
|
||||
SOURCES += \
|
||||
../../src/armv5te/os/os-linux.c
|
||||
}
|
||||
|
||||
SOURCES += \
|
||||
../../src/pxa255/pxa255_mem.c \
|
||||
../../src/pxa255/pxa255_DMA.c \
|
||||
../../src/pxa255/pxa255_DSP.c \
|
||||
../../src/pxa255/pxa255_GPIO.c \
|
||||
../../src/pxa255/pxa255_IC.c \
|
||||
../../src/pxa255/pxa255_LCD.c \
|
||||
../../src/pxa255/pxa255_PwrClk.c \
|
||||
../../src/pxa255/pxa255_RTC.c \
|
||||
../../src/pxa255/pxa255_TIMR.c \
|
||||
../../src/pxa255/pxa255_UART.c \
|
||||
../../src/pxa255/pxa255.c \
|
||||
../../src/armv5te/arm_interpreter.cpp \
|
||||
../../src/armv5te/cpu.cpp \
|
||||
../../src/armv5te/coproc.cpp \
|
||||
../../src/armv5te/emuVarPool.c \
|
||||
../../src/armv5te/thumb_interpreter.cpp \
|
||||
../../src/armv5te/mem.c \
|
||||
../../src/armv5te/mmu.c \
|
||||
../../src/tungstenCBus.c
|
||||
|
||||
HEADERS += \
|
||||
../../src/pxa255/pxa255_CPU.h \
|
||||
../../src/pxa255/pxa255_mem.h \
|
||||
../../src/pxa255/pxa255_DMA.h \
|
||||
../../src/pxa255/pxa255_DSP.h \
|
||||
../../src/pxa255/pxa255_GPIO.h \
|
||||
../../src/pxa255/pxa255_IC.h \
|
||||
../../src/pxa255/pxa255_LCD.h \
|
||||
../../src/pxa255/pxa255_PwrClk.h \
|
||||
../../src/pxa255/pxa255_RTC.h \
|
||||
../../src/pxa255/pxa255_TIMR.h \
|
||||
../../src/pxa255/pxa255_UART.h \
|
||||
../../src/pxa255/pxa255_types.h \
|
||||
../../src/pxa255/pxa255_math64.h \
|
||||
../../src/pxa255/pxa255.h \
|
||||
../../src/armv5te/os/os.h \
|
||||
../../src/armv5te/asmcode.h \
|
||||
../../src/armv5te/bitfield.h \
|
||||
../../src/armv5te/cpu.h \
|
||||
../../src/armv5te/emu.h \
|
||||
../../src/armv5te/mem.h \
|
||||
../../src/armv5te/translate.h \
|
||||
../../src/armv5te/cpudefs.h \
|
||||
../../src/armv5te/debug.h \
|
||||
../../src/armv5te/mmu.h \
|
||||
../../src/armv5te/armsnippets.h \
|
||||
../../src/armv5te/literalpool.h \
|
||||
../../src/tungstenCBus.h
|
||||
}
|
||||
|
||||
CONFIG += c++11
|
||||
@@ -90,12 +195,19 @@ CONFIG += c++11
|
||||
INCLUDEPATH += $$PWD/qt-common/include
|
||||
|
||||
SOURCES += \
|
||||
../../src/dbvz.c \
|
||||
../../src/fileLauncher/fatFs/diskio.c \
|
||||
../../src/fileLauncher/fatFs/ff.c \
|
||||
../../src/fileLauncher/fatFs/ffsystem.c \
|
||||
../../src/fileLauncher/fatFs/ffunicode.c \
|
||||
../../src/fileLauncher/launcher.c \
|
||||
debugviewer.cpp \
|
||||
emuwrapper.cpp \
|
||||
main.cpp \
|
||||
mainwindow.cpp \
|
||||
statemanager.cpp \
|
||||
touchscreen.cpp \
|
||||
settingsmanager.cpp \
|
||||
../../src/audio/blip_buf.c \
|
||||
../../src/debug/sandbox.c \
|
||||
../../src/ads7846.c \
|
||||
@@ -112,16 +224,21 @@ SOURCES += \
|
||||
../../src/m68k/m68kopnz.c \
|
||||
../../src/m68k/m68kops.c \
|
||||
../../src/expansionHardware.c \
|
||||
settingsmanager.cpp \
|
||||
../../src/m515Bus.c \
|
||||
../../src/dbvzRegisters.c
|
||||
../../src/m515Bus.c
|
||||
|
||||
HEADERS += \
|
||||
../../src/dbvz.h \
|
||||
../../src/fileLauncher/fatFs/diskio.h \
|
||||
../../src/fileLauncher/fatFs/ff.h \
|
||||
../../src/fileLauncher/fatFs/ffconf.h \
|
||||
../../src/fileLauncher/launcher.h \
|
||||
../../src/pxa255/pxa255Accessors.c.h \
|
||||
debugviewer.h \
|
||||
emuwrapper.h \
|
||||
mainwindow.h \
|
||||
statemanager.h \
|
||||
touchscreen.h \
|
||||
settingsmanager.h \
|
||||
../../src/audio/blip_buf.h \
|
||||
../../src/debug/sandbox.h \
|
||||
../../src/debug/sandboxTrapNumToName.c.h \
|
||||
@@ -148,11 +265,9 @@ HEADERS += \
|
||||
../../src/expansionHardware.h \
|
||||
../../src/sdCardAccessors.c.h \
|
||||
../../src/sdCardCrcTables.c.h \
|
||||
settingsmanager.h \
|
||||
../../src/m515Bus.h \
|
||||
../../src/dbvzRegisterAccessors.c.h \
|
||||
../../src/dbvzTiming.c.h \
|
||||
../../src/dbvzRegisters.h
|
||||
|
||||
FORMS += \
|
||||
mainwindow.ui \
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include "debugviewer.h"
|
||||
#include "ui_debugviewer.h"
|
||||
|
||||
#include <QVector>
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "mainwindow.h"
|
||||
@@ -44,24 +44,6 @@ int64_t DebugViewer::numberFromString(QString str, bool negativeAllowed){
|
||||
return value;
|
||||
}
|
||||
|
||||
QString DebugViewer::stringFromNumber(int64_t number, bool hex, uint32_t forcedZeros){
|
||||
QString numString;
|
||||
|
||||
if(hex){
|
||||
numString += QString::number(number, 16).toUpper();
|
||||
while(numString.length() < (int)forcedZeros)numString.push_front("0");
|
||||
numString.push_front("0x");
|
||||
}
|
||||
else{
|
||||
numString += QString::number(qAbs(number), 10);
|
||||
while(numString.length() < (int)forcedZeros)numString.push_front("0");
|
||||
if(number < 0)
|
||||
numString.push_front("-");
|
||||
}
|
||||
|
||||
return numString;
|
||||
}
|
||||
|
||||
void DebugViewer::debugRadioButtonHandler(){
|
||||
switch(bitsPerEntry){
|
||||
case 8:
|
||||
@@ -94,12 +76,12 @@ void DebugViewer::on_debugGetHexValues_clicked(){
|
||||
|
||||
if(address != INT64_MIN && length != INT64_MIN && length != 0 && address + bits / 8 * length - 1 <= 0xFFFFFFFF){
|
||||
for(int64_t count = 0; count < length; count++){
|
||||
uint64_t data = emu.getEmulatorMemory(address, bits);
|
||||
uint64_t data = emu.debugGetEmulatorMemory(address, bits);
|
||||
QString value;
|
||||
value += stringFromNumber(address, true, 8);
|
||||
value += QString::asprintf("0x%08X", (uint32_t)address);
|
||||
value += ":";
|
||||
if(data != UINT64_MAX)
|
||||
value += stringFromNumber(data, true, bits / 8 * 2);
|
||||
value += QString::asprintf("0x%0*X", bits / 8 * 2, (uint32_t)data);
|
||||
else
|
||||
value += "Unsafe Access";
|
||||
ui->debugValueList->addItem(value);
|
||||
@@ -143,22 +125,16 @@ void DebugViewer::on_debugDump_clicked(){
|
||||
}
|
||||
|
||||
void DebugViewer::on_debugShowRegisters_clicked(){
|
||||
std::vector<uint32_t> registers = ((MainWindow*)parentWidget())->emu.getCpuRegisters();
|
||||
EmuWrapper& emu = ((MainWindow*)parentWidget())->emu;
|
||||
|
||||
ui->debugValueList->clear();
|
||||
for(uint8_t dRegs = 0; dRegs < 8; dRegs++)
|
||||
ui->debugValueList->addItem("D" + stringFromNumber(dRegs, false, 0) + ":" + stringFromNumber(registers[dRegs], true, 8));
|
||||
for(uint8_t aRegs = 0; aRegs < 8; aRegs++)
|
||||
ui->debugValueList->addItem("A" + stringFromNumber(aRegs, false, 0) + ":" + stringFromNumber(registers[8 + aRegs], true, 8));
|
||||
ui->debugValueList->addItem("SP:" + stringFromNumber(registers[15], true, 8));
|
||||
ui->debugValueList->addItem("PC:" + stringFromNumber(registers[16], true, 8));
|
||||
ui->debugValueList->addItem("SR:" + stringFromNumber(registers[17], true, 4));
|
||||
ui->debugValueList->addItems(emu.debugGetCpuRegisterString().split('\n'));
|
||||
}
|
||||
|
||||
void DebugViewer::on_debugShowDebugLogs_clicked(){
|
||||
EmuWrapper& emu = ((MainWindow*)parentWidget())->emu;
|
||||
std::vector<QString>& debugStrings = emu.getDebugStrings();
|
||||
std::vector<uint64_t>& duplicateCallCount = emu.getDuplicateCallCount();
|
||||
QVector<QString>& debugStrings = emu.debugGetLogEntrys();
|
||||
QVector<uint64_t>& duplicateCallCount = emu.debugGetDuplicateLogEntryCount();
|
||||
int64_t length = numberFromString(ui->debugLength->text(), true/*negative allowed*/);
|
||||
|
||||
ui->debugValueList->clear();
|
||||
@@ -181,6 +157,6 @@ void DebugViewer::on_debugShowDebugLogs_clicked(){
|
||||
void DebugViewer::on_debugEraseDebugLogs_clicked(){
|
||||
EmuWrapper& emu = ((MainWindow*)parentWidget())->emu;
|
||||
|
||||
emu.getDebugStrings().clear();
|
||||
emu.getDuplicateCallCount().clear();
|
||||
emu.debugGetLogEntrys().clear();
|
||||
emu.debugGetDuplicateLogEntryCount().clear();
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ public:
|
||||
|
||||
private:
|
||||
int64_t numberFromString(QString str, bool negativeAllowed);
|
||||
QString stringFromNumber(int64_t number, bool hex, uint32_t forcedZeros = 0);
|
||||
|
||||
private slots:
|
||||
void debugRadioButtonHandler();
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QPixmap>
|
||||
#include <QImage>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QByteArray>
|
||||
#include <QDateTime>
|
||||
#include <QDate>
|
||||
@@ -10,25 +13,31 @@
|
||||
#include <new>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "emuwrapper.h"
|
||||
#include "../../src/emulator.h"
|
||||
#include "../../src/fileLauncher/launcher.h"
|
||||
|
||||
extern "C"{
|
||||
#include "../../src/flx68000.h"
|
||||
#if defined(EMU_SUPPORT_PALM_OS5)
|
||||
#include "../../src/pxa255/pxa255.h"
|
||||
#endif
|
||||
#include "../../src/debug/sandbox.h"
|
||||
}
|
||||
|
||||
|
||||
#define MAX_LOG_ENTRY_LENGTH 200
|
||||
|
||||
|
||||
static bool alreadyExists = false;//there can only be one of this class since it wrappers C code
|
||||
|
||||
static std::vector<QString> debugStrings;
|
||||
static std::vector<uint64_t> duplicateCallCount;
|
||||
uint32_t frontendDebugStringSize;
|
||||
char* frontendDebugString;
|
||||
static QVector<QString> debugStrings;
|
||||
static QVector<uint64_t> duplicateCallCount;
|
||||
uint32_t frontendDebugStringSize;
|
||||
char* frontendDebugString;
|
||||
|
||||
|
||||
void frontendHandleDebugPrint(){
|
||||
@@ -62,8 +71,8 @@ EmuWrapper::EmuWrapper(){
|
||||
emuPaused = false;
|
||||
emuNewFrameReady = false;
|
||||
|
||||
frontendDebugString = new char[200];
|
||||
frontendDebugStringSize = 200;
|
||||
frontendDebugString = new char[MAX_LOG_ENTRY_LENGTH];
|
||||
frontendDebugStringSize = MAX_LOG_ENTRY_LENGTH;
|
||||
}
|
||||
|
||||
EmuWrapper::~EmuWrapper(){
|
||||
@@ -74,6 +83,9 @@ EmuWrapper::~EmuWrapper(){
|
||||
frontendDebugStringSize = 0;
|
||||
debugStrings.clear();
|
||||
duplicateCallCount.clear();
|
||||
|
||||
//allow creating a new emu class after the old one is closed
|
||||
alreadyExists = false;
|
||||
}
|
||||
|
||||
void EmuWrapper::emuThreadRun(){
|
||||
@@ -94,46 +106,54 @@ void EmuWrapper::emuThreadRun(){
|
||||
}
|
||||
}
|
||||
|
||||
void EmuWrapper::writeOutSaves(){
|
||||
if(emuRamFilePath != ""){
|
||||
QFile ramFile(emuRamFilePath);
|
||||
uint32_t emuRamSize = emulatorGetRamSize();
|
||||
uint8_t* emuRamData = new uint8_t[emuRamSize];
|
||||
|
||||
emulatorSaveRam(emuRamData, emuRamSize);
|
||||
|
||||
//save out RAM before exit
|
||||
if(ramFile.open(QFile::WriteOnly | QFile::Truncate)){
|
||||
ramFile.write((const char*)emuRamData, emuRamSize);
|
||||
ramFile.close();
|
||||
}
|
||||
|
||||
delete[] emuRamData;
|
||||
}
|
||||
if(emuSdCardFilePath != ""){
|
||||
uint32_t emuSdCardSize = emulatorGetSdCardSize();
|
||||
uint8_t* emuSdCardData = new uint8_t[emuSdCardSize];
|
||||
|
||||
if(emulatorGetSdCardData(emuSdCardData, emuSdCardSize) == EMU_ERROR_NONE){
|
||||
QFile sdCardFile(emuSdCardFilePath);
|
||||
|
||||
//save out SD card before exit
|
||||
if(sdCardFile.open(QFile::WriteOnly | QFile::Truncate)){
|
||||
sdCardFile.write((const char*)emuSdCardData, emuSdCardSize);
|
||||
sdCardFile.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t EmuWrapper::init(const QString& romPath, const QString& bootloaderPath, const QString& ramPath, const QString& sdCardPath, uint32_t features){
|
||||
if(!emuRunning && !emuInited){
|
||||
//start emu
|
||||
uint32_t error;
|
||||
QFile romFile(romPath);
|
||||
QFile bootloaderFile(bootloaderPath);
|
||||
QByteArray romData;
|
||||
QByteArray bootloaderData;
|
||||
buffer_t romBuff;
|
||||
buffer_t bootloaderBuff;
|
||||
|
||||
if(romFile.open(QFile::ReadOnly | QFile::ExistingOnly)){
|
||||
romData = romFile.readAll();
|
||||
romFile.close();
|
||||
|
||||
romBuff.data = (uint8_t*)romData.data();
|
||||
romBuff.size = romData.size();
|
||||
}
|
||||
else{
|
||||
if(!romFile.open(QFile::ReadOnly | QFile::ExistingOnly))
|
||||
return EMU_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if(bootloaderPath != ""){
|
||||
if(bootloaderFile.open(QFile::ReadOnly | QFile::ExistingOnly)){
|
||||
bootloaderData = bootloaderFile.readAll();
|
||||
bootloaderFile.close();
|
||||
|
||||
bootloaderBuff.data = (uint8_t*)bootloaderData.data();
|
||||
bootloaderBuff.size = bootloaderData.size();
|
||||
}
|
||||
else{
|
||||
if(!bootloaderFile.open(QFile::ReadOnly | QFile::ExistingOnly))
|
||||
return EMU_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
else{
|
||||
bootloaderBuff.data = NULL;
|
||||
bootloaderBuff.size = 0;
|
||||
}
|
||||
|
||||
error = emulatorInit(romBuff, bootloaderBuff, features);
|
||||
error = emulatorInit((uint8_t*)romFile.readAll().data(), romFile.size(), (uint8_t*)bootloaderFile.readAll().data(), bootloaderFile.size(), features);
|
||||
if(error == EMU_ERROR_NONE){
|
||||
QTime now = QTime::currentTime();
|
||||
|
||||
@@ -142,41 +162,18 @@ uint32_t EmuWrapper::init(const QString& romPath, const QString& bootloaderPath,
|
||||
if(ramPath != ""){
|
||||
QFile ramFile(ramPath);
|
||||
|
||||
if(ramFile.exists()){
|
||||
if(ramFile.open(QFile::ReadOnly | QFile::ExistingOnly)){
|
||||
QByteArray ramData;
|
||||
buffer_t emuRam;
|
||||
|
||||
ramData = ramFile.readAll();
|
||||
ramFile.close();
|
||||
|
||||
emuRam.data = (uint8_t*)ramData.data();
|
||||
emuRam.size = ramData.size();
|
||||
|
||||
//only copy in data if its the correct size, the file will be overwritten on exit with the devices RAM either way
|
||||
//(changing RAM size requires a factory reset for now and always will when going from 128mb back to 16mb)
|
||||
if(emuRam.size == emulatorGetRamSize())
|
||||
emulatorLoadRam(emuRam);
|
||||
}
|
||||
if(ramFile.open(QFile::ReadOnly | QFile::ExistingOnly)){
|
||||
emulatorLoadRam((uint8_t*)ramFile.readAll().data(), ramFile.size());
|
||||
ramFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
if(sdCardPath != ""){
|
||||
QFile sdCardFile(sdCardPath);
|
||||
|
||||
if(sdCardFile.exists()){
|
||||
if(sdCardFile.open(QFile::ReadOnly | QFile::ExistingOnly)){
|
||||
QByteArray sdCardData;
|
||||
buffer_t newSdCard;
|
||||
|
||||
sdCardData = sdCardFile.readAll();
|
||||
sdCardFile.close();
|
||||
|
||||
newSdCard.data = (uint8_t*)sdCardData.data();
|
||||
newSdCard.size = sdCardData.size();
|
||||
|
||||
emulatorInsertSdCard(newSdCard, false);
|
||||
}
|
||||
if(sdCardFile.open(QFile::ReadOnly | QFile::ExistingOnly)){
|
||||
emulatorInsertSdCard((uint8_t*)sdCardFile.readAll().data(), sdCardFile.size(), NULL);
|
||||
sdCardFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,6 +191,10 @@ uint32_t EmuWrapper::init(const QString& romPath, const QString& bootloaderPath,
|
||||
else{
|
||||
return error;
|
||||
}
|
||||
|
||||
romFile.close();
|
||||
if(bootloaderPath != "")
|
||||
bootloaderFile.close();
|
||||
}
|
||||
|
||||
return EMU_ERROR_NONE;
|
||||
@@ -205,36 +206,8 @@ void EmuWrapper::exit(){
|
||||
if(emuThread.joinable())
|
||||
emuThread.join();
|
||||
if(emuInited){
|
||||
if(emuRamFilePath != ""){
|
||||
QFile ramFile(emuRamFilePath);
|
||||
buffer_t emuRam;
|
||||
emuRam.size = emulatorGetRamSize();
|
||||
emuRam.data = new uint8_t[emuRam.size];
|
||||
|
||||
emulatorSaveRam(emuRam);
|
||||
|
||||
//save out RAM before exit
|
||||
if(ramFile.open(QFile::WriteOnly | QFile::Truncate)){
|
||||
ramFile.write((const char*)emuRam.data, emuRam.size);
|
||||
ramFile.close();
|
||||
}
|
||||
|
||||
delete[] emuRam.data;
|
||||
}
|
||||
if(emuSdCardFilePath != ""){
|
||||
buffer_t emuSdCard = emulatorGetSdCardBuffer();
|
||||
|
||||
if(emuSdCard.data){
|
||||
QFile sdCardFile(emuSdCardFilePath);
|
||||
|
||||
//save out SD card before exit
|
||||
if(sdCardFile.open(QFile::WriteOnly | QFile::Truncate)){
|
||||
sdCardFile.write((const char*)emuSdCard.data, emuSdCard.size);
|
||||
sdCardFile.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
emulatorExit();
|
||||
writeOutSaves();
|
||||
emulatorDeinit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,6 +244,139 @@ void EmuWrapper::reset(bool hard){
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t EmuWrapper::bootFromFileOrDirectory(const QString& mainPath){
|
||||
bool wasPaused = isPaused();
|
||||
uint32_t error = EMU_ERROR_NONE;
|
||||
QFileInfo pathInfo(mainPath);
|
||||
QStringList paths;
|
||||
QVector<QByteArray> fileDataBuffers;
|
||||
QVector<QByteArray> fileInfoBuffers;
|
||||
launcher_file_t* files;
|
||||
QFile ramFile(mainPath + ".ram");
|
||||
QFile sdCardFile(mainPath + ".sd.img");
|
||||
bool hasSaveRam;
|
||||
bool hasSaveSdCard;
|
||||
int newestBootableFile = -1;
|
||||
|
||||
if(!wasPaused)
|
||||
pause();
|
||||
|
||||
if(pathInfo.isDir()){
|
||||
//get Palm file list
|
||||
QDir dirInfo(mainPath);
|
||||
QStringList filters;
|
||||
|
||||
filters += "*.prc";
|
||||
filters += "*.pdb";
|
||||
filters += "*.pqa";
|
||||
filters += "*.img";
|
||||
|
||||
paths = dirInfo.entryList(filters);
|
||||
|
||||
//make paths full direct paths
|
||||
for(int index = 0; index < paths.length(); index++)
|
||||
paths[index].prepend(mainPath + "/");
|
||||
}
|
||||
else if(pathInfo.exists()){
|
||||
//use single file
|
||||
paths = QStringList(mainPath);
|
||||
}
|
||||
else{
|
||||
//error
|
||||
error = EMU_ERROR_INVALID_PARAMETER;
|
||||
goto errorOccurred;
|
||||
}
|
||||
|
||||
fileDataBuffers.resize(paths.length());
|
||||
fileInfoBuffers.resize(paths.length());
|
||||
files = new launcher_file_t[paths.length()];
|
||||
|
||||
memset(files, 0x00, sizeof(launcher_file_t) * paths.length());
|
||||
|
||||
for(int index = 0; index < paths.length(); index++){
|
||||
QFile appFile(paths[index]);
|
||||
|
||||
if(appFile.open(QFile::ReadOnly | QFile::ExistingOnly)){
|
||||
QString suffix = QFileInfo(paths[index]).suffix().toLower();
|
||||
|
||||
fileDataBuffers[index] = appFile.readAll();
|
||||
appFile.close();
|
||||
|
||||
files[index].fileData = (uint8_t*)fileDataBuffers[index].data();
|
||||
files[index].fileSize = fileDataBuffers[index].size();
|
||||
|
||||
if(suffix == "prc"){
|
||||
files[index].type = LAUNCHER_FILE_TYPE_PRC;
|
||||
newestBootableFile = index;
|
||||
}
|
||||
else if(suffix == "pdb"){
|
||||
files[index].type = LAUNCHER_FILE_TYPE_PDB;
|
||||
}
|
||||
else if(suffix == "pqa"){
|
||||
files[index].type = LAUNCHER_FILE_TYPE_PQA;
|
||||
newestBootableFile = index;
|
||||
}
|
||||
else if(suffix == "img"){
|
||||
QFile infoFile(paths[index].remove(paths[index].size() - 3, 3) + "info");//swap "img" for "info"
|
||||
|
||||
if(infoFile.open(QFile::ReadOnly | QFile::ExistingOnly)){
|
||||
fileInfoBuffers[index] = infoFile.readAll();
|
||||
files[index].infoData = (uint8_t*)fileInfoBuffers[index].data();
|
||||
files[index].infoSize = fileInfoBuffers[index].size();
|
||||
infoFile.close();
|
||||
}
|
||||
files[index].type = LAUNCHER_FILE_TYPE_IMG;
|
||||
newestBootableFile = index;
|
||||
}
|
||||
else{
|
||||
error = EMU_ERROR_INVALID_PARAMETER;
|
||||
goto errorOccurred;
|
||||
}
|
||||
}
|
||||
else{
|
||||
error = EMU_ERROR_RESOURCE_LOCKED;
|
||||
goto errorOccurred;
|
||||
}
|
||||
}
|
||||
|
||||
if(newestBootableFile != -1){
|
||||
files[newestBootableFile].boot = true;
|
||||
}
|
||||
else{
|
||||
error = EMU_ERROR_INVALID_PARAMETER;
|
||||
goto errorOccurred;
|
||||
}
|
||||
|
||||
//save the current data for the last program launched, or the standard device image if none where launched
|
||||
writeOutSaves();
|
||||
|
||||
//its OK if these fail, the buffer will just be NULL, 0 if they do
|
||||
hasSaveRam = ramFile.open(QFile::ReadOnly | QFile::ExistingOnly);
|
||||
hasSaveSdCard = sdCardFile.open(QFile::ReadOnly | QFile::ExistingOnly);
|
||||
|
||||
error = launcherLaunch(files, paths.length(), hasSaveRam ? (uint8_t*)ramFile.readAll().data() : NULL, hasSaveRam ? ramFile.size() : 0, hasSaveSdCard ? (uint8_t*)sdCardFile.readAll().data() : NULL, hasSaveSdCard ? sdCardFile.size() : 0);
|
||||
if(error != EMU_ERROR_NONE)
|
||||
goto errorOccurred;
|
||||
|
||||
//its OK if these fail
|
||||
if(hasSaveRam)
|
||||
ramFile.close();
|
||||
if(hasSaveSdCard)
|
||||
sdCardFile.close();
|
||||
|
||||
//everything worked, set output save files
|
||||
emuRamFilePath = mainPath + ".ram";
|
||||
emuSdCardFilePath = mainPath + ".sd.img";
|
||||
|
||||
//need this goto because the emulator must be released before returning
|
||||
errorOccurred:
|
||||
|
||||
if(!wasPaused)
|
||||
resume();
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
uint32_t EmuWrapper::saveState(const QString& path){
|
||||
bool wasPaused = isPaused();
|
||||
uint32_t error = EMU_ERROR_INVALID_PARAMETER;
|
||||
@@ -281,13 +387,11 @@ uint32_t EmuWrapper::saveState(const QString& path){
|
||||
|
||||
//save here
|
||||
if(stateFile.open(QFile::WriteOnly)){
|
||||
buffer_t stateData;
|
||||
uint32_t stateSize = emulatorGetStateSize();
|
||||
uint8_t* stateData = new uint8_t[stateSize];
|
||||
|
||||
stateData.size = emulatorGetStateSize();
|
||||
stateData.data = new uint8_t[stateData.size];
|
||||
|
||||
emulatorSaveState(stateData);//no need to check for errors since the buffer is always the right size
|
||||
stateFile.write((const char*)stateData.data, stateData.size);
|
||||
emulatorSaveState(stateData, stateSize);//no need to check for errors since the buffer is always the right size
|
||||
stateFile.write((const char*)stateData, stateSize);
|
||||
stateFile.close();
|
||||
|
||||
error = EMU_ERROR_NONE;
|
||||
@@ -307,17 +411,11 @@ uint32_t EmuWrapper::loadState(const QString& path){
|
||||
if(!wasPaused)
|
||||
pause();
|
||||
|
||||
if(stateFile.open(QFile::ReadOnly)){
|
||||
QByteArray stateDataBuffer;
|
||||
buffer_t stateData;
|
||||
|
||||
stateDataBuffer = stateFile.readAll();
|
||||
stateFile.close();
|
||||
stateData.data = (uint8_t*)stateDataBuffer.data();
|
||||
stateData.size = stateDataBuffer.size();
|
||||
|
||||
if(emulatorLoadState(stateData))
|
||||
if(stateFile.open(QFile::ReadOnly | QFile::ExistingOnly)){
|
||||
if(emulatorLoadState((uint8_t*)stateFile.readAll().data(), stateFile.size()))
|
||||
error = EMU_ERROR_NONE;
|
||||
stateFile.close();
|
||||
|
||||
}
|
||||
|
||||
if(!wasPaused)
|
||||
@@ -367,7 +465,40 @@ void EmuWrapper::setKeyValue(uint8_t key, bool pressed){
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t EmuWrapper::installApplication(const QString& path){
|
||||
QVector<QString>& EmuWrapper::debugGetLogEntrys(){
|
||||
return debugStrings;
|
||||
}
|
||||
|
||||
QVector<uint64_t>& EmuWrapper::debugGetDuplicateLogEntryCount(){
|
||||
return duplicateCallCount;
|
||||
}
|
||||
|
||||
QString EmuWrapper::debugGetCpuRegisterString(){
|
||||
QString regString = "";
|
||||
|
||||
#if defined(EMU_SUPPORT_PALM_OS5)
|
||||
if(palmEmulatingTungstenC){
|
||||
for(uint8_t regs = 0; regs < 16; regs++)
|
||||
regString += QString::asprintf("R%d:0x%08X\n", regs, pxa255GetRegister(regs));
|
||||
regString.resize(regString.size() - 1);//remove extra '\n'
|
||||
}
|
||||
else{
|
||||
#endif
|
||||
for(uint8_t dRegs = 0; dRegs < 8; dRegs++)
|
||||
regString += QString::asprintf("D%d:0x%08X\n", dRegs, flx68000GetRegister(dRegs));
|
||||
for(uint8_t aRegs = 0; aRegs < 8; aRegs++)
|
||||
regString += QString::asprintf("A%d:0x%08X\n", aRegs, flx68000GetRegister(8 + aRegs));
|
||||
regString += QString::asprintf("SP:0x%08X\n", flx68000GetRegister(15));
|
||||
regString += QString::asprintf("PC:0x%08X\n", flx68000GetRegister(16));
|
||||
regString += QString::asprintf("SR:0x%04X", flx68000GetRegister(17));
|
||||
#if defined(EMU_SUPPORT_PALM_OS5)
|
||||
}
|
||||
#endif
|
||||
|
||||
return regString;
|
||||
}
|
||||
|
||||
uint32_t EmuWrapper::debugInstallApplication(const QString& path){
|
||||
bool wasPaused = isPaused();
|
||||
uint32_t error = EMU_ERROR_INVALID_PARAMETER;
|
||||
QFile appFile(path);
|
||||
@@ -375,15 +506,15 @@ uint32_t EmuWrapper::installApplication(const QString& path){
|
||||
if(!wasPaused)
|
||||
pause();
|
||||
|
||||
if(appFile.open(QFile::ReadOnly)){
|
||||
QByteArray appDataBuffer;
|
||||
buffer_t appData;
|
||||
if(appFile.open(QFile::ReadOnly | QFile::ExistingOnly)){
|
||||
QByteArray appDataBuffer = appFile.readAll();
|
||||
uintptr_t values[2];
|
||||
|
||||
values[0] = (uintptr_t)appDataBuffer.data();
|
||||
values[1] = appDataBuffer.size();
|
||||
|
||||
appDataBuffer = appFile.readAll();
|
||||
appFile.close();
|
||||
appData.data = (uint8_t*)appDataBuffer.data();
|
||||
appData.size = appDataBuffer.size();
|
||||
error = sandboxCommand(SANDBOX_CMD_DEBUG_INSTALL_APP, &appData);
|
||||
error = sandboxCommand(SANDBOX_CMD_DEBUG_INSTALL_APP, values);
|
||||
}
|
||||
|
||||
if(!wasPaused)
|
||||
@@ -392,22 +523,6 @@ uint32_t EmuWrapper::installApplication(const QString& path){
|
||||
return error;
|
||||
}
|
||||
|
||||
std::vector<QString>& EmuWrapper::getDebugStrings(){
|
||||
return debugStrings;
|
||||
}
|
||||
|
||||
std::vector<uint64_t>& EmuWrapper::getDuplicateCallCount(){
|
||||
return duplicateCallCount;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> EmuWrapper::getCpuRegisters(){
|
||||
std::vector<uint32_t> registers;
|
||||
|
||||
for(uint8_t reg = 0; reg < 18; reg++)
|
||||
registers.push_back(flx68000GetRegister(reg));
|
||||
return registers;
|
||||
}
|
||||
|
||||
uint64_t EmuWrapper::getEmulatorMemory(uint32_t address, uint8_t size){
|
||||
uint64_t EmuWrapper::debugGetEmulatorMemory(uint32_t address, uint8_t size){
|
||||
return flx68000ReadArbitraryMemory(address, size);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <QPixmap>
|
||||
#include <QVector>
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../../src/emulator.h"
|
||||
@@ -27,6 +27,7 @@ private:
|
||||
input_t emuInput;
|
||||
|
||||
void emuThreadRun();
|
||||
void writeOutSaves();
|
||||
|
||||
public:
|
||||
enum{
|
||||
@@ -48,6 +49,7 @@ public:
|
||||
void pause();
|
||||
void resume();
|
||||
void reset(bool hard);
|
||||
uint32_t bootFromFileOrDirectory(const QString& mainPath);
|
||||
uint32_t saveState(const QString& path);
|
||||
uint32_t loadState(const QString& path);
|
||||
bool isInited() const{return emuInited;}
|
||||
@@ -56,12 +58,6 @@ public:
|
||||
void setPenValue(float x, float y, bool touched);
|
||||
void setKeyValue(uint8_t key, bool pressed);
|
||||
|
||||
uint32_t installApplication(const QString& path);
|
||||
|
||||
std::vector<QString>& getDebugStrings();
|
||||
std::vector<uint64_t>& getDuplicateCallCount();
|
||||
std::vector<uint32_t> getCpuRegisters();
|
||||
|
||||
uint16_t screenWidth() const{return palmFramebufferWidth;}
|
||||
uint16_t screenHeight() const{return palmFramebufferHeight;}
|
||||
bool newFrameReady() const{return emuNewFrameReady;}
|
||||
@@ -72,5 +68,9 @@ public:
|
||||
const int16_t* getAudioSamples() const{return palmAudio;}
|
||||
bool getPowerButtonLed() const{return palmMisc.powerButtonLed;}
|
||||
|
||||
uint64_t getEmulatorMemory(uint32_t address, uint8_t size);
|
||||
QVector<QString>& debugGetLogEntrys();
|
||||
QVector<uint64_t>& debugGetDuplicateLogEntryCount();
|
||||
QString debugGetCpuRegisterString();
|
||||
uint32_t debugInstallApplication(const QString& path);
|
||||
uint64_t debugGetEmulatorMemory(uint32_t address, uint8_t size);
|
||||
};
|
||||
|
||||
@@ -108,8 +108,9 @@ MainWindow::MainWindow(QWidget* parent) :
|
||||
ui->stateManager->installEventFilter(this);
|
||||
ui->screenshot->installEventFilter(this);
|
||||
ui->settings->installEventFilter(this);
|
||||
ui->install->installEventFilter(this);
|
||||
ui->debugInstall->installEventFilter(this);
|
||||
ui->debugger->installEventFilter(this);
|
||||
ui->bootApp->installEventFilter(this);
|
||||
|
||||
//hide onscreen keys if needed
|
||||
ui->up->setHidden(hideOnscreenKeys);
|
||||
@@ -333,10 +334,11 @@ void MainWindow::on_ctrlBtn_clicked(){
|
||||
ui->power->setEnabled(true);
|
||||
|
||||
ui->screenshot->setEnabled(true);
|
||||
ui->install->setEnabled(true);
|
||||
ui->debugInstall->setEnabled(true);
|
||||
ui->stateManager->setEnabled(true);
|
||||
ui->debugger->setEnabled(true);
|
||||
ui->reset->setEnabled(true);
|
||||
ui->bootApp->setEnabled(true);
|
||||
|
||||
ui->ctrlBtn->setIcon(QIcon(":/buttons/images/pause.svg"));
|
||||
}
|
||||
@@ -356,12 +358,12 @@ void MainWindow::on_ctrlBtn_clicked(){
|
||||
ui->ctrlBtn->repaint();
|
||||
}
|
||||
|
||||
void MainWindow::on_install_clicked(){
|
||||
void MainWindow::on_debugInstall_clicked(){
|
||||
if(emu.isInited()){
|
||||
QString app = QFileDialog::getOpenFileName(this, "Select Application", QDir::root().path(), "Palm OS App (*.prc *.pdb *.pqa)");
|
||||
QString app = QFileDialog::getOpenFileName(this, "Select File", QDir::root().path(), "Palm OS File (*.prc *.pdb *.pqa)");
|
||||
|
||||
if(app != ""){
|
||||
uint32_t error = emu.installApplication(app);
|
||||
uint32_t error = emu.debugInstallApplication(app);
|
||||
|
||||
if(error != EMU_ERROR_NONE)
|
||||
popupErrorDialog("Could not install app, Error:" + QString::number(error));
|
||||
@@ -426,3 +428,16 @@ void MainWindow::on_settings_clicked(){
|
||||
if(wasInited && !wasPaused)
|
||||
emu.resume();
|
||||
}
|
||||
|
||||
void MainWindow::on_bootApp_clicked(){
|
||||
if(emu.isInited()){
|
||||
QString appDir = QFileDialog::getExistingDirectory(this, "Select Directory", QDir::root().path());
|
||||
|
||||
if(appDir != ""){
|
||||
uint32_t error = emu.bootFromFileOrDirectory(appDir);
|
||||
|
||||
if(error != EMU_ERROR_NONE)
|
||||
popupErrorDialog("Could not load apps, Error:" + QString::number(error));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,12 +67,13 @@ private slots:
|
||||
|
||||
//frontend buttons
|
||||
void on_ctrlBtn_clicked();
|
||||
void on_install_clicked();
|
||||
void on_debugInstall_clicked();
|
||||
void on_debugger_clicked();
|
||||
void on_screenshot_clicked();
|
||||
void on_stateManager_clicked();
|
||||
void on_reset_clicked();
|
||||
void on_settings_clicked();
|
||||
void on_bootApp_clicked();
|
||||
|
||||
private:
|
||||
SettingsManager* settingsManager;
|
||||
|
||||
@@ -240,23 +240,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
<widget class="QPushButton" name="debugger">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="mainwindow.qrc">
|
||||
<normaloff>:/buttons/images/debugger.svg</normaloff>:/buttons/images/debugger.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QPushButton" name="addressBook">
|
||||
<property name="enabled">
|
||||
@@ -274,23 +257,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="install">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="mainwindow.qrc">
|
||||
<normaloff>:/buttons/images/install.svg</normaloff>:/buttons/images/install.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="5">
|
||||
<widget class="QPushButton" name="todo">
|
||||
<property name="enabled">
|
||||
@@ -354,6 +320,56 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="5">
|
||||
<widget class="QPushButton" name="debugger">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="mainwindow.qrc">
|
||||
<normaloff>:/buttons/images/debugger.svg</normaloff>:/buttons/images/debugger.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
<widget class="QPushButton" name="debugInstall">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="mainwindow.qrc">
|
||||
<normaloff>:/buttons/images/install.svg</normaloff>:/buttons/images/install.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="bootApp">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Boot</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
14
readme.md
14
readme.md
@@ -1,4 +1,4 @@
|
||||
# Palm m515/OS 4.1 emulator(Mu)
|
||||
# Palm OS emulator(Mu)
|
||||
|
||||
This is a complete restart of my Palm OS emulator, with the last one the code got too messy and the goal too ambitious(to emulate every palm API in software and for compatibility with all Palm OS versions and devices).
|
||||
|
||||
@@ -17,15 +17,17 @@ It is the best Palm brand OS 4 device that has no special hardware builtin(cellp
|
||||
|
||||
The Palm keyboard attachment will likely be emulated later on so the PC keyboard can be used on text fields.
|
||||
|
||||
## What about OS 5
|
||||
## What about OS 5?
|
||||
|
||||
I am planning on adding Tungsten C support eventually.
|
||||
|
||||
## Credits
|
||||
Musashi v3.4 (last version that builds outside of MAME)(68k Core)
|
||||
blip_buf 1.1.0 (Audio Resampler)
|
||||
http://www.iconarchive.com/show/crystal-clear-icons-by-everaldo/App-palm-icon.html (Desktop Icon)
|
||||
http://tango.freedesktop.org/Tango_Icon_Library (Palm Action Buttons, All UI buttons)
|
||||
[Firebird Emu](https://github.com/nspire-emus/firebird) (ARMv5TE Core)
|
||||
[uARM](https://dmitry.gr/?r=05.Projects&proj=07.%20Linux%20on%208bit) (PXA255 CPU Peripherals)
|
||||
[Musashi v3.4](https://github.com/kstenerud/Musashi) (last version that builds outside of MAME)(68k Core)
|
||||
[blip_buf 1.1.0](https://github.com/TASVideos/BizHawk/tree/master/blip_buf) (Audio Resampler)
|
||||
https://www.iconarchive.com/show/crystal-clear-icons-by-everaldo/App-palm-icon.html (Desktop Icon)
|
||||
https://tango.freedesktop.org/Tango_Icon_Library (Palm Action Buttons, All UI buttons)
|
||||
https://www.iconfinder.com/icons/2317746/chip_computer_cpu_device_frequency_microchip_processor_icon (hwTestSuite Icon)
|
||||
https://www.flaticon.com/free-icon/cow_235371#term=cow&page=1&position=13 (muExpDriver Icon)
|
||||
https://findicons.com/icon/164302/cursor (Libretro Port Joystick Cursor)
|
||||
|
||||
@@ -16,6 +16,10 @@ add Palm Tungsten C support
|
||||
------------------------------------------
|
||||
v1.0.0 to v1.1.0(Feb 25 2019 - Easter 2019)
|
||||
|
||||
Core:
|
||||
*PLLFSR timing hack is gone, CLK32 bit now just toggles properly mid CLK32
|
||||
*SD card access routines are now 6<->7X faster
|
||||
|
||||
Qt GUI:
|
||||
*can now set the feature bits
|
||||
*can now set the resource directory
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "emulator.h"
|
||||
#include "portability.h"
|
||||
#include "dbvzRegisters.h"
|
||||
#include "dbvz.h"
|
||||
|
||||
|
||||
bool ads7846PenIrqEnabled;
|
||||
|
||||
537
src/armv5te/arm_interpreter.cpp
Normal file
537
src/armv5te/arm_interpreter.cpp
Normal file
@@ -0,0 +1,537 @@
|
||||
#include <cassert>
|
||||
|
||||
#include "asmcode.h"
|
||||
#include "cpu.h"
|
||||
#include "cpudefs.h"
|
||||
#include "debug.h"
|
||||
#include "mmu.h"
|
||||
|
||||
// Detect overflow after an addition or subtraction
|
||||
#define ADD_OVERFLOW(left, right, sum) ((int32_t)(((left) ^ (sum)) & ((right) ^ (sum))) < 0)
|
||||
#define SUB_OVERFLOW(left, right, sum) ((int32_t)(((left) ^ (right)) & ((left) ^ (sum))) < 0)
|
||||
|
||||
static uint32_t add(uint32_t left, uint32_t right, int carry, int setcc) {
|
||||
uint32_t sum = left + right + carry;
|
||||
if (!setcc)
|
||||
return sum;
|
||||
|
||||
if (sum < left) carry = 1;
|
||||
if (sum > left) carry = 0;
|
||||
arm.cpsr_c = carry;
|
||||
arm.cpsr_v = ADD_OVERFLOW(left, right, sum);
|
||||
return sum;
|
||||
}
|
||||
|
||||
// "uint8_t shift_val" is correct here. If it is a shift by register, only the bottom 8 bits are looked at.
|
||||
static uint32_t shift(uint32_t value, uint8_t shift_type, uint8_t shift_val, bool setcc, bool has_rs)
|
||||
{
|
||||
if(shift_val == 0)
|
||||
{
|
||||
if(unlikely(!has_rs))
|
||||
{
|
||||
switch(shift_type)
|
||||
{
|
||||
case SH_ROR:
|
||||
{
|
||||
// RRX
|
||||
bool carry = arm.cpsr_c;
|
||||
if(setcc) arm.cpsr_c = value & 1;
|
||||
return value >> 1 | uint32_t(carry) << 31;
|
||||
}
|
||||
case SH_ASR:
|
||||
case SH_LSR: // #32 is encoded as LSR #0
|
||||
return shift(value, shift_type, 32, setcc, false);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
else if(likely(shift_val < 32))
|
||||
{
|
||||
switch(shift_type)
|
||||
{
|
||||
case SH_LSL:
|
||||
if(setcc) arm.cpsr_c = (value >> (32 - shift_val)) & 1;
|
||||
return value << shift_val;
|
||||
case SH_LSR:
|
||||
if(setcc) arm.cpsr_c = (value >> (shift_val - 1)) & 1;
|
||||
return value >> shift_val;
|
||||
case SH_ASR:
|
||||
if(setcc) arm.cpsr_c = (value >> (shift_val - 1)) & 1;
|
||||
if(value & (1u << 31)) //TODO: Verify!
|
||||
return ~((~value) >> shift_val);
|
||||
else
|
||||
return value >> shift_val;
|
||||
case SH_ROR:
|
||||
if(setcc) arm.cpsr_c = (value >> (shift_val - 1)) & 1;
|
||||
return value >> shift_val | (value << (32 - shift_val));
|
||||
}
|
||||
}
|
||||
else if(shift_val == 32 || shift_type == SH_ASR || shift_type == SH_ROR)
|
||||
{
|
||||
switch(shift_type)
|
||||
{
|
||||
case SH_LSL: if(setcc) arm.cpsr_c = value & 1; return 0;
|
||||
case SH_LSR: if(setcc) arm.cpsr_c = !!(value & (1u << 31)); return 0;
|
||||
case SH_ASR: if(setcc) arm.cpsr_c = !!(value & (1u << 31));
|
||||
if(value & (1u << 31))
|
||||
return 0xFFFFFFFF;
|
||||
else
|
||||
return 0x00000000;
|
||||
case SH_ROR: return shift(value, SH_ROR, shift_val & 0b11111, setcc, false);
|
||||
}
|
||||
}
|
||||
else // shift_val > 32
|
||||
{
|
||||
if(setcc)
|
||||
arm.cpsr_c = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t addr_mode_2(Instruction i)
|
||||
{
|
||||
if(!i.mem_proc.not_imm)
|
||||
return i.mem_proc.immed;
|
||||
|
||||
return shift(reg_pc(i.mem_proc.rm), i.mem_proc.shift, i.mem_proc.shift_imm, false, false);
|
||||
}
|
||||
|
||||
static uint32_t rotated_imm(Instruction i, bool setcc)
|
||||
{
|
||||
uint32_t imm = i.data_proc.immed_8;
|
||||
uint8_t count = i.data_proc.rotate_imm << 1;
|
||||
if(count == 0)
|
||||
return imm;
|
||||
|
||||
imm = (imm >> count) | (imm << (32 - count));
|
||||
if(setcc)
|
||||
arm.cpsr_c = !!(imm & (1u << 31));
|
||||
return imm;
|
||||
}
|
||||
|
||||
static uint32_t addr_mode_1(Instruction i, bool setcc)
|
||||
{
|
||||
if(i.data_proc.imm)
|
||||
return rotated_imm(i, setcc);
|
||||
|
||||
if(i.data_proc.reg_shift)
|
||||
return shift(reg_pc(i.data_proc.rm), i.data_proc.shift, reg(i.data_proc.rs), setcc, true);
|
||||
else
|
||||
return shift(reg_pc(i.data_proc.rm), i.data_proc.shift, i.data_proc.shift_imm, setcc, false);
|
||||
}
|
||||
|
||||
static inline void set_nz_flags(uint32_t value) {
|
||||
arm.cpsr_n = value >> 31;
|
||||
arm.cpsr_z = value == 0;
|
||||
}
|
||||
|
||||
static inline void set_nz_flags_64(uint64_t value) {
|
||||
arm.cpsr_n = value >> 63;
|
||||
arm.cpsr_z = value == 0;
|
||||
}
|
||||
|
||||
void do_arm_instruction(Instruction i)
|
||||
{
|
||||
bool exec = true;
|
||||
|
||||
// Shortcut for unconditional instructions
|
||||
if(likely(i.cond == CC_AL))
|
||||
goto always;
|
||||
|
||||
switch(i.cond)
|
||||
{
|
||||
case CC_EQ: case CC_NE: exec = arm.cpsr_z; break;
|
||||
case CC_CS: case CC_CC: exec = arm.cpsr_c; break;
|
||||
case CC_MI: case CC_PL: exec = arm.cpsr_n; break;
|
||||
case CC_VS: case CC_VC: exec = arm.cpsr_v; break;
|
||||
case CC_HI: case CC_LS: exec = !arm.cpsr_z && arm.cpsr_c; break;
|
||||
case CC_GE: case CC_LT: exec = arm.cpsr_n == arm.cpsr_v; break;
|
||||
case CC_GT: case CC_LE: exec = !arm.cpsr_z && arm.cpsr_n == arm.cpsr_v; break;
|
||||
case CC_NV:
|
||||
if((i.raw & 0xFD70F000) == 0xF550F000)
|
||||
return;
|
||||
else if((i.raw & 0xFE000000) == 0xFA000000)
|
||||
{
|
||||
// BLX
|
||||
arm.reg[14] = arm.reg[15];
|
||||
arm.reg[15] += 4 + ((int32_t) (i.raw << 8) >> 6) + (i.raw >> 23 & 2);
|
||||
arm.cpsr_low28 |= 0x20; // Enter Thumb mode
|
||||
return;
|
||||
}
|
||||
else
|
||||
undefined_instruction();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
exec ^= i.cond & 1;
|
||||
|
||||
if(!exec)
|
||||
return;
|
||||
|
||||
always:
|
||||
uint32_t insn = i.raw;
|
||||
|
||||
if((insn & 0xE000090) == 0x0000090)
|
||||
{
|
||||
// MUL, SWP, etc.
|
||||
// LDRH, STRSH, etc.
|
||||
int type = insn >> 5 & 3;
|
||||
if (type == 0) {
|
||||
if ((insn & 0xFC000F0) == 0x0000090) {
|
||||
/* MUL, MLA: 32x32 to 32 multiplications */
|
||||
uint32_t res = reg(insn & 15)
|
||||
* reg(insn >> 8 & 15);
|
||||
if (insn & 0x0200000)
|
||||
res += reg(insn >> 12 & 15);
|
||||
|
||||
set_reg(insn >> 16 & 15, res);
|
||||
if (insn & 0x0100000) set_nz_flags(res);
|
||||
} else if ((insn & 0xF8000F0) == 0x0800090) {
|
||||
/* UMULL, UMLAL, SMULL, SMLAL: 32x32 to 64 multiplications */
|
||||
uint32_t left = reg(insn & 15);
|
||||
uint32_t right = reg(insn >> 8 & 15);
|
||||
uint32_t reg_lo = insn >> 12 & 15;
|
||||
uint32_t reg_hi = insn >> 16 & 15;
|
||||
|
||||
if (reg_lo == reg_hi)
|
||||
error("RdLo and RdHi cannot be same for 64-bit multiply");
|
||||
|
||||
uint64_t res;
|
||||
if (insn & 0x0400000) res = (int64_t)(int32_t)left * (int32_t)right;
|
||||
else res = (uint64_t)left * right;
|
||||
if (insn & 0x0200000) {
|
||||
/* Accumulate */
|
||||
res += (uint64_t)reg(reg_hi) << 32 | reg(reg_lo);
|
||||
}
|
||||
|
||||
set_reg(reg_lo, res);
|
||||
set_reg(reg_hi, res >> 32);
|
||||
if (insn & 0x0100000) set_nz_flags_64(res);
|
||||
} else if ((insn & 0xFB00FF0) == 0x1000090) {
|
||||
/* SWP, SWPB */
|
||||
uint32_t addr = reg(insn >> 16 & 15);
|
||||
uint32_t ld, st = reg(insn & 15);
|
||||
if (insn & 0x0400000) {
|
||||
ld = read_byte(addr); write_byte(addr, st);
|
||||
} else {
|
||||
ld = read_word(addr); write_word(addr, st);
|
||||
}
|
||||
set_reg(insn >> 12 & 15, ld);
|
||||
} else {
|
||||
undefined_instruction();
|
||||
}
|
||||
} else {
|
||||
/* Load/store halfword, signed byte/halfword, or doubleword */
|
||||
int base_reg = insn >> 16 & 15;
|
||||
int data_reg = insn >> 12 & 15;
|
||||
int offset = (insn & (1 << 22))
|
||||
? (insn & 0x0F) | (insn >> 4 & 0xF0)
|
||||
: reg(insn & 15);
|
||||
bool writeback = false;
|
||||
uint32_t addr = reg_pc(base_reg);
|
||||
|
||||
if (!(insn & (1 << 23))) // Subtracted offset
|
||||
offset = -offset;
|
||||
|
||||
if (insn & (1 << 24)) { // Offset or pre-indexed addressing
|
||||
addr += offset;
|
||||
offset = 0;
|
||||
writeback = insn & (1 << 21);
|
||||
} else {
|
||||
if(insn & (1 << 21))
|
||||
mmu_check_priv(addr, !((insn & (1 << 20)) || type == 2));
|
||||
|
||||
writeback = true;
|
||||
}
|
||||
|
||||
if (insn & (1 << 20)) {
|
||||
uint32_t data;
|
||||
if (base_reg == data_reg && writeback)
|
||||
error("Load instruction modifies base register twice");
|
||||
if (type == 1) data = read_half(addr); /* LDRH */
|
||||
else if (type == 2) data = (int8_t) read_byte(addr); /* LDRSB */
|
||||
else data = (int16_t)read_half(addr); /* LDRSH */
|
||||
set_reg(data_reg, data);
|
||||
} else if (type == 1) { /* STRH */
|
||||
write_half(addr, reg(data_reg));
|
||||
} else {
|
||||
if (data_reg & 1) error("LDRD/STRD with odd-numbered data register");
|
||||
if (type == 2) { /* LDRD */
|
||||
if ((base_reg & ~1) == data_reg && writeback)
|
||||
error("Load instruction modifies base register twice");
|
||||
uint32_t low = read_word(addr);
|
||||
uint32_t high = read_word(addr + 4);
|
||||
set_reg(data_reg, low);
|
||||
set_reg(data_reg + 1, high);
|
||||
} else { /* STRD */
|
||||
write_word(addr, reg(data_reg));
|
||||
write_word(addr + 4, reg(data_reg + 1));
|
||||
}
|
||||
}
|
||||
if (writeback)
|
||||
set_reg(base_reg, addr + offset);
|
||||
}
|
||||
}
|
||||
else if((insn & 0xD900000) == 0x1000000)
|
||||
{
|
||||
// BLX, MRS, MSR, SMUL, etc.
|
||||
if ((insn & 0xFFFFFD0) == 0x12FFF10) {
|
||||
/* B(L)X: Branch(, link,) and exchange T bit */
|
||||
uint32_t target = reg_pc(insn & 15);
|
||||
if (insn & 0x20)
|
||||
arm.reg[14] = arm.reg[15];
|
||||
set_reg_bx(15, target);
|
||||
} else if ((insn & 0xFBF0FFF) == 0x10F0000) {
|
||||
/* MRS: Move reg <- status */
|
||||
set_reg(insn >> 12 & 15, (insn & 0x0400000) ? get_spsr() : get_cpsr());
|
||||
} else if ((insn & 0xFB0FFF0) == 0x120F000 ||
|
||||
(insn & 0xFB0F000) == 0x320F000) {
|
||||
/* MSR: Move status <- reg/imm */
|
||||
uint32_t val, mask = 0;
|
||||
if (insn & 0x2000000)
|
||||
val = rotated_imm(i, false);
|
||||
else
|
||||
val = reg(insn & 15);
|
||||
if (insn & 0x0080000) mask |= 0xFF000000;
|
||||
if (insn & 0x0040000) mask |= 0x00FF0000;
|
||||
if (insn & 0x0020000) mask |= 0x0000FF00;
|
||||
if (insn & 0x0010000) mask |= 0x000000FF;
|
||||
if (insn & 0x0400000)
|
||||
set_spsr(val, mask);
|
||||
else
|
||||
set_cpsr(val, mask);
|
||||
} else if ((insn & 0xF900090) == 0x1000080) {
|
||||
int32_t left = reg(insn & 15);
|
||||
int16_t right = reg((insn >> 8) & 15) >> ((insn & 0x40) ? 16 : 0);
|
||||
int32_t product;
|
||||
int type = insn >> 21 & 3;
|
||||
|
||||
if (type == 1) {
|
||||
/* SMULW<y>, SMLAW<y>: Signed 32x16 to 48 multiply, uses only top 32 bits */
|
||||
product = (int64_t)left * right >> 16;
|
||||
if (!(insn & 0x20))
|
||||
goto accumulate;
|
||||
} else {
|
||||
/* SMUL<x><y>, SMLA<x><y>, SMLAL<x><y>: Signed 16x16 to 32 multiply */
|
||||
product = (int16_t)(left >> ((insn & 0x20) ? 16 : 0)) * right;
|
||||
}
|
||||
if (type == 2) {
|
||||
/* SMLAL<x><y>: 64-bit accumulate */
|
||||
uint32_t reg_lo = insn >> 12 & 15;
|
||||
uint32_t reg_hi = insn >> 16 & 15;
|
||||
int64_t sum;
|
||||
if (reg_lo == reg_hi)
|
||||
error("RdLo and RdHi cannot be same for 64-bit accumulate");
|
||||
sum = product + ((uint64_t)reg(reg_hi) << 32 | reg(reg_lo));
|
||||
set_reg(reg_lo, sum);
|
||||
set_reg(reg_hi, sum >> 32);
|
||||
} else if (type == 0) accumulate: {
|
||||
/* SMLA<x><y>, SMLAW<y>: 32-bit accumulate */
|
||||
int32_t acc = reg(insn >> 12 & 15);
|
||||
int32_t sum = product + acc;
|
||||
/* Set Q flag on overflow */
|
||||
arm.cpsr_low28 |= ADD_OVERFLOW(product, acc, sum) << 27;
|
||||
set_reg(insn >> 16 & 15, sum);
|
||||
} else {
|
||||
/* SMUL<x><y>, SMULW<y>: No accumulate */
|
||||
set_reg(insn >> 16 & 15, product);
|
||||
}
|
||||
} else if ((insn & 0xF900FF0) == 0x1000050) {
|
||||
/* QADD, QSUB, QDADD, QDSUB: Saturated arithmetic */
|
||||
int32_t left = reg(insn & 15);
|
||||
int32_t right = reg(insn >> 16 & 15);
|
||||
int32_t res, overflow;
|
||||
if (insn & 0x400000) {
|
||||
/* Doubled right operand */
|
||||
res = right << 1;
|
||||
if (ADD_OVERFLOW(right, right, res)) {
|
||||
/* Overflow, set Q flag and saturate */
|
||||
arm.cpsr_low28 |= 1 << 27;
|
||||
res = (res < 0) ? 0x7FFFFFFF : 0x80000000;
|
||||
}
|
||||
right = res;
|
||||
}
|
||||
if (!(insn & 0x200000)) {
|
||||
res = left + right;
|
||||
overflow = ADD_OVERFLOW(left, right, res);
|
||||
} else {
|
||||
res = left - right;
|
||||
overflow = SUB_OVERFLOW(left, right, res);
|
||||
}
|
||||
if (overflow) {
|
||||
/* Set Q flag and saturate */
|
||||
arm.cpsr_low28 |= 1 << 27;
|
||||
res = (res < 0) ? 0x7FFFFFFF : 0x80000000;
|
||||
}
|
||||
set_reg(insn >> 12 & 15, res);
|
||||
} else if ((insn & 0xFFF0FF0) == 0x16F0F10) {
|
||||
/* CLZ: Count leading zeros */
|
||||
int32_t value = reg(insn & 15);
|
||||
uint32_t zeros;
|
||||
for (zeros = 0; zeros < 32 && value >= 0; zeros++)
|
||||
value <<= 1;
|
||||
set_reg(insn >> 12 & 15, zeros);
|
||||
} else if ((insn & 0xFFF000F0) == 0xE1200070) {
|
||||
gui_debug_printf("Software breakpoint at %08x (%04x)\n",
|
||||
arm.reg[15], (insn >> 4 & 0xFFF0) | (insn & 0xF));
|
||||
debugger(DBG_EXEC_BREAKPOINT, 0);
|
||||
} else
|
||||
undefined_instruction();
|
||||
}
|
||||
else if(likely((insn & 0xC000000) == 0x0000000))
|
||||
{
|
||||
// Data processing
|
||||
bool carry = arm.cpsr_c,
|
||||
setcc = i.data_proc.s;
|
||||
|
||||
uint32_t left = reg_pc(i.data_proc.rn),
|
||||
right = addr_mode_1(i, setcc),
|
||||
res = 0;
|
||||
|
||||
switch(i.data_proc.op)
|
||||
{
|
||||
case OP_AND: res = left & right; break;
|
||||
case OP_EOR: res = left ^ right; break;
|
||||
case OP_SUB: res = add( left, ~right, 1, setcc); break;
|
||||
case OP_RSB: res = add(~left, right, 1, setcc); break;
|
||||
case OP_ADD: res = add( left, right, 0, setcc); break;
|
||||
case OP_ADC: res = add( left, right, carry, setcc); break;
|
||||
case OP_SBC: res = add( left, ~right, carry, setcc); break;
|
||||
case OP_RSC: res = add(~left, right, carry, setcc); break;
|
||||
case OP_TST: res = left & right; break;
|
||||
case OP_TEQ: res = left ^ right; break;
|
||||
case OP_CMP: res = add( left, ~right, 1, setcc); break;
|
||||
case OP_CMN: res = add( left, right, 0, setcc); break;
|
||||
case OP_ORR: res = left | right; break;
|
||||
case OP_MOV: res = right; break;
|
||||
case OP_BIC: res = left & ~right; break;
|
||||
case OP_MVN: res = ~right; break;
|
||||
}
|
||||
|
||||
if(i.data_proc.op < OP_TST || i.data_proc.op > OP_CMN)
|
||||
set_reg_pc(i.data_proc.rd, res);
|
||||
|
||||
if(setcc)
|
||||
{
|
||||
// Used for returning from exceptions, for instance
|
||||
if(i.data_proc.rd == 15)
|
||||
set_cpsr_full(get_spsr());
|
||||
else
|
||||
{
|
||||
arm.cpsr_n = res >> 31;
|
||||
arm.cpsr_z = res == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if((insn & 0xFF000F0) == 0x7F000F0)
|
||||
undefined_instruction();
|
||||
else if((insn & 0xC000000) == 0x4000000)
|
||||
{
|
||||
// LDR, STRB, etc.
|
||||
uint32_t base = reg_pc(i.mem_proc.rn),
|
||||
offset = addr_mode_2(i);
|
||||
if(!i.mem_proc.u)
|
||||
offset = -offset;
|
||||
|
||||
// Pre-indexed or offset
|
||||
if(i.mem_proc.p)
|
||||
base += offset; // Writeback for pre-indexed handled after access
|
||||
else if(i.mem_proc.w) // Usermode Access
|
||||
mmu_check_priv(base, !i.mem_proc.l);
|
||||
|
||||
// Byte access
|
||||
if(i.mem_proc.b)
|
||||
{
|
||||
if(i.mem_proc.l) set_reg_bx(i.mem_proc.rd, read_byte(base));
|
||||
else write_byte(base, reg_pc_mem(i.mem_proc.rd));
|
||||
}
|
||||
else
|
||||
{
|
||||
if(i.mem_proc.l) set_reg_bx(i.mem_proc.rd, read_word(base));
|
||||
else write_word(base, reg_pc_mem(i.mem_proc.rd));
|
||||
}
|
||||
|
||||
// Post-indexed addressing
|
||||
if(!i.mem_proc.p)
|
||||
base += offset;
|
||||
|
||||
// Writeback
|
||||
if(!i.mem_proc.p || i.mem_proc.w)
|
||||
set_reg(i.mem_proc.rn, base);
|
||||
}
|
||||
else if((insn & 0xE000000) == 0x8000000)
|
||||
{
|
||||
// LDM, STM, etc.
|
||||
int base_reg = insn >> 16 & 15;
|
||||
uint32_t addr = reg(base_reg);
|
||||
uint32_t new_base = addr;
|
||||
int count = __builtin_popcount(i.mem_multi.reglist);
|
||||
|
||||
if (i.mem_multi.u) { // Increasing
|
||||
if (i.mem_multi.w) // Writeback
|
||||
new_base += count * 4;
|
||||
if (i.mem_multi.p) // Preincrement
|
||||
addr += 4;
|
||||
} else { // Decreasing
|
||||
addr -= count * 4;
|
||||
if (i.mem_multi.w) // Writeback
|
||||
new_base = addr;
|
||||
if (!i.mem_multi.p) // Postdecrement
|
||||
addr += 4;
|
||||
}
|
||||
|
||||
for (unsigned reg = 0, reglist = i.mem_multi.reglist; reglist && reg < 15; reglist >>= 1, reg++) {
|
||||
if ((reglist & 1) == 0)
|
||||
continue;
|
||||
|
||||
uint32_t *reg_ptr = &arm.reg[reg];
|
||||
if (i.mem_multi.s && !i.mem_multi.w && !(i.mem_multi.reglist & (1<<15))) {
|
||||
// User-mode registers
|
||||
int mode = arm.cpsr_low28 & 0x1F;
|
||||
if (reg >= 13) {
|
||||
if (mode != MODE_USR && mode != MODE_SYS) reg_ptr = &arm.r13_usr[reg - 13];
|
||||
} else if (reg >= 8) {
|
||||
if (mode == MODE_FIQ) reg_ptr = &arm.r8_usr[reg - 8];
|
||||
}
|
||||
}
|
||||
if (i.mem_multi.l) { // Load
|
||||
if (reg_ptr == &arm.reg[base_reg]) {
|
||||
if (i.mem_multi.w) // Writeback
|
||||
error("Load instruction modifies base register twice");
|
||||
reg_ptr = &new_base;
|
||||
}
|
||||
*reg_ptr = read_word(addr);
|
||||
} else { // Store
|
||||
write_word(addr, *reg_ptr);
|
||||
}
|
||||
addr += 4;
|
||||
}
|
||||
if (i.mem_multi.reglist & (1 << 15)) {
|
||||
if (i.mem_multi.l) // Load
|
||||
set_reg_bx(15, read_word(addr));
|
||||
else // Store
|
||||
write_word(addr, reg_pc_mem(15));
|
||||
}
|
||||
arm.reg[base_reg] = new_base;
|
||||
if (i.mem_multi.l && i.mem_multi.s && i.mem_multi.reglist & (1<<15))
|
||||
set_cpsr_full(get_spsr());
|
||||
}
|
||||
else if((insn & 0xE000000) == 0xA000000)
|
||||
{
|
||||
// B and BL
|
||||
if(i.branch.l)
|
||||
arm.reg[14] = arm.reg[15];
|
||||
arm.reg[15] += (int32_t) (i.branch.immed << 8) >> 6;
|
||||
arm.reg[15] += 4;
|
||||
}
|
||||
else if((insn & 0xF000F10) == 0xE000F10)
|
||||
do_cp15_instruction(i);
|
||||
else if((insn & 0xF000000) == 0xF000000)
|
||||
cpu_exception(EX_SWI);
|
||||
else
|
||||
undefined_instruction();
|
||||
}
|
||||
11
src/armv5te/armsnippets.h
Normal file
11
src/armv5te/armsnippets.h
Normal file
@@ -0,0 +1,11 @@
|
||||
/* Declarations for armsnippets */
|
||||
|
||||
#ifndef _H_ARMSNIPPETS
|
||||
#define _H_ARMSNIPPETS
|
||||
|
||||
//#include <stdbool.h>
|
||||
|
||||
#define armloader_cb()
|
||||
//bool armloader_load_snippet(enum SNIPPETS snippet, struct armloader_load_params params[], unsigned params_num, void (*callback)(struct arm_state *));
|
||||
|
||||
#endif
|
||||
167
src/armv5te/asmcode.c
Normal file
167
src/armv5te/asmcode.c
Normal file
@@ -0,0 +1,167 @@
|
||||
#include "asmcode.h"
|
||||
#include "debug.h"
|
||||
#include "mmu.h"
|
||||
#include "mem.h"
|
||||
|
||||
//TODO: Read breakpoints, alignment checks
|
||||
|
||||
#if defined(NO_TRANSLATION)
|
||||
void flush_translations() {}
|
||||
#endif
|
||||
|
||||
uint32_t FASTCALL read_word(uint32_t addr)
|
||||
{
|
||||
uintptr_t entry = *(uintptr_t*)(addr_cache + ((addr >> 10) << 1));
|
||||
|
||||
//If the sum doesn't contain the address directly
|
||||
if(unlikely(entry & AC_FLAGS))
|
||||
{
|
||||
if(entry & AC_INVALID) //Invalid entry
|
||||
{
|
||||
addr_cache_miss(addr, false, data_abort);
|
||||
return read_word(addr);
|
||||
}
|
||||
else //Physical address
|
||||
{
|
||||
entry &= ~AC_FLAGS;
|
||||
entry += addr;
|
||||
return mmio_read_word(entry);
|
||||
}
|
||||
}
|
||||
|
||||
entry += addr;
|
||||
|
||||
return *(uint32_t*)entry;
|
||||
}
|
||||
|
||||
uint32_t FASTCALL read_byte(uint32_t addr)
|
||||
{
|
||||
uintptr_t entry = *(uintptr_t*)(addr_cache + ((addr >> 10) << 1));
|
||||
|
||||
//If the sum doesn't contain the address directly
|
||||
if(unlikely(entry & AC_FLAGS))
|
||||
{
|
||||
if(entry & AC_INVALID) //Invalid entry
|
||||
{
|
||||
addr_cache_miss(addr, false, data_abort);
|
||||
return read_byte(addr);
|
||||
}
|
||||
else //Physical address
|
||||
{
|
||||
entry &= ~AC_FLAGS;
|
||||
entry += addr;
|
||||
return mmio_read_byte(entry);
|
||||
}
|
||||
}
|
||||
|
||||
entry += addr;
|
||||
|
||||
return *(uint8_t*)entry;
|
||||
}
|
||||
|
||||
uint32_t FASTCALL read_half(uint32_t addr)
|
||||
{
|
||||
addr &= ~1;
|
||||
uintptr_t entry = *(uintptr_t*)(addr_cache + ((addr >> 10) << 1));
|
||||
|
||||
//If the sum doesn't contain the address directly
|
||||
if(unlikely(entry & AC_FLAGS))
|
||||
{
|
||||
if(entry & AC_INVALID) //Invalid entry
|
||||
{
|
||||
addr_cache_miss(addr, false, data_abort);
|
||||
return read_half(addr);
|
||||
}
|
||||
else //Physical address
|
||||
{
|
||||
entry &= ~AC_FLAGS;
|
||||
entry += addr;
|
||||
return mmio_read_half(entry);
|
||||
}
|
||||
}
|
||||
|
||||
entry += addr;
|
||||
|
||||
return *(uint16_t*)entry;
|
||||
}
|
||||
|
||||
void FASTCALL write_byte(uint32_t addr, uint32_t value)
|
||||
{
|
||||
uintptr_t entry = *(uintptr_t*)(addr_cache + ((addr >> 10) << 1) + 1);
|
||||
|
||||
//If the sum doesn't contain the address directly
|
||||
if(unlikely(entry & AC_FLAGS))
|
||||
{
|
||||
if(entry & AC_INVALID) //Invalid entry
|
||||
{
|
||||
addr_cache_miss(addr, true, data_abort);
|
||||
return write_byte(addr, value);
|
||||
}
|
||||
else //Physical address
|
||||
{
|
||||
entry &= ~AC_FLAGS;
|
||||
entry += addr;
|
||||
return mmio_write_byte(entry, value);
|
||||
}
|
||||
}
|
||||
|
||||
entry += addr;
|
||||
|
||||
if(RAM_FLAGS(entry & ~3) & DO_WRITE_ACTION)
|
||||
write_action((void*) entry);
|
||||
*(uint8_t*)entry = value;
|
||||
}
|
||||
|
||||
void FASTCALL write_half(uint32_t addr, uint32_t value)
|
||||
{
|
||||
addr &= ~1;
|
||||
uintptr_t entry = *(uintptr_t*)(addr_cache + ((addr >> 10) << 1) + 1);
|
||||
|
||||
//If the sum doesn't contain the address directly
|
||||
if(unlikely(entry & AC_FLAGS))
|
||||
{
|
||||
if(entry & AC_INVALID) //Invalid entry
|
||||
{
|
||||
addr_cache_miss(addr, true, data_abort);
|
||||
return write_half(addr, value);
|
||||
}
|
||||
else //Physical address
|
||||
{
|
||||
entry &= ~AC_FLAGS;
|
||||
entry += addr;
|
||||
return mmio_write_half(entry, value);
|
||||
}
|
||||
}
|
||||
|
||||
entry += addr;
|
||||
|
||||
if(RAM_FLAGS(entry & ~3) & DO_WRITE_ACTION)
|
||||
write_action((void*) entry);
|
||||
*(uint16_t*)entry = value;
|
||||
}
|
||||
|
||||
void FASTCALL write_word(uint32_t addr, uint32_t value)
|
||||
{
|
||||
uintptr_t entry = *(uintptr_t*)(addr_cache + ((addr >> 10) << 1) + 1);
|
||||
|
||||
//If the sum doesn't contain the address directly
|
||||
if(unlikely(entry & AC_FLAGS))
|
||||
{
|
||||
if(entry & AC_INVALID) //Invalid entry
|
||||
{
|
||||
addr_cache_miss(addr, true, data_abort);
|
||||
return write_word(addr, value);
|
||||
}
|
||||
else //Physical address
|
||||
{
|
||||
entry &= ~AC_FLAGS;
|
||||
entry += addr;
|
||||
return mmio_write_word(entry, value);
|
||||
}
|
||||
}
|
||||
entry += addr;
|
||||
|
||||
if(RAM_FLAGS(entry & ~3) & DO_WRITE_ACTION)
|
||||
write_action((void*) entry);
|
||||
*(uint32_t*)entry = value;
|
||||
}
|
||||
50
src/armv5te/asmcode.h
Normal file
50
src/armv5te/asmcode.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/* Declarations for asmcode.S */
|
||||
|
||||
#ifndef _H_ASMCODE
|
||||
#define _H_ASMCODE
|
||||
|
||||
#include "emu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(__arm__) || defined(__aarch64__)
|
||||
// Supply the pointer to the instruction directly to avoid read_instruction
|
||||
void translation_enter(void *ptr) __asm__("translation_enter");
|
||||
#define TRANSLATION_ENTER_HAS_PTR 1
|
||||
#else
|
||||
void translation_enter() __asm__("translation_enter");
|
||||
#define TRANSLATION_ENTER_HAS_PTR 0
|
||||
#endif
|
||||
|
||||
// Jump to the translated code starting at ptr.
|
||||
// Checks for cycle_count_delta and exits if necessary.
|
||||
void translation_jmp(void *ptr) __asm__("translation_jmp");
|
||||
// Checks whether code at ptr is now translated and if so, jumps to it
|
||||
void translation_jmp_ptr(void *ptr) __asm__("translation_jmp_ptr");
|
||||
|
||||
void * FASTCALL read_instruction(uint32_t addr) __asm__("read_instruction");
|
||||
uint32_t FASTCALL read_byte(uint32_t addr) __asm__("read_byte");
|
||||
uint32_t FASTCALL read_half(uint32_t addr) __asm__("read_half");
|
||||
uint32_t FASTCALL read_word(uint32_t addr) __asm__("read_word");
|
||||
void FASTCALL write_byte(uint32_t addr, uint32_t value) __asm__("write_byte");
|
||||
void FASTCALL write_half(uint32_t addr, uint32_t value) __asm__("write_half");
|
||||
void FASTCALL write_word(uint32_t addr, uint32_t value) __asm__("write_word");
|
||||
|
||||
#if defined(__arm__) || defined(__aarch64__) || defined(__x86_64__)
|
||||
// For some JITs there's in internal version called by JIT'ed code that can't be called
|
||||
// from outside the JIT'ed code
|
||||
uint32_t FASTCALL read_byte_asm(uint32_t addr) __asm__("read_byte_asm");
|
||||
uint32_t FASTCALL read_half_asm(uint32_t addr) __asm__("read_half_asm");
|
||||
uint32_t FASTCALL read_word_asm(uint32_t addr) __asm__("read_word_asm");
|
||||
void FASTCALL write_byte_asm(uint32_t addr, uint32_t value) __asm__("write_byte_asm");
|
||||
void FASTCALL write_half_asm(uint32_t addr, uint32_t value) __asm__("write_half_asm");
|
||||
void FASTCALL write_word_asm(uint32_t addr, uint32_t value) __asm__("write_word_asm");
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
400
src/armv5te/asmcode_aarch64.S
Normal file
400
src/armv5te/asmcode_aarch64.S
Normal file
@@ -0,0 +1,400 @@
|
||||
.macro loadsym reg, name
|
||||
#ifdef __clang__
|
||||
adrp \reg, \name\()@PAGE
|
||||
add \reg, \reg, \name\()@PAGEOFF
|
||||
#else
|
||||
adrp \reg, \name
|
||||
add \reg, \reg, #:lo12:\name
|
||||
#endif
|
||||
.endm
|
||||
|
||||
.data
|
||||
.align 3
|
||||
|
||||
.global arm
|
||||
.global cycle_count_delta
|
||||
.global cpu_events
|
||||
.global addr_cache
|
||||
|
||||
translation_sp: .global translation_sp
|
||||
.xword 0
|
||||
|
||||
.text
|
||||
.align 2
|
||||
|
||||
.global data_abort
|
||||
|
||||
translation_enter: .global translation_enter
|
||||
stp x19, x30, [sp, #-16]!
|
||||
stp x21, x22, [sp, #-16]!
|
||||
stp x23, x24, [sp, #-16]!
|
||||
stp x25, x26, [sp, #-16]!
|
||||
|
||||
// Store s into translation_sp
|
||||
mov x1, sp
|
||||
loadsym x2, translation_sp
|
||||
str x1, [x2]
|
||||
|
||||
loadsym x19, arm
|
||||
loadsym x26, addr_cache
|
||||
ldr x26, [x26]
|
||||
|
||||
bl load_virt
|
||||
b translation_next_enter
|
||||
|
||||
// Enter translation, check for thumb
|
||||
translation_next_bx: .global translation_next_bx
|
||||
tbnz w0, #0, to_thumb // if(pc & 1) goto to_thumb;
|
||||
|
||||
// Enter translation; sets arm.reg[15] = w0
|
||||
translation_next: .global translation_next
|
||||
mrs x17, nzcv
|
||||
str w0, [x19, #15*4]
|
||||
|
||||
mov w1, w0
|
||||
|
||||
lsr x0, x0, #10
|
||||
lsl x0, x0, #4
|
||||
ldr x0, [x26, x0] // x0 = addr_cache[(x0 >> 10) << 1]
|
||||
tbnz x0, #0, save_return // if(x0 & 1) goto save_return;
|
||||
|
||||
// Load RAM_FLAGS
|
||||
add x0, x0, x1 // x0 = pointer to memory at x0
|
||||
|
||||
// Enter translation at x0 (has to be ptr to arm.reg[15]'s memory)
|
||||
translation_next_enter:
|
||||
loadsym x23, cpu_events
|
||||
ldr w23, [x23]
|
||||
cbnz w23, save_return // if(cpu_events) goto save_return;
|
||||
|
||||
mov x21, #65*1024*1024
|
||||
ldr w21, [x0, x21] // w21 = RAM_FLAGS(x0)
|
||||
tbz w21, #5, save_return // if((RAM_FLAGS(x0) & RF_CODE_TRANSLATED) == 0) goto save_return;
|
||||
lsr w21, w21, #9 // w21 = w21 >> RFS_TRANSLATION_INDEX
|
||||
|
||||
loadsym x23, translation_table
|
||||
add x23, x23, x21, lsl #5 // x23 = &translation_table[RAM_FLAGS(x0) >> RFS_TRANSLATION_INDEX]
|
||||
|
||||
ldr x24, [x23, #1*8] // x24 = x3->jump_table
|
||||
ldp x25, x21, [x23, #2*8] // x25 = x23->start_ptr; x21 = x23->end_ptr
|
||||
|
||||
sub x21, x21, x0 // x21 = end_ptr - insn_ptr
|
||||
lsr x21, x21, #2 // x21: number of instructions until the end
|
||||
|
||||
sub x25, x0, x25 // x25 = insn_ptr - start_ptr
|
||||
//lsr x25, x25, #2 // x25 = count of instructions
|
||||
//lsl x25, x25, #3
|
||||
lsl x25, x25, #1
|
||||
ldr x0, [x24, x25] // x0 = jump_table[(insn_ptr - start_ptr) / 4]
|
||||
|
||||
// add number of instructions to cycle_count_delta
|
||||
loadsym x24, cycle_count_delta
|
||||
ldr w25, [x24]
|
||||
add w25, w25, w21
|
||||
str w25, [x24]
|
||||
cmp w25, #0
|
||||
bpl save_return // if(cycle_count_delta > 0) goto save_return;
|
||||
|
||||
msr nzcv, x17
|
||||
br x0
|
||||
|
||||
translation_jmp_ptr: .global translation_jmp_ptr
|
||||
mrs x17, nzcv
|
||||
|
||||
b translation_next_enter
|
||||
|
||||
translation_jmp: .global translation_jmp
|
||||
mrs x17, nzcv
|
||||
|
||||
// add number of instructions to cycle_count_delta
|
||||
loadsym x24, cycle_count_delta
|
||||
ldr w25, [x24]
|
||||
adds w25, w25, #4 // We don't know how much will be executed, so use a possible number
|
||||
bpl save_return
|
||||
str w25, [x24]
|
||||
|
||||
msr nzcv, x17
|
||||
|
||||
br x0
|
||||
|
||||
to_thumb:
|
||||
mrs x17, nzcv
|
||||
sub w0, w0, #1
|
||||
str w0, [x19, #15*4] // arm.reg[15] = w0 - 1
|
||||
ldr w1, [x19, #16*4]
|
||||
orr w1, w1, #0x20
|
||||
str w1, [x19, #16*4] // arm.cpsr_low28 |= 0x20
|
||||
|
||||
save_return:
|
||||
loadsym x0, translation_sp
|
||||
mov x1, #0
|
||||
str x1, [x0]
|
||||
|
||||
bl save_virt
|
||||
|
||||
ldp x25, x26, [sp], #16
|
||||
ldp x23, x24, [sp], #16
|
||||
ldp x21, x22, [sp], #16
|
||||
ldp x19, x30, [sp], #16
|
||||
ret
|
||||
|
||||
// Saves the virtual CPU state
|
||||
save_virt:
|
||||
stp w2, w3, [x19, #0*4]
|
||||
stp w4, w5, [x19, #2*4]
|
||||
stp w6, w7, [x19, #4*4]
|
||||
stp w8, w9, [x19, #6*4]
|
||||
stp w10, w11, [x19, #8*4]
|
||||
stp w12, w13, [x19, #10*4]
|
||||
stp w14, w15, [x19, #12*4]
|
||||
str w16, [x19, #14*4]
|
||||
// Save nzcv (in x17) to struct arm_state again
|
||||
ubfx x2, x17, #31, #1
|
||||
ubfx x3, x17, #30, #1
|
||||
ubfx x4, x17, #29, #1
|
||||
ubfx x5, x17, #28, #1
|
||||
strb w2, [x19, #17*4+0]
|
||||
strb w3, [x19, #17*4+1]
|
||||
strb w4, [x19, #17*4+2]
|
||||
strb w5, [x19, #17*4+3]
|
||||
ret
|
||||
|
||||
// Loads the virtual CPU state
|
||||
// x19 has to be a pointer to the arm_state
|
||||
load_virt:
|
||||
// Assemble virtual cpsr_nzcv in x17
|
||||
ldrb w17, [x19, #17*4+0] // x17 = bit 31
|
||||
ldrb w3, [x19, #17*4+1] // w3 = bit 30
|
||||
ldrb w4, [x19, #17*4+2] // w4 = bit 29
|
||||
ldrb w5, [x19, #17*4+3] // w5 = bit 28
|
||||
lsl w17, w17, #31
|
||||
bfi w17, w3, #30, #1
|
||||
bfi w17, w4, #29, #1
|
||||
bfi w17, w5, #28, #1
|
||||
ldp w2, w3, [x19, #0*4]
|
||||
ldp w4, w5, [x19, #2*4]
|
||||
ldp w6, w7, [x19, #4*4]
|
||||
ldp w8, w9, [x19, #6*4]
|
||||
ldp w10, w11, [x19, #8*4]
|
||||
ldp w12, w13, [x19, #10*4]
|
||||
ldp w14, w15, [x19, #12*4]
|
||||
ldr w16, [x19, #14*4]
|
||||
ret
|
||||
|
||||
read_word_asm: .global read_word_asm
|
||||
lsr w22, w0, #10
|
||||
add x21, x26, x22, lsl #4 // x21 = &addr_cache[(x0 >> 10) << 1]
|
||||
0: ldr x22, [x21] // x22 = *x21
|
||||
tbnz x22, #0, 1f
|
||||
ldr w0, [x22, x0]
|
||||
ret
|
||||
|
||||
// Not cached
|
||||
1: tbnz x22, #1, 2f
|
||||
// MMIO
|
||||
bic x22, x22, #3
|
||||
stp x30, x23, [sp, #-16]!
|
||||
add x0, x0, x22
|
||||
mrs x17, nzcv
|
||||
bl save_virt
|
||||
bl mmio_read_word
|
||||
bl load_virt
|
||||
ldp x30, x23, [sp], #16
|
||||
msr nzcv, x17
|
||||
ret
|
||||
|
||||
// Invalid
|
||||
2: stp x30, x23, [sp, #-16]!
|
||||
stp x0, x1, [sp, #-16]!
|
||||
mrs x17, nzcv
|
||||
bl save_virt
|
||||
mov x1, #0
|
||||
loadsym x2, data_abort
|
||||
bl addr_cache_miss
|
||||
bl load_virt
|
||||
ldp x0, x1, [sp], #16
|
||||
ldp x30, x23, [sp], #16
|
||||
msr nzcv, x17
|
||||
b 0b // Try again
|
||||
|
||||
read_half_asm: .global read_half_asm
|
||||
bic w22, w22, #1
|
||||
lsr w22, w0, #10
|
||||
add x21, x26, x22, lsl #4 // x21 = &addr_cache[(x0 >> 10) << 1]
|
||||
0: ldr x22, [x21] // x22 = *x21
|
||||
tbnz x22, #0, 1f
|
||||
ldrh w0, [x22, x0]
|
||||
ret
|
||||
|
||||
// Not cached
|
||||
1: tbnz x22, #1, 2f
|
||||
// MMIO
|
||||
bic x22, x22, #3
|
||||
stp x30, x23, [sp, #-16]!
|
||||
add x0, x0, x22
|
||||
mrs x17, nzcv
|
||||
bl save_virt
|
||||
bl mmio_read_half
|
||||
bl load_virt
|
||||
ldp x30, x23, [sp], #16
|
||||
msr nzcv, x17
|
||||
ret
|
||||
|
||||
// Invalid
|
||||
2: stp x30, x23, [sp, #-16]!
|
||||
stp x0, x1, [sp, #-16]!
|
||||
mrs x17, nzcv
|
||||
bl save_virt
|
||||
mov x1, #0
|
||||
loadsym x2, data_abort
|
||||
bl addr_cache_miss
|
||||
bl load_virt
|
||||
ldp x0, x1, [sp], #16
|
||||
ldp x30, x23, [sp], #16
|
||||
msr nzcv, x17
|
||||
b 0b // Try again
|
||||
|
||||
read_byte_asm: .global read_byte_asm
|
||||
lsr w22, w0, #10
|
||||
add x21, x26, x22, lsl #4 // x21 = &addr_cache[(x0 >> 10) << 1]
|
||||
0: ldr x22, [x21] // x22 = *x21
|
||||
tbnz x22, #0, 1f
|
||||
ldrb w0, [x22, x0]
|
||||
ret
|
||||
|
||||
// Not cached
|
||||
1: tbnz x22, #1, 2f
|
||||
// MMIO
|
||||
bic x22, x22, #3
|
||||
stp x30, x23, [sp, #-16]!
|
||||
add x0, x0, x22
|
||||
mrs x17, nzcv
|
||||
bl save_virt
|
||||
bl mmio_read_byte
|
||||
bl load_virt
|
||||
ldp x30, x23, [sp], #16
|
||||
msr nzcv, x17
|
||||
ret
|
||||
|
||||
// Invalid
|
||||
2: stp x30, x23, [sp, #-16]!
|
||||
stp x0, x1, [sp, #-16]!
|
||||
mrs x17, nzcv
|
||||
bl save_virt
|
||||
mov x1, #0
|
||||
loadsym x2, data_abort
|
||||
bl addr_cache_miss
|
||||
bl load_virt
|
||||
ldp x0, x1, [sp], #16
|
||||
ldp x30, x23, [sp], #16
|
||||
msr nzcv, x17
|
||||
b 0b // Try again
|
||||
|
||||
write_word_asm: .global write_word_asm
|
||||
lsr w22, w0, #10
|
||||
add x21, x26, x22, lsl #4 // x21 = &addr_cache[(x0 >> 10) << 1]
|
||||
0: ldr x22, [x21, #8] // x22 = *(x21+1)
|
||||
tbnz x22, #0, 1f
|
||||
str w1, [x22, x0]
|
||||
ret
|
||||
|
||||
// Not cached
|
||||
1: tbnz x22, #1, 2f
|
||||
// MMIO
|
||||
bic x22, x22, #3
|
||||
stp x30, x23, [sp, #-16]!
|
||||
add x0, x0, x22
|
||||
mrs x17, nzcv
|
||||
bl save_virt
|
||||
bl mmio_write_word
|
||||
bl load_virt
|
||||
ldp x30, x23, [sp], #16
|
||||
msr nzcv, x17
|
||||
ret
|
||||
|
||||
// Invalid
|
||||
2: stp x30, x23, [sp, #-16]!
|
||||
stp x0, x1, [sp, #-16]!
|
||||
mrs x17, nzcv
|
||||
bl save_virt
|
||||
mov x1, #1
|
||||
loadsym x2, data_abort
|
||||
bl addr_cache_miss
|
||||
bl load_virt
|
||||
ldp x0, x1, [sp], #16
|
||||
ldp x30, x23, [sp], #16
|
||||
msr nzcv, x17
|
||||
b 0b // Try again
|
||||
|
||||
write_half_asm: .global write_half_asm
|
||||
bic w22, w22, #1
|
||||
lsr w22, w0, #10
|
||||
add x21, x26, x22, lsl #4 // x21 = &addr_cache[(x0 >> 10) << 1]
|
||||
0: ldr x22, [x21, #8] // x22 = *(x21+1)
|
||||
tbnz x22, #0, 1f
|
||||
strh w1, [x22, x0]
|
||||
ret
|
||||
|
||||
// Not cached
|
||||
1: tbnz x22, #1, 2f
|
||||
// MMIO
|
||||
bic x22, x22, #3
|
||||
stp x30, x23, [sp, #-16]!
|
||||
add x0, x0, x22
|
||||
mrs x17, nzcv
|
||||
bl save_virt
|
||||
bl mmio_write_half
|
||||
bl load_virt
|
||||
ldp x30, x23, [sp], #16
|
||||
msr nzcv, x17
|
||||
ret
|
||||
|
||||
// Invalid
|
||||
2: stp x30, x23, [sp, #-16]!
|
||||
stp x0, x1, [sp, #-16]!
|
||||
mrs x17, nzcv
|
||||
bl save_virt
|
||||
mov x1, #1
|
||||
loadsym x2, data_abort
|
||||
bl addr_cache_miss
|
||||
bl load_virt
|
||||
ldp x0, x1, [sp], #16
|
||||
ldp x30, x23, [sp], #16
|
||||
msr nzcv, x17
|
||||
b 0b // Try again
|
||||
|
||||
write_byte_asm: .global write_byte_asm
|
||||
lsr w22, w0, #10
|
||||
add x21, x26, x22, lsl #4 // x21 = &addr_cache[(x0 >> 10) << 1]
|
||||
0: ldr x22, [x21, #8] // x22 = *(x21+1)
|
||||
tbnz x22, #0, 1f
|
||||
strb w1, [x22, x0]
|
||||
ret
|
||||
|
||||
// Not cached
|
||||
1: tbnz x22, #1, 2f
|
||||
// MMIO
|
||||
bic x22, x22, #3
|
||||
stp x30, x23, [sp, #-16]!
|
||||
add x0, x0, x22
|
||||
mrs x17, nzcv
|
||||
bl save_virt
|
||||
bl mmio_write_byte
|
||||
bl load_virt
|
||||
ldp x30, x23, [sp], #16
|
||||
msr nzcv, x17
|
||||
ret
|
||||
|
||||
// Invalid
|
||||
2: stp x30, x23, [sp, #-16]!
|
||||
stp x0, x1, [sp, #-16]!
|
||||
mrs x17, nzcv
|
||||
bl save_virt
|
||||
mov x1, #1
|
||||
loadsym x2, data_abort
|
||||
bl addr_cache_miss
|
||||
bl load_virt
|
||||
ldp x0, x1, [sp], #16
|
||||
ldp x30, x23, [sp], #16
|
||||
msr nzcv, x17
|
||||
b 0b // Try again
|
||||
334
src/armv5te/asmcode_arm.S
Normal file
334
src/armv5te/asmcode_arm.S
Normal file
@@ -0,0 +1,334 @@
|
||||
.data
|
||||
translation_sp: .global translation_sp
|
||||
.word 0
|
||||
.global arm
|
||||
.global get_cpsr_flags
|
||||
.global set_cpsr_flags
|
||||
.global cycle_count_delta
|
||||
.global cpu_events
|
||||
.global read_instruction
|
||||
|
||||
.text
|
||||
|
||||
#ifdef __clang__
|
||||
#define streqh strheq
|
||||
#define streqb strbeq
|
||||
#define ldreqh ldrheq
|
||||
#define ldreqb ldrbeq
|
||||
#endif
|
||||
|
||||
#define RF_CODE_TRANSLATED 32
|
||||
#define RFS_TRANSLATION_INDEX 9
|
||||
|
||||
#define AC_INVALID 0b10
|
||||
#define AC_NOT_PTR 0b01
|
||||
#define AC_FLAGS (AC_INVALID|AC_NOT_PTR)
|
||||
|
||||
#if defined(__clang__)
|
||||
/* On iOS, .text relocations are permitted and due to
|
||||
a bug it's unable to handle our way of
|
||||
avoiding them anyway... */
|
||||
|
||||
.macro sym name, no=1
|
||||
.endm
|
||||
|
||||
.macro loadsym reg, name, no=1
|
||||
ldr \reg, =\name
|
||||
.endm
|
||||
#else
|
||||
|
||||
/* What you can see here is a really bad way of avoiding
|
||||
relocations in the .text section.
|
||||
For each use of an external symbol, you need to insert
|
||||
a sym (name), (number) here, which generates a word containing the
|
||||
distance to the symbol relative to pc.
|
||||
To use it, use loadsym (reg), (name), (number).
|
||||
The whole process is a way of working around
|
||||
https://sourceware.org/bugzilla/show_bug.cgi?id=18009
|
||||
*/
|
||||
|
||||
.macro sym name, no=1
|
||||
addr_\name\()_\no\(): .word \name - _tmp_\name\()_\no - 8
|
||||
.endm
|
||||
|
||||
.macro loadsym reg, name, no=1
|
||||
ldr \reg, addr_\name\()_\no
|
||||
_tmp_\name\()_\no: add \reg, \reg, pc
|
||||
.endm
|
||||
|
||||
sym translation_sp
|
||||
sym arm
|
||||
sym cpu_events
|
||||
sym translation_table
|
||||
sym cycle_count_delta, 2
|
||||
sym translation_sp, 2
|
||||
//sym cpu_events, 2
|
||||
sym cycle_count_delta, 3
|
||||
sym addr_cache
|
||||
sym data_abort
|
||||
sym addr_cache, 2
|
||||
sym data_abort, 2
|
||||
sym addr_cache, 3
|
||||
sym data_abort, 3
|
||||
sym addr_cache, 4
|
||||
sym data_abort, 4
|
||||
sym addr_cache, 5
|
||||
sym data_abort, 5
|
||||
sym addr_cache, 6
|
||||
sym data_abort, 6
|
||||
|
||||
#endif
|
||||
|
||||
// r0: pointer to the instruction
|
||||
translation_enter: .global translation_enter
|
||||
push {r4-r11, lr}
|
||||
mov r4, r0
|
||||
loadsym r0, translation_sp
|
||||
str sp, [r0]
|
||||
|
||||
bl get_cpsr
|
||||
mov r11, r0 // r11 = cpsr
|
||||
|
||||
mov r0, r4
|
||||
|
||||
add r1, r0, #65*1024*1024 // r1 = &(RAM_FLAGS(r0))
|
||||
ldr r1, [r1]
|
||||
|
||||
loadsym r10, arm // r10 is a pointer to the global arm_state
|
||||
b translation_next_enter
|
||||
|
||||
// r0: pc
|
||||
translation_next_bx: .global translation_next_bx
|
||||
tst r0, #1
|
||||
bne to_thumb
|
||||
|
||||
// r0: pc
|
||||
translation_next: .global translation_next
|
||||
str r0, [r10, #15*4] // save to arm.reg[15]
|
||||
|
||||
bl read_instruction // r0 = pointer to ARM code
|
||||
cmp r0, #0
|
||||
beq save_return
|
||||
|
||||
translation_jmp_ptr: .global translation_jmp_ptr
|
||||
add r1, r0, #65*1024*1024 // r1 = &(RAM_FLAGS(r0))
|
||||
ldr r1, [r1]
|
||||
tst r1, #RF_CODE_TRANSLATED
|
||||
beq save_return // not translated
|
||||
|
||||
// r0: pointer to instruction
|
||||
// r1: RAM_FLAGS(r0)
|
||||
translation_next_enter:
|
||||
loadsym r2, cpu_events
|
||||
ldr r2, [r2]
|
||||
cmp r2, #0
|
||||
bne save_return
|
||||
|
||||
mov r1, r1, lsr #RFS_TRANSLATION_INDEX // r1 is translation index
|
||||
mov r1, r1, lsl #4
|
||||
loadsym r2, translation_table
|
||||
add r1, r2, r1 // r1 points to struct translation now
|
||||
|
||||
ldr r2, [r1, #1*4] // load translation.jump_table
|
||||
ldr r3, [r1, #2*4] // load translation.start_ptr
|
||||
ldr r4, [r1, #3*4] // load translation.end_ptr
|
||||
|
||||
sub r4, r4, r0 // r4 = end_ptr - pc_ptr
|
||||
mov r4, r4, lsr #2 // r4 = number of instructions to the end
|
||||
loadsym r6, cycle_count_delta, 2
|
||||
ldr r5, [r6]
|
||||
add r5, r5, r4 // add r4 to cycle_count_delta
|
||||
str r5, [r6]
|
||||
cmp r5, #0
|
||||
bpl save_return
|
||||
|
||||
sub r0, r0, r3 // r0 = pc_ptr - start_ptr
|
||||
msr cpsr_f, r11
|
||||
ldr pc, [r2, r0] // jump to jump_table[r0]
|
||||
|
||||
to_thumb:
|
||||
sub r0, r0, #1
|
||||
str r0, [r10, #15*4] // arm.reg[PC] = r0
|
||||
ldr r1, [r10, #16*4]
|
||||
orr r1, r1, #0x20 // Set thumb bit
|
||||
str r1, [r10, #16*4]
|
||||
b save_return
|
||||
|
||||
// Invoked from within translated code to jump to other, already translated code
|
||||
// Target address of translated code is in r0 - only r10 and r11 have to be preserved
|
||||
// This is used to check for events and leave the translation to process them.
|
||||
// arm.reg[15] must be set already!
|
||||
translation_jmp: .global translation_jmp
|
||||
/* loadsym r1, cpu_events, 2
|
||||
ldr r2, [r1]
|
||||
cmp r2, #0
|
||||
bne save_return*/
|
||||
|
||||
loadsym r1, cycle_count_delta, 3
|
||||
ldr r2, [r1]
|
||||
add r2, #4 // We don't know how much will be executed, so use a possible number
|
||||
str r2, [r1]
|
||||
cmp r2, #0
|
||||
bpl save_return
|
||||
|
||||
msr cpsr_f, r11
|
||||
bx r0 // Jump to the target
|
||||
|
||||
// Save flags and leave, arm.reg[15] must be set already!
|
||||
save_return:
|
||||
loadsym r1, translation_sp, 2
|
||||
mov r0, #0
|
||||
str r0, [r1]
|
||||
|
||||
str r11, [r10, #18*4] // save to arm.cpsr_flags
|
||||
mov r0, r11 // apply arm.cpsr_flags to arm.cpsr_*
|
||||
|
||||
pop {r4-r11, lr}
|
||||
|
||||
b set_cpsr_flags
|
||||
|
||||
// Below is basically a handcoded assembly version of asmcode.c
|
||||
//TODO: Invoke write_action for translation invalidation!
|
||||
|
||||
write_word_asm: .global write_word_asm
|
||||
// r0 is address, r1 is value
|
||||
loadsym r2, addr_cache
|
||||
ldr r2, [r2]
|
||||
mov r3, r0, lsr #9
|
||||
orr r3, r3, #1
|
||||
ldr r3, [r2, r3, lsl #2] // r3 contains ac_entry
|
||||
tst r3, #AC_FLAGS
|
||||
streq r1, [r3, r0]
|
||||
bxeq lr
|
||||
str r11, [r10, #18*4] // save to arm.cpsr_flags
|
||||
tst r3, #AC_INVALID
|
||||
bne write_word_invalid
|
||||
bic r3, #AC_FLAGS
|
||||
add r0, r3, r0
|
||||
b mmio_write_word
|
||||
write_word_invalid:
|
||||
push {r0, r1, r2, lr} //r2 for stack alignment
|
||||
mov r1, #1
|
||||
loadsym r2, data_abort
|
||||
bl addr_cache_miss
|
||||
pop {r0, r1, r2, lr}
|
||||
b write_word_asm
|
||||
|
||||
write_half_asm: .global write_half_asm
|
||||
// r0 is address, r1 is value
|
||||
loadsym r2, addr_cache, 2
|
||||
bic r0, r0, #1
|
||||
ldr r2, [r2]
|
||||
mov r3, r0, lsr #9
|
||||
orr r3, r3, #1
|
||||
ldr r3, [r2, r3, lsl #2] // r3 contains ac_entry
|
||||
tst r3, #AC_FLAGS
|
||||
streqh r1, [r3, r0]
|
||||
bxeq lr
|
||||
str r11, [r10, #18*4] // save to arm.cpsr_flags
|
||||
tst r3, #AC_INVALID
|
||||
bne write_half_invalid
|
||||
bic r3, #AC_FLAGS
|
||||
add r0, r3, r0
|
||||
b mmio_write_half
|
||||
write_half_invalid:
|
||||
push {r0, r1, r2, lr} //r2 for stack alignment
|
||||
mov r1, #1
|
||||
loadsym r2, data_abort, 2
|
||||
bl addr_cache_miss
|
||||
pop {r0, r1, r2, lr}
|
||||
b write_half_asm
|
||||
|
||||
write_byte_asm: .global write_byte_asm
|
||||
// r0 is address, r1 is value
|
||||
loadsym r2, addr_cache, 3
|
||||
ldr r2, [r2]
|
||||
mov r3, r0, lsr #9
|
||||
orr r3, r3, #1
|
||||
ldr r3, [r2, r3, lsl #2] // r3 contains ac_entry
|
||||
tst r3, #AC_FLAGS
|
||||
streqb r1, [r3, r0]
|
||||
bxeq lr
|
||||
str r11, [r10, #18*4] // save to arm.cpsr_flags
|
||||
tst r3, #AC_INVALID
|
||||
bne write_byte_invalid
|
||||
bic r3, #AC_FLAGS
|
||||
add r0, r3, r0
|
||||
b mmio_write_byte
|
||||
write_byte_invalid:
|
||||
push {r0, r1, r2, lr} //r2 for stack alignment
|
||||
mov r1, #1
|
||||
loadsym r2, data_abort, 3
|
||||
bl addr_cache_miss
|
||||
pop {r0, r1, r2, lr}
|
||||
b write_byte_asm
|
||||
|
||||
read_word_asm: .global read_word_asm
|
||||
// r0 is address
|
||||
loadsym r2, addr_cache, 4
|
||||
ldr r2, [r2]
|
||||
mov r3, r0, lsr #10
|
||||
ldr r3, [r2, r3, lsl #3] // r3 contains ac_entry
|
||||
tst r3, #AC_FLAGS
|
||||
ldreq r0, [r3, r0]
|
||||
bxeq lr
|
||||
str r11, [r10, #18*4] // save to arm.cpsr_flags
|
||||
tst r3, #AC_INVALID
|
||||
bne read_word_invalid
|
||||
bic r3, #AC_FLAGS
|
||||
add r0, r3, r0
|
||||
b mmio_read_word
|
||||
read_word_invalid:
|
||||
push {r0, lr}
|
||||
mov r1, #0
|
||||
loadsym r2, data_abort, 4
|
||||
bl addr_cache_miss
|
||||
pop {r0, lr}
|
||||
b read_word_asm
|
||||
|
||||
read_half_asm: .global read_half_asm
|
||||
// r0 is address
|
||||
loadsym r2, addr_cache, 5
|
||||
bic r0, r0, #1
|
||||
ldr r2, [r2]
|
||||
mov r3, r0, lsr #10
|
||||
ldr r3, [r2, r3, lsl #3] // r3 contains ac_entry
|
||||
tst r3, #AC_FLAGS
|
||||
ldreqh r0, [r3, r0]
|
||||
bxeq lr
|
||||
str r11, [r10, #18*4] // save to arm.cpsr_flags
|
||||
tst r3, #AC_INVALID
|
||||
bne read_half_invalid
|
||||
bic r3, #AC_FLAGS
|
||||
add r0, r3, r0
|
||||
b mmio_read_half
|
||||
read_half_invalid:
|
||||
push {r0, lr}
|
||||
mov r1, #0
|
||||
loadsym r2, data_abort, 5
|
||||
bl addr_cache_miss
|
||||
pop {r0, lr}
|
||||
b read_half_asm
|
||||
|
||||
read_byte_asm: .global read_byte_asm
|
||||
// r0 is address
|
||||
loadsym r2, addr_cache, 6
|
||||
ldr r2, [r2]
|
||||
mov r3, r0, lsr #10
|
||||
ldr r3, [r2, r3, lsl #3] // r3 contains ac_entry
|
||||
tst r3, #AC_FLAGS
|
||||
ldreqb r0, [r3, r0]
|
||||
bxeq lr
|
||||
str r11, [r10, #18*4] // save to arm.cpsr_flags
|
||||
tst r3, #AC_INVALID
|
||||
bne read_byte_invalid
|
||||
bic r3, #AC_FLAGS
|
||||
add r0, r3, r0
|
||||
b mmio_read_byte
|
||||
read_byte_invalid:
|
||||
push {r0, lr}
|
||||
mov r1, #0
|
||||
loadsym r2, data_abort, 6
|
||||
bl addr_cache_miss
|
||||
pop {r0, lr}
|
||||
b read_byte_asm
|
||||
496
src/armv5te/asmcode_x86.S
Normal file
496
src/armv5te/asmcode_x86.S
Normal file
@@ -0,0 +1,496 @@
|
||||
// arm_state structure offsets
|
||||
#define ARM_PC 60
|
||||
#define ARM_CPSR 64
|
||||
#define ARM_FLAG_C 70
|
||||
#define ARM_CONTROL 72
|
||||
|
||||
// translation structure offsets
|
||||
#define TRANS_JUMP_TABLE 4
|
||||
#define TRANS_END_PTR 12
|
||||
|
||||
#define RAM_FLAGS (70*1024*1024) // = MEM_MAXSIZE
|
||||
#define RF_READ_BREAKPOINT 1
|
||||
#define RF_WRITE_BREAKPOINT 2
|
||||
#define RF_EXEC_BREAKPOINT 4
|
||||
#define RF_EXEC_DEBUG_NEXT 8
|
||||
#define RF_CODE_TRANSLATED 32
|
||||
#define RF_CODE_NO_TRANSLATE 64
|
||||
#define RF_READ_ONLY 128
|
||||
#define RF_ARMLOADER_CB 256
|
||||
#define RFS_TRANSLATION_INDEX 9
|
||||
|
||||
#define WRITE_SPECIAL_FLAGS 2+32+64
|
||||
|
||||
// List of locations of addresses which need to be relocated to addr_cache
|
||||
// (necessary since it's now allocated at runtime)
|
||||
.data
|
||||
.globl ac_reloc_start
|
||||
ac_reloc_start:
|
||||
|
||||
.macro AC_RELOC; 0: .data; .long 0b - 4; .text; .endm
|
||||
|
||||
.text
|
||||
.globl translation_enter
|
||||
translation_enter:
|
||||
pushl %ebp
|
||||
movl %esp, %ebp
|
||||
pushl %ebx
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
movl %esp, in_translation_esp
|
||||
|
||||
movl $arm, %ebx
|
||||
movl ARM_PC(%ebx), %eax
|
||||
jmp translation_next
|
||||
|
||||
.globl translation_next_bx
|
||||
translation_next_bx:
|
||||
testb $1, %al
|
||||
jne switch_to_thumb
|
||||
|
||||
.globl translation_next
|
||||
translation_next:
|
||||
movl %eax, ARM_PC(%ebx)
|
||||
|
||||
cmpl $0, cycle_count_delta
|
||||
jns return
|
||||
|
||||
cmpl $0, cpu_events
|
||||
jnz return
|
||||
|
||||
// eax = VM_MEM_PTR(eax)
|
||||
movl %eax, %ecx
|
||||
shrl $10, %ecx
|
||||
addl 0(, %ecx, 8), %eax
|
||||
AC_RELOC
|
||||
testl $0x80000003, %eax
|
||||
jnz return
|
||||
addr_ok:
|
||||
|
||||
movl RAM_FLAGS(%eax), %edx
|
||||
testb $RF_CODE_TRANSLATED, %dl
|
||||
jz return // Not translated
|
||||
|
||||
movl %eax, in_translation_pc_ptr
|
||||
|
||||
shrl $RFS_TRANSLATION_INDEX, %edx
|
||||
shll $4, %edx
|
||||
addl $translation_table, %edx
|
||||
|
||||
// Add one cycle for each instruction from this point to the end
|
||||
movl TRANS_END_PTR(%edx), %ecx
|
||||
subl %eax, %ecx
|
||||
shrl $2, %ecx
|
||||
addl %ecx, cycle_count_delta
|
||||
|
||||
movl TRANS_JUMP_TABLE(%edx), %edx
|
||||
jmp *(%edx, %eax)
|
||||
|
||||
return:
|
||||
andl $0, in_translation_esp
|
||||
popl %edi
|
||||
popl %esi
|
||||
popl %ebx
|
||||
popl %ebp
|
||||
ret
|
||||
|
||||
switch_to_thumb:
|
||||
decl %eax
|
||||
movl %eax, ARM_PC(%ebx)
|
||||
orb $0x20, ARM_CPSR(%ebx)
|
||||
jmp return
|
||||
|
||||
// These shift procedures are called only from translated code,
|
||||
// so they may assume that %ebx == _arm
|
||||
.align 4
|
||||
.globl arm_shift_proc
|
||||
arm_shift_proc:
|
||||
.long lsl
|
||||
.long lsr
|
||||
.long asr
|
||||
.long 0
|
||||
.long lsl_carry
|
||||
.long lsr_carry
|
||||
.long asr_carry
|
||||
.long ror_carry
|
||||
|
||||
.text
|
||||
lsl:
|
||||
cmpb $32, %cl
|
||||
jae ls_32
|
||||
shll %cl, %eax
|
||||
ret
|
||||
|
||||
lsr:
|
||||
cmpb $32, %cl
|
||||
jae ls_32
|
||||
shrl %cl, %eax
|
||||
ret
|
||||
ls_32:
|
||||
xorl %eax, %eax
|
||||
ret
|
||||
|
||||
asr:
|
||||
cmpb $32, %cl
|
||||
jae asr_32
|
||||
sarl %cl, %eax
|
||||
ret
|
||||
asr_32:
|
||||
sarl $31, %eax
|
||||
ret
|
||||
|
||||
lsl_carry:
|
||||
cmpb $32, %cl
|
||||
jae lsl_carry_32
|
||||
testb %cl, %cl
|
||||
je lsl_carry_zero
|
||||
shll %cl, %eax
|
||||
setc ARM_FLAG_C(%ebx)
|
||||
lsl_carry_zero:
|
||||
ret
|
||||
lsl_carry_32:
|
||||
jne ls_carry_33
|
||||
shrl $1, %eax
|
||||
setc ARM_FLAG_C(%ebx)
|
||||
xorl %eax, %eax
|
||||
ret
|
||||
|
||||
lsr_carry:
|
||||
cmpb $32, %cl
|
||||
jae lsr_carry_32
|
||||
testb %cl, %cl
|
||||
je lsr_carry_zero
|
||||
shrl %cl, %eax
|
||||
setc ARM_FLAG_C(%ebx)
|
||||
lsr_carry_zero:
|
||||
ret
|
||||
lsr_carry_32:
|
||||
jne ls_carry_33
|
||||
shll $1, %eax
|
||||
setc ARM_FLAG_C(%ebx)
|
||||
xorl %eax, %eax
|
||||
ret
|
||||
ls_carry_33:
|
||||
xorl %eax, %eax
|
||||
movb %al, ARM_FLAG_C(%ebx)
|
||||
ret
|
||||
|
||||
asr_carry:
|
||||
cmpb $32, %cl
|
||||
jae asr_carry_32
|
||||
testb %cl, %cl
|
||||
je asr_carry_zero
|
||||
sarl %cl, %eax
|
||||
setc ARM_FLAG_C(%ebx)
|
||||
asr_carry_zero:
|
||||
ret
|
||||
asr_carry_32:
|
||||
sarl $31, %eax
|
||||
sets ARM_FLAG_C(%ebx)
|
||||
ret
|
||||
|
||||
ror_carry:
|
||||
testb $31, %cl
|
||||
jz ror_carry_mult_32
|
||||
rorl %cl, %eax
|
||||
setc ARM_FLAG_C(%ebx)
|
||||
ror_carry_zero:
|
||||
ret
|
||||
ror_carry_mult_32:
|
||||
testb %cl, %cl
|
||||
je ror_carry_zero
|
||||
testl %eax, %eax
|
||||
sets ARM_FLAG_C(%ebx)
|
||||
ret
|
||||
|
||||
// uint32_t FASTCALL read_byte(uint32_t addr);
|
||||
.globl read_byte
|
||||
.align 16
|
||||
read_byte:
|
||||
movl %ecx, %eax
|
||||
shrl $10, %eax
|
||||
addl 0(, %eax, 8), %ecx
|
||||
AC_RELOC
|
||||
js rb_slow
|
||||
movl %ecx, %edx
|
||||
andl $-4, %edx
|
||||
testb $RF_READ_BREAKPOINT, RAM_FLAGS(%edx)
|
||||
jnz rb_special
|
||||
rb_fast:
|
||||
movzbl (%ecx), %eax
|
||||
ret
|
||||
rb_special:
|
||||
call read_special
|
||||
jmp rb_fast
|
||||
rb_slow:
|
||||
movl 0(, %eax, 8), %eax
|
||||
AC_RELOC
|
||||
subl %eax, %ecx
|
||||
shll $10, %eax
|
||||
jc rb_miss
|
||||
addl %eax, %ecx
|
||||
jmp mmio_read_byte
|
||||
rb_miss:
|
||||
call read_miss
|
||||
jmp read_byte
|
||||
|
||||
// uint32_t FASTCALL read_half(uint32_t addr);
|
||||
.globl read_half
|
||||
.align 16
|
||||
read_half:
|
||||
movl %ecx, %eax
|
||||
shrl $10, %eax
|
||||
addl 0(, %eax, 8), %ecx
|
||||
AC_RELOC
|
||||
testl $0x80000001, %ecx
|
||||
jnz rh_slow
|
||||
movl %ecx, %edx
|
||||
andl $-4, %edx
|
||||
testb $RF_READ_BREAKPOINT, RAM_FLAGS(%edx)
|
||||
jnz rh_special
|
||||
rh_fast:
|
||||
movzwl (%ecx), %eax
|
||||
ret
|
||||
rh_special:
|
||||
call read_special
|
||||
jmp rh_fast
|
||||
rh_slow:
|
||||
movl 0(, %eax, 8), %eax
|
||||
AC_RELOC
|
||||
subl %eax, %ecx
|
||||
testl $1, %ecx
|
||||
jnz rh_unaligned
|
||||
shll $10, %eax
|
||||
jc rh_miss
|
||||
addl %eax, %ecx
|
||||
jmp mmio_read_half
|
||||
rh_miss:
|
||||
call read_miss
|
||||
jmp read_half
|
||||
rh_unaligned:
|
||||
call align_fault
|
||||
decl %ecx
|
||||
jmp read_half
|
||||
|
||||
// uint32_t FASTCALL read_word(uint32_t addr);
|
||||
.globl read_word
|
||||
.align 16
|
||||
read_word:
|
||||
movl %ecx, %eax
|
||||
shrl $10, %eax
|
||||
addl 0(, %eax, 8), %ecx
|
||||
AC_RELOC
|
||||
testl $0x80000003, %ecx
|
||||
jnz rw_slow
|
||||
testb $RF_READ_BREAKPOINT, RAM_FLAGS(%ecx)
|
||||
jnz rw_special
|
||||
rw_fast:
|
||||
movl (%ecx), %eax
|
||||
ret
|
||||
rw_special:
|
||||
call read_special
|
||||
jmp rw_fast
|
||||
rw_slow:
|
||||
movl 0(, %eax, 8), %eax
|
||||
AC_RELOC
|
||||
subl %eax, %ecx
|
||||
testl $3, %ecx
|
||||
jnz rw_unaligned
|
||||
shll $10, %eax
|
||||
jc rw_miss
|
||||
addl %eax, %ecx
|
||||
jmp mmio_read_word
|
||||
rw_miss:
|
||||
call read_miss
|
||||
jmp read_word
|
||||
rw_unaligned:
|
||||
call align_fault
|
||||
andl $-4, %ecx
|
||||
jmp read_word
|
||||
|
||||
// uint32_t FASTCALL read_word_ldr(uint32_t addr);
|
||||
.globl read_word_ldr
|
||||
.align 16
|
||||
read_word_ldr:
|
||||
movl %ecx, %eax
|
||||
shrl $10, %eax
|
||||
addl 0(, %eax, 8), %ecx
|
||||
AC_RELOC
|
||||
testl $0x80000003, %ecx
|
||||
jnz rwl_slow
|
||||
testb $RF_READ_BREAKPOINT, RAM_FLAGS(%ecx)
|
||||
jnz rw_special
|
||||
movl (%ecx), %eax
|
||||
ret
|
||||
rwl_slow:
|
||||
movl 0(, %eax, 8), %eax
|
||||
AC_RELOC
|
||||
subl %eax, %ecx
|
||||
testl $3, %ecx
|
||||
jnz rwl_unaligned
|
||||
shll $10, %eax
|
||||
jc rw_miss
|
||||
addl %eax, %ecx
|
||||
jmp mmio_read_word
|
||||
rwl_unaligned:
|
||||
pushl %ecx
|
||||
call rw_unaligned
|
||||
popl %ecx
|
||||
shll $3, %ecx // Unaligned ldr rotates the word so that
|
||||
rorl %cl, %eax // addressed byte ends up in low position
|
||||
ret
|
||||
|
||||
read_special:
|
||||
pushl %ecx
|
||||
pushl %ecx
|
||||
call read_action
|
||||
popl %ecx
|
||||
popl %ecx
|
||||
ret
|
||||
read_miss:
|
||||
pushl %ecx
|
||||
pushl $data_abort
|
||||
pushl $0
|
||||
pushl %ecx
|
||||
call addr_cache_miss
|
||||
addl $12, %esp
|
||||
popl %ecx
|
||||
ret
|
||||
|
||||
// void FASTCALL write_byte(uint32_t addr, uint8_t value);
|
||||
.globl write_byte
|
||||
.align 16
|
||||
write_byte:
|
||||
movl %ecx, %eax
|
||||
shrl $10, %eax
|
||||
addl 4(, %eax, 8), %ecx
|
||||
AC_RELOC
|
||||
js wb_slow
|
||||
movl %ecx, %eax
|
||||
andl $-4, %eax
|
||||
testb $WRITE_SPECIAL_FLAGS, RAM_FLAGS(%eax)
|
||||
jnz wb_special
|
||||
wb_fast:
|
||||
movb %dl, (%ecx)
|
||||
ret
|
||||
wb_special:
|
||||
call write_special
|
||||
jmp wb_fast
|
||||
wb_slow:
|
||||
movl 4(, %eax, 8), %eax
|
||||
AC_RELOC
|
||||
subl %eax, %ecx
|
||||
shll $10, %eax
|
||||
jc wb_miss
|
||||
addl %eax, %ecx
|
||||
jmp mmio_write_byte
|
||||
wb_miss:
|
||||
call write_miss
|
||||
jmp write_byte
|
||||
|
||||
// void FASTCALL write_half(uint32_t addr, uint16_t value);
|
||||
.globl write_half
|
||||
.align 16
|
||||
write_half:
|
||||
movl %ecx, %eax
|
||||
shrl $10, %eax
|
||||
addl 4(, %eax, 8), %ecx
|
||||
AC_RELOC
|
||||
testl $0x80000001, %ecx
|
||||
jnz wh_slow
|
||||
movl %ecx, %eax
|
||||
andl $-4, %eax
|
||||
testb $WRITE_SPECIAL_FLAGS, RAM_FLAGS(%eax)
|
||||
jnz wh_special
|
||||
wh_fast:
|
||||
movw %dx, (%ecx)
|
||||
ret
|
||||
wh_special:
|
||||
call write_special
|
||||
jmp wh_fast
|
||||
wh_slow:
|
||||
movl 4(, %eax, 8), %eax
|
||||
AC_RELOC
|
||||
subl %eax, %ecx
|
||||
testl $1, %ecx
|
||||
jnz wh_unaligned
|
||||
shll $10, %eax
|
||||
jc wh_miss
|
||||
addl %eax, %ecx
|
||||
jmp mmio_write_half
|
||||
wh_miss:
|
||||
call write_miss
|
||||
jmp write_half
|
||||
wh_unaligned:
|
||||
call align_fault
|
||||
decl %ecx
|
||||
jmp write_half
|
||||
|
||||
// void FASTCALL write_word(uint32_t addr, uint32_t value);
|
||||
.globl write_word
|
||||
.align 16
|
||||
write_word:
|
||||
movl %ecx, %eax
|
||||
shrl $10, %eax
|
||||
addl 4(, %eax, 8), %ecx
|
||||
AC_RELOC
|
||||
testl $0x80000003, %ecx
|
||||
jnz ww_slow
|
||||
testb $WRITE_SPECIAL_FLAGS, RAM_FLAGS(%ecx)
|
||||
jnz ww_special
|
||||
ww_fast:
|
||||
movl %edx, (%ecx)
|
||||
ret
|
||||
ww_special:
|
||||
call write_special
|
||||
jmp ww_fast
|
||||
ww_slow:
|
||||
movl 4(, %eax, 8), %eax
|
||||
AC_RELOC
|
||||
subl %eax, %ecx
|
||||
testl $3, %ecx
|
||||
jnz ww_unaligned
|
||||
shll $10, %eax
|
||||
jc ww_miss
|
||||
addl %eax, %ecx
|
||||
jmp mmio_write_word
|
||||
ww_miss:
|
||||
call write_miss
|
||||
jmp write_word
|
||||
ww_unaligned:
|
||||
call align_fault
|
||||
andl $-4, %ecx
|
||||
jmp write_word
|
||||
|
||||
write_special:
|
||||
pushl %edx
|
||||
pushl %ecx
|
||||
pushl %ecx
|
||||
call write_action
|
||||
popl %ecx
|
||||
popl %ecx
|
||||
popl %edx
|
||||
ret
|
||||
write_miss:
|
||||
pushl %edx
|
||||
pushl %ecx
|
||||
pushl $data_abort
|
||||
pushl $1
|
||||
pushl %ecx
|
||||
call addr_cache_miss
|
||||
addl $12, %esp
|
||||
popl %ecx
|
||||
popl %edx
|
||||
ret
|
||||
|
||||
align_fault:
|
||||
testb $2, arm+ARM_CONTROL
|
||||
jz 1f
|
||||
pushl $1
|
||||
pushl %ecx
|
||||
call data_abort
|
||||
1: ret
|
||||
|
||||
.data
|
||||
.globl ac_reloc_end
|
||||
ac_reloc_end:
|
||||
335
src/armv5te/asmcode_x86_64.S
Normal file
335
src/armv5te/asmcode_x86_64.S
Normal file
@@ -0,0 +1,335 @@
|
||||
// arm_state structure offsets
|
||||
#define ARM_PC 60
|
||||
#define ARM_CPSR 64
|
||||
#define ARM_FLAG_C 70
|
||||
#define ARM_CONTROL 72
|
||||
|
||||
// translation structure offsets
|
||||
#define TRANS_JUMP_TABLE 0x08
|
||||
#define TRANS_START_PTR 0x10
|
||||
#define TRANS_END_PTR 0x18
|
||||
|
||||
// RAM_FLAGS used to have "// = MEM_MAXSIZE" at the end but the clang assembler copys the "//" into the macro, commenting out the arguments of the opcodes that use it
|
||||
#define RAM_FLAGS (70*1024*1024)
|
||||
#define RF_READ_BREAKPOINT 1
|
||||
#define RF_WRITE_BREAKPOINT 2
|
||||
#define RF_EXEC_BREAKPOINT 4
|
||||
#define RF_EXEC_DEBUG_NEXT 8
|
||||
#define RF_CODE_TRANSLATED 32
|
||||
#define RF_CODE_NO_TRANSLATE 64
|
||||
#define RF_READ_ONLY 128
|
||||
#define RF_ARMLOADER_CB 256
|
||||
#define RFS_TRANSLATION_INDEX 9
|
||||
|
||||
#define DO_READ_ACTION (RF_READ_BREAKPOINT)
|
||||
#define DO_WRITE_ACTION (RF_WRITE_BREAKPOINT | RF_CODE_TRANSLATED | RF_CODE_NO_TRANSLATE)
|
||||
|
||||
translation_enter: .global translation_enter
|
||||
push %rbp
|
||||
mov %rsp, %rbp
|
||||
push %rbx
|
||||
push %rsi
|
||||
push %rdi
|
||||
mov %rsp, in_translation_rsp(%rip)
|
||||
|
||||
lea arm(%rip), %rbx
|
||||
mov ARM_PC(%rbx), %eax
|
||||
jmp translation_next
|
||||
|
||||
translation_next_bx: .global translation_next_bx
|
||||
testb $1, %al
|
||||
jne switch_to_thumb
|
||||
|
||||
translation_next: .global translation_next
|
||||
mov %eax, ARM_PC(%rbx)
|
||||
|
||||
lea cycle_count_delta(%rip), %r8
|
||||
cmpl $0, (%r8)
|
||||
jns return
|
||||
|
||||
lea cpu_events(%rip), %r8
|
||||
cmpl $0, (%r8)
|
||||
jnz return
|
||||
|
||||
mov ARM_PC(%rbx), %edi
|
||||
push %rdi // For 16 byte stack alignment (call pushes 8 itself)
|
||||
call read_instruction
|
||||
pop %rdi
|
||||
cmp $0, %rax
|
||||
jz return
|
||||
|
||||
addr_ok:
|
||||
movl RAM_FLAGS(%rax), %edx
|
||||
testb $RF_CODE_TRANSLATED, %dl
|
||||
jz return // Not translated
|
||||
|
||||
lea in_translation_pc_ptr(%rip), %r8
|
||||
mov %rax, (%r8)
|
||||
|
||||
shr $RFS_TRANSLATION_INDEX, %rdx
|
||||
shl $5, %rdx
|
||||
lea translation_table(%rip), %r8
|
||||
add %r8, %rdx
|
||||
|
||||
// Add one cycle for each instruction from this point to the end
|
||||
mov TRANS_END_PTR(%rdx), %rcx
|
||||
sub %rax, %rcx
|
||||
shr $2, %rcx
|
||||
lea cycle_count_delta(%rip), %r8
|
||||
add %ecx, (%r8)
|
||||
|
||||
mov %rax, %rcx
|
||||
sub TRANS_START_PTR(%rdx), %rcx
|
||||
mov TRANS_JUMP_TABLE(%rdx), %rdx
|
||||
jmp *(%rdx, %rcx, 2)
|
||||
//That is the same as
|
||||
//shr $2, %rcx
|
||||
//jmp *(%rdx, %rcx, 8)
|
||||
|
||||
return:
|
||||
lea in_translation_rsp(%rip), %r8
|
||||
movq $0, (%r8)
|
||||
pop %rdi
|
||||
pop %rsi
|
||||
pop %rbx
|
||||
pop %rbp
|
||||
ret
|
||||
|
||||
switch_to_thumb:
|
||||
dec %eax
|
||||
mov %eax, ARM_PC(%rbx)
|
||||
orb $0x20, ARM_CPSR(%rbx)
|
||||
jmp return
|
||||
|
||||
.data
|
||||
// These shift procedures are called only from translated code,
|
||||
// so they may assume that %rbx == _arm
|
||||
.align 4
|
||||
arm_shift_proc: .global arm_shift_proc
|
||||
.quad lsl
|
||||
.quad lsr
|
||||
.quad asr
|
||||
.quad 0
|
||||
.quad lsl_carry
|
||||
.quad lsr_carry
|
||||
.quad asr_carry
|
||||
.quad ror_carry
|
||||
|
||||
.text
|
||||
lsl:
|
||||
cmpb $32, %cl
|
||||
jae ls_32
|
||||
shl %cl, %eax
|
||||
ret
|
||||
|
||||
lsr:
|
||||
cmpb $32, %cl
|
||||
jae ls_32
|
||||
shr %cl, %eax
|
||||
ret
|
||||
ls_32:
|
||||
xor %eax, %eax
|
||||
ret
|
||||
|
||||
asr:
|
||||
cmpb $32, %cl
|
||||
jae asr_32
|
||||
sar %cl, %eax
|
||||
ret
|
||||
asr_32:
|
||||
sar $31, %eax
|
||||
ret
|
||||
|
||||
lsl_carry:
|
||||
cmpb $32, %cl
|
||||
jae lsl_carry_32
|
||||
testb %cl, %cl
|
||||
je lsl_carry_zero
|
||||
shl %cl, %eax
|
||||
setc ARM_FLAG_C(%rbx)
|
||||
lsl_carry_zero:
|
||||
ret
|
||||
lsl_carry_32:
|
||||
jne ls_carry_33
|
||||
shr $1, %eax
|
||||
setc ARM_FLAG_C(%rbx)
|
||||
xor %eax, %eax
|
||||
ret
|
||||
|
||||
lsr_carry:
|
||||
cmpb $32, %cl
|
||||
jae lsr_carry_32
|
||||
testb %cl, %cl
|
||||
je lsr_carry_zero
|
||||
shr %cl, %eax
|
||||
setc ARM_FLAG_C(%rbx)
|
||||
lsr_carry_zero:
|
||||
ret
|
||||
lsr_carry_32:
|
||||
jne ls_carry_33
|
||||
shl $1, %eax
|
||||
setc ARM_FLAG_C(%rbx)
|
||||
xor %eax, %eax
|
||||
ret
|
||||
ls_carry_33:
|
||||
xor %eax, %eax
|
||||
movb %al, ARM_FLAG_C(%rbx)
|
||||
ret
|
||||
|
||||
asr_carry:
|
||||
cmpb $32, %cl
|
||||
jae asr_carry_32
|
||||
testb %cl, %cl
|
||||
je asr_carry_zero
|
||||
sar %cl, %eax
|
||||
setc ARM_FLAG_C(%rbx)
|
||||
asr_carry_zero:
|
||||
ret
|
||||
asr_carry_32:
|
||||
sar $31, %eax
|
||||
sets ARM_FLAG_C(%rbx)
|
||||
ret
|
||||
|
||||
ror_carry:
|
||||
testb $31, %cl
|
||||
jz ror_carry_mult_32
|
||||
ror %cl, %eax
|
||||
setc ARM_FLAG_C(%rbx)
|
||||
ror_carry_zero:
|
||||
ret
|
||||
ror_carry_mult_32:
|
||||
testb %cl, %cl
|
||||
je ror_carry_zero
|
||||
test %eax, %eax
|
||||
sets ARM_FLAG_C(%rbx)
|
||||
ret
|
||||
|
||||
read_word_asm: .global read_word_asm
|
||||
mov %rdi, %rax
|
||||
shr $10, %rax
|
||||
shl $1, %rax
|
||||
mov addr_cache(%rip), %r8
|
||||
mov (%r8, %rax, 8), %rax
|
||||
test $3, %rax
|
||||
jnz rwa_miss
|
||||
movl (%rax, %rdi), %eax
|
||||
ret
|
||||
rwa_miss:
|
||||
push %rdx
|
||||
push %rcx
|
||||
call read_word
|
||||
pop %rcx
|
||||
pop %rdx
|
||||
ret
|
||||
|
||||
write_word_asm: .global write_word_asm
|
||||
mov %rdi, %rax
|
||||
shr $10, %rax
|
||||
shl $1, %rax
|
||||
add $1, %rax
|
||||
mov addr_cache(%rip), %r8
|
||||
mov (%r8, %rax, 8), %rax
|
||||
test $3, %rax
|
||||
jnz wwa_miss
|
||||
movl %esi, (%rax, %rdi)
|
||||
testq $DO_WRITE_ACTION, RAM_FLAGS(%rax, %rdi)
|
||||
jnz write_action_asm
|
||||
ret
|
||||
wwa_miss:
|
||||
push %rdx
|
||||
push %rcx
|
||||
call write_word
|
||||
pop %rcx
|
||||
pop %rdx
|
||||
ret
|
||||
|
||||
read_half_asm: .global read_half_asm
|
||||
and $-2, %rdi
|
||||
mov %rdi, %rax
|
||||
shr $10, %rax
|
||||
shl $1, %rax
|
||||
mov addr_cache(%rip), %r8
|
||||
mov (%r8, %rax, 8), %rax
|
||||
test $3, %rax
|
||||
jnz rha_miss
|
||||
movzwl (%rax, %rdi), %eax
|
||||
ret
|
||||
rha_miss:
|
||||
push %rdx
|
||||
push %rcx
|
||||
call read_half
|
||||
pop %rcx
|
||||
pop %rdx
|
||||
ret
|
||||
|
||||
write_half_asm: .global write_half_asm
|
||||
and $-2, %rdi
|
||||
mov %rdi, %rax
|
||||
shr $10, %rax
|
||||
shl $1, %rax
|
||||
add $1, %rax
|
||||
mov addr_cache(%rip), %r8
|
||||
mov (%r8, %rax, 8), %rax
|
||||
test $3, %rax
|
||||
jnz wha_miss
|
||||
movw %si, (%rax, %rdi)
|
||||
testq $DO_WRITE_ACTION, RAM_FLAGS(%rax, %rdi)
|
||||
jnz write_action_asm
|
||||
ret
|
||||
wha_miss:
|
||||
push %rdx
|
||||
push %rcx
|
||||
call write_half
|
||||
pop %rcx
|
||||
pop %rdx
|
||||
ret
|
||||
|
||||
read_byte_asm: .global read_byte_asm
|
||||
mov %rdi, %rax
|
||||
shr $10, %rax
|
||||
shl $1, %rax
|
||||
mov addr_cache(%rip), %r8
|
||||
mov (%r8, %rax, 8), %rax
|
||||
test $3, %rax
|
||||
jnz rba_miss
|
||||
movzb (%rax, %rdi), %rax
|
||||
ret
|
||||
rba_miss:
|
||||
push %rdx
|
||||
push %rcx
|
||||
call read_byte
|
||||
pop %rcx
|
||||
pop %rdx
|
||||
ret
|
||||
|
||||
write_byte_asm: .global write_byte_asm
|
||||
mov %rdi, %rax
|
||||
shr $10, %rax
|
||||
shl $1, %rax
|
||||
add $1, %rax
|
||||
mov addr_cache(%rip), %r8
|
||||
mov (%r8, %rax, 8), %rax
|
||||
test $3, %rax
|
||||
jnz wba_miss
|
||||
xchg %rsi, %rdx // Can't use %rsi directly
|
||||
movb %dl, (%rax, %rdi)
|
||||
xchg %rsi, %rdx
|
||||
testq $DO_WRITE_ACTION, RAM_FLAGS(%rax, %rdi)
|
||||
jnz write_action_asm
|
||||
ret
|
||||
wba_miss:
|
||||
push %rdx
|
||||
push %rcx
|
||||
call write_byte
|
||||
pop %rcx
|
||||
pop %rdx
|
||||
ret
|
||||
|
||||
write_action_asm:
|
||||
add %rax, %rdi
|
||||
push %rdx
|
||||
push %rcx
|
||||
call write_action
|
||||
pop %rcx
|
||||
pop %rdx
|
||||
ret
|
||||
75
src/armv5te/bitfield.h
Normal file
75
src/armv5te/bitfield.h
Normal file
@@ -0,0 +1,75 @@
|
||||
#ifndef BITFIELD_H
|
||||
#define BITFIELD_H
|
||||
|
||||
/* Portable and safe implementation of C bitfields
|
||||
Source: http://blog.codef00.com/2014/12/06/portable-bitfields-using-c11/ */
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
template <size_t LastBit>
|
||||
struct MinimumTypeHelper {
|
||||
typedef
|
||||
typename std::conditional<LastBit == 0 , void,
|
||||
typename std::conditional<LastBit <= 8 , uint8_t,
|
||||
typename std::conditional<LastBit <= 16, uint16_t,
|
||||
typename std::conditional<LastBit <= 32, uint32_t,
|
||||
typename std::conditional<LastBit <= 64, uint64_t,
|
||||
void>::type>::type>::type>::type>::type type;
|
||||
};
|
||||
|
||||
template <size_t Index, size_t Bits = 1>
|
||||
class BitField {
|
||||
private:
|
||||
enum {
|
||||
Mask = (1u << Bits) - 1u
|
||||
};
|
||||
|
||||
typedef typename MinimumTypeHelper<Index + Bits>::type T;
|
||||
public:
|
||||
template <class T2>
|
||||
BitField &operator=(T2 value) {
|
||||
value_ = (value_ & ~(Mask << Index)) | ((value & Mask) << Index);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T2>
|
||||
T2 as() { return (value_ >> Index) & Mask; }
|
||||
operator T() const { return (value_ >> Index) & Mask; }
|
||||
explicit operator bool() const { return value_ & (Mask << Index); }
|
||||
BitField &operator++() { return *this = *this + 1; }
|
||||
T operator++(int) { T r = *this; ++*this; return r; }
|
||||
BitField &operator--() { return *this = *this - 1; }
|
||||
T operator--(int) { T r = *this; ++*this; return r; }
|
||||
|
||||
private:
|
||||
T value_;
|
||||
};
|
||||
|
||||
|
||||
template <size_t Index>
|
||||
class BitField<Index, 1> {
|
||||
private:
|
||||
enum {
|
||||
Bits = 1,
|
||||
Mask = 0x01
|
||||
};
|
||||
|
||||
typedef typename MinimumTypeHelper<Index + Bits>::type T;
|
||||
public:
|
||||
template <typename T2>
|
||||
T2 as() { return (value_ >> Index) & Mask; }
|
||||
BitField &operator=(bool value) {
|
||||
value_ = (value_ & ~(Mask << Index)) | (value << Index);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator bool() const { return value_ & (Mask << Index); }
|
||||
|
||||
private:
|
||||
T value_;
|
||||
};
|
||||
|
||||
#endif // BITFIELD_H
|
||||
|
||||
137
src/armv5te/coproc.cpp
Normal file
137
src/armv5te/coproc.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
#include "cpu.h"
|
||||
#include "cpudefs.h"
|
||||
#include "mmu.h"
|
||||
|
||||
void do_cp15_mrc(uint32_t insn)
|
||||
{
|
||||
uint32_t value;
|
||||
switch (insn & 0xEF00EF) {
|
||||
case 0x000000: /* MRC p15, 0, <Rd>, c0, c0, 0: ID Code Register */
|
||||
//value = 0x41069264; /* ARM926EJ-S revision 4 */
|
||||
value = 0x69052100;//Intel PXA255 "01101001000001010010000100000000"
|
||||
break;
|
||||
case 0x000010: /* MRC p15, 0, <Rd>, c0, c0, 1: Cache Type Register */
|
||||
value = 0x1D112152; /* ICache: 16KB 4-way 8 word, DCache: 8KB 4-way 8 word */
|
||||
break;
|
||||
case 0x000020: /* MRC p15, 0, <Rd>, c0, c0, 2: TCM Status Register */
|
||||
value = 0;
|
||||
break;
|
||||
case 0x010000: /* MRC p15, 0, <Rd>, c1, c0, 0: Control Register */
|
||||
value = arm.control;
|
||||
break;
|
||||
case 0x020000: /* MRC p15, 0, <Rd>, c2, c0, 0: Translation Table Base Register */
|
||||
value = arm.translation_table_base;
|
||||
break;
|
||||
case 0x030000: /* MRC p15, 0, <Rd>, c3, c0, 0: Domain Access Control Register */
|
||||
value = arm.domain_access_control;
|
||||
break;
|
||||
case 0x050000: /* MRC p15, 0, <Rd>, c5, c0, 0: Data Fault Status Register */
|
||||
value = arm.data_fault_status;
|
||||
break;
|
||||
case 0x050020: /* MRC p15, 0, <Rd>, c5, c0, 1: Instruction Fault Status Register */
|
||||
value = arm.instruction_fault_status;
|
||||
break;
|
||||
case 0x060000: /* MRC p15, 0, <Rd>, c6, c0, 0: Fault Address Register */
|
||||
value = arm.fault_address;
|
||||
break;
|
||||
case 0x07006A: /* MRC p15, 0, <Rd>, c7, c10, 3: Test and clean DCache */
|
||||
value = 1 << 30;
|
||||
break;
|
||||
case 0x07006E: /* MRC p15, 0, <Rd>, c7, c14, 3: Test, clean, and invalidate DCache */
|
||||
value = 1 << 30;
|
||||
break;
|
||||
case 0x0D0000: /* MRC p15, 0, <Rd>, c13, c0, 0: Read FCSE PID */
|
||||
value = 0;
|
||||
break;
|
||||
case 0x0F0000: /* MRC p15, 0, <Rd>, c15, c0, 0: Debug Override Register */
|
||||
// Unimplemented
|
||||
value = 0;
|
||||
break;
|
||||
default:
|
||||
warn("Unknown coprocessor instruction MRC %08X", insn);
|
||||
value = 0;
|
||||
break;
|
||||
}
|
||||
if ((insn >> 12 & 15) == 15) {
|
||||
arm.cpsr_n = value >> 31 & 1;
|
||||
arm.cpsr_z = value >> 30 & 1;
|
||||
arm.cpsr_c = value >> 29 & 1;
|
||||
arm.cpsr_v = value >> 28 & 1;
|
||||
} else
|
||||
arm.reg[insn >> 12 & 15] = value;
|
||||
}
|
||||
|
||||
void do_cp15_mcr(uint32_t insn)
|
||||
{
|
||||
uint32_t value = reg(insn >> 12 & 15);
|
||||
switch (insn & 0xEF00EF) {
|
||||
case 0x010000: { /* MCR p15, 0, <Rd>, c1, c0, 0: Control Register */
|
||||
uint32_t change = value ^ arm.control;
|
||||
if ((value & 0xFFFF8CF0) != 0x00050070)
|
||||
error("Bad or unimplemented control register value: %x (unsupported: %x)\n", value, (value & 0xFFFF8CF8) ^ 0x00050078);
|
||||
arm.control = value;
|
||||
if (change & 1) // MMU is being turned on or off
|
||||
addr_cache_flush();
|
||||
break;
|
||||
}
|
||||
case 0x020000: /* MCR p15, 0, <Rd>, c2, c0, 0: Translation Table Base Register */
|
||||
arm.translation_table_base = value & ~0x3FFF;
|
||||
addr_cache_flush();
|
||||
break;
|
||||
case 0x030000: /* MCR p15, 0, <Rd>, c3, c0, 0: Domain Access Control Register */
|
||||
arm.domain_access_control = value;
|
||||
addr_cache_flush();
|
||||
break;
|
||||
case 0x050000: /* MCR p15, 0, <Rd>, c5, c0, 0: Data Fault Status Register */
|
||||
arm.data_fault_status = value;
|
||||
break;
|
||||
case 0x050020: /* MCR p15, 0, <Rd>, c5, c0, 1: Instruction Fault Status Register */
|
||||
arm.instruction_fault_status = value;
|
||||
break;
|
||||
case 0x060000: /* MCR p15, 0, <Rd>, c6, c0, 0: Fault Address Register */
|
||||
arm.fault_address = value;
|
||||
break;
|
||||
case 0x070080: /* MCR p15, 0, <Rd>, c7, c0, 4: Wait for interrupt */
|
||||
cycle_count_delta = 0;
|
||||
if (arm.interrupts == 0) {
|
||||
arm.reg[15] -= 4;
|
||||
cpu_events |= EVENT_WAITING;
|
||||
}
|
||||
break;
|
||||
case 0x080005: /* MCR p15, 0, <Rd>, c8, c5, 0: Invalidate instruction TLB */
|
||||
case 0x080007: /* MCR p15, 0, <Rd>, c8, c7, 0: Invalidate TLB */
|
||||
case 0x080025: /* MCR p15, 0, <Rd>, c8, c5, 1: Invalidate instruction TLB entry */
|
||||
case 0x080027: /* MCR p15, 0, <Rd>, c8, c7, 1: Invalidate TLB (used by polydumper) */
|
||||
case 0x070005: /* MCR p15, 0, <Rd>, c7, c5, 0: Invalidate ICache */
|
||||
case 0x070025: /* MCR p15, 0, <Rd>, c7, c5, 1: Invalidate ICache line */
|
||||
case 0x070007: /* MCR p15, 0, <Rd>, c7, c7, 0: Invalidate ICache and DCache */
|
||||
addr_cache_flush();
|
||||
break;
|
||||
|
||||
case 0x080006: /* MCR p15, 0, <Rd>, c8, c6, 0: Invalidate data TLB */
|
||||
case 0x080026: /* MCR p15, 0, <Rd>, c8, c6, 1: Invalidate data TLB entry */
|
||||
case 0x070026: /* MCR p15, 0, <Rd>, c7, c6, 1: Invalidate single DCache entry */
|
||||
case 0x07002A: /* MCR p15, 0, <Rd>, c7, c10, 1: Clean DCache line */
|
||||
case 0x07002E: /* MCR p15, 0, <Rd>, c7, c14, 1: Clean and invalidate single DCache entry */
|
||||
case 0x07008A: /* MCR p15, 0, <Rd>, c7, c10, 4: Drain write buffer */
|
||||
case 0x0F0000: /* MCR p15, 0, <Rd>, c15, c0, 0: Debug Override Register */
|
||||
#ifdef SUPPORT_LINUX
|
||||
// Normally ignored, but somehow needed for linux to boot correctly
|
||||
addr_cache_flush();
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
warn("Unknown coprocessor instruction MCR %08X", insn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void do_cp15_instruction(Instruction i)
|
||||
{
|
||||
uint32_t insn = i.raw;
|
||||
if(insn & 0x00100000)
|
||||
return do_cp15_mrc(insn);
|
||||
else
|
||||
return do_cp15_mcr(insn);
|
||||
}
|
||||
|
||||
430
src/armv5te/cpu.cpp
Normal file
430
src/armv5te/cpu.cpp
Normal file
@@ -0,0 +1,430 @@
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <mutex>
|
||||
|
||||
// Uncomment the following line to measure the time until the OS is loaded
|
||||
// #define BENCHMARK
|
||||
#ifdef BENCHMARK
|
||||
#include <ctime>
|
||||
#endif
|
||||
|
||||
#include "armsnippets.h"
|
||||
#include "asmcode.h"
|
||||
#include "cpu.h"
|
||||
#include "cpudefs.h"
|
||||
#include "debug.h"
|
||||
#include "emu.h"
|
||||
#include "mem.h"
|
||||
#include "mmu.h"
|
||||
#include "translate.h"
|
||||
|
||||
// Global CPU state
|
||||
struct arm_state arm;
|
||||
|
||||
void cpu_arm_loop()
|
||||
{
|
||||
while (!exiting && cycle_count_delta < 0 && current_instr_size == 4)
|
||||
{
|
||||
arm.reg[15] &= ~0x3; // Align PC
|
||||
Instruction *p = static_cast<Instruction*>(read_instruction(arm.reg[15]));
|
||||
|
||||
#ifdef BENCHMARK
|
||||
static clock_t start = 0;
|
||||
if(arm.reg[15] == 0)
|
||||
{
|
||||
start = clock();
|
||||
turbo_mode = true;
|
||||
}
|
||||
else if(arm.reg[15] == 0x10000000 || arm.reg[15] == 0x11800000)
|
||||
{
|
||||
clock_t diff = clock() - start;
|
||||
printf("%ld ms\n", diff / 1000);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t *flags_ptr = &RAM_FLAGS(p);
|
||||
|
||||
// Check for pending events
|
||||
if(cpu_events)
|
||||
{
|
||||
// Events other than DEBUG_STEP are handled outside
|
||||
if(cpu_events & ~EVENT_DEBUG_STEP)
|
||||
break;
|
||||
goto enter_debugger;
|
||||
}
|
||||
|
||||
// TODO: Other flags
|
||||
if(*flags_ptr & (RF_EXEC_BREAKPOINT | RF_EXEC_DEBUG_NEXT | RF_ARMLOADER_CB))
|
||||
{
|
||||
if(*flags_ptr & RF_ARMLOADER_CB)
|
||||
{
|
||||
*flags_ptr &= ~RF_ARMLOADER_CB;
|
||||
armloader_cb();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(*flags_ptr & RF_EXEC_BREAKPOINT)
|
||||
gui_debug_printf("Breakpoint at 0x%08x\n", arm.reg[15]);
|
||||
enter_debugger:
|
||||
uint32_t pc = arm.reg[15];
|
||||
debugger(DBG_EXEC_BREAKPOINT, 0);
|
||||
if(arm.reg[15] != pc)
|
||||
continue; // Debugger changed PC
|
||||
}
|
||||
}
|
||||
#ifndef NO_TRANSLATION
|
||||
else if(do_translate && !(*flags_ptr & DONT_TRANSLATE) && (*flags_ptr & RF_CODE_EXECUTED))
|
||||
translate(arm.reg[15], &p->raw);
|
||||
|
||||
// If the instruction is translated, use the translation
|
||||
if((~cpu_events & EVENT_DEBUG_STEP) && *flags_ptr & RF_CODE_TRANSLATED)
|
||||
{
|
||||
#if TRANSLATION_ENTER_HAS_PTR
|
||||
translation_enter(p);
|
||||
#else
|
||||
translation_enter();
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
*flags_ptr |= RF_CODE_EXECUTED;
|
||||
#endif
|
||||
|
||||
arm.reg[15] += 4; // Increment now to account for the pipeline
|
||||
++cycle_count_delta;
|
||||
do_arm_instruction(*p);
|
||||
}
|
||||
}
|
||||
|
||||
// Makes arm.reg[15] point to the current instruction
|
||||
void fix_pc_for_fault()
|
||||
{
|
||||
#ifndef NO_TRANSLATION
|
||||
translate_fix_pc();
|
||||
#endif
|
||||
|
||||
arm.reg[15] -= current_instr_size;
|
||||
}
|
||||
|
||||
void prefetch_abort(uint32_t mva, uint8_t status)
|
||||
{
|
||||
warn("Prefetch abort: address=%08x status=%02x\n", mva, status);
|
||||
arm.reg[15] += 4;
|
||||
// Fault address register not changed
|
||||
arm.instruction_fault_status = status;
|
||||
cpu_exception(EX_PREFETCH_ABORT);
|
||||
if (mva == arm.reg[15])
|
||||
error("Abort occurred with exception vectors unmapped");
|
||||
#ifndef NO_SETJMP
|
||||
__builtin_longjmp(restart_after_exception, 1);
|
||||
#else
|
||||
assert(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void data_abort(uint32_t mva, uint8_t status)
|
||||
{
|
||||
fix_pc_for_fault();
|
||||
warn("Data abort: address=%08x status=%02x instruction at %08x\n", mva, status, arm.reg[15]);
|
||||
arm.reg[15] += 8;
|
||||
arm.fault_address = mva;
|
||||
arm.data_fault_status = status;
|
||||
cpu_exception(EX_DATA_ABORT);
|
||||
#ifndef NO_SETJMP
|
||||
__builtin_longjmp(restart_after_exception, 1);
|
||||
#else
|
||||
assert(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void undefined_instruction()
|
||||
{
|
||||
fix_pc_for_fault();
|
||||
warn("Undefined instruction at %08x\n", arm.reg[15]);
|
||||
arm.reg[15] += current_instr_size;
|
||||
cpu_exception(EX_UNDEFINED);
|
||||
#ifndef NO_SETJMP
|
||||
__builtin_longjmp(restart_after_exception, 1);
|
||||
#else
|
||||
assert(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void *try_ptr(uint32_t addr)
|
||||
{
|
||||
//There are two different addr_cache formats...
|
||||
#ifdef AC_FLAGS
|
||||
uintptr_t entry = *(uintptr_t*)(addr_cache + ((addr >> 10) << 1));
|
||||
|
||||
if(unlikely(entry & AC_FLAGS))
|
||||
{
|
||||
if(entry & AC_INVALID)
|
||||
return addr_cache_miss(addr, false, nullptr);
|
||||
else // MMIO stuff
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
entry += addr;
|
||||
return (void*)entry;
|
||||
#else
|
||||
void *ptr = &addr_cache[(addr >> 10) << 1][addr];
|
||||
if(unlikely((uintptr_t)ptr & AC_NOT_PTR))
|
||||
ptr = addr_cache_miss(addr, false, nullptr);
|
||||
|
||||
return ptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void * FASTCALL read_instruction(uint32_t addr)
|
||||
{
|
||||
//There are two different addr_cache formats...
|
||||
#ifdef AC_FLAGS
|
||||
uintptr_t entry = *(uintptr_t*)(addr_cache + ((addr >> 10) << 1));
|
||||
|
||||
if(unlikely(entry & AC_FLAGS))
|
||||
{
|
||||
if(entry & AC_INVALID)
|
||||
return addr_cache_miss(addr, false, prefetch_abort);
|
||||
else // Executing MMIO stuff
|
||||
error("PC in MMIO range: 0x%x\n", addr);
|
||||
}
|
||||
|
||||
entry += addr;
|
||||
return (void*)entry;
|
||||
#else
|
||||
void *ptr = &addr_cache[(addr >> 10) << 1][addr];
|
||||
if(unlikely((uintptr_t)ptr & AC_NOT_PTR))
|
||||
{
|
||||
ptr = addr_cache_miss(addr, false, prefetch_abort);
|
||||
if (!ptr)
|
||||
error("Bad PC: %08x\n", addr);
|
||||
}
|
||||
return ptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Update cpu_events
|
||||
void cpu_int_check()
|
||||
{
|
||||
//events arnt threaded like this in Mu, plus this breaks the RetroArch build, some undefined reference thing
|
||||
//static std::mutex mut;
|
||||
//std::lock_guard<std::mutex> lg(mut);
|
||||
|
||||
if (arm.interrupts & ~arm.cpsr_low28 & 0x80)
|
||||
cpu_events |= EVENT_IRQ;
|
||||
else
|
||||
cpu_events &= ~EVENT_IRQ;
|
||||
|
||||
if (arm.interrupts & ~arm.cpsr_low28 & 0x40)
|
||||
cpu_events |= EVENT_FIQ;
|
||||
else
|
||||
cpu_events &= ~EVENT_FIQ;
|
||||
}
|
||||
|
||||
static const constexpr uint8_t exc_flags[] = {
|
||||
MODE_SVC | 0xC0, /* Reset */
|
||||
MODE_UND | 0x80, /* Undefined instruction */
|
||||
MODE_SVC | 0x80, /* Software interrupt */
|
||||
MODE_ABT | 0x80, /* Prefetch abort */
|
||||
MODE_ABT | 0x80, /* Data abort */
|
||||
0, /* Reserved */
|
||||
MODE_IRQ | 0x80, /* IRQ */
|
||||
MODE_FIQ | 0xC0, /* FIQ */
|
||||
};
|
||||
|
||||
void cpu_exception(int type)
|
||||
{
|
||||
/* Switch mode, disable interrupts */
|
||||
uint32_t old_cpsr = get_cpsr();
|
||||
set_cpsr_full((old_cpsr & ~0x3F) | exc_flags[type]);
|
||||
*ptr_spsr() = old_cpsr;
|
||||
|
||||
/* Branch-and-link to exception handler */
|
||||
arm.reg[14] = arm.reg[15];
|
||||
arm.reg[15] = type << 2;
|
||||
if (arm.control & 0x2000) /* High vectors */
|
||||
arm.reg[15] += 0xFFFF0000;
|
||||
}
|
||||
|
||||
uint32_t get_cpsr_flags()
|
||||
{
|
||||
return arm.cpsr_n << 31
|
||||
| arm.cpsr_z << 30
|
||||
| arm.cpsr_c << 29
|
||||
| arm.cpsr_v << 28;
|
||||
}
|
||||
|
||||
void set_cpsr_flags(uint32_t flags)
|
||||
{
|
||||
arm.cpsr_n = (flags >> 31) & 1;
|
||||
arm.cpsr_z = (flags >> 30) & 1;
|
||||
arm.cpsr_c = (flags >> 29) & 1;
|
||||
arm.cpsr_v = (flags >> 28) & 1;
|
||||
}
|
||||
|
||||
// Get full CPSR register
|
||||
uint32_t FASTCALL get_cpsr()
|
||||
{
|
||||
return arm.cpsr_n << 31
|
||||
| arm.cpsr_z << 30
|
||||
| arm.cpsr_c << 29
|
||||
| arm.cpsr_v << 28
|
||||
| arm.cpsr_low28;
|
||||
}
|
||||
|
||||
void set_cpsr_full(uint32_t cpsr)
|
||||
{
|
||||
uint8_t old_mode = arm.cpsr_low28 & 0x1F,
|
||||
new_mode = cpsr & 0x1F;
|
||||
|
||||
if(old_mode == new_mode)
|
||||
goto same_mode;
|
||||
|
||||
// Only FIQ mode has more than 2 regs banked
|
||||
if(old_mode == MODE_FIQ)
|
||||
std::copy(arm.reg + 8, arm.reg + 13, arm.r8_fiq);
|
||||
else
|
||||
std::copy(arm.reg + 8, arm.reg + 13, arm.r8_usr);
|
||||
|
||||
switch(old_mode)
|
||||
{
|
||||
case MODE_USR: case MODE_SYS:
|
||||
std::copy(arm.reg + 13, arm.reg + 15, arm.r13_usr);
|
||||
break;
|
||||
case MODE_FIQ:
|
||||
std::copy(arm.reg + 13, arm.reg + 15, arm.r13_fiq);
|
||||
break;
|
||||
case MODE_IRQ:
|
||||
std::copy(arm.reg + 13, arm.reg + 15, arm.r13_irq);
|
||||
break;
|
||||
case MODE_SVC:
|
||||
std::copy(arm.reg + 13, arm.reg + 15, arm.r13_svc);
|
||||
break;
|
||||
case MODE_ABT:
|
||||
std::copy(arm.reg + 13, arm.reg + 15, arm.r13_abt);
|
||||
break;
|
||||
case MODE_UND:
|
||||
std::copy(arm.reg + 13, arm.reg + 15, arm.r13_und);
|
||||
break;
|
||||
default: assert(false);
|
||||
}
|
||||
|
||||
if(new_mode == MODE_FIQ)
|
||||
std::copy(arm.r8_fiq, arm.r8_fiq + 5, arm.reg + 8);
|
||||
else
|
||||
std::copy(arm.r8_usr, arm.r8_usr + 5, arm.reg + 8);
|
||||
|
||||
switch(new_mode)
|
||||
{
|
||||
case MODE_USR: case MODE_SYS:
|
||||
std::copy(arm.r13_usr, arm.r13_usr + 2, arm.reg + 13);
|
||||
break;
|
||||
case MODE_FIQ:
|
||||
std::copy(arm.r13_fiq, arm.r13_fiq + 2, arm.reg + 13);
|
||||
break;
|
||||
case MODE_IRQ:
|
||||
std::copy(arm.r13_irq, arm.r13_irq + 2, arm.reg + 13);
|
||||
break;
|
||||
case MODE_SVC:
|
||||
std::copy(arm.r13_svc, arm.r13_svc + 2, arm.reg + 13);
|
||||
break;
|
||||
case MODE_ABT:
|
||||
std::copy(arm.r13_abt, arm.r13_abt + 2, arm.reg + 13);
|
||||
break;
|
||||
case MODE_UND:
|
||||
std::copy(arm.r13_und, arm.r13_und + 2, arm.reg + 13);
|
||||
break;
|
||||
default: error("Invalid mode 0x%x\n", new_mode);
|
||||
}
|
||||
|
||||
// Access permissions are different
|
||||
if((old_mode == MODE_USR) ^ (new_mode == MODE_USR))
|
||||
addr_cache_flush();
|
||||
|
||||
same_mode:
|
||||
if(cpsr & 0x01000000)
|
||||
error("Jazelle not implemented!");
|
||||
|
||||
arm.cpsr_n = (cpsr >> 31) & 1;
|
||||
arm.cpsr_z = (cpsr >> 30) & 1;
|
||||
arm.cpsr_c = (cpsr >> 29) & 1;
|
||||
arm.cpsr_v = (cpsr >> 28) & 1;
|
||||
arm.cpsr_low28 = cpsr & 0x090000FF; // Mask off reserved bits
|
||||
cpu_int_check();
|
||||
}
|
||||
|
||||
void FASTCALL set_cpsr(uint32_t cpsr, uint32_t mask) {
|
||||
if (!(arm.cpsr_low28 & 0x0F)) {
|
||||
/* User mode. Don't change privileged or execution state bits */
|
||||
mask &= ~0x010000FF;
|
||||
}
|
||||
cpsr = (cpsr & mask) | (get_cpsr() & ~mask);
|
||||
if (cpsr & 0x20)
|
||||
error("Cannot set T bit with MSR instruction");
|
||||
set_cpsr_full(cpsr);
|
||||
}
|
||||
|
||||
uint32_t *ptr_spsr()
|
||||
{
|
||||
switch (arm.cpsr_low28 & 0x1F)
|
||||
{
|
||||
case MODE_FIQ: return &arm.spsr_fiq;
|
||||
case MODE_IRQ: return &arm.spsr_irq;
|
||||
case MODE_SVC: return &arm.spsr_svc;
|
||||
case MODE_ABT: return &arm.spsr_abt;
|
||||
case MODE_UND: return &arm.spsr_und;
|
||||
}
|
||||
error("Attempted to access SPSR from user or system mode");
|
||||
}
|
||||
|
||||
uint32_t FASTCALL get_spsr() {
|
||||
return *ptr_spsr();
|
||||
}
|
||||
|
||||
void FASTCALL set_spsr(uint32_t spsr, uint32_t mask) {
|
||||
*ptr_spsr() ^= (*ptr_spsr() ^ spsr) & mask;
|
||||
}
|
||||
|
||||
uint32_t reg(uint8_t i)
|
||||
{
|
||||
if(unlikely(i == 15))
|
||||
error("PC invalid in this context!\n");
|
||||
return arm.reg[i];
|
||||
}
|
||||
|
||||
uint32_t reg_pc(uint8_t i)
|
||||
{
|
||||
if(unlikely(i == 15))
|
||||
return arm.reg[15] + 4;
|
||||
return arm.reg[i];
|
||||
}
|
||||
|
||||
uint32_t reg_pc_mem(uint8_t i)
|
||||
{
|
||||
if(unlikely(i == 15))
|
||||
return arm.reg[15] + 8;
|
||||
return arm.reg[i];
|
||||
}
|
||||
|
||||
void set_reg(uint8_t i, uint32_t value)
|
||||
{
|
||||
if(unlikely(i == 15))
|
||||
error("PC invalid in this context!\n");
|
||||
arm.reg[i] = value;
|
||||
}
|
||||
|
||||
void set_reg_pc(uint8_t i, uint32_t value)
|
||||
{
|
||||
arm.reg[i] = value;
|
||||
}
|
||||
|
||||
void set_reg_bx(uint8_t i, uint32_t value)
|
||||
{
|
||||
arm.reg[i] = value;
|
||||
|
||||
if(i == 15 && (value & 1))
|
||||
{
|
||||
arm.cpsr_low28 |= 0x20; // Enter Thumb mode
|
||||
arm.reg[15] -= 1;
|
||||
}
|
||||
}
|
||||
110
src/armv5te/cpu.h
Normal file
110
src/armv5te/cpu.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/* Declarations for cpu.c */
|
||||
|
||||
#ifndef CPU_H
|
||||
#define CPU_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct arm_state { // Remember to update asmcode.S if this gets rearranged
|
||||
uint32_t reg[16]; // Registers for current mode.
|
||||
|
||||
uint32_t cpsr_low28; // CPSR bits 0-27
|
||||
uint8_t cpsr_n; // CPSR bit 31
|
||||
uint8_t cpsr_z; // CPSR bit 30
|
||||
uint8_t cpsr_c; // CPSR bit 29
|
||||
uint8_t cpsr_v; // CPSR bit 28
|
||||
|
||||
#if defined(__arm__)
|
||||
uint32_t cpsr_flags; // Only used in ARM translations
|
||||
#endif
|
||||
|
||||
/* CP15 registers */
|
||||
uint32_t control;
|
||||
uint32_t translation_table_base;
|
||||
uint32_t domain_access_control;
|
||||
uint8_t data_fault_status, instruction_fault_status, pad1, pad2; // pad1 and pad2 for better alignment
|
||||
uint32_t fault_address;
|
||||
|
||||
uint32_t r8_usr[5], r13_usr[2];
|
||||
uint32_t r8_fiq[5], r13_fiq[2], spsr_fiq;
|
||||
uint32_t r13_irq[2], spsr_irq;
|
||||
uint32_t r13_svc[2], spsr_svc;
|
||||
uint32_t r13_abt[2], spsr_abt;
|
||||
uint32_t r13_und[2], spsr_und;
|
||||
|
||||
uint8_t interrupts;
|
||||
uint32_t cpu_events_state; // Only used for suspend and resume!
|
||||
}
|
||||
#ifndef __EMSCRIPTEN__
|
||||
__attribute__((packed))
|
||||
#endif
|
||||
arm_state;
|
||||
extern struct arm_state arm __asm__("arm");
|
||||
|
||||
#define MODE_USR 0x10
|
||||
#define MODE_FIQ 0x11
|
||||
#define MODE_IRQ 0x12
|
||||
#define MODE_SVC 0x13
|
||||
#define MODE_ABT 0x17
|
||||
#define MODE_UND 0x1B
|
||||
#define MODE_SYS 0x1F
|
||||
#define PRIVILEGED_MODE() (arm.cpsr_low28 & 3)
|
||||
#define USER_MODE() (!(arm.cpsr_low28 & 3))
|
||||
|
||||
#define EX_RESET 0
|
||||
#define EX_UNDEFINED 1
|
||||
#define EX_SWI 2
|
||||
#define EX_PREFETCH_ABORT 3
|
||||
#define EX_DATA_ABORT 4
|
||||
#define EX_IRQ 6
|
||||
#define EX_FIQ 7
|
||||
|
||||
#define current_instr_size ((arm.cpsr_low28 & 0x20) ? 2 /* thumb */ : 4)
|
||||
|
||||
// Needed for the assembler calling convention
|
||||
#ifdef __i386__
|
||||
#ifdef FASTCALL
|
||||
#undef FASTCALL
|
||||
#endif
|
||||
#define FASTCALL __attribute__((fastcall))
|
||||
#else
|
||||
#define FASTCALL
|
||||
#endif
|
||||
|
||||
void cpu_int_check();
|
||||
uint32_t FASTCALL get_cpsr() __asm__("get_cpsr");
|
||||
void set_cpsr_full(uint32_t cpsr);
|
||||
void FASTCALL set_cpsr(uint32_t cpsr, uint32_t mask);
|
||||
uint32_t FASTCALL get_spsr();
|
||||
void FASTCALL set_spsr(uint32_t cpsr, uint32_t mask);
|
||||
uint32_t *ptr_spsr();
|
||||
uint32_t get_cpsr_flags();
|
||||
void set_cpsr_flags(uint32_t flags) __asm__("set_cpsr_flags");
|
||||
void cpu_exception(int type);
|
||||
void fix_pc_for_fault();
|
||||
void *try_ptr(uint32_t addr);
|
||||
void cpu_interpret_instruction(uint32_t insn);
|
||||
void cpu_arm_loop();
|
||||
void cpu_thumb_loop();
|
||||
typedef void fault_proc(uint32_t mva, uint8_t status);
|
||||
fault_proc prefetch_abort, data_abort __asm__("data_abort");
|
||||
void undefined_instruction();
|
||||
|
||||
uint32_t reg(uint8_t i);
|
||||
uint32_t reg_pc(uint8_t i);
|
||||
uint32_t reg_pc_mem(uint8_t i);
|
||||
void set_reg(uint8_t i, uint32_t value);
|
||||
void set_reg_pc(uint8_t i, uint32_t value);
|
||||
void set_reg_bx(uint8_t i, uint32_t value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
156
src/armv5te/cpudefs.h
Normal file
156
src/armv5te/cpudefs.h
Normal file
@@ -0,0 +1,156 @@
|
||||
#ifndef CPUDEFS_H
|
||||
#define CPUDEFS_H
|
||||
|
||||
#include "bitfield.h"
|
||||
|
||||
// Structure of instructions, enums etc.
|
||||
|
||||
union Instruction {
|
||||
uint32_t raw;
|
||||
|
||||
BitField<28, 4> cond;
|
||||
BitField<0, 28> rest;
|
||||
|
||||
union {
|
||||
BitField<24> l;
|
||||
BitField<0, 24> immed;
|
||||
} branch;
|
||||
|
||||
union {
|
||||
BitField<0, 4> rm;
|
||||
BitField<5> l;
|
||||
} bx;
|
||||
|
||||
union {
|
||||
BitField<21, 4> op;
|
||||
BitField<20> s;
|
||||
BitField<16, 4> rn;
|
||||
BitField<12, 4> rd;
|
||||
BitField<25> imm;
|
||||
|
||||
// If imm
|
||||
BitField<0, 8> immed_8;
|
||||
BitField<8, 4> rotate_imm;
|
||||
// else
|
||||
BitField<4> reg_shift;
|
||||
BitField<0, 4> rm;
|
||||
BitField<5, 2> shift;
|
||||
// If reg_shift
|
||||
BitField<8, 4> rs;
|
||||
// else
|
||||
BitField<7, 5> shift_imm;
|
||||
} data_proc; // ADD, MOV, etc.
|
||||
|
||||
union {
|
||||
BitField<25> not_imm;
|
||||
BitField<24> p;
|
||||
BitField<23> u;
|
||||
BitField<22> b;
|
||||
BitField<21> w;
|
||||
BitField<20> l;
|
||||
BitField<16, 4> rn;
|
||||
BitField<12, 4> rd;
|
||||
|
||||
// If not_imm == 0
|
||||
BitField<0, 12> immed;
|
||||
// else
|
||||
BitField<0, 4> rm;
|
||||
BitField<5, 2> shift;
|
||||
BitField<7, 5> shift_imm;
|
||||
} mem_proc; // LDR, STRB, etc.
|
||||
|
||||
union {
|
||||
BitField<24> p;
|
||||
BitField<23> u;
|
||||
BitField<22> i;
|
||||
BitField<21> w;
|
||||
BitField<20> l;
|
||||
BitField<16, 4> rn;
|
||||
BitField<12, 4> rd;
|
||||
|
||||
BitField<6> s;
|
||||
BitField<5> h;
|
||||
|
||||
// If i
|
||||
BitField<8, 4> immed_h;
|
||||
BitField<0, 4> immed_l;
|
||||
// else
|
||||
BitField<0, 4> rm;
|
||||
} mem_proc2; // LDRH, STRSH, etc.
|
||||
|
||||
union {
|
||||
BitField<23> l;
|
||||
BitField<21> a;
|
||||
BitField<20> s;
|
||||
|
||||
BitField<8, 4> rs;
|
||||
BitField<0, 4> rm;
|
||||
|
||||
// If l
|
||||
BitField<16, 4> rdhi;
|
||||
BitField<12, 4> rdlo;
|
||||
// else
|
||||
BitField<16, 4> rd;
|
||||
BitField<12, 4> rn;
|
||||
} mult;
|
||||
|
||||
union {
|
||||
BitField<22> r;
|
||||
BitField<12, 4> rd;
|
||||
} mrs;
|
||||
|
||||
union {
|
||||
BitField<24> p;
|
||||
BitField<23> u;
|
||||
BitField<22> s;
|
||||
BitField<21> w;
|
||||
BitField<20> l;
|
||||
BitField<16, 4> rn;
|
||||
BitField<0, 16> reglist;
|
||||
} mem_multi;
|
||||
};
|
||||
|
||||
enum Condition {
|
||||
CC_EQ=0, CC_NE,
|
||||
CC_CS, CC_CC,
|
||||
CC_MI, CC_PL,
|
||||
CC_VS, CC_VC,
|
||||
CC_HI, CC_LS,
|
||||
CC_GE, CC_LT,
|
||||
CC_GT, CC_LE,
|
||||
CC_AL, CC_NV
|
||||
};
|
||||
|
||||
enum ShiftType {
|
||||
SH_LSL=0,
|
||||
SH_LSR,
|
||||
SH_ASR,
|
||||
SH_ROR // RRX if count == 0
|
||||
};
|
||||
|
||||
enum DataOp {
|
||||
OP_AND=0,
|
||||
OP_EOR,
|
||||
OP_SUB,
|
||||
OP_RSB,
|
||||
OP_ADD,
|
||||
OP_ADC,
|
||||
OP_SBC,
|
||||
OP_RSC,
|
||||
OP_TST,
|
||||
OP_TEQ,
|
||||
OP_CMP,
|
||||
OP_CMN,
|
||||
OP_ORR,
|
||||
OP_MOV,
|
||||
OP_BIC,
|
||||
OP_MVN
|
||||
};
|
||||
|
||||
// Defined in arm_interpreter.cpp
|
||||
void do_arm_instruction(Instruction i);
|
||||
// Defined in coproc.cpp
|
||||
void do_cp15_instruction(Instruction i);
|
||||
|
||||
#endif // CPUDEFS_H
|
||||
|
||||
58
src/armv5te/debug.h
Normal file
58
src/armv5te/debug.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/* Declarations for debug.c */
|
||||
#ifndef _H_DEBUG
|
||||
#define _H_DEBUG
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <string>
|
||||
//extern std::string ln_target_folder;
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern FILE *debugger_input;
|
||||
|
||||
/*
|
||||
extern bool gdb_connected;
|
||||
extern bool in_debugger;
|
||||
extern int rdbg_port;
|
||||
*/
|
||||
#define gdb_connected false
|
||||
#define in_debugger false
|
||||
#define rdbg_port 0
|
||||
|
||||
enum DBG_REASON {
|
||||
DBG_USER,
|
||||
DBG_EXCEPTION,
|
||||
DBG_EXEC_BREAKPOINT,
|
||||
DBG_READ_BREAKPOINT,
|
||||
DBG_WRITE_BREAKPOINT,
|
||||
};
|
||||
|
||||
/*
|
||||
void *virt_mem_ptr(uint32_t addr, uint32_t size);
|
||||
void backtrace(uint32_t fp);
|
||||
int process_debug_cmd(char *cmdline);
|
||||
void debugger(enum DBG_REASON reason, uint32_t addr);
|
||||
void rdebug_recv(void);
|
||||
bool rdebug_bind(unsigned int port);
|
||||
void rdebug_quit();
|
||||
*/
|
||||
#define virt_mem_ptr(x, y) NULL
|
||||
#define backtrace(x)
|
||||
#define process_debug_cmd(x) 0
|
||||
#define debugger(x, y)
|
||||
#define rdebug_recv()
|
||||
#define rdebug_bind(x) false
|
||||
#define rdebug_quit()
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
65
src/armv5te/emu.h
Normal file
65
src/armv5te/emu.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#ifndef _H_EMU
|
||||
#define _H_EMU
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "mem.h"
|
||||
|
||||
#include "../emulator.h"
|
||||
#include "../portability.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Can also be set manually
|
||||
#if !defined(__i386__) && !defined(__x86_64__) && !(defined(__arm__) && !defined(__thumb__)) && !(defined(__aarch64__))
|
||||
#define NO_TRANSLATION
|
||||
#endif
|
||||
|
||||
// on iOS, setjmp and longjmp are broken
|
||||
#ifdef IS_IOS_BUILD
|
||||
#define NO_SETJMP
|
||||
#endif
|
||||
|
||||
static inline uint16_t BSWAP16(uint16_t x) { return x << 8 | x >> 8; }
|
||||
#define BSWAP32(x) __builtin_bswap32(x)
|
||||
|
||||
extern int cycle_count_delta __asm__("cycle_count_delta");
|
||||
extern uint32_t cpu_events __asm__("cpu_events");
|
||||
#define EVENT_IRQ 1
|
||||
#define EVENT_FIQ 2
|
||||
#define EVENT_RESET 4
|
||||
#define EVENT_DEBUG_STEP 8
|
||||
#define EVENT_WAITING 16
|
||||
|
||||
// Settings
|
||||
extern bool exiting;
|
||||
extern bool do_translate;
|
||||
|
||||
enum { LOG_CPU, LOG_IO, LOG_FLASH, LOG_INTS, LOG_ICOUNT, LOG_USB, LOG_GDB, MAX_LOG };
|
||||
#define LOG_TYPE_TBL "CIFQ#UG"
|
||||
void logprintf(int type, const char *str, ...);
|
||||
void emuprintf(const char *format, ...);
|
||||
|
||||
//void warn(const char *fmt, ...);
|
||||
#define warn(...) debugLog(__VA_ARGS__)
|
||||
//__attribute__((noreturn)) void error(const char *fmt, ...);
|
||||
#define error(...) abort()
|
||||
|
||||
// Is actually a jmp_buf, but __builtin_*jmp is used instead
|
||||
// as the MinGW variant is buggy
|
||||
extern void *restart_after_exception[32];
|
||||
|
||||
// GUI callbacks
|
||||
#define gui_debug_printf(...) debugLog(__VA_ARGS__)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
19
src/armv5te/emuVarPool.c
Normal file
19
src/armv5te/emuVarPool.c
Normal file
@@ -0,0 +1,19 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "mem.h"
|
||||
#include "emu.h"
|
||||
|
||||
|
||||
/* cycle_count_delta is a (usually negative) number telling what the time is relative
|
||||
* to the next scheduled event. See sched.c */
|
||||
int cycle_count_delta;
|
||||
|
||||
bool exiting;
|
||||
bool do_translate;
|
||||
|
||||
void *restart_after_exception[32];
|
||||
|
||||
uint32_t cpu_events;
|
||||
37
src/armv5te/literalpool.h
Normal file
37
src/armv5te/literalpool.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef LITERALPOOL_H
|
||||
#define LITERALPOOL_H
|
||||
|
||||
/* Code shared by various translators, this file keeps the
|
||||
arch-independant parts. */
|
||||
|
||||
struct LiteralRef {
|
||||
void *inst;
|
||||
uintptr_t value;
|
||||
};
|
||||
|
||||
static constexpr size_t MAX_LITERALS = 1024;
|
||||
static LiteralRef literals[MAX_LITERALS];
|
||||
static size_t literals_count = 0;
|
||||
|
||||
void literalpool_add(uintptr_t value)
|
||||
{
|
||||
if(literals_count >= MAX_LITERALS)
|
||||
{
|
||||
literals_count = 0; // Otherwise it won't ever be empty again
|
||||
error("Literal pool full, please increase the size");
|
||||
return;
|
||||
}
|
||||
|
||||
literals[literals_count++] = LiteralRef { .inst = translate_current,
|
||||
.value = value };
|
||||
}
|
||||
|
||||
/* Function implemented by the architecture.
|
||||
It needs to iterate through all elements in literals until literals_count,
|
||||
emit the literal into a suitable location and fixup all instructions that
|
||||
reference it. If alignment is not an issue, certain values can be optimized,
|
||||
but in such cases the loading instruction and the literal pool need to agree.
|
||||
It then needs to reset literals_count to 0. */
|
||||
void literalpool_fill();
|
||||
|
||||
#endif //LITERALPOOL_H
|
||||
143
src/armv5te/mem.c
Normal file
143
src/armv5te/mem.c
Normal file
@@ -0,0 +1,143 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "emu.h"
|
||||
#include "os/os.h"
|
||||
#include "mem.h"
|
||||
#include "translate.h"
|
||||
|
||||
uint8_t (*read_byte_map[64])(uint32_t addr);
|
||||
uint16_t (*read_half_map[64])(uint32_t addr);
|
||||
uint32_t (*read_word_map[64])(uint32_t addr);
|
||||
void (*write_byte_map[64])(uint32_t addr, uint8_t value);
|
||||
void (*write_half_map[64])(uint32_t addr, uint16_t value);
|
||||
void (*write_word_map[64])(uint32_t addr, uint32_t value);
|
||||
|
||||
/* For invalid/unknown physical addresses */
|
||||
uint8_t bad_read_byte(uint32_t addr) { warn("Bad read_byte: %08x", addr); return 0; }
|
||||
uint16_t bad_read_half(uint32_t addr) { warn("Bad read_half: %08x", addr); return 0; }
|
||||
uint32_t bad_read_word(uint32_t addr) { warn("Bad read_word: %08x", addr); return 0; }
|
||||
void bad_write_byte(uint32_t addr, uint8_t value) { warn("Bad write_byte: %08x %02x", addr, value); }
|
||||
void bad_write_half(uint32_t addr, uint16_t value) { warn("Bad write_half: %08x %04x", addr, value); }
|
||||
void bad_write_word(uint32_t addr, uint32_t value) { warn("Bad write_word: %08x %08x", addr, value); }
|
||||
|
||||
uint8_t *mem_and_flags = NULL;
|
||||
struct mem_area_desc mem_areas[2];
|
||||
|
||||
void *phys_mem_ptr(uint32_t addr, uint32_t size) {
|
||||
unsigned int i;
|
||||
for (i = 0; i < sizeof(mem_areas)/sizeof(*mem_areas); i++) {
|
||||
uint32_t offset = addr - mem_areas[i].base;
|
||||
if (offset < mem_areas[i].size && size <= mem_areas[i].size - offset)
|
||||
return mem_areas[i].ptr + offset;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t phys_mem_addr(void *ptr) {
|
||||
int i;
|
||||
for (i = 0; i < 3; i++) {
|
||||
uint32_t offset = (uint8_t *)ptr - mem_areas[i].ptr;
|
||||
if (offset < mem_areas[i].size)
|
||||
return mem_areas[i].base + offset;
|
||||
}
|
||||
return -1; // should never happen
|
||||
}
|
||||
|
||||
void read_action(void *ptr) {
|
||||
// this is just debugging stuff
|
||||
/*
|
||||
uint32_t addr = phys_mem_addr(ptr);
|
||||
if (!gdb_connected)
|
||||
emuprintf("Hit read breakpoint at %08x. Entering debugger.\n", addr);
|
||||
debugger(DBG_READ_BREAKPOINT, addr);
|
||||
*/
|
||||
}
|
||||
|
||||
void write_action(void *ptr) {
|
||||
// this is just debugging stuff
|
||||
uint32_t addr = phys_mem_addr(ptr);
|
||||
uint32_t *flags = &RAM_FLAGS((size_t)ptr & ~3);
|
||||
/*
|
||||
// this is just debugging stuff
|
||||
if (*flags & RF_WRITE_BREAKPOINT) {
|
||||
if (!gdb_connected)
|
||||
emuprintf("Hit write breakpoint at %08x. Entering debugger.\n", addr);
|
||||
debugger(DBG_WRITE_BREAKPOINT, addr);
|
||||
}
|
||||
*/
|
||||
#ifndef NO_TRANSLATION
|
||||
if (*flags & RF_CODE_TRANSLATED) {
|
||||
logprintf(LOG_CPU, "Wrote to translated code at %08x. Deleting translations.\n", addr);
|
||||
invalidate_translation(*flags >> RFS_TRANSLATION_INDEX);
|
||||
} else {
|
||||
*flags &= ~RF_CODE_NO_TRANSLATE;
|
||||
}
|
||||
*flags &= ~RF_CODE_EXECUTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* 00000000, 10000000, A4000000: ROM and RAM */
|
||||
uint8_t memory_read_byte(uint32_t addr) {
|
||||
uint8_t *ptr = phys_mem_ptr(addr, 1);
|
||||
if (!ptr) return bad_read_byte(addr);
|
||||
if (RAM_FLAGS((size_t)ptr & ~3) & DO_READ_ACTION) read_action(ptr);
|
||||
return *ptr;
|
||||
}
|
||||
uint16_t memory_read_half(uint32_t addr) {
|
||||
uint16_t *ptr = phys_mem_ptr(addr, 2);
|
||||
if (!ptr) return bad_read_half(addr);
|
||||
if (RAM_FLAGS((size_t)ptr & ~3) & DO_READ_ACTION) read_action(ptr);
|
||||
return *ptr;
|
||||
}
|
||||
uint32_t memory_read_word(uint32_t addr) {
|
||||
uint32_t *ptr = phys_mem_ptr(addr, 4);
|
||||
if (!ptr) return bad_read_word(addr);
|
||||
if (RAM_FLAGS(ptr) & DO_READ_ACTION) read_action(ptr);
|
||||
return *ptr;
|
||||
}
|
||||
void memory_write_byte(uint32_t addr, uint8_t value) {
|
||||
uint8_t *ptr = phys_mem_ptr(addr, 1);
|
||||
if (!ptr) { bad_write_byte(addr, value); return; }
|
||||
uint32_t flags = RAM_FLAGS((size_t)ptr & ~3);
|
||||
if (flags & RF_READ_ONLY) { bad_write_byte(addr, value); return; }
|
||||
if (flags & DO_WRITE_ACTION) write_action(ptr);
|
||||
*ptr = value;
|
||||
}
|
||||
void memory_write_half(uint32_t addr, uint16_t value) {
|
||||
uint16_t *ptr = phys_mem_ptr(addr, 2);
|
||||
if (!ptr) { bad_write_half(addr, value); return; }
|
||||
uint32_t flags = RAM_FLAGS((size_t)ptr & ~3);
|
||||
if (flags & RF_READ_ONLY) { bad_write_half(addr, value); return; }
|
||||
if (flags & DO_WRITE_ACTION) write_action(ptr);
|
||||
*ptr = value;
|
||||
}
|
||||
void memory_write_word(uint32_t addr, uint32_t value) {
|
||||
uint32_t *ptr = phys_mem_ptr(addr, 4);
|
||||
if (!ptr) { bad_write_word(addr, value); return; }
|
||||
uint32_t flags = RAM_FLAGS(ptr);
|
||||
if (flags & RF_READ_ONLY) { bad_write_word(addr, value); return; }
|
||||
if (flags & DO_WRITE_ACTION) write_action(ptr);
|
||||
*ptr = value;
|
||||
}
|
||||
|
||||
uint32_t FASTCALL mmio_read_byte(uint32_t addr) {
|
||||
return read_byte_map[addr >> 26](addr);
|
||||
}
|
||||
uint32_t FASTCALL mmio_read_half(uint32_t addr) {
|
||||
return read_half_map[addr >> 26](addr);
|
||||
}
|
||||
uint32_t FASTCALL mmio_read_word(uint32_t addr) {
|
||||
return read_word_map[addr >> 26](addr);
|
||||
}
|
||||
void FASTCALL mmio_write_byte(uint32_t addr, uint32_t value) {
|
||||
write_byte_map[addr >> 26](addr, value);
|
||||
}
|
||||
void FASTCALL mmio_write_half(uint32_t addr, uint32_t value) {
|
||||
write_half_map[addr >> 26](addr, value);
|
||||
}
|
||||
void FASTCALL mmio_write_word(uint32_t addr, uint32_t value) {
|
||||
write_word_map[addr >> 26](addr, value);
|
||||
}
|
||||
84
src/armv5te/mem.h
Normal file
84
src/armv5te/mem.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/* Declarations for memory.c */
|
||||
|
||||
#ifndef _H_MEM
|
||||
#define _H_MEM
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "cpu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MEM_MAXSIZE (70*1024*1024) // also defined as RAM_FLAGS in asmcode.S
|
||||
|
||||
extern uint8_t (*read_byte_map[64])(uint32_t addr);
|
||||
extern uint16_t (*read_half_map[64])(uint32_t addr);
|
||||
extern uint32_t (*read_word_map[64])(uint32_t addr);
|
||||
extern void (*write_byte_map[64])(uint32_t addr, uint8_t value);
|
||||
extern void (*write_half_map[64])(uint32_t addr, uint16_t value);
|
||||
extern void (*write_word_map[64])(uint32_t addr, uint32_t value);
|
||||
|
||||
// Must be allocated below 2GB (see comments for mmu.c)
|
||||
extern uint8_t *mem_and_flags;
|
||||
struct mem_area_desc {
|
||||
uint32_t base, size;
|
||||
uint8_t *ptr;
|
||||
};
|
||||
extern struct mem_area_desc mem_areas[2];
|
||||
void *phys_mem_ptr(uint32_t addr, uint32_t size);
|
||||
uint32_t phys_mem_addr(void *ptr);
|
||||
|
||||
/* Each word of memory has a flag word associated with it. For fast access,
|
||||
* flags are located at a constant offset from the memory data itself.
|
||||
*
|
||||
* These can't be per-byte because a translation index wouldn't fit then.
|
||||
* This does mean byte/halfword accesses have to mask off the low bits to
|
||||
* check flags, but the alternative would be another 32MB of memory overhead. */
|
||||
#define RAM_FLAGS(memptr) (*(uint32_t *)((uint8_t *)(memptr) + MEM_MAXSIZE))
|
||||
|
||||
#define RF_READ_BREAKPOINT 1
|
||||
#define RF_WRITE_BREAKPOINT 2
|
||||
#define RF_EXEC_BREAKPOINT 4
|
||||
#define RF_EXEC_DEBUG_NEXT 8
|
||||
#define RF_CODE_EXECUTED 16
|
||||
#define RF_CODE_TRANSLATED 32
|
||||
#define RF_CODE_NO_TRANSLATE 64
|
||||
#define RF_READ_ONLY 128
|
||||
#define RF_ARMLOADER_CB 256
|
||||
#define RFS_TRANSLATION_INDEX 9
|
||||
|
||||
#define DO_READ_ACTION (RF_READ_BREAKPOINT)
|
||||
#define DO_WRITE_ACTION (RF_WRITE_BREAKPOINT | RF_CODE_TRANSLATED | RF_CODE_NO_TRANSLATE | RF_CODE_EXECUTED)
|
||||
#define DONT_TRANSLATE (RF_EXEC_BREAKPOINT | RF_EXEC_DEBUG_NEXT | RF_CODE_TRANSLATED | RF_CODE_NO_TRANSLATE)
|
||||
|
||||
uint8_t bad_read_byte(uint32_t addr);
|
||||
uint16_t bad_read_half(uint32_t addr);
|
||||
uint32_t bad_read_word(uint32_t addr);
|
||||
void bad_write_byte(uint32_t addr, uint8_t value);
|
||||
void bad_write_half(uint32_t addr, uint16_t value);
|
||||
void bad_write_word(uint32_t addr, uint32_t value);
|
||||
|
||||
void write_action(void *ptr) __asm__("write_action");
|
||||
void read_action(void *ptr) __asm__("read_action");
|
||||
|
||||
uint8_t memory_read_byte(uint32_t addr);
|
||||
uint16_t memory_read_half(uint32_t addr);
|
||||
uint32_t memory_read_word(uint32_t addr);
|
||||
void memory_write_byte(uint32_t addr, uint8_t value);
|
||||
void memory_write_half(uint32_t addr, uint16_t value);
|
||||
void memory_write_word(uint32_t addr, uint32_t value);
|
||||
|
||||
uint32_t FASTCALL mmio_read_byte(uint32_t addr) __asm__("mmio_read_byte");
|
||||
uint32_t FASTCALL mmio_read_half(uint32_t addr) __asm__("mmio_read_half");
|
||||
uint32_t FASTCALL mmio_read_word(uint32_t addr) __asm__("mmio_read_word");
|
||||
void FASTCALL mmio_write_byte(uint32_t addr, uint32_t value) __asm__("mmio_write_byte");
|
||||
void FASTCALL mmio_write_half(uint32_t addr, uint32_t value) __asm__("mmio_write_half");
|
||||
void FASTCALL mmio_write_word(uint32_t addr, uint32_t value) __asm__("mmio_write_word");
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
288
src/armv5te/mmu.c
Normal file
288
src/armv5te/mmu.c
Normal file
@@ -0,0 +1,288 @@
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include "translate.h"
|
||||
#include "emu.h"
|
||||
#include "cpu.h"
|
||||
#include "mmu.h"
|
||||
#include "mem.h"
|
||||
#include "os/os.h"
|
||||
|
||||
/* Copy of translation table in memory (hack to approximate effect of having a TLB) */
|
||||
static uint32_t mmu_translation_table[0x1000];
|
||||
|
||||
void mmu_dump_tables(void) {
|
||||
if ((arm.control & 1) == 0) {
|
||||
gui_debug_printf("MMU disabled\n");
|
||||
return;
|
||||
}
|
||||
|
||||
gui_debug_printf("MMU translations:\n");
|
||||
uint32_t *tt = (uint32_t*)phys_mem_ptr(arm.translation_table_base, 0x4000);
|
||||
if (!tt) {
|
||||
gui_debug_printf("TTB points to invalid memory, using TLB\n");
|
||||
tt = mmu_translation_table;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < 0x1000; i++) {
|
||||
uint32_t tt_entry = tt[i];
|
||||
uint32_t virt_addr = i << 20;
|
||||
uint32_t virt_shift = 0;
|
||||
uint32_t *l1_table = NULL;
|
||||
uint32_t l1_table_size = 0;;
|
||||
uint32_t l1_entry;
|
||||
uint32_t j = 0;
|
||||
uint32_t page_size = 0;
|
||||
char *page_type = "?";
|
||||
if (!(tt_entry & 3))
|
||||
continue; // Invalid
|
||||
if ((tt_entry & 3) == 2) { // Section (1MB)
|
||||
page_size = 0x100000;
|
||||
page_type = "1mB";
|
||||
l1_entry = tt_entry;
|
||||
j = 0;
|
||||
goto section;
|
||||
} else if ((tt_entry & 3) == 1) { // Coarse page table
|
||||
l1_table = phys_mem_ptr(tt_entry & 0xFFFFFC00, 0x400);
|
||||
l1_table_size = 256;
|
||||
virt_shift = 12;
|
||||
} else if ((tt_entry & 3) == 3) { // Fine page table
|
||||
l1_table = phys_mem_ptr(tt_entry & 0xFFFFF000, 0x1000);
|
||||
l1_table_size = 1024;
|
||||
virt_shift = 10;
|
||||
}
|
||||
|
||||
for (j = 0; j < l1_table_size; j++) {
|
||||
l1_entry = l1_table[j];
|
||||
if (!(l1_entry & 3))
|
||||
continue; // Invalid
|
||||
if ((l1_entry & 3) == 1) { // Large page (64kB)
|
||||
page_size = 0x10000;
|
||||
page_type = "64kB";
|
||||
}
|
||||
else if ((l1_entry & 3) == 2) { // Small page (4kB)
|
||||
page_size = 0x1000;
|
||||
page_type = "4kB";
|
||||
}
|
||||
else if ((l1_entry & 3) == 3) { // Tiny page (1kB)
|
||||
page_size = 0x400;
|
||||
page_type = "1kB";
|
||||
}
|
||||
section:;
|
||||
gui_debug_printf("%08x -> %08x (%s) (0x%8x)\n", virt_addr + j * (1 << virt_shift), l1_entry & -page_size, page_type, l1_entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Translate a virtual address to a physical address */
|
||||
uint32_t mmu_translate(uint32_t addr, bool writing, fault_proc *fault, uint8_t *s_status) {
|
||||
uint32_t page_size;
|
||||
if (!(arm.control & 1))
|
||||
return addr;
|
||||
|
||||
uint32_t *table = mmu_translation_table;
|
||||
uint32_t entry = table[addr >> 20];
|
||||
uint32_t domain = entry >> 5 & 0x0F;
|
||||
uint32_t status = domain << 4;
|
||||
uint32_t ap;
|
||||
|
||||
switch (entry & 3) {
|
||||
default: /* Invalid */
|
||||
if (s_status) *s_status = status + 0x5;
|
||||
if (fault) fault(addr, status + 0x5); /* Section translation fault */
|
||||
return 0xFFFFFFFF;
|
||||
case 1: /* Course page table (one entry per 4kB) */
|
||||
table = (uint32_t *)(intptr_t)phys_mem_ptr(entry & 0xFFFFFC00, 0x400);
|
||||
if (!table) {
|
||||
if (fault) error("Bad page table pointer");
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
entry = table[addr >> 12 & 0xFF];
|
||||
break;
|
||||
case 2: /* Section (1MB) */
|
||||
page_size = 0x100000;
|
||||
ap = entry >> 6;
|
||||
goto section;
|
||||
case 3: /* Fine page table (one entry per 1kB) */
|
||||
table = (uint32_t *)(intptr_t)phys_mem_ptr(entry & 0xFFFFF000, 0x1000);
|
||||
if (!table) {
|
||||
if (fault) error("Bad page table pointer");
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
entry = table[addr >> 10 & 0x3FF];
|
||||
break;
|
||||
}
|
||||
|
||||
status += 2;
|
||||
switch (entry & 3) {
|
||||
default: /* Invalid */
|
||||
if (s_status) *s_status = status + 0x5;
|
||||
if (fault) fault(addr, status + 0x5); /* Page translation fault */
|
||||
return 0xFFFFFFFF;
|
||||
case 1: /* Large page (64kB) */
|
||||
page_size = 0x10000;
|
||||
ap = entry >> (addr >> 13 & 6);
|
||||
break;
|
||||
case 2: /* Small page (4kB) */
|
||||
page_size = 0x1000;
|
||||
ap = entry >> (addr >> 9 & 6);
|
||||
break;
|
||||
case 3: /* Tiny page (1kB) */
|
||||
page_size = 0x400;
|
||||
ap = entry;
|
||||
break;
|
||||
}
|
||||
section:;
|
||||
|
||||
uint32_t domain_access = arm.domain_access_control >> (domain << 1) & 3;
|
||||
if (domain_access != 3) {
|
||||
if (!(domain_access & 1)) {
|
||||
/* 0 (No access) or 2 (Reserved)
|
||||
* Testing shows they both raise domain fault */
|
||||
if (s_status) *s_status = status + 0x9;
|
||||
if (fault) fault(addr, status + 0x9); /* Domain fault */
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
/* 1 (Client) - check access permission bits */
|
||||
switch (ap >> 4 & 3) {
|
||||
case 0: /* Controlled by S/R bits */
|
||||
switch (arm.control >> 8 & 3) {
|
||||
case 0: /* No access */
|
||||
case 3: /* Reserved - testing shows this behaves like 0 */
|
||||
perm_fault:
|
||||
if (s_status) *s_status = status + 0xD;
|
||||
if (fault) fault(addr, status + 0xD); /* Permission fault */
|
||||
return 0xFFFFFFFF;
|
||||
case 1: /* System - read-only for privileged, no access for user */
|
||||
if (USER_MODE() || writing)
|
||||
goto perm_fault;
|
||||
break;
|
||||
case 2: /* ROM - read-only */
|
||||
if (writing)
|
||||
goto perm_fault;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 1: /* Read/write for privileged, no access for user */
|
||||
if (USER_MODE())
|
||||
goto perm_fault;
|
||||
break;
|
||||
case 2: /* Read/write for privileged, read-only for user */
|
||||
if (writing && USER_MODE())
|
||||
goto perm_fault;
|
||||
break;
|
||||
case 3: /* Read/write */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (entry & -page_size) | (addr & (page_size - 1));
|
||||
}
|
||||
|
||||
void mmu_check_priv(uint32_t addr, bool writing)
|
||||
{
|
||||
uint8_t status = 0;
|
||||
uint32_t saved_cpsr = arm.cpsr_low28;
|
||||
arm.cpsr_low28 &= ~3;
|
||||
mmu_translate(addr, writing, NULL, &status);
|
||||
arm.cpsr_low28 = saved_cpsr;
|
||||
if((status & 0xF) == 0xD)
|
||||
data_abort(addr, status);
|
||||
}
|
||||
|
||||
ac_entry *addr_cache = NULL;
|
||||
|
||||
// Keep a list of valid entries so we can invalidate everything quickly
|
||||
#define AC_VALID_MAX 256
|
||||
static uint32_t ac_valid_index;
|
||||
static uint32_t ac_valid_list[AC_VALID_MAX];
|
||||
|
||||
static void addr_cache_invalidate(int i) {
|
||||
AC_SET_ENTRY_INVALID(addr_cache[i], i >> 1 << 10)
|
||||
}
|
||||
|
||||
#if OS_HAS_PAGEFAULT_HANDLER
|
||||
|
||||
/* Since only a small fraction of the virtual address space, and therefore
|
||||
* only a small fraction of the pages making up addr_cache, will be in use
|
||||
* at a time, we can keep only a few pages committed and thereby reduce
|
||||
* the memory used by a lot. */
|
||||
#define AC_COMMIT_MAX 128
|
||||
#define AC_PAGE_SIZE 4096
|
||||
|
||||
bool addr_cache_pagefault(void *addr) {
|
||||
static uint8_t ac_commit_map[AC_NUM_ENTRIES * sizeof(ac_entry) / AC_PAGE_SIZE];
|
||||
static ac_entry *ac_commit_list[AC_COMMIT_MAX];
|
||||
static uint32_t ac_commit_index;
|
||||
|
||||
ac_entry *page = (ac_entry *)((uintptr_t)addr & -AC_PAGE_SIZE);
|
||||
uint32_t offset = page - addr_cache;
|
||||
if (offset >= AC_NUM_ENTRIES)
|
||||
return false;
|
||||
ac_entry *oldpage = ac_commit_list[ac_commit_index];
|
||||
if (oldpage) {
|
||||
//printf("Freeing %p, ", oldpage);
|
||||
os_sparse_decommit(oldpage, AC_PAGE_SIZE);
|
||||
ac_commit_map[offset / (AC_PAGE_SIZE / sizeof(ac_entry))] = 0;
|
||||
}
|
||||
//printf("Committing %p\n", page);
|
||||
if (!os_sparse_commit(page, AC_PAGE_SIZE))
|
||||
return false;
|
||||
ac_commit_map[offset / (AC_PAGE_SIZE / sizeof(ac_entry))] = 1;
|
||||
|
||||
uint32_t i;
|
||||
for (i = 0; i < (AC_PAGE_SIZE / sizeof(ac_entry)); i++)
|
||||
addr_cache_invalidate(offset + i);
|
||||
|
||||
ac_commit_list[ac_commit_index] = page;
|
||||
ac_commit_index = (ac_commit_index + 1) % AC_COMMIT_MAX;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void *addr_cache_miss(uint32_t virt, bool writing, fault_proc *fault) {
|
||||
ac_entry entry;
|
||||
uintptr_t phys = mmu_translate(virt, writing, fault, NULL);
|
||||
uint8_t *ptr = phys_mem_ptr(phys, 1);
|
||||
if (ptr && !(writing && (RAM_FLAGS((size_t)ptr & ~3) & RF_READ_ONLY))) {
|
||||
AC_SET_ENTRY_PTR(entry, virt, ptr)
|
||||
//printf("addr_cache_miss VA=%08x ptr=%p entry=%p\n", virt, ptr, entry);
|
||||
} else {
|
||||
AC_SET_ENTRY_PHYS(entry, virt, phys)
|
||||
//printf("addr_cache_miss VA=%08x PA=%08x entry=%p\n", virt, phys, entry);
|
||||
}
|
||||
uint32_t oldoffset = ac_valid_list[ac_valid_index];
|
||||
uint32_t offset = (virt >> 10) * 2 + writing;
|
||||
//if (ac_commit_map[oldoffset / (AC_PAGE_SIZE / sizeof(ac_entry))])
|
||||
addr_cache_invalidate(oldoffset);
|
||||
addr_cache[offset] = entry;
|
||||
ac_valid_list[ac_valid_index] = offset;
|
||||
ac_valid_index = (ac_valid_index + 1) % AC_VALID_MAX;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void addr_cache_flush() {
|
||||
#ifndef SUPPORT_LINUX
|
||||
/* The OS does something incredibly stupid: For every access to the flash,
|
||||
* it disables the MMU and flushes all buffers and caches. Argh.
|
||||
* This causes us to drop all translations, so work around this by ignoring
|
||||
* flushes triggered by the flash access code, which is run in SRAM. */
|
||||
if (arm.reg[15] >> 24 == 0xa4)
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (arm.control & 1) {
|
||||
void *table = phys_mem_ptr(arm.translation_table_base, 0x4000);
|
||||
if (!table)
|
||||
error("Bad translation table base register: %x", arm.translation_table_base);
|
||||
memcpy(mmu_translation_table, table, 0x4000);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < AC_VALID_MAX; i++) {
|
||||
uint32_t offset = ac_valid_list[i];
|
||||
// if (ac_commit_map[offset / (AC_PAGE_SIZE / sizeof(ac_entry))])
|
||||
addr_cache_invalidate(offset);
|
||||
}
|
||||
|
||||
flush_translations();
|
||||
}
|
||||
78
src/armv5te/mmu.h
Normal file
78
src/armv5te/mmu.h
Normal file
@@ -0,0 +1,78 @@
|
||||
#ifndef _H_MMU
|
||||
#define _H_MMU
|
||||
|
||||
#include "cpu.h"
|
||||
#include "emu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Translate a VA to a PA, using a page table lookup */
|
||||
uint32_t mmu_translate(uint32_t addr, bool writing, fault_proc *fault, uint8_t *s_status);
|
||||
|
||||
/* Used for t-type (usermode-like) access */
|
||||
void mmu_check_priv(uint32_t addr, bool writing);
|
||||
|
||||
/* addr_cache is used to cache the translation of VA to PA per page.
|
||||
* It has two entries per page (1k), one for reading and one for writing.
|
||||
* The format for 32-bit x86 with JIT is different to minimize the needed
|
||||
* registers in the x86 memory access functions and won't work for all
|
||||
* platforms:
|
||||
*
|
||||
* a) Pointer entry
|
||||
* The result of adding the virtual address (VA) to the entry has bit 31
|
||||
* clear, and that sum is a pointer to within mem_and_flags.
|
||||
* It would be cleaner to just use bit 0 or 1 to distinguish this case, but
|
||||
* this way we can simultaneously check both that this is a pointer entry,
|
||||
* and that the address is aligned, with a single bit test instruction.
|
||||
* b) Physical address entry
|
||||
* VA + entry has bit 31 set, and the entry (not the sum) has bit 22 clear.
|
||||
* Bits 0-21 contain the difference between virtual and physical address.
|
||||
* c) Invalid entry
|
||||
* VA + entry has bit 31 set, entry has bit 22 set. Entry is invalid and
|
||||
* addr_cache_miss must be called.
|
||||
*
|
||||
* In all other cases the format is simpler:
|
||||
* a) Pointer entry: Bit 0 clear, rest difference to pointer to start of physical page
|
||||
* b) Physical entry: Bit 0 set, Bit 1 clear, rest difference to physical address
|
||||
* c) Invalid entry: Bit 0 set, Bit 1 set
|
||||
*/
|
||||
|
||||
#define AC_NUM_ENTRIES (((1ull << 32) >> 10) * 2)
|
||||
typedef uint8_t *ac_entry;
|
||||
extern ac_entry *addr_cache __asm__("addr_cache");
|
||||
|
||||
#if defined(__i386__) && !defined(NO_TRANSLATION)
|
||||
#define AC_SET_ENTRY_PTR(entry, va, ptr) \
|
||||
entry = (ptr) - (va);
|
||||
#define AC_NOT_PTR 0x80000000
|
||||
#define AC_INVALID (1 << 22)
|
||||
#define AC_SET_ENTRY_PHYS(entry, va, pa) \
|
||||
entry = (ac_entry)(((pa) - (va)) >> 10); \
|
||||
entry += (~(uintptr_t)((va) + entry) & AC_NOT_PTR);
|
||||
#define AC_SET_ENTRY_INVALID(entry, va) \
|
||||
entry = (ac_entry)(AC_INVALID); \
|
||||
entry += (~(uintptr_t)((va) + entry) & AC_NOT_PTR);
|
||||
#else
|
||||
#define AC_SET_ENTRY_PTR(entry, va, ptr) \
|
||||
entry = (ptr) - (va);
|
||||
#define AC_NOT_PTR 0x1
|
||||
#define AC_INVALID 0x2
|
||||
#define AC_FLAGS 0x3
|
||||
#define AC_SET_ENTRY_PHYS(entry, va, pa) \
|
||||
entry = (ac_entry)(((pa) - (va)) | AC_NOT_PTR);
|
||||
#define AC_SET_ENTRY_INVALID(entry, va) \
|
||||
entry = (ac_entry)(AC_INVALID | AC_NOT_PTR);
|
||||
#endif
|
||||
|
||||
bool addr_cache_pagefault(void *addr);
|
||||
void *addr_cache_miss(uint32_t addr, bool writing, fault_proc *fault) __asm__("addr_cache_miss");
|
||||
void addr_cache_flush();
|
||||
void mmu_dump_tables(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
136
src/armv5te/os/os-linux.c
Normal file
136
src/armv5te/os/os-linux.c
Normal file
@@ -0,0 +1,136 @@
|
||||
#define _GNU_SOURCE
|
||||
#define _XOPEN_SOURCE
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#ifdef __APPLE__
|
||||
#include <mach/clock.h>
|
||||
#include <mach/mach.h>
|
||||
#endif
|
||||
|
||||
#ifndef MAP_32BIT
|
||||
#define MAP_32BIT 0
|
||||
#endif
|
||||
|
||||
#include "os.h"
|
||||
#include "../debug.h"
|
||||
#include "../mmu.h"
|
||||
|
||||
|
||||
#ifdef IS_IOS_BUILD
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
int iOS_is_debugger_attached()
|
||||
{
|
||||
struct kinfo_proc info;
|
||||
size_t info_size = sizeof(info);
|
||||
int name[4];
|
||||
|
||||
name[0] = CTL_KERN;
|
||||
name[1] = KERN_PROC;
|
||||
name[2] = KERN_PROC_PID;
|
||||
name[3] = getpid();
|
||||
|
||||
info.kp_proc.p_flag = 0;
|
||||
|
||||
sysctl(name, 4, &info, &info_size, NULL, 0);
|
||||
|
||||
return (info.kp_proc.p_flag & P_TRACED) != 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void *os_reserve(size_t size)
|
||||
{
|
||||
#ifdef __i386__
|
||||
// Has to have bit 31 zero
|
||||
void *ptr = mmap((void*)0x70000000, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON|MAP_32BIT, -1, 0);
|
||||
#else
|
||||
void *ptr = mmap((void*)0, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0);
|
||||
#endif
|
||||
|
||||
if(ptr == MAP_FAILED)
|
||||
return NULL;
|
||||
|
||||
msync(ptr, size, MS_SYNC|MS_INVALIDATE);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void os_free(void *ptr, size_t size)
|
||||
{
|
||||
if(ptr)
|
||||
munmap(ptr, size);
|
||||
}
|
||||
|
||||
void *os_alloc_executable(size_t size)
|
||||
{
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
// Has to be in 32-bit space for the JIT
|
||||
void *ptr = mmap((void*)0x30000000, size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED|MAP_ANON|MAP_32BIT, -1, 0);
|
||||
#else
|
||||
void *ptr = mmap((void*)0x0, size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED|MAP_ANON, -1, 0);
|
||||
#endif
|
||||
|
||||
if(ptr == MAP_FAILED)
|
||||
return NULL;
|
||||
|
||||
msync(ptr, size, MS_SYNC|MS_INVALIDATE);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
__attribute__((unused)) static void make_writable(void *addr)
|
||||
{
|
||||
uintptr_t ps = sysconf(_SC_PAGE_SIZE);
|
||||
void *prev = (void*)((uintptr_t)(addr) & (~(ps - 1)));
|
||||
if(mprotect(prev, ps, PROT_READ | PROT_WRITE | PROT_EXEC) != 0)
|
||||
emuprintf("mprotect failed.\n");
|
||||
}
|
||||
|
||||
void addr_cache_init()
|
||||
{
|
||||
// Only run this if not already initialized
|
||||
if(addr_cache)
|
||||
return;
|
||||
|
||||
addr_cache = mmap((void*)0, AC_NUM_ENTRIES * sizeof(ac_entry), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
|
||||
if(addr_cache == MAP_FAILED)
|
||||
{
|
||||
addr_cache = NULL;
|
||||
fprintf(stderr, "Failed to mmap addr_cache.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
setbuf(stdout, NULL);
|
||||
|
||||
#if !defined(AC_FLAGS)
|
||||
unsigned int i;
|
||||
for(unsigned int i = 0; i < AC_NUM_ENTRIES; ++i)
|
||||
{
|
||||
AC_SET_ENTRY_INVALID(addr_cache[i], (i >> 1) << 10)
|
||||
}
|
||||
#else
|
||||
memset(addr_cache, 0xFF, AC_NUM_ENTRIES * sizeof(ac_entry));
|
||||
#endif
|
||||
|
||||
#if defined(__i386__) && !defined(NO_TRANSLATION)
|
||||
// Relocate the assembly code that wants addr_cache at a fixed address
|
||||
extern uint32_t *ac_reloc_start[] __asm__("ac_reloc_start"), *ac_reloc_end[] __asm__("ac_reloc_end");
|
||||
for(uint32_t **reloc = ac_reloc_start; reloc != ac_reloc_end; reloc++)
|
||||
{
|
||||
make_writable(*reloc);
|
||||
**reloc += (uintptr_t)addr_cache;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void addr_cache_deinit()
|
||||
{
|
||||
if(addr_cache)
|
||||
munmap(addr_cache, AC_NUM_ENTRIES * sizeof(ac_entry));
|
||||
|
||||
addr_cache = NULL;
|
||||
}
|
||||
134
src/armv5te/os/os-win32.c
Normal file
134
src/armv5te/os/os-win32.c
Normal file
@@ -0,0 +1,134 @@
|
||||
#include "os.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <assert.h>
|
||||
#include <conio.h>
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <share.h>
|
||||
|
||||
#include "../emu.h"
|
||||
#include "../mmu.h"
|
||||
|
||||
void *os_reserve(size_t size)
|
||||
{
|
||||
return VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
}
|
||||
|
||||
void os_free(void *ptr, size_t size)
|
||||
{
|
||||
(void) size;
|
||||
VirtualFree(ptr, 0, MEM_RELEASE);
|
||||
}
|
||||
|
||||
#if OS_HAS_PAGEFAULT_HANDLER
|
||||
void *os_commit(void *addr, size_t size)
|
||||
{
|
||||
return VirtualAlloc(addr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
}
|
||||
|
||||
void *os_sparse_commit(void *page, size_t size)
|
||||
{
|
||||
return VirtualAlloc(page, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
}
|
||||
|
||||
void os_sparse_decommit(void *page, size_t size)
|
||||
{
|
||||
VirtualFree(page, size, MEM_DECOMMIT);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
void *os_alloc_executable(size_t size)
|
||||
{
|
||||
return VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||
}
|
||||
|
||||
#if OS_HAS_PAGEFAULT_HANDLER
|
||||
static int addr_cache_exception(PEXCEPTION_RECORD er, void *x, void *y, void *z) {
|
||||
(void) x; (void) y; (void) z;
|
||||
if (er->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
|
||||
if (addr_cache_pagefault((void *)er->ExceptionInformation[1]))
|
||||
return 0; // Continue execution
|
||||
}
|
||||
return 1; // Continue search
|
||||
}
|
||||
#endif
|
||||
|
||||
void addr_cache_init() {
|
||||
// Don't run more than once
|
||||
if(addr_cache)
|
||||
return;
|
||||
|
||||
DWORD flags = MEM_RESERVE;
|
||||
|
||||
#if defined(AC_FLAGS)
|
||||
// Commit memory to not trigger segfaults which make debugging a PITA
|
||||
flags |= MEM_COMMIT;
|
||||
#endif
|
||||
|
||||
addr_cache = VirtualAlloc(NULL, AC_NUM_ENTRIES * sizeof(ac_entry), flags, PAGE_READWRITE);
|
||||
if(!addr_cache){
|
||||
printf("Cant allocate addr_cache!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#if !defined(AC_FLAGS)
|
||||
unsigned int i;
|
||||
for(unsigned int i = 0; i < AC_NUM_ENTRIES; ++i)
|
||||
{
|
||||
AC_SET_ENTRY_INVALID(addr_cache[i], (i >> 1) << 10)
|
||||
}
|
||||
#else
|
||||
memset(addr_cache, 0xFF, AC_NUM_ENTRIES * sizeof(ac_entry));
|
||||
#endif
|
||||
|
||||
#if defined(__i386__) && !defined(NO_TRANSLATION)
|
||||
// Relocate the assembly code that wants addr_cache at a fixed address
|
||||
extern DWORD *ac_reloc_start[] __asm__("ac_reloc_start"), *ac_reloc_end[] __asm__("ac_reloc_end");
|
||||
DWORD **reloc;
|
||||
for (reloc = ac_reloc_start; reloc != ac_reloc_end; reloc++) {
|
||||
DWORD prot;
|
||||
VirtualProtect(*reloc, 4, PAGE_EXECUTE_READWRITE, &prot);
|
||||
**reloc += (DWORD)addr_cache;
|
||||
VirtualProtect(*reloc, 4, prot, &prot);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if OS_HAS_PAGEFAULT_HANDLER
|
||||
void os_faulthandler_arm(os_exception_frame_t *frame)
|
||||
{
|
||||
assert(frame->prev == NULL);
|
||||
|
||||
frame->function = (void *)addr_cache_exception;
|
||||
asm ("movl %%fs:(%1), %0" : "=r" (frame->prev) : "r" (0));
|
||||
asm ("movl %0, %%fs:(%1)" : : "r" (frame), "r" (0));
|
||||
}
|
||||
|
||||
void os_faulthandler_unarm(os_exception_frame_t *frame)
|
||||
{
|
||||
assert(frame->prev != NULL);
|
||||
|
||||
asm ("movl %0, %%fs:(%1)" : : "r" (frame->prev), "r" (0));
|
||||
frame->prev = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
void addr_cache_deinit() {
|
||||
if(!addr_cache)
|
||||
return;
|
||||
|
||||
#if defined(__i386__) && !defined(NO_TRANSLATION)
|
||||
// Undo the relocations
|
||||
extern DWORD *ac_reloc_start[] __asm__("ac_reloc_start"), *ac_reloc_end[] __asm__("ac_reloc_end");
|
||||
DWORD **reloc;
|
||||
for (reloc = ac_reloc_start; reloc != ac_reloc_end; reloc++)
|
||||
**reloc -= (DWORD)addr_cache;
|
||||
#endif
|
||||
|
||||
VirtualFree(addr_cache, 0, MEM_RELEASE);
|
||||
addr_cache = NULL;
|
||||
}
|
||||
47
src/armv5te/os/os.h
Normal file
47
src/armv5te/os/os.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef OS_H
|
||||
#define OS_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef IS_IOS_BUILD
|
||||
int iOS_is_debugger_attached();
|
||||
#endif
|
||||
|
||||
#if !defined(__x86_64__) && !defined(NO_TRANSLATION) && (defined(_WIN32) || defined(WIN32))
|
||||
#define OS_HAS_PAGEFAULT_HANDLER 1
|
||||
#else
|
||||
#define OS_HAS_PAGEFAULT_HANDLER 0
|
||||
#endif
|
||||
|
||||
void *os_reserve(size_t size);
|
||||
void *os_alloc_executable(size_t size);
|
||||
void os_free(void *ptr, size_t size);
|
||||
|
||||
#if OS_HAS_PAGEFAULT_HANDLER
|
||||
// The Win32 mechanism to handle pagefaults uses SEH, which requires a linked
|
||||
// list of handlers on the stack. The frame has to stay alive on the stack and
|
||||
// armed during all addr_cache accesses.
|
||||
|
||||
typedef struct { void *prev, *function; } os_exception_frame_t;
|
||||
void os_faulthandler_arm(os_exception_frame_t *frame);
|
||||
void os_faulthandler_unarm(os_exception_frame_t *frame);
|
||||
|
||||
void *os_commit(void *addr, size_t size);
|
||||
void *os_sparse_commit(void *page, size_t size);
|
||||
void os_sparse_decommit(void *page, size_t size);
|
||||
#endif
|
||||
|
||||
void addr_cache_init();
|
||||
void addr_cache_deinit();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
310
src/armv5te/thumb_interpreter.cpp
Normal file
310
src/armv5te/thumb_interpreter.cpp
Normal file
@@ -0,0 +1,310 @@
|
||||
#include "asmcode.h"
|
||||
#include "cpu.h"
|
||||
#include "debug.h"
|
||||
#include "emu.h"
|
||||
#include "mem.h"
|
||||
#include "mmu.h"
|
||||
|
||||
static uint32_t shift(int type, uint32_t res, uint32_t count, int setcc) {
|
||||
//TODO: Verify!
|
||||
if (count == 0) {
|
||||
/* For all types, a count of 0 does nothing and does not affect carry. */
|
||||
return res;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
default: /* not used, obviously - here to shut up gcc warning */
|
||||
case 0: /* LSL */
|
||||
if (count >= 32) {
|
||||
if (setcc) arm.cpsr_c = (count == 32) ? (res & 1) : 0;
|
||||
return 0;
|
||||
}
|
||||
if (setcc) arm.cpsr_c = res >> (32 - count) & 1;
|
||||
return res << count;
|
||||
case 1: /* LSR */
|
||||
if (count >= 32) {
|
||||
if (setcc) arm.cpsr_c = (count == 32) ? (res >> 31) : 0;
|
||||
return 0;
|
||||
}
|
||||
if (setcc) arm.cpsr_c = res >> (count - 1) & 1;
|
||||
return res >> count;
|
||||
case 2: /* ASR */
|
||||
if (count >= 32) {
|
||||
count = 31;
|
||||
if (setcc) arm.cpsr_c = res >> 31;
|
||||
} else {
|
||||
if (setcc) arm.cpsr_c = res >> (count - 1) & 1;
|
||||
}
|
||||
return (int32_t)res >> count;
|
||||
case 3: /* ROR */
|
||||
count &= 31;
|
||||
res = res >> count | res << (32 - count);
|
||||
if (setcc) arm.cpsr_c = res >> 31;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t get_reg(int rn) {
|
||||
if (rn == 15) error("Invalid use of R15");
|
||||
return arm.reg[rn];
|
||||
}
|
||||
|
||||
static uint32_t get_reg_pc_thumb(int rn) {
|
||||
return arm.reg[rn] + ((rn == 15) ? 2 : 0);
|
||||
}
|
||||
|
||||
static inline void set_reg_pc0(int rn, uint32_t value) {
|
||||
arm.reg[rn] = value;
|
||||
}
|
||||
|
||||
/* Detect overflow after an addition or subtraction. */
|
||||
#define ADD_OVERFLOW(left, right, sum) ((int32_t)(((left) ^ (sum)) & ((right) ^ (sum))) < 0)
|
||||
#define SUB_OVERFLOW(left, right, sum) ((int32_t)(((left) ^ (right)) & ((left) ^ (sum))) < 0)
|
||||
|
||||
/* Do an addition, setting C/V flags accordingly. */
|
||||
static uint32_t add(uint32_t left, uint32_t right, int carry, int setcc) {
|
||||
uint32_t sum = left + right + carry;
|
||||
if (setcc) {
|
||||
if (sum < left) carry = 1;
|
||||
if (sum > left) carry = 0;
|
||||
arm.cpsr_c = carry;
|
||||
arm.cpsr_v = ADD_OVERFLOW(left, right, sum);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
static inline void set_nz_flags(uint32_t value) {
|
||||
arm.cpsr_n = value >> 31;
|
||||
arm.cpsr_z = value == 0;
|
||||
}
|
||||
|
||||
void cpu_thumb_loop() {
|
||||
while (!exiting && cycle_count_delta < 0 && current_instr_size == 2) {
|
||||
uint16_t *insnp = (uint16_t*) read_instruction(arm.reg[15] & ~1);
|
||||
uint16_t insn = *insnp;
|
||||
uintptr_t flags = RAM_FLAGS((uintptr_t)insnp & ~3);
|
||||
|
||||
if (cpu_events != 0) {
|
||||
if (cpu_events & ~EVENT_DEBUG_STEP)
|
||||
break;
|
||||
goto enter_debugger;
|
||||
}
|
||||
|
||||
if (flags & (RF_EXEC_BREAKPOINT | RF_EXEC_DEBUG_NEXT)) {
|
||||
if (flags & RF_EXEC_BREAKPOINT)
|
||||
gui_debug_printf("Breakpoint at 0x%08x\n", arm.reg[15]);
|
||||
enter_debugger:
|
||||
uint32_t pc = arm.reg[15];
|
||||
debugger(DBG_EXEC_BREAKPOINT, 0);
|
||||
if(arm.reg[15] != pc)
|
||||
continue; // Debugger changed PC
|
||||
}
|
||||
|
||||
arm.reg[15] += 2;
|
||||
cycle_count_delta++;
|
||||
|
||||
#define CASE_x2(base) case base: case base+1
|
||||
#define CASE_x4(base) CASE_x2(base): CASE_x2(base+2)
|
||||
#define CASE_x8(base) CASE_x4(base): CASE_x4(base+4)
|
||||
#define REG0 arm.reg[insn & 7]
|
||||
#define REG3 arm.reg[insn >> 3 & 7]
|
||||
#define REG6 arm.reg[insn >> 6 & 7]
|
||||
#define REG8 arm.reg[insn >> 8 & 7]
|
||||
switch (insn >> 8) {
|
||||
CASE_x8(0x00): /* LSL Rd, Rm, #imm */
|
||||
CASE_x8(0x08): /* LSR Rd, Rm, #imm */
|
||||
CASE_x8(0x10): /* ASR Rd, Rm, #imm */
|
||||
set_nz_flags(REG0 = shift(insn >> 11, REG3, insn >> 6 & 31, true));
|
||||
break;
|
||||
CASE_x2(0x18): /* ADD Rd, Rn, Rm */ set_nz_flags(REG0 = add(REG3, REG6, 0, true)); break;
|
||||
CASE_x2(0x1A): /* SUB Rd, Rn, Rm */ set_nz_flags(REG0 = add(REG3, ~REG6, 1, true)); break;
|
||||
CASE_x2(0x1C): /* ADD Rd, Rn, #imm */ set_nz_flags(REG0 = add(REG3, insn >> 6 & 7, 0, true)); break;
|
||||
CASE_x2(0x1E): /* SUB Rd, Rn, #imm */ set_nz_flags(REG0 = add(REG3, ~(insn >> 6 & 7), 1, true)); break;
|
||||
CASE_x8(0x20): /* MOV Rd, #imm */ set_nz_flags(REG8 = insn & 0xFF); break;
|
||||
CASE_x8(0x28): /* CMP Rn, #imm */ set_nz_flags(add(REG8, ~(insn & 0xFF), 1, true)); break;
|
||||
CASE_x8(0x30): /* ADD Rd, #imm */ set_nz_flags(REG8 = add(REG8, insn & 0xFF, 0, true)); break;
|
||||
CASE_x8(0x38): /* SUB Rd, #imm */ set_nz_flags(REG8 = add(REG8, ~(insn & 0xFF), 1, true)); break;
|
||||
CASE_x4(0x40): {
|
||||
uint32_t *dst = ®0;
|
||||
uint32_t res;
|
||||
uint32_t src = REG3;
|
||||
switch (insn >> 6 & 15) {
|
||||
default:
|
||||
case 0x0: /* AND */ res = *dst &= src; break;
|
||||
case 0x1: /* EOR */ res = *dst ^= src; break;
|
||||
case 0x2: /* LSL */ res = *dst = shift(0, *dst, src & 0xFF, true); break;
|
||||
case 0x3: /* LSR */ res = *dst = shift(1, *dst, src & 0xFF, true); break;
|
||||
case 0x4: /* ASR */ res = *dst = shift(2, *dst, src & 0xFF, true); break;
|
||||
case 0x5: /* ADC */ res = *dst = add(*dst, src, arm.cpsr_c, true); break;
|
||||
case 0x6: /* SBC */ res = *dst = add(*dst, ~src, arm.cpsr_c, true); break;
|
||||
case 0x7: /* ROR */ res = *dst = shift(3, *dst, src & 0xFF, true); break;
|
||||
case 0x8: /* TST */ res = *dst & src; break;
|
||||
case 0x9: /* NEG */ res = *dst = add(0, ~src, 1, true); break;
|
||||
case 0xA: /* CMP */ res = add(*dst, ~src, 1, true); break;
|
||||
case 0xB: /* CMN */ res = add(*dst, src, 0, true); break;
|
||||
case 0xC: /* ORR */ res = *dst |= src; break;
|
||||
case 0xD: /* MUL */ res = *dst *= src; break;
|
||||
case 0xE: /* BIC */ res = *dst &= ~src; break;
|
||||
case 0xF: /* MVN */ res = *dst = ~src; break;
|
||||
}
|
||||
set_nz_flags(res);
|
||||
break;
|
||||
}
|
||||
case 0x44: { /* ADD Rd, Rm (high registers allowed) */
|
||||
uint32_t left = (insn >> 4 & 8) | (insn & 7), right = insn >> 3 & 15;
|
||||
set_reg_pc0(left, get_reg_pc_thumb(left) + get_reg_pc_thumb(right));
|
||||
break;
|
||||
}
|
||||
case 0x45: { /* CMP Rn, Rm (high registers allowed) */
|
||||
uint32_t left = (insn >> 4 & 8) | (insn & 7), right = insn >> 3 & 15;
|
||||
set_nz_flags(add(get_reg(left), ~get_reg_pc_thumb(right), 1, true));
|
||||
break;
|
||||
}
|
||||
case 0x46: { /* MOV Rd, Rm (high registers allowed) */
|
||||
uint32_t left = (insn >> 4 & 8) | (insn & 7), right = insn >> 3 & 15;
|
||||
set_reg_pc0(left, get_reg_pc_thumb(right));
|
||||
break;
|
||||
}
|
||||
case 0x47: { /* BX/BLX Rm (high register allowed) */
|
||||
uint32_t target = get_reg_pc_thumb(insn >> 3 & 15);
|
||||
if (insn & 0x80)
|
||||
arm.reg[14] = arm.reg[15] + 1;
|
||||
arm.reg[15] = target & ~1;
|
||||
if (!(target & 1)) {
|
||||
arm.cpsr_low28 &= ~0x20; /* Exit THUMB mode */
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
CASE_x8(0x48): /* LDR reg, [PC, #imm] */ REG8 = read_word(((arm.reg[15] + 2) & -4) + ((insn & 0xFF) << 2)); break;
|
||||
CASE_x2(0x50): /* STR Rd, [Rn, Rm] */ write_word(REG3 + REG6, REG0); break;
|
||||
CASE_x2(0x52): /* STRH Rd, [Rn, Rm] */ write_half(REG3 + REG6, REG0); break;
|
||||
CASE_x2(0x54): /* STRB Rd, [Rn, Rm] */ write_byte(REG3 + REG6, REG0); break;
|
||||
CASE_x2(0x56): /* LDRSB Rd, [Rn, Rm] */ REG0 = (int8_t)read_byte(REG3 + REG6); break;
|
||||
CASE_x2(0x58): /* LDR Rd, [Rn, Rm] */ REG0 = read_word(REG3 + REG6); break;
|
||||
CASE_x2(0x5A): /* LDRH Rd, [Rn, Rm] */ REG0 = read_half(REG3 + REG6); break;
|
||||
CASE_x2(0x5C): /* LDRB Rd, [Rn, Rm] */ REG0 = read_byte(REG3 + REG6); break;
|
||||
CASE_x2(0x5E): /* LDRSH Rd, [Rn, Rm] */ REG0 = (int16_t)read_half(REG3 + REG6); break;
|
||||
CASE_x8(0x60): /* STR Rd, [Rn, #imm] */ write_word(REG3 + (insn >> 4 & 124), REG0); break;
|
||||
CASE_x8(0x68): /* LDR Rd, [Rn, #imm] */ REG0 = read_word(REG3 + (insn >> 4 & 124)); break;
|
||||
CASE_x8(0x70): /* STRB Rd, [Rn, #imm] */ write_byte(REG3 + (insn >> 6 & 31), REG0); break;
|
||||
CASE_x8(0x78): /* LDRB Rd, [Rn, #imm] */ REG0 = read_byte(REG3 + (insn >> 6 & 31)); break;
|
||||
CASE_x8(0x80): /* STRH Rd, [Rn, #imm] */ write_half(REG3 + (insn >> 5 & 62), REG0); break;
|
||||
CASE_x8(0x88): /* LDRH Rd, [Rn, #imm] */ REG0 = read_half(REG3 + (insn >> 5 & 62)); break;
|
||||
CASE_x8(0x90): /* STR Rd, [SP, #imm] */ write_word(arm.reg[13] + ((insn & 0xFF) << 2), REG8); break;
|
||||
CASE_x8(0x98): /* LDR Rd, [SP, #imm] */ REG8 = read_word(arm.reg[13] + ((insn & 0xFF) << 2)); break;
|
||||
CASE_x8(0xA0): /* ADD Rd, PC, #imm */ REG8 = ((arm.reg[15] + 2) & -4) + ((insn & 0xFF) << 2); break;
|
||||
CASE_x8(0xA8): /* ADD Rd, SP, #imm */ REG8 = arm.reg[13] + ((insn & 0xFF) << 2); break;
|
||||
case 0xB0: /* ADD/SUB SP, #imm */
|
||||
arm.reg[13] += ((insn & 0x80) ? -(insn & 0x7F) : (insn & 0x7F)) << 2;
|
||||
break;
|
||||
|
||||
CASE_x2(0xB4): { /* PUSH {reglist[,LR]} */
|
||||
int i;
|
||||
uint32_t addr = arm.reg[13];
|
||||
for (i = 8; i >= 0; i--)
|
||||
addr -= (insn >> i & 1) * 4;
|
||||
uint32_t sp = addr;
|
||||
for (i = 0; i < 8; i++)
|
||||
if (insn >> i & 1)
|
||||
write_word(addr, arm.reg[i]), addr += 4;
|
||||
if (insn & 0x100)
|
||||
write_word(addr, arm.reg[14]);
|
||||
arm.reg[13] = sp;
|
||||
break;
|
||||
}
|
||||
|
||||
CASE_x2(0xBC): { /* POP {reglist[,PC]} */
|
||||
int i;
|
||||
uint32_t addr = arm.reg[13];
|
||||
for (i = 0; i < 8; i++)
|
||||
if (insn >> i & 1)
|
||||
arm.reg[i] = read_word(addr), addr += 4;
|
||||
if (insn & 0x100) {
|
||||
uint32_t target = read_word(addr); addr += 4;
|
||||
arm.reg[15] = target & ~1;
|
||||
if (!(target & 1)) {
|
||||
arm.cpsr_low28 &= ~0x20;
|
||||
arm.reg[13] = addr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
arm.reg[13] = addr;
|
||||
break;
|
||||
}
|
||||
case 0xBE:
|
||||
printf("Software breakpoint at %08x (%02x)\n", arm.reg[15], insn & 0xFF);
|
||||
debugger(DBG_EXEC_BREAKPOINT, 0);
|
||||
break;
|
||||
|
||||
CASE_x8(0xC0): { /* STMIA Rn!, {reglist} */
|
||||
int i;
|
||||
uint32_t addr = REG8;
|
||||
for (i = 0; i < 8; i++)
|
||||
if (insn >> i & 1)
|
||||
write_word(addr, arm.reg[i]), addr += 4;
|
||||
REG8 = addr;
|
||||
break;
|
||||
}
|
||||
CASE_x8(0xC8): { /* LDMIA Rn!, {reglist} */
|
||||
int i;
|
||||
uint32_t addr = REG8;
|
||||
uint32_t tmp = 0; // value not used, just suppressing uninitialized variable warning
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (insn >> i & 1) {
|
||||
if (i == (insn >> 8 & 7))
|
||||
tmp = read_word(addr);
|
||||
else
|
||||
arm.reg[i] = read_word(addr);
|
||||
addr += 4;
|
||||
}
|
||||
}
|
||||
// must set address register last so it is unchanged on exception
|
||||
REG8 = addr;
|
||||
if (insn >> (insn >> 8 & 7) & 1)
|
||||
REG8 = tmp;
|
||||
break;
|
||||
}
|
||||
#define BRANCH_IF(cond) if (cond) arm.reg[15] += 2 + ((int8_t)insn << 1); break;
|
||||
case 0xD0: /* BEQ */ BRANCH_IF(arm.cpsr_z)
|
||||
case 0xD1: /* BNE */ BRANCH_IF(!arm.cpsr_z)
|
||||
case 0xD2: /* BCS */ BRANCH_IF(arm.cpsr_c)
|
||||
case 0xD3: /* BCC */ BRANCH_IF(!arm.cpsr_c)
|
||||
case 0xD4: /* BMI */ BRANCH_IF(arm.cpsr_n)
|
||||
case 0xD5: /* BPL */ BRANCH_IF(!arm.cpsr_n)
|
||||
case 0xD6: /* BVS */ BRANCH_IF(arm.cpsr_v)
|
||||
case 0xD7: /* BVC */ BRANCH_IF(!arm.cpsr_v)
|
||||
case 0xD8: /* BHI */ BRANCH_IF(arm.cpsr_c > arm.cpsr_z)
|
||||
case 0xD9: /* BLS */ BRANCH_IF(arm.cpsr_c <= arm.cpsr_z)
|
||||
case 0xDA: /* BGE */ BRANCH_IF(arm.cpsr_n == arm.cpsr_v)
|
||||
case 0xDB: /* BLT */ BRANCH_IF(arm.cpsr_n != arm.cpsr_v)
|
||||
case 0xDC: /* BGT */ BRANCH_IF(!arm.cpsr_z && arm.cpsr_n == arm.cpsr_v)
|
||||
case 0xDD: /* BLE */ BRANCH_IF(arm.cpsr_z || arm.cpsr_n != arm.cpsr_v)
|
||||
|
||||
case 0xDF: /* SWI */
|
||||
cpu_exception(EX_SWI);
|
||||
return; /* Exits THUMB mode */
|
||||
|
||||
CASE_x8(0xE0): /* B */ arm.reg[15] += 2 + ((int32_t)insn << 21 >> 20); break;
|
||||
CASE_x8(0xE8): { /* Second half of BLX */
|
||||
uint32_t target = (arm.reg[14] + ((insn & 0x7FF) << 1)) & ~3;
|
||||
arm.reg[14] = arm.reg[15] + 1;
|
||||
arm.reg[15] = target;
|
||||
arm.cpsr_low28 &= ~0x20; /* Exit THUMB mode */
|
||||
return;
|
||||
}
|
||||
CASE_x8(0xF0): /* First half of BL/BLX */
|
||||
arm.reg[14] = arm.reg[15] + 2 + ((int32_t)insn << 21 >> 9);
|
||||
break;
|
||||
CASE_x8(0xF8): { /* Second half of BL */
|
||||
uint32_t target = arm.reg[14] + ((insn & 0x7FF) << 1);
|
||||
arm.reg[14] = arm.reg[15] + 1;
|
||||
arm.reg[15] = target;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
undefined_instruction();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
33
src/armv5te/translate.h
Normal file
33
src/armv5te/translate.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/* Declarations for translate.c */
|
||||
|
||||
#ifndef _H_TRANSLATE
|
||||
#define _H_TRANSLATE
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct translation {
|
||||
uintptr_t unused;
|
||||
void** jump_table;
|
||||
uint32_t *start_ptr;
|
||||
uint32_t *end_ptr;
|
||||
} __attribute__((packed));
|
||||
extern struct translation translation_table[] __asm__("translation_table");
|
||||
#define INSN_BUFFER_SIZE 0x1000000
|
||||
|
||||
bool translate_init();
|
||||
void translate_deinit();
|
||||
void translate(uint32_t start_pc, uint32_t *insnp);
|
||||
void flush_translations();
|
||||
void invalidate_translation(int index);
|
||||
void translate_fix_pc();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
907
src/armv5te/translate_aarch64.cpp
Normal file
907
src/armv5te/translate_aarch64.cpp
Normal file
@@ -0,0 +1,907 @@
|
||||
/* How the AArch64 translation works:
|
||||
* AArch64 has enough registers to keep the entire ARM CPU state available.
|
||||
* In JIT'd code, these physical registers have a fixed role:
|
||||
* x0: First parameter and return value of helper functions in asmcode_aarch64.S
|
||||
* x1: Second parameter to helper functions in asmcode_aarch64.S
|
||||
* w2-w16: arm.reg[0] - arm.reg[14]
|
||||
* x17: Used as a scratch register for keeping a copy of the virtual cpsr_nzcv
|
||||
* (x18: Platform specific, can't use this)
|
||||
* x19: Pointer to the arm_state struct
|
||||
* x21-x24: Used as temporary registers by various subroutines
|
||||
* x25: Temporary register, not touched by (read|write)_asm routines
|
||||
* x26: Pointer to addr_cache contents
|
||||
* x27-x29: Not preserved, so don't touch.
|
||||
* x30: lr
|
||||
* x31: sp
|
||||
* x18 and x30 are callee-save, so must be saved and restored properly.
|
||||
* The other registers are part of the caller-save x0-x18 area and do not need
|
||||
* to be preserved. They need to be written back into the virtual arm struct
|
||||
* before calling into compiler-generated code though.
|
||||
*/
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
|
||||
#include "asmcode.h"
|
||||
#include "cpudefs.h"
|
||||
#include "emu.h"
|
||||
#include "translate.h"
|
||||
#include "mem.h"
|
||||
#include "mmu.h"
|
||||
#include "os/os.h"
|
||||
|
||||
#ifdef IS_IOS_BUILD
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#ifndef __aarch64__
|
||||
#error "I'm sorry Dave, I'm afraid I can't do that."
|
||||
#endif
|
||||
|
||||
/* Helper functions in asmcode_aarch64.S */
|
||||
extern "C" {
|
||||
extern void translation_next(uint32_t new_pc) __asm__("translation_next");
|
||||
extern void translation_next_bx(uint32_t new_pc) __asm__("translation_next_bx");
|
||||
extern void **translation_sp __asm__("translation_sp");
|
||||
#ifdef IS_IOS_BUILD
|
||||
int sys_cache_control(int function, void *start, size_t len);
|
||||
#endif
|
||||
}
|
||||
|
||||
enum Reg : uint8_t {
|
||||
R0 = 0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, SP, LR, PC
|
||||
};
|
||||
|
||||
enum PReg : uint8_t {
|
||||
W0 = 0, W1, W2, W3, W4, W5, W6, W7, W8, W9, W10, W11, W12, W13, W14, W15, W16, W17, W25 = 25, W26,
|
||||
X0 = W0, X1 = W1, X21 = 21, X30 = 30, X31 = 31, WZR = 31
|
||||
};
|
||||
|
||||
/* This function returns the physical register that contains the virtual register vreg.
|
||||
vreg must not be PC. */
|
||||
static PReg mapreg(const uint8_t vreg)
|
||||
{
|
||||
// See the first comment on register mapping
|
||||
assert(vreg < PC);
|
||||
return static_cast<PReg>(vreg + 2);
|
||||
}
|
||||
|
||||
uint32_t *translate_buffer = nullptr,
|
||||
*translate_current = nullptr;
|
||||
|
||||
#include "literalpool.h"
|
||||
|
||||
#define MAX_TRANSLATIONS 0x40000
|
||||
struct translation translation_table[MAX_TRANSLATIONS];
|
||||
uint32_t *jump_table[MAX_TRANSLATIONS*2],
|
||||
**jump_table_current = jump_table;
|
||||
static unsigned int next_translation_index = 0;
|
||||
|
||||
static void emit(const uint32_t instruction)
|
||||
{
|
||||
*translate_current++ = instruction;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) void emit_brk()
|
||||
{
|
||||
emit(0xd4200000); // brk #0
|
||||
}
|
||||
|
||||
// Sets the physical register xd to imm
|
||||
static void emit_mov_imm(const PReg xd, uint64_t imm)
|
||||
{
|
||||
if(imm > 0xFFFF)
|
||||
{
|
||||
literalpool_add(imm);
|
||||
if(imm > 0xFFFFFFFFul)
|
||||
emit(0x58000000 | xd); // ldr xd, [pc, #<offset>]
|
||||
else
|
||||
emit(0x18000000 | xd); // ldr xd, [pc, #<offset>]
|
||||
return;
|
||||
}
|
||||
|
||||
emit(0xd2800000 | ((imm & 0xFFFF) << 5) | xd); // movz xd, #imm, lsl #0
|
||||
imm >>= 16;
|
||||
if(imm & 0xFFFF)
|
||||
emit(0xf2a00000 | ((imm & 0xFFFF) << 5) | xd); // movk xd, #imm, lsl #16
|
||||
|
||||
/* Literal pool used instead.
|
||||
|
||||
imm >>= 16;
|
||||
if(imm & 0xFFFF)
|
||||
emit(0xf2c00000 | ((imm & 0xFFFF) << 5) | xd); // movk xd, #imm, lsl #32
|
||||
|
||||
imm >>= 16;
|
||||
if(imm & 0xFFFF)
|
||||
emit(0xf2e00000 | ((imm & 0xFFFF) << 5) | xd); // movk xd, #imm, lsl #48
|
||||
*/
|
||||
}
|
||||
|
||||
static void emit_mov_reg(const PReg wd, const PReg wm)
|
||||
{
|
||||
emit(0x2a0003e0 | (wm << 16) | wd);
|
||||
}
|
||||
|
||||
void literalpool_fill()
|
||||
{
|
||||
for(unsigned int i = 0; i < literals_count; ++i)
|
||||
{
|
||||
auto &&literal = literals[i];
|
||||
if(!literal.inst)
|
||||
continue;
|
||||
|
||||
// Emit the literal
|
||||
uint32_t *literal_loc = translate_current;
|
||||
emit(static_cast<uint32_t>(literal.value));
|
||||
if(literal.value > 0xFFFFFFFFul)
|
||||
emit(static_cast<uint32_t>(literal.value >> 32));
|
||||
|
||||
// Fixup all literal references for the same value
|
||||
for(unsigned int j = i; j < literals_count; ++j)
|
||||
{
|
||||
auto &&literal_ref = literals[j];
|
||||
if(!literal_ref.inst || literal_ref.value != literal.value)
|
||||
continue;
|
||||
|
||||
ptrdiff_t diff = literal_loc - reinterpret_cast<uint32_t*>(literal_ref.inst);
|
||||
if(diff < -0x40000 || diff > 0x3ffff)
|
||||
error("Literal unreachable");
|
||||
|
||||
*reinterpret_cast<uint32_t*>(literal_ref.inst) |= diff << 5;
|
||||
literal_ref.inst = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
literals_count = 0;
|
||||
}
|
||||
|
||||
static __attribute__((unused)) uint32_t maybe_branch(const void *target)
|
||||
{
|
||||
ptrdiff_t diff = reinterpret_cast<const uint32_t*>(target) - translate_current;
|
||||
if(diff > 0x3FFFFFF || -diff > 0x4000000)
|
||||
return 0;
|
||||
|
||||
return 0x12000000 | diff;
|
||||
}
|
||||
|
||||
// Jumps directly to target, destroys x21
|
||||
static void emit_jmp(const void *target)
|
||||
{
|
||||
/* uint32_t branch = maybe_branch(target);
|
||||
if(branch)
|
||||
return emit(branch);*/
|
||||
|
||||
emit_mov_imm(X21, reinterpret_cast<const uint64_t>(target));
|
||||
emit(0xd61f02a0); // br x21
|
||||
}
|
||||
|
||||
// Calls target, destroys x21 and x30
|
||||
static void emit_call(const void *target)
|
||||
{
|
||||
/* uint32_t branch = maybe_branch(target);
|
||||
if(branch)
|
||||
return emit(branch) | (1 << 31);*/
|
||||
|
||||
emit_mov_imm(X21, reinterpret_cast<const uint64_t>(target));
|
||||
emit(0xd63f02a0); // blr x21
|
||||
}
|
||||
|
||||
bool translate_init()
|
||||
{
|
||||
if(translate_buffer)
|
||||
return true;
|
||||
|
||||
#ifdef IS_IOS_BUILD
|
||||
#include <sys/syscall.h>
|
||||
if (!iOS_is_debugger_attached())
|
||||
{
|
||||
syscall(SYS_ptrace, 0 /*PTRACE_TRACEME*/, 0, 0, 0);
|
||||
if (!iOS_is_debugger_attached())
|
||||
{
|
||||
fprintf(stderr, "Was not able to force ptrace to allow JIT :(\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
translate_current = translate_buffer = reinterpret_cast<uint32_t*>(os_alloc_executable(INSN_BUFFER_SIZE));
|
||||
jump_table_current = jump_table;
|
||||
next_translation_index = 0;
|
||||
|
||||
return translate_buffer != nullptr;
|
||||
}
|
||||
|
||||
void translate_deinit()
|
||||
{
|
||||
if(!translate_buffer)
|
||||
return;
|
||||
|
||||
os_free(translate_buffer, INSN_BUFFER_SIZE);
|
||||
translate_current = translate_buffer = nullptr;
|
||||
}
|
||||
|
||||
void translate(uint32_t pc_start, uint32_t *insn_ptr_start)
|
||||
{
|
||||
if(next_translation_index >= MAX_TRANSLATIONS)
|
||||
{
|
||||
warn("Out of translation slots!");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef IS_IOS_BUILD
|
||||
// Mark translate_buffer as RW_
|
||||
mprotect(translate_buffer, INSN_BUFFER_SIZE, PROT_READ | PROT_WRITE);
|
||||
#endif
|
||||
|
||||
uint32_t **jump_table_start = jump_table_current;
|
||||
uint32_t pc = pc_start, *insn_ptr = insn_ptr_start;
|
||||
// Pointer to translation of current instruction
|
||||
uint32_t *translate_buffer_inst_start = translate_current;
|
||||
// Pointer to struct translation for this block
|
||||
translation *this_translation = &translation_table[next_translation_index];
|
||||
|
||||
// Whether we can avoid the jump to translation_next at the end
|
||||
bool jumps_away = false;
|
||||
// Whether to stop translating code
|
||||
bool stop_here = false;
|
||||
// cond_branch points to the b.cond, see below
|
||||
uint32_t *cond_branch = nullptr;
|
||||
|
||||
// We know this already. end_ptr will be set after the loop
|
||||
this_translation->jump_table = reinterpret_cast<void**>(jump_table_start);
|
||||
this_translation->start_ptr = insn_ptr_start;
|
||||
|
||||
while(1)
|
||||
{
|
||||
// Translate further?
|
||||
if(stop_here
|
||||
|| size_t((translate_current + 16) - translate_buffer) > (INSN_BUFFER_SIZE/sizeof(*translate_buffer))
|
||||
|| RAM_FLAGS(insn_ptr) & DONT_TRANSLATE
|
||||
|| (pc ^ pc_start) & ~0x3ff)
|
||||
goto exit_translation;
|
||||
|
||||
Instruction i;
|
||||
i.raw = *insn_ptr;
|
||||
|
||||
*jump_table_current = translate_current;
|
||||
|
||||
if(unlikely(i.cond == CC_NV))
|
||||
{
|
||||
if((i.raw & 0xFD70F000) == 0xF550F000)
|
||||
goto instruction_translated; // PLD
|
||||
else
|
||||
goto unimpl;
|
||||
}
|
||||
else if(unlikely(i.cond != CC_AL))
|
||||
{
|
||||
// Conditional instruction -> Generate jump over code with inverse condition
|
||||
cond_branch = translate_current;
|
||||
emit(0x54000000 | (i.cond ^ 1));
|
||||
}
|
||||
|
||||
if((i.raw & 0xE000090) == 0x0000090)
|
||||
{
|
||||
if(!i.mem_proc2.s && !i.mem_proc2.h)
|
||||
{
|
||||
// Not mem_proc2 -> multiply or swap
|
||||
if((i.raw & 0x0FB00000) == 0x01000000)
|
||||
goto unimpl; // SWP/SWPB not implemented
|
||||
|
||||
// MUL, UMLAL, etc.
|
||||
if(i.mult.rm == PC || i.mult.rs == PC
|
||||
|| i.mult.rn == PC || i.mult.rd == PC)
|
||||
goto unimpl; // PC as register not implemented
|
||||
|
||||
if(i.mult.s)
|
||||
goto unimpl; // setcc not implemented (not easily possible as no aarch64 instruction only changes n and z)
|
||||
|
||||
if ((i.raw & 0xFC000F0) == 0x0000090)
|
||||
{
|
||||
uint32_t instruction = 0x1B000000; // madd w0, w0, w0, w0
|
||||
|
||||
instruction |= (mapreg(i.mult.rs) << 16) | (mapreg(i.mult.rm) << 5) | mapreg(i.mult.rd);
|
||||
if(i.mult.a)
|
||||
instruction |= mapreg(i.mult.rn) << 10;
|
||||
else
|
||||
instruction |= WZR << 10;
|
||||
|
||||
emit(instruction);
|
||||
goto instruction_translated;
|
||||
}
|
||||
|
||||
goto unimpl; // UMULL, UMLAL, SMULL, SMLAL not implemented
|
||||
}
|
||||
|
||||
if(i.mem_proc2.s || !i.mem_proc2.h)
|
||||
goto unimpl; // Signed byte/halfword and doubleword not implemented
|
||||
|
||||
if(i.mem_proc2.rn == PC
|
||||
|| i.mem_proc2.rd == PC
|
||||
|| i.mem_proc2.rm == PC)
|
||||
goto unimpl; // PC as operand or dest. not implemented
|
||||
|
||||
// Load base into w0
|
||||
emit_mov_reg(W0, mapreg(i.mem_proc2.rn));
|
||||
|
||||
// Offset into w25
|
||||
if(i.mem_proc2.i) // Immediate offset
|
||||
emit_mov_imm(W25, (i.mem_proc2.immed_h << 4) | i.mem_proc2.immed_l);
|
||||
else // Register offset
|
||||
emit_mov_reg(W25, mapreg(i.mem_proc2.rm));
|
||||
|
||||
// Get final address..
|
||||
if(i.mem_proc2.p)
|
||||
{
|
||||
// ..into r0
|
||||
if(i.mem_proc2.u)
|
||||
emit(0x0b190000); // add w0, w0, w25
|
||||
else
|
||||
emit(0x4b190000); // sub w0, w0, w25
|
||||
|
||||
if(i.mem_proc2.w) // Writeback: final address into rn
|
||||
emit_mov_reg(mapreg(i.mem_proc2.rn), W0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ..into r5
|
||||
if(i.mem_proc2.u)
|
||||
emit(0x0b190019); // add w25, w0, w25
|
||||
else
|
||||
emit(0x4b190019); // sub w25, w0, w25
|
||||
}
|
||||
|
||||
if(i.mem_proc2.l)
|
||||
{
|
||||
emit_call(reinterpret_cast<void*>(read_half_asm));
|
||||
emit_mov_reg(mapreg(i.mem_proc2.rd), W0);
|
||||
}
|
||||
else
|
||||
{
|
||||
emit_mov_reg(W1, mapreg(i.mem_proc2.rd));
|
||||
emit_call(reinterpret_cast<void*>(write_half_asm));
|
||||
}
|
||||
|
||||
// Post-indexed: final address in r5 back into rn
|
||||
if(!i.mem_proc2.p)
|
||||
emit_mov_reg(mapreg(i.mem_proc2.rn), W25);
|
||||
|
||||
}
|
||||
else if((i.raw & 0xD900000) == 0x1000000)
|
||||
{
|
||||
if((i.raw & 0xFFFFFD0) == 0x12FFF10)
|
||||
{
|
||||
//B(L)X
|
||||
if(i.bx.rm == PC)
|
||||
goto unimpl;
|
||||
|
||||
emit_mov_reg(W0, mapreg(i.bx.rm));
|
||||
|
||||
if(i.bx.l)
|
||||
emit_mov_imm(mapreg(LR), pc + 4);
|
||||
else if(i.cond == CC_AL)
|
||||
stop_here = jumps_away = true;
|
||||
|
||||
emit_jmp(reinterpret_cast<void*>(translation_next_bx));
|
||||
}
|
||||
else
|
||||
goto unimpl;
|
||||
}
|
||||
else if((i.raw & 0xC000000) == 0x0000000)
|
||||
{
|
||||
// Data processing
|
||||
if(!i.data_proc.imm && i.data_proc.reg_shift) // reg shift
|
||||
goto unimpl;
|
||||
|
||||
if(i.data_proc.s
|
||||
&& !(i.data_proc.op == OP_ADD || i.data_proc.op == OP_SUB || i.data_proc.op == OP_CMP || i.data_proc.op == OP_CMN
|
||||
#ifndef SUPPORT_LINUX
|
||||
|| i.data_proc.op == OP_TST
|
||||
#endif
|
||||
))
|
||||
{
|
||||
/* We can't translate the S-bit that easily,
|
||||
as the barrel shifter output does not influence
|
||||
the carry flag anymore. */
|
||||
goto unimpl;
|
||||
}
|
||||
|
||||
if(i.data_proc.op == OP_RSB)
|
||||
{
|
||||
// RSB is not possible on AArch64, so try to convert it to SUB
|
||||
if(!i.data_proc.imm && !i.data_proc.reg_shift && i.data_proc.shift == SH_LSL && i.data_proc.shift_imm == 0)
|
||||
{
|
||||
// Swap rm and rn, change op to SUB
|
||||
unsigned int tmp = i.data_proc.rm;
|
||||
i.data_proc.rm = i.data_proc.rn;
|
||||
i.data_proc.rn = tmp;
|
||||
i.data_proc.op = OP_SUB;
|
||||
}
|
||||
else
|
||||
goto unimpl;
|
||||
}
|
||||
|
||||
if(i.data_proc.op == OP_RSC || i.data_proc.op == OP_TEQ)
|
||||
goto unimpl;
|
||||
|
||||
if(i.data_proc.shift == SH_ROR && !i.data_proc.imm && (i.data_proc.op == OP_SUB || i.data_proc.op == OP_ADD || i.data_proc.shift_imm == 0))
|
||||
goto unimpl; // ADD/SUB do not support ROR. RRX (encoded as ror #0) is not supported anywhere
|
||||
|
||||
// Using pc is not supported
|
||||
if(i.data_proc.rd == PC
|
||||
|| (!i.data_proc.imm && i.data_proc.rm == PC))
|
||||
goto unimpl;
|
||||
|
||||
// AArch64 ADC and SBC do not support a shifted reg
|
||||
if((i.data_proc.op == OP_ADC || i.data_proc.op == OP_SBC)
|
||||
&& (i.data_proc.shift != SH_LSL || i.data_proc.shift_imm != 0))
|
||||
goto unimpl;
|
||||
|
||||
// Shortcut for simple register mov (mov rd, rm)
|
||||
if((i.raw & 0xFFF0FF0) == 0x1a00000)
|
||||
{
|
||||
emit_mov_reg(mapreg(i.data_proc.rd), mapreg(i.data_proc.rm));
|
||||
goto instruction_translated;
|
||||
}
|
||||
|
||||
static const uint32_t opmap[] =
|
||||
{
|
||||
0x0A000000, // AND
|
||||
0x4A000000, // EOR
|
||||
0x4B000000, // SUB
|
||||
0, // RSB not possible
|
||||
0x0B000000, // ADD
|
||||
0x1A000000, // ADC (no shift!)
|
||||
0x5A000000, // SBC (no shift!)
|
||||
0, // RSC not possible
|
||||
#ifdef SUPPORT_LINUX
|
||||
0, // TST not possible, carry and overflow flags not identical
|
||||
#else
|
||||
0x6A00001F, // TST
|
||||
#endif
|
||||
0, // TEQ not possible
|
||||
0x6B00001F, // CMP
|
||||
0x2B00001F, // CMN
|
||||
0x2A000000, // ORR
|
||||
0x2A0003E0, // MOV (ORR with rn = wzr)
|
||||
0x0A200000, // BIC
|
||||
0x2A2003E0, // MVN (ORRN with rn = wzr)
|
||||
};
|
||||
|
||||
uint32_t instruction = opmap[i.data_proc.op];
|
||||
|
||||
if(i.data_proc.s)
|
||||
instruction |= 1 << 29;
|
||||
|
||||
if(i.data_proc.op < OP_TST || i.data_proc.op > OP_CMP)
|
||||
instruction |= mapreg(i.data_proc.rd);
|
||||
|
||||
if(i.data_proc.op != OP_MOV && i.data_proc.op != OP_MVN)
|
||||
{
|
||||
if(i.data_proc.rn != PC)
|
||||
instruction |= mapreg(i.data_proc.rn) << 5;
|
||||
else
|
||||
{
|
||||
emit_mov_imm(W1, pc + 8);
|
||||
instruction |= W1 << 5;
|
||||
}
|
||||
}
|
||||
|
||||
if(!i.data_proc.imm)
|
||||
{
|
||||
instruction |= mapreg(i.data_proc.rm) << 16;
|
||||
instruction |= i.data_proc.shift << 22;
|
||||
instruction |= i.data_proc.shift_imm << 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Could be optimized further,
|
||||
// some AArch64 ops support immediate values
|
||||
|
||||
uint32_t immed = i.data_proc.immed_8;
|
||||
uint8_t count = i.data_proc.rotate_imm << 1;
|
||||
if(count)
|
||||
immed = (immed >> count) | (immed << (32 - count));
|
||||
|
||||
if(i.data_proc.op == OP_MOV)
|
||||
{
|
||||
emit_mov_imm(mapreg(i.data_proc.rd), immed);
|
||||
goto instruction_translated;
|
||||
}
|
||||
|
||||
if(immed <= 0xFFF
|
||||
&& (i.data_proc.op == OP_ADD || i.data_proc.op == OP_SUB
|
||||
|| i.data_proc.op == OP_CMP || i.data_proc.op == OP_CMN))
|
||||
{
|
||||
/* Those instructions support a normal 12bit (optionally shifted left by 12) immediate.
|
||||
This is not the case for logical instructions, they use awfully complicated useless
|
||||
terrible garbage called "bitmask operand" that not even binutils can encode properly. */
|
||||
instruction &= ~(0x1E000000);
|
||||
instruction |= 1 << 28 | (immed << 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
emit_mov_imm(W0, immed);
|
||||
/* All those operations are nops (or with 0)
|
||||
instruction |= W0 << 16;
|
||||
instruction |= SH_LSL << 22;
|
||||
instruction |= 0 << 10;*/
|
||||
}
|
||||
}
|
||||
|
||||
emit(instruction);
|
||||
}
|
||||
else if((i.raw & 0xFF000F0) == 0x7F000F0)
|
||||
goto unimpl; // undefined
|
||||
else if((i.raw & 0xC000000) == 0x4000000)
|
||||
{
|
||||
// Memory access: LDR, STRB, etc.
|
||||
// User mode access not implemented
|
||||
if(!i.mem_proc.p && i.mem_proc.w)
|
||||
goto unimpl;
|
||||
|
||||
if(i.mem_proc.not_imm && (i.mem_proc.rm == PC || (i.mem_proc.shift == SH_ROR && i.mem_proc.shift_imm == 0)))
|
||||
goto unimpl;
|
||||
|
||||
if((i.mem_proc.rd == i.mem_proc.rn || i.mem_proc.rn == PC) && (!i.mem_proc.p || i.mem_proc.w))
|
||||
goto unimpl; // Writeback into PC not implemented
|
||||
|
||||
bool offset_is_zero = !i.mem_proc.not_imm && i.mem_proc.immed == 0;
|
||||
|
||||
// Address into w0
|
||||
if(i.mem_proc.rn != PC)
|
||||
emit_mov_reg(W0, mapreg(i.mem_proc.rn));
|
||||
else if(i.mem_proc.not_imm)
|
||||
emit_mov_imm(W0, pc + 8);
|
||||
else // Address known
|
||||
{
|
||||
int offset = i.mem_proc.u ? i.mem_proc.immed :
|
||||
-i.mem_proc.immed;
|
||||
|
||||
emit_mov_imm(W0, pc + 8 + offset);
|
||||
offset_is_zero = true;
|
||||
}
|
||||
|
||||
// Skip offset calculation
|
||||
if(offset_is_zero)
|
||||
goto no_offset;
|
||||
|
||||
// Offset into w25
|
||||
if(!i.mem_proc.not_imm) // Immediate offset
|
||||
emit_mov_imm(W25, i.mem_proc.immed);
|
||||
else // Register offset, shifted
|
||||
{
|
||||
uint32_t instruction = 0x2a0003f9; // orr w25, wzr, wX, <shift>, #<amount>
|
||||
instruction |= mapreg(i.mem_proc.rm) << 16;
|
||||
instruction |= i.mem_proc.shift << 22;
|
||||
instruction |= i.mem_proc.shift_imm << 10;
|
||||
|
||||
emit(instruction);
|
||||
}
|
||||
|
||||
// Get final address..
|
||||
if(i.mem_proc.p)
|
||||
{
|
||||
// ..into r0
|
||||
if(i.mem_proc.u)
|
||||
emit(0x0b190000); // add w0, w0, w25
|
||||
else
|
||||
emit(0x4b190000); // sub w0, w0, w25
|
||||
|
||||
// It has to be stored AFTER the memory access, to be able
|
||||
// to perform the access again after an abort.
|
||||
if(i.mem_proc.w) // Writeback: final address into w25
|
||||
emit_mov_reg(W25, W0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ..into w25
|
||||
if(i.mem_proc.u)
|
||||
emit(0x0b190019); // add w25, w0, w25
|
||||
else
|
||||
emit(0x4b190019); // sub w25, w0, w25
|
||||
}
|
||||
|
||||
no_offset:
|
||||
if(i.mem_proc.l)
|
||||
{
|
||||
emit_call(reinterpret_cast<void*>(i.mem_proc.b ? read_byte_asm : read_word_asm));
|
||||
if(i.mem_proc.rd != PC)
|
||||
emit_mov_reg(mapreg(i.mem_proc.rd), W0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(i.mem_proc.rd != PC)
|
||||
emit_mov_reg(W1, mapreg(i.mem_proc.rd)); // w1 is the value
|
||||
else
|
||||
emit_mov_imm(W1, pc + 12);
|
||||
|
||||
emit_call(reinterpret_cast<void*>(i.mem_proc.b ? write_byte_asm : write_word_asm));
|
||||
}
|
||||
|
||||
if(!offset_is_zero && (!i.mem_proc.p || i.mem_proc.w))
|
||||
emit_mov_reg(mapreg(i.mem_proc.rn), W25);
|
||||
|
||||
// Jump after writeback, to support post-indexed jumps
|
||||
if(i.mem_proc.l && i.mem_proc.rd == PC)
|
||||
{
|
||||
// pc is destination register
|
||||
emit_jmp(reinterpret_cast<void*>(translation_next_bx));
|
||||
// It's an unconditional jump
|
||||
if(i.cond == CC_AL)
|
||||
jumps_away = stop_here = true;
|
||||
}
|
||||
}
|
||||
else if((i.raw & 0xE000000) == 0x8000000)
|
||||
{
|
||||
// LDM/STM
|
||||
if(i.mem_multi.s)
|
||||
goto unimpl; // Exception return or usermode not implemented
|
||||
|
||||
if(i.mem_multi.rn == PC)
|
||||
goto unimpl;
|
||||
|
||||
uint16_t reglist = i.mem_multi.reglist;
|
||||
if(reglist & (1 << i.mem_multi.rn))
|
||||
goto unimpl; // Loading/Saving address register
|
||||
|
||||
int count = __builtin_popcount(reglist), offset, wb_offset;
|
||||
|
||||
if(i.mem_multi.u) // Increment
|
||||
{
|
||||
wb_offset = count * 4;
|
||||
offset = 0;
|
||||
}
|
||||
else // Decrement
|
||||
{
|
||||
wb_offset = count * -4;
|
||||
offset = wb_offset;
|
||||
}
|
||||
|
||||
if(i.mem_multi.p == i.mem_multi.u)
|
||||
offset += 4;
|
||||
|
||||
// Base reg in w25
|
||||
emit_mov_reg(W25, mapreg(i.mem_multi.rn));
|
||||
unsigned int reg = 0;
|
||||
while(reglist)
|
||||
{
|
||||
if((reglist & 1) == 0)
|
||||
goto next;
|
||||
|
||||
if(offset >= 0)
|
||||
emit(0x11000320 | (offset << 10)); // add w0, w25, #offset
|
||||
else
|
||||
emit(0x51000320 | (-offset << 10)); // sub w0, w25, #-offset
|
||||
|
||||
if(i.mem_multi.l)
|
||||
{
|
||||
emit_call(reinterpret_cast<void*>(read_word_asm));
|
||||
if(reg != PC)
|
||||
emit_mov_reg(mapreg(reg), W0);
|
||||
//else written below
|
||||
}
|
||||
else
|
||||
{
|
||||
if(reg == PC)
|
||||
emit_mov_imm(W1, pc + 12);
|
||||
else
|
||||
emit_mov_reg(W1, mapreg(reg));
|
||||
|
||||
emit_call(reinterpret_cast<void*>(write_word_asm));
|
||||
}
|
||||
|
||||
offset += 4;
|
||||
next:
|
||||
reglist >>= 1;
|
||||
++reg;
|
||||
}
|
||||
|
||||
if(i.mem_multi.w)
|
||||
{
|
||||
if(wb_offset >= 0)
|
||||
emit(0x11000320 | (wb_offset << 10) | mapreg(i.mem_multi.rn)); // add wN, w25, #wb_offset
|
||||
else
|
||||
emit(0x51000320 | (-wb_offset << 10) | mapreg(i.mem_multi.rn)); // sub wN, w25, #-wb_offset
|
||||
}
|
||||
|
||||
if(i.mem_multi.l && (i.mem_multi.reglist & (1 << PC))) // Loading PC
|
||||
{
|
||||
// PC already in W0 (last one loaded)
|
||||
emit_jmp(reinterpret_cast<void*>(translation_next_bx));
|
||||
if(i.cond == CC_AL)
|
||||
jumps_away = stop_here = true;
|
||||
}
|
||||
}
|
||||
else if((i.raw & 0xE000000) == 0xA000000)
|
||||
{
|
||||
if(i.branch.l)
|
||||
{
|
||||
// Save return address in LR
|
||||
emit_mov_imm(mapreg(LR), pc + 4);
|
||||
}
|
||||
else if(i.cond == CC_AL)
|
||||
jumps_away = stop_here = true;
|
||||
|
||||
uint32_t addr = pc + ((int32_t)(i.raw << 8) >> 6) + 8;
|
||||
uint32_t *ptr = reinterpret_cast<uint32_t*>(try_ptr(addr));
|
||||
|
||||
if(ptr == nullptr)
|
||||
{
|
||||
emit_mov_imm(W0, addr);
|
||||
emit_jmp(reinterpret_cast<void*>(translation_next));
|
||||
}
|
||||
else if(!(RAM_FLAGS(ptr) & RF_CODE_TRANSLATED))
|
||||
{
|
||||
// Update PC manually
|
||||
emit_mov_imm(W0, addr);
|
||||
emit(0xb9003e60); // str w0, [x19, #15*4]
|
||||
emit_mov_imm(X0, uintptr_t(ptr));
|
||||
emit_jmp(reinterpret_cast<void*>(translation_jmp_ptr));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get address of translated code to jump to it
|
||||
translation *target_translation = &translation_table[RAM_FLAGS(ptr) >> RFS_TRANSLATION_INDEX];
|
||||
uintptr_t jmp_target = reinterpret_cast<uintptr_t>(target_translation->jump_table[ptr - target_translation->start_ptr]);
|
||||
|
||||
// Update PC manually
|
||||
emit_mov_imm(W0, addr);
|
||||
emit(0xb9003e60); // str w0, [x19, #15*4]
|
||||
emit_mov_imm(X0, jmp_target);
|
||||
emit_jmp(reinterpret_cast<void*>(translation_jmp));
|
||||
}
|
||||
}
|
||||
else
|
||||
goto unimpl;
|
||||
|
||||
instruction_translated:
|
||||
|
||||
if(cond_branch)
|
||||
{
|
||||
// Fixup the branch above
|
||||
*cond_branch |= ((translate_current - cond_branch) & 0x7FFFF) << 5;
|
||||
cond_branch = nullptr;
|
||||
}
|
||||
|
||||
RAM_FLAGS(insn_ptr) |= (RF_CODE_TRANSLATED | next_translation_index << RFS_TRANSLATION_INDEX);
|
||||
|
||||
++jump_table_current;
|
||||
++insn_ptr;
|
||||
pc += 4;
|
||||
}
|
||||
|
||||
unimpl:
|
||||
// Throw away partial translation
|
||||
translate_current = *jump_table_current;
|
||||
RAM_FLAGS(insn_ptr) |= RF_CODE_NO_TRANSLATE;
|
||||
|
||||
exit_translation:
|
||||
|
||||
if(insn_ptr == insn_ptr_start)
|
||||
{
|
||||
#ifdef IS_IOS_BUILD
|
||||
// Mark translate_buffer as R_X
|
||||
// Even if no translation was done, pages got marked RW_
|
||||
mprotect(translate_buffer, INSN_BUFFER_SIZE, PROT_READ | PROT_EXEC);
|
||||
#endif
|
||||
|
||||
// No virtual instruction got translated, just drop everything
|
||||
translate_current = translate_buffer_inst_start;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!jumps_away)
|
||||
{
|
||||
uint32_t *ptr = reinterpret_cast<uint32_t*>(try_ptr(pc));
|
||||
|
||||
if(ptr == nullptr)
|
||||
{
|
||||
emit_mov_imm(W0, pc);
|
||||
emit_jmp(reinterpret_cast<void*>(translation_next));
|
||||
}
|
||||
else if(!(RAM_FLAGS(ptr) & RF_CODE_TRANSLATED))
|
||||
{
|
||||
// Update PC manually
|
||||
emit_mov_imm(W0, pc);
|
||||
emit(0xb9003e60); // str w0, [x19, #15*4]
|
||||
emit_mov_imm(X0, uintptr_t(ptr));
|
||||
emit_jmp(reinterpret_cast<void*>(translation_jmp_ptr));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get address of translated code to jump to it
|
||||
translation *target_translation = &translation_table[RAM_FLAGS(ptr) >> RFS_TRANSLATION_INDEX];
|
||||
uintptr_t jmp_target = reinterpret_cast<uintptr_t>(target_translation->jump_table[ptr - target_translation->start_ptr]);
|
||||
|
||||
// Update PC manually
|
||||
emit_mov_imm(W0, pc);
|
||||
emit(0xb9003e60); // str w0, [x19, #15*4]
|
||||
emit_mov_imm(X0, jmp_target);
|
||||
emit_jmp(reinterpret_cast<void*>(translation_jmp));
|
||||
}
|
||||
}
|
||||
|
||||
// Only flush cache until the literal pool
|
||||
uint32_t *code_end = translate_current;
|
||||
// Emit the literal pool
|
||||
literalpool_fill();
|
||||
|
||||
this_translation->end_ptr = insn_ptr;
|
||||
this_translation->unused = reinterpret_cast<uintptr_t>(translate_current);
|
||||
|
||||
next_translation_index += 1;
|
||||
|
||||
// Flush the instruction cache
|
||||
#ifdef IS_IOS_BUILD
|
||||
// Mark translate_buffer as R_X
|
||||
mprotect(translate_buffer, INSN_BUFFER_SIZE, PROT_READ | PROT_EXEC);
|
||||
sys_cache_control(1 /* kCacheFunctionPrepareForExecution */, jump_table_start[0], (code_end-jump_table_start[0])*4);
|
||||
#else
|
||||
__builtin___clear_cache(jump_table_start[0], code_end);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void _invalidate_translation(int index)
|
||||
{
|
||||
/* Disabled for now. write_action not called in asmcode_aarch64.S so this can't happen
|
||||
and translation_pc_ptr is inaccurate due to translation_jmp anyway.
|
||||
|
||||
uint32_t flags = RAM_FLAGS(translation_pc_ptr);
|
||||
if ((flags & RF_CODE_TRANSLATED) && (int)(flags >> RFS_TRANSLATION_INDEX) == index)
|
||||
error("Cannot modify currently executing code block.");
|
||||
*/
|
||||
|
||||
uint32_t *start = translation_table[index].start_ptr;
|
||||
uint32_t *end = translation_table[index].end_ptr;
|
||||
for (; start < end; start++)
|
||||
RAM_FLAGS(start) &= ~(RF_CODE_TRANSLATED | (~0u << RFS_TRANSLATION_INDEX));
|
||||
}
|
||||
|
||||
void flush_translations()
|
||||
{
|
||||
for(unsigned int index = 0; index < next_translation_index; index++)
|
||||
_invalidate_translation(index);
|
||||
|
||||
next_translation_index = 0;
|
||||
translate_current = translate_buffer;
|
||||
jump_table_current = jump_table;
|
||||
}
|
||||
|
||||
void invalidate_translation(int index)
|
||||
{
|
||||
/* Due to translation_jmp using absolute pointers in the JIT, we can't just
|
||||
invalidate a single translation. */
|
||||
#ifdef SUPPORT_LINUX
|
||||
(void) index;
|
||||
flush_translations();
|
||||
#else
|
||||
_invalidate_translation(index);
|
||||
#endif
|
||||
}
|
||||
|
||||
void translate_fix_pc()
|
||||
{
|
||||
if (!translation_sp)
|
||||
return;
|
||||
|
||||
uint32_t *insnp = reinterpret_cast<uint32_t*>(try_ptr(arm.reg[15]));
|
||||
uint32_t flags = 0;
|
||||
if(!insnp || !((flags = RAM_FLAGS(insnp)) & RF_CODE_TRANSLATED))
|
||||
return error("Couldn't get PC for fault");
|
||||
|
||||
int index = flags >> RFS_TRANSLATION_INDEX;
|
||||
assert(insnp >= translation_table[index].start_ptr);
|
||||
assert(insnp < translation_table[index].end_ptr);
|
||||
|
||||
void *ret_pc = translation_sp[-2];
|
||||
|
||||
unsigned int jump_index = insnp - translation_table[index].start_ptr;
|
||||
unsigned int translation_insts = translation_table[index].end_ptr - translation_table[index].start_ptr;
|
||||
|
||||
for(unsigned int i = jump_index; ret_pc > translation_table[index].jump_table[i] && i < translation_insts; ++i)
|
||||
arm.reg[15] += 4;
|
||||
|
||||
cycle_count_delta -= translation_table[index].end_ptr - insnp;
|
||||
translation_sp = nullptr;
|
||||
|
||||
assert(!(arm.cpsr_low28 & 0x20));
|
||||
}
|
||||
1203
src/armv5te/translate_arm.cpp
Normal file
1203
src/armv5te/translate_arm.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1039
src/armv5te/translate_x86.c
Normal file
1039
src/armv5te/translate_x86.c
Normal file
File diff suppressed because it is too large
Load Diff
1044
src/armv5te/translate_x86_64.c
Normal file
1044
src/armv5te/translate_x86_64.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "emulator.h"
|
||||
#include "specs/dragonballVzRegisterSpec.h"
|
||||
#include "dbvzRegisters.h"
|
||||
#include "dbvz.h"
|
||||
#include "m515Bus.h"
|
||||
#include "portability.h"
|
||||
#include "flx68000.h"
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "audio/blip_buf.h"
|
||||
#include "debug/sandbox.h"
|
||||
|
||||
|
||||
uint8_t dbvzReg[DBVZ_REG_SIZE];
|
||||
dbvz_chip_t dbvzChipSelects[DBVZ_CHIP_END];
|
||||
double dbvzSysclksPerClk32;//how many SYSCLK cycles before toggling the 32.768 kHz crystal
|
||||
uint32_t dbvzFrameClk32s;//how many CLK32s have happened in the current frame
|
||||
@@ -420,8 +420,6 @@ uint16_t dbvzGetRegister16(uint32_t address){
|
||||
}
|
||||
|
||||
case PLLFSR:
|
||||
//this is a hack, it makes the busy wait in HwrDelay finish instantly, prevents issues with the power button
|
||||
registerArrayWrite16(PLLFSR, registerArrayRead16(PLLFSR) ^ 0x8000);
|
||||
return registerArrayRead16(PLLFSR);
|
||||
|
||||
//32 bit registers accessed as 16 bit
|
||||
@@ -1007,11 +1005,11 @@ void dbvzSetRegister32(uint32_t address, uint32_t value){
|
||||
}
|
||||
}
|
||||
|
||||
void dbvzResetRegisters(void){
|
||||
void dbvzReset(void){
|
||||
uint32_t oldRtc = registerArrayRead32(RTCTIME);//preserve RTCTIME
|
||||
uint16_t oldDayr = registerArrayRead16(DAYR);//preserve DAYR
|
||||
|
||||
memset(palmReg, 0x00, DBVZ_REG_SIZE - DBVZ_BOOTLOADER_SIZE);
|
||||
memset(dbvzReg, 0x00, DBVZ_REG_SIZE - DBVZ_BOOTLOADER_SIZE);
|
||||
dbvzSysclksPerClk32 = 0.0;
|
||||
clk32Counter = 0;
|
||||
pctlrCpuClockDivider = 1.0;
|
||||
@@ -1157,9 +1155,295 @@ void dbvzResetRegisters(void){
|
||||
updateBacklightAmplifierStatus();
|
||||
|
||||
dbvzSysclksPerClk32 = sysclksPerClk32();
|
||||
|
||||
dbvzResetAddressSpace();
|
||||
flx68000Reset();
|
||||
}
|
||||
|
||||
void dbvzLoadBootloader(uint8_t* data, uint32_t size){
|
||||
uint16_t index;
|
||||
|
||||
if(!data)
|
||||
size = 0;
|
||||
|
||||
size = uintMin(size, DBVZ_BOOTLOADER_SIZE);
|
||||
|
||||
//copy size bytes from buffer to bootloader area
|
||||
for(index = 0; index < size; index++)
|
||||
registerArrayWrite8(DBVZ_REG_SIZE - DBVZ_BOOTLOADER_SIZE + index, data[index]);
|
||||
|
||||
//fill remainig space with 0x00
|
||||
for(; index < DBVZ_BOOTLOADER_SIZE; index++)
|
||||
registerArrayWrite8(DBVZ_REG_SIZE - DBVZ_BOOTLOADER_SIZE + index, 0x00);
|
||||
}
|
||||
|
||||
void dbvzSetRtc(uint16_t days, uint8_t hours, uint8_t minutes, uint8_t seconds){
|
||||
registerArrayWrite32(RTCTIME, hours << 24 & 0x1F000000 | minutes << 16 & 0x003F0000 | seconds & 0x0000003F);
|
||||
registerArrayWrite16(DAYR, days & 0x01FF);
|
||||
}
|
||||
|
||||
uint32_t dbvzStateSize(void){
|
||||
uint32_t size = 0;
|
||||
|
||||
size += flx68000StateSize();
|
||||
size += DBVZ_REG_SIZE;//hardware registers
|
||||
size += DBVZ_TOTAL_MEMORY_BANKS;
|
||||
size += sizeof(uint32_t) * 4 * DBVZ_CHIP_END;//chip select states
|
||||
size += sizeof(uint8_t) * 5 * DBVZ_CHIP_END;//chip select states
|
||||
size += sizeof(uint64_t) * 5;//32.32 fixed point double, timerXCycleCounter and CPU cycle timers
|
||||
size += sizeof(int8_t);//pllSleepWait
|
||||
size += sizeof(int8_t);//pllWakeWait
|
||||
size += sizeof(uint32_t);//clk32Counter
|
||||
size += sizeof(uint64_t);//pctlrCpuClockDivider
|
||||
size += sizeof(uint16_t) * 2;//timerStatusReadAcknowledge
|
||||
size += sizeof(uint8_t);//portDInterruptLastValue
|
||||
size += sizeof(uint16_t) * 9;//RX 8 * 16 SPI1 FIFO, 1 index is for FIFO full
|
||||
size += sizeof(uint16_t) * 9;//TX 8 * 16 SPI1 FIFO, 1 index is for FIFO full
|
||||
size += sizeof(uint8_t) * 5;//spi1(R/T)x(Read/Write)Position / spi1RxOverflowed
|
||||
size += sizeof(int32_t);//pwm1ClocksToNextSample
|
||||
size += sizeof(uint8_t) * 6;//pwm1Fifo[6]
|
||||
size += sizeof(uint8_t) * 2;//pwm1(Read/Write)
|
||||
|
||||
//debugLog("size is:%d\n", size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void dbvzSaveState(uint8_t* data){
|
||||
uint32_t offset = 0;
|
||||
uint8_t index;
|
||||
|
||||
//CPU core
|
||||
flx68000SaveState(data + offset);
|
||||
offset += flx68000StateSize();
|
||||
|
||||
//memory
|
||||
memcpy(data + offset, dbvzReg, DBVZ_REG_SIZE);
|
||||
swap16BufferIfLittle(data + offset, DBVZ_REG_SIZE / sizeof(uint16_t));
|
||||
offset += DBVZ_REG_SIZE;
|
||||
memcpy(data + offset, dbvzBankType, DBVZ_TOTAL_MEMORY_BANKS);
|
||||
offset += DBVZ_TOTAL_MEMORY_BANKS;
|
||||
for(index = DBVZ_CHIP_BEGIN; index < DBVZ_CHIP_END; index++){
|
||||
writeStateValue8(data + offset, dbvzChipSelects[index].enable);
|
||||
offset += sizeof(uint8_t);
|
||||
writeStateValue32(data + offset, dbvzChipSelects[index].start);
|
||||
offset += sizeof(uint32_t);
|
||||
writeStateValue32(data + offset, dbvzChipSelects[index].lineSize);
|
||||
offset += sizeof(uint32_t);
|
||||
writeStateValue32(data + offset, dbvzChipSelects[index].mask);
|
||||
offset += sizeof(uint32_t);
|
||||
writeStateValue8(data + offset, dbvzChipSelects[index].inBootMode);
|
||||
offset += sizeof(uint8_t);
|
||||
writeStateValue8(data + offset, dbvzChipSelects[index].readOnly);
|
||||
offset += sizeof(uint8_t);
|
||||
writeStateValue8(data + offset, dbvzChipSelects[index].readOnlyForProtectedMemory);
|
||||
offset += sizeof(uint8_t);
|
||||
writeStateValue8(data + offset, dbvzChipSelects[index].supervisorOnlyProtectedMemory);
|
||||
offset += sizeof(uint8_t);
|
||||
writeStateValue32(data + offset, dbvzChipSelects[index].unprotectedSize);
|
||||
offset += sizeof(uint32_t);
|
||||
}
|
||||
|
||||
//timing
|
||||
writeStateValueDouble(data + offset, dbvzSysclksPerClk32);
|
||||
offset += sizeof(uint64_t);
|
||||
writeStateValueDouble(data + offset, palmCycleCounter);
|
||||
offset += sizeof(uint64_t);
|
||||
writeStateValueDouble(data + offset, palmClockMultiplier);
|
||||
offset += sizeof(uint64_t);
|
||||
writeStateValue8(data + offset, pllSleepWait);
|
||||
offset += sizeof(int8_t);
|
||||
writeStateValue8(data + offset, pllWakeWait);
|
||||
offset += sizeof(int8_t);
|
||||
writeStateValue32(data + offset, clk32Counter);
|
||||
offset += sizeof(uint32_t);
|
||||
writeStateValueDouble(data + offset, pctlrCpuClockDivider);
|
||||
offset += sizeof(uint64_t);
|
||||
writeStateValueDouble(data + offset, timerCycleCounter[0]);
|
||||
offset += sizeof(uint64_t);
|
||||
writeStateValueDouble(data + offset, timerCycleCounter[1]);
|
||||
offset += sizeof(uint64_t);
|
||||
writeStateValue16(data + offset, timerStatusReadAcknowledge[0]);
|
||||
offset += sizeof(uint16_t);
|
||||
writeStateValue16(data + offset, timerStatusReadAcknowledge[1]);
|
||||
offset += sizeof(uint16_t);
|
||||
writeStateValue8(data + offset, portDInterruptLastValue);
|
||||
offset += sizeof(uint8_t);
|
||||
|
||||
//SPI1
|
||||
for(index = 0; index < 9; index++){
|
||||
writeStateValue16(data + offset, spi1RxFifo[index]);
|
||||
offset += sizeof(uint16_t);
|
||||
}
|
||||
for(index = 0; index < 9; index++){
|
||||
writeStateValue16(data + offset, spi1TxFifo[index]);
|
||||
offset += sizeof(uint16_t);
|
||||
}
|
||||
writeStateValue8(data + offset, spi1RxReadPosition);
|
||||
offset += sizeof(uint8_t);
|
||||
writeStateValue8(data + offset, spi1RxWritePosition);
|
||||
offset += sizeof(uint8_t);
|
||||
writeStateValue8(data + offset, spi1RxOverflowed);
|
||||
offset += sizeof(uint8_t);
|
||||
writeStateValue8(data + offset, spi1TxReadPosition);
|
||||
offset += sizeof(uint8_t);
|
||||
writeStateValue8(data + offset, spi1TxWritePosition);
|
||||
offset += sizeof(uint8_t);
|
||||
|
||||
//PWM1, audio
|
||||
writeStateValue32(data + offset, pwm1ClocksToNextSample);
|
||||
offset += sizeof(int32_t);
|
||||
for(index = 0; index < 6; index++){
|
||||
writeStateValue8(data + offset, pwm1Fifo[index]);
|
||||
offset += sizeof(uint8_t);
|
||||
}
|
||||
writeStateValue8(data + offset, pwm1ReadPosition);
|
||||
offset += sizeof(uint8_t);
|
||||
writeStateValue8(data + offset, pwm1WritePosition);
|
||||
offset += sizeof(uint8_t);
|
||||
|
||||
//debugLog("save offset is:%d\n", offset);
|
||||
}
|
||||
|
||||
void dbvzLoadState(uint8_t* data){
|
||||
uint32_t offset = 0;
|
||||
uint8_t index;
|
||||
|
||||
//CPU core
|
||||
flx68000LoadState(data + offset);
|
||||
offset += flx68000StateSize();
|
||||
|
||||
//memory
|
||||
memcpy(dbvzReg, data + offset, DBVZ_REG_SIZE);
|
||||
swap16BufferIfLittle(dbvzReg, DBVZ_REG_SIZE / sizeof(uint16_t));
|
||||
offset += DBVZ_REG_SIZE;
|
||||
memcpy(dbvzBankType, data + offset, DBVZ_TOTAL_MEMORY_BANKS);
|
||||
offset += DBVZ_TOTAL_MEMORY_BANKS;
|
||||
for(index = DBVZ_CHIP_BEGIN; index < DBVZ_CHIP_END; index++){
|
||||
dbvzChipSelects[index].enable = readStateValue8(data + offset);
|
||||
offset += sizeof(uint8_t);
|
||||
dbvzChipSelects[index].start = readStateValue32(data + offset);
|
||||
offset += sizeof(uint32_t);
|
||||
dbvzChipSelects[index].lineSize = readStateValue32(data + offset);
|
||||
offset += sizeof(uint32_t);
|
||||
dbvzChipSelects[index].mask = readStateValue32(data + offset);
|
||||
offset += sizeof(uint32_t);
|
||||
dbvzChipSelects[index].inBootMode = readStateValue8(data + offset);
|
||||
offset += sizeof(uint8_t);
|
||||
dbvzChipSelects[index].readOnly = readStateValue8(data + offset);
|
||||
offset += sizeof(uint8_t);
|
||||
dbvzChipSelects[index].readOnlyForProtectedMemory = readStateValue8(data + offset);
|
||||
offset += sizeof(uint8_t);
|
||||
dbvzChipSelects[index].supervisorOnlyProtectedMemory = readStateValue8(data + offset);
|
||||
offset += sizeof(uint8_t);
|
||||
dbvzChipSelects[index].unprotectedSize = readStateValue32(data + offset);
|
||||
offset += sizeof(uint32_t);
|
||||
}
|
||||
|
||||
//timing
|
||||
dbvzSysclksPerClk32 = readStateValueDouble(data + offset);
|
||||
offset += sizeof(uint64_t);
|
||||
palmCycleCounter = readStateValueDouble(data + offset);
|
||||
offset += sizeof(uint64_t);
|
||||
palmClockMultiplier = readStateValueDouble(data + offset);
|
||||
offset += sizeof(uint64_t);
|
||||
pllSleepWait = readStateValue8(data + offset);
|
||||
offset += sizeof(int8_t);
|
||||
pllWakeWait = readStateValue8(data + offset);
|
||||
offset += sizeof(int8_t);
|
||||
clk32Counter = readStateValue32(data + offset);
|
||||
offset += sizeof(uint32_t);
|
||||
pctlrCpuClockDivider = readStateValueDouble(data + offset);
|
||||
offset += sizeof(uint64_t);
|
||||
timerCycleCounter[0] = readStateValueDouble(data + offset);
|
||||
offset += sizeof(uint64_t);
|
||||
timerCycleCounter[1] = readStateValueDouble(data + offset);
|
||||
offset += sizeof(uint64_t);
|
||||
timerStatusReadAcknowledge[0] = readStateValue16(data + offset);
|
||||
offset += sizeof(uint16_t);
|
||||
timerStatusReadAcknowledge[1] = readStateValue16(data + offset);
|
||||
offset += sizeof(uint16_t);
|
||||
portDInterruptLastValue = readStateValue8(data + offset);
|
||||
offset += sizeof(uint8_t);
|
||||
|
||||
//SPI1
|
||||
for(index = 0; index < 9; index++){
|
||||
spi1RxFifo[index] = readStateValue16(data + offset);
|
||||
offset += sizeof(uint16_t);
|
||||
}
|
||||
for(index = 0; index < 9; index++){
|
||||
spi1TxFifo[index] = readStateValue16(data + offset);
|
||||
offset += sizeof(uint16_t);
|
||||
}
|
||||
spi1RxReadPosition = readStateValue8(data + offset);
|
||||
offset += sizeof(uint8_t);
|
||||
spi1RxWritePosition = readStateValue8(data + offset);
|
||||
offset += sizeof(uint8_t);
|
||||
spi1RxOverflowed = readStateValue8(data + offset);
|
||||
offset += sizeof(uint8_t);
|
||||
spi1TxReadPosition = readStateValue8(data + offset);
|
||||
offset += sizeof(uint8_t);
|
||||
spi1TxWritePosition = readStateValue8(data + offset);
|
||||
offset += sizeof(uint8_t);
|
||||
|
||||
//PWM1, audio
|
||||
pwm1ClocksToNextSample = readStateValue32(data + offset);
|
||||
offset += sizeof(int32_t);
|
||||
for(index = 0; index < 6; index++){
|
||||
pwm1Fifo[index] = readStateValue8(data + offset);
|
||||
offset += sizeof(uint8_t);
|
||||
}
|
||||
pwm1ReadPosition = readStateValue8(data + offset);
|
||||
offset += sizeof(uint8_t);
|
||||
pwm1WritePosition = readStateValue8(data + offset);
|
||||
offset += sizeof(uint8_t);
|
||||
|
||||
//debugLog("load offset is:%d\n", offset);
|
||||
}
|
||||
|
||||
void dbvzLoadStateFinished(void){
|
||||
flx68000LoadStateFinished();
|
||||
}
|
||||
|
||||
void dbvzExecute(void){
|
||||
uint32_t samples;
|
||||
|
||||
//I/O
|
||||
m515RefreshInputState();
|
||||
|
||||
//CPU
|
||||
dbvzFrameClk32s = 0;
|
||||
for(; palmCycleCounter < (double)M515_CRYSTAL_FREQUENCY / EMU_FPS; palmCycleCounter += 1.0){
|
||||
uint8_t cpuTimeSegments;
|
||||
|
||||
dbvzBeginClk32();
|
||||
|
||||
for(cpuTimeSegments = 0; cpuTimeSegments < 2; cpuTimeSegments++){
|
||||
double cyclesRemaining = dbvzSysclksPerClk32 / 2.0;
|
||||
|
||||
while(cyclesRemaining >= 1.0){
|
||||
double sysclks = floatMin(cyclesRemaining, DBVZ_SYSCLK_PRECISION);
|
||||
int32_t cpuCycles = sysclks * pctlrCpuClockDivider * palmClockMultiplier;
|
||||
|
||||
if(cpuCycles > 0)
|
||||
flx68000Execute(cpuCycles);
|
||||
dbvzAddSysclks(sysclks);
|
||||
|
||||
cyclesRemaining -= sysclks;
|
||||
}
|
||||
|
||||
//toggle CLK32 bit in PLLFSR, it indicates the current state of CLK32 so it must start false and be changed to true in the middle of CLK32
|
||||
registerArrayWrite16(PLLFSR, registerArrayRead16(PLLFSR) ^ 0x8000);
|
||||
}
|
||||
|
||||
dbvzEndClk32();
|
||||
dbvzFrameClk32s++;
|
||||
}
|
||||
palmCycleCounter -= (double)M515_CRYSTAL_FREQUENCY / EMU_FPS;
|
||||
|
||||
//audio
|
||||
blip_end_frame(palmAudioResampler, blip_clocks_needed(palmAudioResampler, AUDIO_SAMPLES_PER_FRAME));
|
||||
blip_read_samples(palmAudioResampler, palmAudio, AUDIO_SAMPLES_PER_FRAME, true);
|
||||
MULTITHREAD_LOOP(samples) for(samples = 0; samples < AUDIO_SAMPLES_PER_FRAME * 2; samples += 2)
|
||||
palmAudio[samples + 1] = palmAudio[samples];
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef DBVZ_REGISTERS_H
|
||||
#define DBVZ_REGISTERS_H
|
||||
#ifndef DBVZ_H
|
||||
#define DBVZ_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
@@ -64,6 +64,7 @@ typedef struct{
|
||||
}dbvz_chip_t;
|
||||
|
||||
//variables
|
||||
extern uint8_t dbvzReg[];
|
||||
extern dbvz_chip_t dbvzChipSelects[];
|
||||
extern double dbvzSysclksPerClk32;
|
||||
extern uint32_t dbvzFrameClk32s;
|
||||
@@ -116,7 +117,14 @@ void dbvzSetRegister16(uint32_t address, uint16_t value);
|
||||
void dbvzSetRegister32(uint32_t address, uint32_t value);
|
||||
|
||||
//config
|
||||
void dbvzResetRegisters(void);
|
||||
void dbvzReset(void);
|
||||
void dbvzLoadBootloader(uint8_t* data, uint32_t size);
|
||||
void dbvzSetRtc(uint16_t days, uint8_t hours, uint8_t minutes, uint8_t seconds);
|
||||
uint32_t dbvzStateSize(void);
|
||||
void dbvzSaveState(uint8_t* data);
|
||||
void dbvzLoadState(uint8_t* data);
|
||||
void dbvzLoadStateFinished(void);
|
||||
|
||||
void dbvzExecute(void);
|
||||
|
||||
#endif
|
||||
@@ -11,12 +11,12 @@ static uint8_t getPortKValue(void);
|
||||
static uint8_t getPortMValue(void);
|
||||
|
||||
//basic accessors
|
||||
static uint8_t registerArrayRead8(uint32_t address){return BUFFER_READ_8(palmReg, address, 0xFFF);}
|
||||
static uint16_t registerArrayRead16(uint32_t address){return BUFFER_READ_16(palmReg, address, 0xFFF);}
|
||||
static uint32_t registerArrayRead32(uint32_t address){return BUFFER_READ_32(palmReg, address, 0xFFF);}
|
||||
static void registerArrayWrite8(uint32_t address, uint8_t value){BUFFER_WRITE_8(palmReg, address, 0xFFF, value);}
|
||||
static void registerArrayWrite16(uint32_t address, uint16_t value){BUFFER_WRITE_16(palmReg, address, 0xFFF, value);}
|
||||
static void registerArrayWrite32(uint32_t address, uint32_t value){BUFFER_WRITE_32(palmReg, address, 0xFFF, value);}
|
||||
static uint8_t registerArrayRead8(uint32_t address){return M68K_BUFFER_READ_8(dbvzReg, address, 0xFFF);}
|
||||
static uint16_t registerArrayRead16(uint32_t address){return M68K_BUFFER_READ_16(dbvzReg, address, 0xFFF);}
|
||||
static uint32_t registerArrayRead32(uint32_t address){return M68K_BUFFER_READ_32(dbvzReg, address, 0xFFF);}
|
||||
static void registerArrayWrite8(uint32_t address, uint8_t value){M68K_BUFFER_WRITE_8(dbvzReg, address, 0xFFF, value);}
|
||||
static void registerArrayWrite16(uint32_t address, uint16_t value){M68K_BUFFER_WRITE_16(dbvzReg, address, 0xFFF, value);}
|
||||
static void registerArrayWrite32(uint32_t address, uint32_t value){M68K_BUFFER_WRITE_32(dbvzReg, address, 0xFFF, value);}
|
||||
|
||||
//interrupt setters, used for setting an interrupt with masking by IMR and logging in IPR
|
||||
static void setIprIsrBit(uint32_t interruptBit){
|
||||
@@ -107,7 +107,7 @@ int32_t pwm1FifoRunSample(int32_t now, int32_t clockOffset){
|
||||
//try to get next sample, if none are available play old sample
|
||||
if(pwm1FifoEntrys() > 0)
|
||||
pwm1ReadPosition = (pwm1ReadPosition + 1) % 6;
|
||||
dutyCycle = fMin((float)pwm1Fifo[pwm1ReadPosition] / period, 1.00);
|
||||
dutyCycle = floatMin((float)pwm1Fifo[pwm1ReadPosition] / period, 1.00);
|
||||
|
||||
for(index = 0; index < repeat; index++){
|
||||
#if !defined(EMU_NO_SAFETY)
|
||||
@@ -383,19 +383,22 @@ static void setSpiCont1(uint16_t value){
|
||||
if(value & oldSpiCont1 & 0x0200 && value & 0x0100){
|
||||
while(spi1TxFifoEntrys() > 0){
|
||||
uint16_t currentTxFifoEntry = spi1TxFifoRead();
|
||||
uint16_t newRxFifoEntry = 0x0000;
|
||||
uint16_t newRxFifoEntry;// = 0x0000;
|
||||
uint8_t bitCount = (value & 0x000F) + 1;
|
||||
uint16_t startBit = 1 << (bitCount - 1);
|
||||
uint8_t bits;
|
||||
//uint16_t startBit = 1 << (bitCount - 1);
|
||||
//uint8_t bits;
|
||||
|
||||
//debugLog("SPI1 transfer, bitCount:%d, PC:0x%08X\n", bitCount, flx68000GetPc());
|
||||
|
||||
//The most significant bit is output when the CPU loads the transmitted data, 13.2.3 SPI 1 Phase and Polarity Configurations MC68VZ328UM.pdf
|
||||
/*
|
||||
for(bits = 0; bits < bitCount; bits++){
|
||||
newRxFifoEntry <<= 1;
|
||||
newRxFifoEntry |= sdCardExchangeBit(!!(currentTxFifoEntry & startBit));
|
||||
currentTxFifoEntry <<= 1;
|
||||
}
|
||||
*/
|
||||
newRxFifoEntry = sdCardExchangeXBitsOptimized(currentTxFifoEntry, bitCount);
|
||||
|
||||
//add received data back to RX FIFO
|
||||
spi1RxFifoWrite(newRxFifoEntry);
|
||||
@@ -583,7 +586,7 @@ static uint8_t getPortDInputPinValues(void){
|
||||
|
||||
//portDInputValues |= 0x80;//battery dead bit, dont know the proper level to set this
|
||||
|
||||
if(palmSdCard.flashChip.data)
|
||||
if(palmSdCard.flashChipData)
|
||||
portDInputValues |= 0x20;
|
||||
|
||||
//kbd row 0
|
||||
|
||||
@@ -169,35 +169,35 @@ static void rtiInterruptClk32(void){
|
||||
//this function is part of endClk32();
|
||||
uint16_t triggeredRtiInterrupts = 0x0000;
|
||||
|
||||
if(clk32Counter % (CRYSTAL_FREQUENCY / 512) == 0){
|
||||
if(clk32Counter % (M515_CRYSTAL_FREQUENCY / 512) == 0){
|
||||
//RIS7 - 512HZ
|
||||
triggeredRtiInterrupts |= 0x8000;
|
||||
}
|
||||
if(clk32Counter % (CRYSTAL_FREQUENCY / 256) == 0){
|
||||
if(clk32Counter % (M515_CRYSTAL_FREQUENCY / 256) == 0){
|
||||
//RIS6 - 256HZ
|
||||
triggeredRtiInterrupts |= 0x4000;
|
||||
}
|
||||
if(clk32Counter % (CRYSTAL_FREQUENCY / 128) == 0){
|
||||
if(clk32Counter % (M515_CRYSTAL_FREQUENCY / 128) == 0){
|
||||
//RIS5 - 128HZ
|
||||
triggeredRtiInterrupts |= 0x2000;
|
||||
}
|
||||
if(clk32Counter % (CRYSTAL_FREQUENCY / 64) == 0){
|
||||
if(clk32Counter % (M515_CRYSTAL_FREQUENCY / 64) == 0){
|
||||
//RIS4 - 64HZ
|
||||
triggeredRtiInterrupts |= 0x1000;
|
||||
}
|
||||
if(clk32Counter % (CRYSTAL_FREQUENCY / 32) == 0){
|
||||
if(clk32Counter % (M515_CRYSTAL_FREQUENCY / 32) == 0){
|
||||
//RIS3 - 32HZ
|
||||
triggeredRtiInterrupts |= 0x0800;
|
||||
}
|
||||
if(clk32Counter % (CRYSTAL_FREQUENCY / 16) == 0){
|
||||
if(clk32Counter % (M515_CRYSTAL_FREQUENCY / 16) == 0){
|
||||
//RIS2 - 16HZ
|
||||
triggeredRtiInterrupts |= 0x0400;
|
||||
}
|
||||
if(clk32Counter % (CRYSTAL_FREQUENCY / 8) == 0){
|
||||
if(clk32Counter % (M515_CRYSTAL_FREQUENCY / 8) == 0){
|
||||
//RIS1 - 8HZ
|
||||
triggeredRtiInterrupts |= 0x0200;
|
||||
}
|
||||
if(clk32Counter % (CRYSTAL_FREQUENCY / 4) == 0){
|
||||
if(clk32Counter % (M515_CRYSTAL_FREQUENCY / 4) == 0){
|
||||
//RIS0 - 4HZ
|
||||
triggeredRtiInterrupts |= 0x0100;
|
||||
}
|
||||
@@ -306,11 +306,8 @@ void dbvzBeginClk32(void){
|
||||
}
|
||||
|
||||
void dbvzEndClk32(void){
|
||||
//currently using toggle on read hack
|
||||
//registerArrayWrite16(PLLFSR, registerArrayRead16(PLLFSR) ^ 0x8000);
|
||||
|
||||
//second position counter
|
||||
if(clk32Counter >= CRYSTAL_FREQUENCY - 1){
|
||||
if(clk32Counter >= M515_CRYSTAL_FREQUENCY - 1){
|
||||
clk32Counter = 0;
|
||||
rtcAddSecondClk32();
|
||||
}
|
||||
@@ -360,11 +357,11 @@ void dbvzAddSysclks(double count){
|
||||
}
|
||||
|
||||
static int32_t audioGetFramePercentIncrementFromClk32s(int32_t count){
|
||||
return (double)count / ((double)CRYSTAL_FREQUENCY / EMU_FPS) * AUDIO_END_OF_FRAME;
|
||||
return (double)count / ((double)M515_CRYSTAL_FREQUENCY / EMU_FPS) * AUDIO_END_OF_FRAME;
|
||||
}
|
||||
|
||||
static int32_t audioGetFramePercentIncrementFromSysclks(double count){
|
||||
return count / (dbvzSysclksPerClk32 * ((double)CRYSTAL_FREQUENCY / EMU_FPS)) * AUDIO_END_OF_FRAME;
|
||||
return count / (dbvzSysclksPerClk32 * ((double)M515_CRYSTAL_FREQUENCY / EMU_FPS)) * AUDIO_END_OF_FRAME;
|
||||
}
|
||||
|
||||
static int32_t audioGetFramePercentage(void){
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include "../m68k/m68kcpu.h"
|
||||
#include "../emulator.h"
|
||||
#include "../ads7846.h"
|
||||
#include "../dbvzRegisters.h"
|
||||
#include "../dbvz.h"
|
||||
#include "../portability.h"
|
||||
#include "../specs/emuFeatureRegisterSpec.h"
|
||||
#include "sandbox.h"
|
||||
@@ -355,7 +355,7 @@ static void freePalmString(uint32_t address){
|
||||
sandboxCallGuestFunction(false, 0x00000000, MemChunkFree, "w(p)", address);
|
||||
}
|
||||
|
||||
static bool installResourceToDevice(buffer_t resourceBuffer){
|
||||
static bool installResourceToDevice(uint8_t* data, uint32_t size){
|
||||
/*
|
||||
#define memNewChunkFlagNonMovable 0x0200
|
||||
#define memNewChunkFlagAllowLarge 0x1000 // this is not in the sdk *g*
|
||||
@@ -375,7 +375,7 @@ static bool installResourceToDevice(buffer_t resourceBuffer){
|
||||
}
|
||||
*/
|
||||
|
||||
uint32_t palmSideResourceData = sandboxCallGuestFunction(false, 0x00000000, MemChunkNew, "p(wlw)", 1/*heapID, storage RAM*/, resourceBuffer.size, 0x1200/*attr, seems to work without memOwnerID*/);
|
||||
uint32_t palmSideResourceData = sandboxCallGuestFunction(false, 0x00000000, MemChunkNew, "p(wlw)", 1/*heapID, storage RAM*/, size, 0x1200/*attr, seems to work without memOwnerID*/);
|
||||
bool storageRamReadOnly = dbvzChipSelects[DBVZ_CHIP_DX_RAM].readOnlyForProtectedMemory;
|
||||
uint16_t error;
|
||||
uint32_t count;
|
||||
@@ -385,8 +385,8 @@ static bool installResourceToDevice(buffer_t resourceBuffer){
|
||||
return false;
|
||||
|
||||
dbvzChipSelects[DBVZ_CHIP_DX_RAM].readOnlyForProtectedMemory = false;//need to unprotect storage RAM
|
||||
for(count = 0; count < resourceBuffer.size; count++)
|
||||
m68k_write_memory_8(palmSideResourceData + count, resourceBuffer.data[count]);
|
||||
for(count = 0; count < size; count++)
|
||||
m68k_write_memory_8(palmSideResourceData + count, data[count]);
|
||||
dbvzChipSelects[DBVZ_CHIP_DX_RAM].readOnlyForProtectedMemory = storageRamReadOnly;//restore old protection state
|
||||
error = sandboxCallGuestFunction(false, 0x00000000, DmCreateDatabaseFromImage, "w(p)", palmSideResourceData);//Err DmCreateDatabaseFromImage(MemPtr bufferP);//this looks best
|
||||
sandboxCallGuestFunction(false, 0x00000000, MemChunkFree, "w(p)", palmSideResourceData);
|
||||
@@ -789,7 +789,7 @@ void sandboxReset(void){
|
||||
sandboxWatchRegionsActive = 0;
|
||||
|
||||
//patch OS here if needed
|
||||
sandboxCommand(SANDBOX_CMD_PATCH_OS, NULL);
|
||||
//sandboxCommand(SANDBOX_CMD_PATCH_OS, NULL);
|
||||
|
||||
//log all register accesses
|
||||
//sandboxCommand(SANDBOX_CMD_REGISTER_WATCH_ENABLE, NULL);
|
||||
@@ -1063,8 +1063,8 @@ uint32_t sandboxCommand(uint32_t command, void* data){
|
||||
break;
|
||||
|
||||
case SANDBOX_CMD_DEBUG_INSTALL_APP:{
|
||||
buffer_t* app = (buffer_t*)data;
|
||||
bool success = installResourceToDevice(*app);
|
||||
uintptr_t* values = data;
|
||||
bool success = installResourceToDevice(values[0], values[1]);
|
||||
|
||||
if(!success)
|
||||
result = EMU_ERROR_OUT_OF_MEMORY;
|
||||
@@ -1098,9 +1098,11 @@ void sandboxOnFrameRun(void){
|
||||
//run at the end of every frame
|
||||
sandboxFramesRan++;
|
||||
|
||||
/*
|
||||
if(sandboxFramesRan == SANDBOX_SECONDS_TO_FRAMES(10)){
|
||||
sandboxCommand(SANDBOX_CMD_TEST_MEMORY_ALIGNMENT, NULL);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void sandboxOnOpcodeRun(void){
|
||||
|
||||
827
src/emulator.c
827
src/emulator.c
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
#ifndef EMULATOR_H
|
||||
#define EMULATOR_H
|
||||
//this is the only header a frontend needs to include
|
||||
//this is the only header a frontend needs to include from the emulator
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -18,8 +18,10 @@ extern "C" {
|
||||
//DEFINE INFO!!!
|
||||
//define EMU_SUPPORT_PALM_OS5 to compile in Tungsten C support(not reccomended for low power devices)
|
||||
//define EMU_MULTITHREADED to speed up long loops
|
||||
//define EMU_MANAGE_HOST_CPU_PIPELINE to optimize the CPU pipeline for the most common cases
|
||||
//define EMU_NO_SAFETY to remove all safety checks
|
||||
//define EMU_BIG_ENDIAN on big endian systems
|
||||
//define EMU_HAVE_FILE_LAUNCHER to enable launching files from the host system
|
||||
//to enable degguging define EMU_DEBUG, all options below do nothing unless EMU_DEBUG is defined
|
||||
//to enable sandbox debugging define EMU_SANDBOX
|
||||
//to enable memory access logging define EMU_SANDBOX_LOG_MEMORY_ACCESSES
|
||||
@@ -44,8 +46,8 @@ static void debugLog(char* str, ...){};
|
||||
|
||||
//config options
|
||||
#define EMU_FPS 60
|
||||
#define EMU_SYSCLK_PRECISION 2000000//the amount of cycles to run before adding SYSCLKs, higher = faster, higher values may skip timer events and lower audio accuracy
|
||||
#define EMU_CPU_PERCENT_WAITING 0.30//account for wait states when reading memory, tested with SysInfo.prc
|
||||
#define DBVZ_SYSCLK_PRECISION 2000000//the amount of cycles to run before adding SYSCLKs, higher = faster, higher values may skip timer events and lower audio accuracy
|
||||
#define DBVZ_CPU_PERCENT_WAITING 0.30//account for wait states when reading memory, tested with SysInfo.prc
|
||||
#define AUDIO_SAMPLE_RATE 48000
|
||||
#define AUDIO_CLOCK_RATE 235929600//smallest amount of time a second can be split into:(2.0 * (14.0 * (255 + 1.0) + 15 + 1.0)) * 32768 == 235929600, used to convert the variable timing of SYSCLK and CLK32 to a fixed location in the current frame 0<->AUDIO_END_OF_FRAME
|
||||
#define AUDIO_SPEAKER_RANGE 0x6000//prevent hitting the top or bottom of the speaker when switching direction rapidly
|
||||
@@ -53,10 +55,18 @@ static void debugLog(char* str, ...){};
|
||||
#define SD_CARD_BLOCK_DATA_PACKET_SIZE (1 + SD_CARD_BLOCK_SIZE + 2)
|
||||
#define SD_CARD_RESPONSE_FIFO_SIZE (SD_CARD_BLOCK_DATA_PACKET_SIZE * 3)
|
||||
#define SD_CARD_NCR_BYTES 1//how many 0xFF bytes come before the R1 response
|
||||
#define SAVE_STATE_VERSION 0
|
||||
#define SAVE_STATE_VERSION 0x00000001
|
||||
#if defined(EMU_SUPPORT_PALM_OS5)
|
||||
#define SAVE_STATE_FOR_TUNGSTEN_C 0x80000000
|
||||
#endif
|
||||
|
||||
|
||||
//system constants
|
||||
#define CRYSTAL_FREQUENCY 32768
|
||||
#if defined(EMU_SUPPORT_PALM_OS5)
|
||||
#define TUNGSTEN_C_CPU_CRYSTAL_FREQUENCY 3686400
|
||||
#define TUNGSTEN_C_RTC_CRYSTAL_FREQUENCY 32768
|
||||
#endif
|
||||
#define M515_CRYSTAL_FREQUENCY 32768
|
||||
#define AUDIO_SAMPLES_PER_FRAME (AUDIO_SAMPLE_RATE / EMU_FPS)
|
||||
#define AUDIO_END_OF_FRAME (AUDIO_CLOCK_RATE / EMU_FPS)
|
||||
|
||||
@@ -82,11 +92,6 @@ enum{
|
||||
};
|
||||
|
||||
//types
|
||||
typedef struct{
|
||||
uint8_t* data;
|
||||
uint32_t size;
|
||||
}buffer_t;
|
||||
|
||||
typedef struct{
|
||||
bool buttonUp;
|
||||
bool buttonDown;
|
||||
@@ -104,22 +109,31 @@ typedef struct{
|
||||
}input_t;
|
||||
|
||||
typedef struct{
|
||||
uint64_t command;
|
||||
uint8_t commandBitsRemaining;
|
||||
uint8_t runningCommand;
|
||||
uint32_t runningCommandVars[3];
|
||||
uint8_t runningCommandPacket[SD_CARD_BLOCK_DATA_PACKET_SIZE];
|
||||
uint8_t responseFifo[SD_CARD_RESPONSE_FIFO_SIZE];
|
||||
uint16_t responseReadPosition;
|
||||
int8_t responseReadPositionBit;
|
||||
uint16_t responseWritePosition;
|
||||
bool commandIsAcmd;
|
||||
bool allowInvalidCrc;
|
||||
bool chipSelect;
|
||||
bool receivingCommand;
|
||||
bool inIdleState;
|
||||
uint8_t csd[16];
|
||||
uint8_t cid[16];
|
||||
uint8_t scr[8];
|
||||
uint32_t ocr;
|
||||
bool writeProtectSwitch;
|
||||
buffer_t flashChip;
|
||||
}sd_card_info_t;
|
||||
|
||||
typedef struct{
|
||||
uint64_t command;
|
||||
uint8_t commandBitsRemaining;
|
||||
uint8_t runningCommand;
|
||||
uint32_t runningCommandVars[3];
|
||||
uint8_t runningCommandPacket[SD_CARD_BLOCK_DATA_PACKET_SIZE];
|
||||
uint8_t responseFifo[SD_CARD_RESPONSE_FIFO_SIZE];
|
||||
uint16_t responseReadPosition;
|
||||
int8_t responseReadPositionBit;
|
||||
uint16_t responseWritePosition;
|
||||
bool commandIsAcmd;
|
||||
bool allowInvalidCrc;
|
||||
bool chipSelect;
|
||||
bool receivingCommand;
|
||||
bool inIdleState;
|
||||
sd_card_info_t sdInfo;
|
||||
uint8_t* flashChipData;
|
||||
uint32_t flashChipSize;
|
||||
}sd_card_t;
|
||||
|
||||
typedef struct{
|
||||
@@ -142,11 +156,13 @@ typedef struct{
|
||||
}emu_reg_t;
|
||||
|
||||
//emulator data, some are GUI interface variables, some should be left alone
|
||||
extern uint8_t* palmRam;//access allowed to read save RAM without allocating a giant buffer, but endianness must be taken into account
|
||||
#if defined(EMU_SUPPORT_PALM_OS5)
|
||||
extern bool palmEmulatingTungstenC;//read allowed, but not advised
|
||||
#endif
|
||||
extern uint8_t* palmRom;//dont touch
|
||||
extern uint8_t* palmReg;//dont touch
|
||||
extern uint8_t* palmRam;//access allowed to read save RAM without allocating a giant buffer, but endianness must be taken into account
|
||||
extern input_t palmInput;//write allowed
|
||||
extern sd_card_t palmSdCard;//dont touch
|
||||
extern sd_card_t palmSdCard;//access allowed to read flash chip data without allocating a giant buffer
|
||||
extern misc_hw_t palmMisc;//read/write allowed
|
||||
extern emu_reg_t palmEmuFeatures;//dont touch
|
||||
extern uint16_t* palmFramebuffer;//read allowed
|
||||
@@ -158,19 +174,20 @@ extern double palmCycleCounter;//dont touch
|
||||
extern double palmClockMultiplier;//dont touch
|
||||
|
||||
//functions
|
||||
uint32_t emulatorInit(buffer_t palmRomDump, buffer_t palmBootDump, uint32_t enabledEmuFeatures);//calling any emulator functions before emulatorInit results in undefined behavior
|
||||
void emulatorExit(void);
|
||||
uint32_t emulatorInit(uint8_t* palmRomData, uint32_t palmRomSize, uint8_t* palmBootloaderData, uint32_t palmBootloaderSize, uint32_t enabledEmuFeatures);
|
||||
void emulatorDeinit(void);
|
||||
void emulatorHardReset(void);
|
||||
void emulatorSoftReset(void);
|
||||
void emulatorSetRtc(uint16_t days, uint8_t hours, uint8_t minutes, uint8_t seconds);
|
||||
uint32_t emulatorGetStateSize(void);
|
||||
bool emulatorSaveState(buffer_t buffer);//true = success
|
||||
bool emulatorLoadState(buffer_t buffer);//true = success
|
||||
bool emulatorSaveState(uint8_t* data, uint32_t size);//true = success
|
||||
bool emulatorLoadState(uint8_t* data, uint32_t size);//true = success
|
||||
uint32_t emulatorGetRamSize(void);
|
||||
bool emulatorSaveRam(buffer_t buffer);//true = success
|
||||
bool emulatorLoadRam(buffer_t buffer);//true = success
|
||||
buffer_t emulatorGetSdCardBuffer(void);//this is a direct pointer to the SD card data, do not free it
|
||||
uint32_t emulatorInsertSdCard(buffer_t image, bool writeProtectSwitch);//use (NULL, desired size) to create a new empty SD card
|
||||
bool emulatorSaveRam(uint8_t* data, uint32_t size);//true = success
|
||||
bool emulatorLoadRam(uint8_t* data, uint32_t size);//true = success
|
||||
uint32_t emulatorInsertSdCard(uint8_t* data, uint32_t size, sd_card_info_t* sdInfo);//use (NULL, desired size) to create a new empty SD card, pass NULL for sdInfo to use defaults
|
||||
uint32_t emulatorGetSdCardSize(void);
|
||||
uint32_t emulatorGetSdCardData(uint8_t* data, uint32_t size);
|
||||
void emulatorEjectSdCard(void);
|
||||
void emulatorRunFrame(void);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "portability.h"
|
||||
#include "specs/emuFeatureRegisterSpec.h"
|
||||
#include "m515Bus.h"
|
||||
#include "dbvzRegisters.h"
|
||||
#include "dbvz.h"
|
||||
#include "silkscreen.h"
|
||||
#include "sed1376.h"
|
||||
#include "flx68000.h"
|
||||
@@ -71,7 +71,7 @@ void expansionHardwareSetRegister(uint32_t address, uint32_t value){
|
||||
switch(value){
|
||||
case CMD_SET_CPU_SPEED:
|
||||
if(palmEmuFeatures.info & FEATURE_FAST_CPU)
|
||||
palmClockMultiplier = (double)palmEmuFeatures.value / 100.0 * (1.00 - EMU_CPU_PERCENT_WAITING);
|
||||
palmClockMultiplier = (double)palmEmuFeatures.value / 100.0 * (1.00 - DBVZ_CPU_PERCENT_WAITING);
|
||||
return;
|
||||
|
||||
case CMD_DEBUG_PRINT:
|
||||
|
||||
125
src/fileLauncher/fatFs/diskio.c
Normal file
125
src/fileLauncher/fatFs/diskio.c
Normal file
@@ -0,0 +1,125 @@
|
||||
#include "ff.h"
|
||||
#include "diskio.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "../../emulator.h"
|
||||
|
||||
|
||||
#define DEV_RAM 0
|
||||
#define PALM_SD_CARD_SECTOR_SIZE 512
|
||||
|
||||
|
||||
static bool cardInitalized = false;
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Get Drive Status */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DSTATUS disk_status (
|
||||
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
||||
)
|
||||
{
|
||||
if(pdrv == DEV_RAM)
|
||||
return cardInitalized ? 0x00 : STA_NOINIT;
|
||||
|
||||
return STA_NOINIT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Inidialize a Drive */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DSTATUS disk_initialize (
|
||||
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
||||
)
|
||||
{
|
||||
if(pdrv == DEV_RAM){
|
||||
memset(palmSdCard.flashChipData, 0x00, palmSdCard.flashChipSize);
|
||||
cardInitalized = true;
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
return STA_NOINIT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Read Sector(s) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DRESULT disk_read (
|
||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
||||
BYTE *buff, /* Data buffer to store read data */
|
||||
DWORD sector, /* Start sector in LBA */
|
||||
UINT count /* Number of sectors to read */
|
||||
)
|
||||
{
|
||||
if(pdrv == DEV_RAM){
|
||||
if(!(sector * PALM_SD_CARD_SECTOR_SIZE + PALM_SD_CARD_SECTOR_SIZE < palmSdCard.flashChipSize))
|
||||
return RES_ERROR;
|
||||
|
||||
memcpy(buff, palmSdCard.flashChipData + sector * PALM_SD_CARD_SECTOR_SIZE, count * PALM_SD_CARD_SECTOR_SIZE);
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Write Sector(s) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#if FF_FS_READONLY == 0
|
||||
|
||||
DRESULT disk_write (
|
||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
||||
const BYTE *buff, /* Data to be written */
|
||||
DWORD sector, /* Start sector in LBA */
|
||||
UINT count /* Number of sectors to write */
|
||||
)
|
||||
{
|
||||
if(pdrv == DEV_RAM){
|
||||
if(!(sector * PALM_SD_CARD_SECTOR_SIZE + PALM_SD_CARD_SECTOR_SIZE < palmSdCard.flashChipSize))
|
||||
return RES_ERROR;
|
||||
|
||||
memcpy(palmSdCard.flashChipData + sector * PALM_SD_CARD_SECTOR_SIZE, buff, count * PALM_SD_CARD_SECTOR_SIZE);
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Miscellaneous Functions */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DRESULT disk_ioctl (
|
||||
BYTE pdrv, /* Physical drive nmuber (0..) */
|
||||
BYTE cmd, /* Control code */
|
||||
void *buff /* Buffer to send/receive control data */
|
||||
)
|
||||
{
|
||||
if(pdrv == DEV_RAM){
|
||||
switch(cmd){
|
||||
case GET_BLOCK_SIZE:
|
||||
*((uint32_t*)buff) = PALM_SD_CARD_SECTOR_SIZE;
|
||||
return RES_OK;
|
||||
|
||||
default:
|
||||
return RES_PARERR;
|
||||
}
|
||||
}
|
||||
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
77
src/fileLauncher/fatFs/diskio.h
Normal file
77
src/fileLauncher/fatFs/diskio.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*-----------------------------------------------------------------------/
|
||||
/ Low level disk interface modlue include file (C)ChaN, 2014 /
|
||||
/-----------------------------------------------------------------------*/
|
||||
|
||||
#ifndef _DISKIO_DEFINED
|
||||
#define _DISKIO_DEFINED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Status of Disk Functions */
|
||||
typedef BYTE DSTATUS;
|
||||
|
||||
/* Results of Disk Functions */
|
||||
typedef enum {
|
||||
RES_OK = 0, /* 0: Successful */
|
||||
RES_ERROR, /* 1: R/W Error */
|
||||
RES_WRPRT, /* 2: Write Protected */
|
||||
RES_NOTRDY, /* 3: Not Ready */
|
||||
RES_PARERR /* 4: Invalid Parameter */
|
||||
} DRESULT;
|
||||
|
||||
|
||||
/*---------------------------------------*/
|
||||
/* Prototypes for disk control functions */
|
||||
|
||||
|
||||
DSTATUS disk_initialize (BYTE pdrv);
|
||||
DSTATUS disk_status (BYTE pdrv);
|
||||
DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
|
||||
DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
|
||||
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
|
||||
|
||||
|
||||
/* Disk Status Bits (DSTATUS) */
|
||||
|
||||
#define STA_NOINIT 0x01 /* Drive not initialized */
|
||||
#define STA_NODISK 0x02 /* No medium in the drive */
|
||||
#define STA_PROTECT 0x04 /* Write protected */
|
||||
|
||||
|
||||
/* Command code for disk_ioctrl fucntion */
|
||||
|
||||
/* Generic command (Used by FatFs) */
|
||||
#define CTRL_SYNC 0 /* Complete pending write process (needed at FF_FS_READONLY == 0) */
|
||||
#define GET_SECTOR_COUNT 1 /* Get media size (needed at FF_USE_MKFS == 1) */
|
||||
#define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */
|
||||
#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at FF_USE_MKFS == 1) */
|
||||
#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */
|
||||
|
||||
/* Generic command (Not used by FatFs) */
|
||||
#define CTRL_POWER 5 /* Get/Set power status */
|
||||
#define CTRL_LOCK 6 /* Lock/Unlock media removal */
|
||||
#define CTRL_EJECT 7 /* Eject media */
|
||||
#define CTRL_FORMAT 8 /* Create physical format on the media */
|
||||
|
||||
/* MMC/SDC specific ioctl command */
|
||||
#define MMC_GET_TYPE 10 /* Get card type */
|
||||
#define MMC_GET_CSD 11 /* Get CSD */
|
||||
#define MMC_GET_CID 12 /* Get CID */
|
||||
#define MMC_GET_OCR 13 /* Get OCR */
|
||||
#define MMC_GET_SDSTAT 14 /* Get SD status */
|
||||
#define ISDIO_READ 55 /* Read data form SD iSDIO register */
|
||||
#define ISDIO_WRITE 56 /* Write data to SD iSDIO register */
|
||||
#define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */
|
||||
|
||||
/* ATA/CF specific ioctl command */
|
||||
#define ATA_GET_REV 20 /* Get F/W revision */
|
||||
#define ATA_GET_MODEL 21 /* Get model name */
|
||||
#define ATA_GET_SN 22 /* Get serial number */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
6554
src/fileLauncher/fatFs/ff.c
Normal file
6554
src/fileLauncher/fatFs/ff.c
Normal file
File diff suppressed because it is too large
Load Diff
420
src/fileLauncher/fatFs/ff.h
Normal file
420
src/fileLauncher/fatFs/ff.h
Normal file
@@ -0,0 +1,420 @@
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ FatFs - Generic FAT Filesystem module R0.13c /
|
||||
/-----------------------------------------------------------------------------/
|
||||
/
|
||||
/ Copyright (C) 2018, ChaN, all right reserved.
|
||||
/
|
||||
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||
/ source and binary forms, with or without modification, are permitted provided
|
||||
/ that the following condition is met:
|
||||
|
||||
/ 1. Redistributions of source code must retain the above copyright notice,
|
||||
/ this condition and the following disclaimer.
|
||||
/
|
||||
/ This software is provided by the copyright holder and contributors "AS IS"
|
||||
/ and any warranties related to this software are DISCLAIMED.
|
||||
/ The copyright owner or contributors be NOT LIABLE for any damages caused
|
||||
/ by use of this software.
|
||||
/
|
||||
/----------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#ifndef FF_DEFINED
|
||||
#define FF_DEFINED 86604 /* Revision ID */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ffconf.h" /* FatFs configuration options */
|
||||
|
||||
#if FF_DEFINED != FFCONF_DEF
|
||||
#error Wrong configuration file (ffconf.h).
|
||||
#endif
|
||||
|
||||
|
||||
/* Integer types used for FatFs API */
|
||||
|
||||
#if 1
|
||||
|
||||
#define FF_INTDEF 2
|
||||
#include <stdint.h>
|
||||
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
|
||||
typedef uint8_t BYTE; /* char must be 8-bit */
|
||||
typedef uint16_t WORD; /* 16-bit unsigned integer */
|
||||
typedef uint16_t WCHAR; /* 16-bit unsigned integer */
|
||||
typedef uint32_t DWORD; /* 32-bit unsigned integer */
|
||||
typedef uint64_t QWORD; /* 64-bit unsigned integer */
|
||||
|
||||
#else
|
||||
|
||||
#if defined(_WIN32) /* Main development platform */
|
||||
#define FF_INTDEF 2
|
||||
#include <windows.h>
|
||||
typedef unsigned __int64 QWORD;
|
||||
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */
|
||||
#define FF_INTDEF 2
|
||||
#include <stdint.h>
|
||||
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
|
||||
typedef unsigned char BYTE; /* char must be 8-bit */
|
||||
typedef uint16_t WORD; /* 16-bit unsigned integer */
|
||||
typedef uint16_t WCHAR; /* 16-bit unsigned integer */
|
||||
typedef uint32_t DWORD; /* 32-bit unsigned integer */
|
||||
typedef uint64_t QWORD; /* 64-bit unsigned integer */
|
||||
#else /* Earlier than C99 */
|
||||
#define FF_INTDEF 1
|
||||
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
|
||||
typedef unsigned char BYTE; /* char must be 8-bit */
|
||||
typedef unsigned short WORD; /* 16-bit unsigned integer */
|
||||
typedef unsigned short WCHAR; /* 16-bit unsigned integer */
|
||||
typedef unsigned long DWORD; /* 32-bit unsigned integer */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* Definitions of volume management */
|
||||
|
||||
#if FF_MULTI_PARTITION /* Multiple partition configuration */
|
||||
typedef struct {
|
||||
BYTE pd; /* Physical drive number */
|
||||
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
|
||||
} PARTITION;
|
||||
extern PARTITION VolToPart[]; /* Volume - Partition resolution table */
|
||||
#endif
|
||||
|
||||
#if FF_STR_VOLUME_ID
|
||||
#ifndef FF_VOLUME_STRS
|
||||
extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Type of path name strings on FatFs API */
|
||||
|
||||
#ifndef _INC_TCHAR
|
||||
#define _INC_TCHAR
|
||||
|
||||
#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */
|
||||
typedef WCHAR TCHAR;
|
||||
#define _T(x) L ## x
|
||||
#define _TEXT(x) L ## x
|
||||
#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */
|
||||
typedef char TCHAR;
|
||||
#define _T(x) u8 ## x
|
||||
#define _TEXT(x) u8 ## x
|
||||
#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */
|
||||
typedef DWORD TCHAR;
|
||||
#define _T(x) U ## x
|
||||
#define _TEXT(x) U ## x
|
||||
#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3)
|
||||
#error Wrong FF_LFN_UNICODE setting
|
||||
#else /* ANSI/OEM code in SBCS/DBCS */
|
||||
typedef char TCHAR;
|
||||
#define _T(x) x
|
||||
#define _TEXT(x) x
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Type of file size variables */
|
||||
|
||||
#if FF_FS_EXFAT
|
||||
#if FF_INTDEF != 2
|
||||
#error exFAT feature wants C99 or later
|
||||
#endif
|
||||
typedef QWORD FSIZE_t;
|
||||
#else
|
||||
typedef DWORD FSIZE_t;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Filesystem object structure (FATFS) */
|
||||
|
||||
typedef struct {
|
||||
BYTE fs_type; /* Filesystem type (0:not mounted) */
|
||||
BYTE pdrv; /* Associated physical drive */
|
||||
BYTE n_fats; /* Number of FATs (1 or 2) */
|
||||
BYTE wflag; /* win[] flag (b0:dirty) */
|
||||
BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */
|
||||
WORD id; /* Volume mount ID */
|
||||
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
|
||||
WORD csize; /* Cluster size [sectors] */
|
||||
#if FF_MAX_SS != FF_MIN_SS
|
||||
WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */
|
||||
#endif
|
||||
#if FF_USE_LFN
|
||||
WCHAR* lfnbuf; /* LFN working buffer */
|
||||
#endif
|
||||
#if FF_FS_EXFAT
|
||||
BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */
|
||||
#endif
|
||||
#if FF_FS_REENTRANT
|
||||
FF_SYNC_t sobj; /* Identifier of sync object */
|
||||
#endif
|
||||
#if !FF_FS_READONLY
|
||||
DWORD last_clst; /* Last allocated cluster */
|
||||
DWORD free_clst; /* Number of free clusters */
|
||||
#endif
|
||||
#if FF_FS_RPATH
|
||||
DWORD cdir; /* Current directory start cluster (0:root) */
|
||||
#if FF_FS_EXFAT
|
||||
DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */
|
||||
DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */
|
||||
DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */
|
||||
#endif
|
||||
#endif
|
||||
DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
|
||||
DWORD fsize; /* Size of an FAT [sectors] */
|
||||
DWORD volbase; /* Volume base sector */
|
||||
DWORD fatbase; /* FAT base sector */
|
||||
DWORD dirbase; /* Root directory base sector/cluster */
|
||||
DWORD database; /* Data base sector */
|
||||
#if FF_FS_EXFAT
|
||||
DWORD bitbase; /* Allocation bitmap base sector */
|
||||
#endif
|
||||
DWORD winsect; /* Current sector appearing in the win[] */
|
||||
BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
|
||||
} FATFS;
|
||||
|
||||
|
||||
|
||||
/* Object ID and allocation information (FFOBJID) */
|
||||
|
||||
typedef struct {
|
||||
FATFS* fs; /* Pointer to the hosting volume of this object */
|
||||
WORD id; /* Hosting volume mount ID */
|
||||
BYTE attr; /* Object attribute */
|
||||
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */
|
||||
DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */
|
||||
FSIZE_t objsize; /* Object size (valid when sclust != 0) */
|
||||
#if FF_FS_EXFAT
|
||||
DWORD n_cont; /* Size of first fragment - 1 (valid when stat == 3) */
|
||||
DWORD n_frag; /* Size of last fragment needs to be written to FAT (valid when not zero) */
|
||||
DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */
|
||||
DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */
|
||||
DWORD c_ofs; /* Offset in the containing directory (valid when file object and sclust != 0) */
|
||||
#endif
|
||||
#if FF_FS_LOCK
|
||||
UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
|
||||
#endif
|
||||
} FFOBJID;
|
||||
|
||||
|
||||
|
||||
/* File object structure (FIL) */
|
||||
|
||||
typedef struct {
|
||||
FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */
|
||||
BYTE flag; /* File status flags */
|
||||
BYTE err; /* Abort flag (error code) */
|
||||
FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
|
||||
DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */
|
||||
DWORD sect; /* Sector number appearing in buf[] (0:invalid) */
|
||||
#if !FF_FS_READONLY
|
||||
DWORD dir_sect; /* Sector number containing the directory entry (not used at exFAT) */
|
||||
BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */
|
||||
#endif
|
||||
#if FF_USE_FASTSEEK
|
||||
DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */
|
||||
#endif
|
||||
#if !FF_FS_TINY
|
||||
BYTE buf[FF_MAX_SS]; /* File private data read/write window */
|
||||
#endif
|
||||
} FIL;
|
||||
|
||||
|
||||
|
||||
/* Directory object structure (DIR) */
|
||||
|
||||
typedef struct {
|
||||
FFOBJID obj; /* Object identifier */
|
||||
DWORD dptr; /* Current read/write offset */
|
||||
DWORD clust; /* Current cluster */
|
||||
DWORD sect; /* Current sector (0:Read operation has terminated) */
|
||||
BYTE* dir; /* Pointer to the directory item in the win[] */
|
||||
BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */
|
||||
#if FF_USE_LFN
|
||||
DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */
|
||||
#endif
|
||||
#if FF_USE_FIND
|
||||
const TCHAR* pat; /* Pointer to the name matching pattern */
|
||||
#endif
|
||||
} DIR;
|
||||
|
||||
|
||||
|
||||
/* File information structure (FILINFO) */
|
||||
|
||||
typedef struct {
|
||||
FSIZE_t fsize; /* File size */
|
||||
WORD fdate; /* Modified date */
|
||||
WORD ftime; /* Modified time */
|
||||
BYTE fattrib; /* File attribute */
|
||||
#if FF_USE_LFN
|
||||
TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */
|
||||
TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */
|
||||
#else
|
||||
TCHAR fname[12 + 1]; /* File name */
|
||||
#endif
|
||||
} FILINFO;
|
||||
|
||||
|
||||
|
||||
/* File function return code (FRESULT) */
|
||||
|
||||
typedef enum {
|
||||
FR_OK = 0, /* (0) Succeeded */
|
||||
FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
|
||||
FR_INT_ERR, /* (2) Assertion failed */
|
||||
FR_NOT_READY, /* (3) The physical drive cannot work */
|
||||
FR_NO_FILE, /* (4) Could not find the file */
|
||||
FR_NO_PATH, /* (5) Could not find the path */
|
||||
FR_INVALID_NAME, /* (6) The path name format is invalid */
|
||||
FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
|
||||
FR_EXIST, /* (8) Access denied due to prohibited access */
|
||||
FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
|
||||
FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
|
||||
FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
|
||||
FR_NOT_ENABLED, /* (12) The volume has no work area */
|
||||
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
|
||||
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */
|
||||
FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
|
||||
FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
|
||||
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
|
||||
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */
|
||||
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
|
||||
} FRESULT;
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* FatFs module application interface */
|
||||
|
||||
FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
|
||||
FRESULT f_close (FIL* fp); /* Close an open file object */
|
||||
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */
|
||||
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */
|
||||
FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */
|
||||
FRESULT f_truncate (FIL* fp); /* Truncate the file */
|
||||
FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */
|
||||
FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */
|
||||
FRESULT f_closedir (DIR* dp); /* Close an open directory */
|
||||
FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */
|
||||
FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */
|
||||
FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */
|
||||
FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */
|
||||
FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */
|
||||
FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */
|
||||
FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */
|
||||
FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */
|
||||
FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */
|
||||
FRESULT f_chdir (const TCHAR* path); /* Change current directory */
|
||||
FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
|
||||
FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */
|
||||
FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
|
||||
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
|
||||
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
|
||||
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
|
||||
FRESULT f_expand (FIL* fp, FSIZE_t szf, BYTE opt); /* Allocate a contiguous block to the file */
|
||||
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
|
||||
FRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */
|
||||
FRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */
|
||||
FRESULT f_setcp (WORD cp); /* Set current code page */
|
||||
int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */
|
||||
int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
|
||||
int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
|
||||
TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
|
||||
|
||||
#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
|
||||
#define f_error(fp) ((fp)->err)
|
||||
#define f_tell(fp) ((fp)->fptr)
|
||||
#define f_size(fp) ((fp)->obj.objsize)
|
||||
#define f_rewind(fp) f_lseek((fp), 0)
|
||||
#define f_rewinddir(dp) f_readdir((dp), 0)
|
||||
#define f_rmdir(path) f_unlink(path)
|
||||
#define f_unmount(path) f_mount(0, path, 0)
|
||||
|
||||
#ifndef EOF
|
||||
#define EOF (-1)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Additional user defined functions */
|
||||
|
||||
/* RTC function */
|
||||
#if !FF_FS_READONLY && !FF_FS_NORTC
|
||||
DWORD get_fattime (void);
|
||||
#endif
|
||||
|
||||
/* LFN support functions */
|
||||
#if FF_USE_LFN >= 1 /* Code conversion (defined in unicode.c) */
|
||||
WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */
|
||||
WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */
|
||||
DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */
|
||||
#endif
|
||||
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
|
||||
void* ff_memalloc (UINT msize); /* Allocate memory block */
|
||||
void ff_memfree (void* mblock); /* Free memory block */
|
||||
#endif
|
||||
|
||||
/* Sync functions */
|
||||
#if FF_FS_REENTRANT
|
||||
int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj); /* Create a sync object */
|
||||
int ff_req_grant (FF_SYNC_t sobj); /* Lock sync object */
|
||||
void ff_rel_grant (FF_SYNC_t sobj); /* Unlock sync object */
|
||||
int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Flags and offset address */
|
||||
|
||||
|
||||
/* File access mode and open method flags (3rd argument of f_open) */
|
||||
#define FA_READ 0x01
|
||||
#define FA_WRITE 0x02
|
||||
#define FA_OPEN_EXISTING 0x00
|
||||
#define FA_CREATE_NEW 0x04
|
||||
#define FA_CREATE_ALWAYS 0x08
|
||||
#define FA_OPEN_ALWAYS 0x10
|
||||
#define FA_OPEN_APPEND 0x30
|
||||
|
||||
/* Fast seek controls (2nd argument of f_lseek) */
|
||||
#define CREATE_LINKMAP ((FSIZE_t)0 - 1)
|
||||
|
||||
/* Format options (2nd argument of f_mkfs) */
|
||||
#define FM_FAT 0x01
|
||||
#define FM_FAT32 0x02
|
||||
#define FM_EXFAT 0x04
|
||||
#define FM_ANY 0x07
|
||||
#define FM_SFD 0x08
|
||||
|
||||
/* Filesystem type (FATFS.fs_type) */
|
||||
#define FS_FAT12 1
|
||||
#define FS_FAT16 2
|
||||
#define FS_FAT32 3
|
||||
#define FS_EXFAT 4
|
||||
|
||||
/* File attribute bits for directory entry (FILINFO.fattrib) */
|
||||
#define AM_RDO 0x01 /* Read only */
|
||||
#define AM_HID 0x02 /* Hidden */
|
||||
#define AM_SYS 0x04 /* System */
|
||||
#define AM_DIR 0x10 /* Directory */
|
||||
#define AM_ARC 0x20 /* Archive */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FF_DEFINED */
|
||||
288
src/fileLauncher/fatFs/ffconf.h
Normal file
288
src/fileLauncher/fatFs/ffconf.h
Normal file
@@ -0,0 +1,288 @@
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ FatFs Functional Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FFCONF_DEF 86604 /* Revision ID */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Function Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_FS_READONLY 0
|
||||
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
|
||||
/ Read-only configuration removes writing API functions, f_write(), f_sync(),
|
||||
/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
|
||||
/ and optional writing functions as well. */
|
||||
|
||||
|
||||
#define FF_FS_MINIMIZE 0
|
||||
/* This option defines minimization level to remove some basic API functions.
|
||||
/
|
||||
/ 0: Basic functions are fully enabled.
|
||||
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
|
||||
/ are removed.
|
||||
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
|
||||
/ 3: f_lseek() function is removed in addition to 2. */
|
||||
|
||||
|
||||
#define FF_USE_STRFUNC 0
|
||||
/* This option switches string functions, f_gets(), f_putc(), f_puts() and f_printf().
|
||||
/
|
||||
/ 0: Disable string functions.
|
||||
/ 1: Enable without LF-CRLF conversion.
|
||||
/ 2: Enable with LF-CRLF conversion. */
|
||||
|
||||
|
||||
#define FF_USE_FIND 0
|
||||
/* This option switches filtered directory read functions, f_findfirst() and
|
||||
/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
|
||||
|
||||
|
||||
#define FF_USE_MKFS 1
|
||||
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_FASTSEEK 0
|
||||
/* This option switches fast seek function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_EXPAND 0
|
||||
/* This option switches f_expand function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_CHMOD 0
|
||||
/* This option switches attribute manipulation functions, f_chmod() and f_utime().
|
||||
/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
|
||||
|
||||
|
||||
#define FF_USE_LABEL 0
|
||||
/* This option switches volume label functions, f_getlabel() and f_setlabel().
|
||||
/ (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_FORWARD 0
|
||||
/* This option switches f_forward() function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Locale and Namespace Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_CODE_PAGE 932
|
||||
/* This option specifies the OEM code page to be used on the target system.
|
||||
/ Incorrect code page setting can cause a file open failure.
|
||||
/
|
||||
/ 437 - U.S.
|
||||
/ 720 - Arabic
|
||||
/ 737 - Greek
|
||||
/ 771 - KBL
|
||||
/ 775 - Baltic
|
||||
/ 850 - Latin 1
|
||||
/ 852 - Latin 2
|
||||
/ 855 - Cyrillic
|
||||
/ 857 - Turkish
|
||||
/ 860 - Portuguese
|
||||
/ 861 - Icelandic
|
||||
/ 862 - Hebrew
|
||||
/ 863 - Canadian French
|
||||
/ 864 - Arabic
|
||||
/ 865 - Nordic
|
||||
/ 866 - Russian
|
||||
/ 869 - Greek 2
|
||||
/ 932 - Japanese (DBCS)
|
||||
/ 936 - Simplified Chinese (DBCS)
|
||||
/ 949 - Korean (DBCS)
|
||||
/ 950 - Traditional Chinese (DBCS)
|
||||
/ 0 - Include all code pages above and configured by f_setcp()
|
||||
*/
|
||||
|
||||
|
||||
#define FF_USE_LFN 3
|
||||
#define FF_MAX_LFN 255
|
||||
/* The FF_USE_LFN switches the support for LFN (long file name).
|
||||
/
|
||||
/ 0: Disable LFN. FF_MAX_LFN has no effect.
|
||||
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
|
||||
/ 2: Enable LFN with dynamic working buffer on the STACK.
|
||||
/ 3: Enable LFN with dynamic working buffer on the HEAP.
|
||||
/
|
||||
/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
|
||||
/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
|
||||
/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
|
||||
/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
|
||||
/ be in range of 12 to 255. It is recommended to be set 255 to fully support LFN
|
||||
/ specification.
|
||||
/ When use stack for the working buffer, take care on stack overflow. When use heap
|
||||
/ memory for the working buffer, memory management functions, ff_memalloc() and
|
||||
/ ff_memfree() in ffsystem.c, need to be added to the project. */
|
||||
|
||||
|
||||
#define FF_LFN_UNICODE 2
|
||||
/* This option switches the character encoding on the API when LFN is enabled.
|
||||
/
|
||||
/ 0: ANSI/OEM in current CP (TCHAR = char)
|
||||
/ 1: Unicode in UTF-16 (TCHAR = WCHAR)
|
||||
/ 2: Unicode in UTF-8 (TCHAR = char)
|
||||
/ 3: Unicode in UTF-32 (TCHAR = DWORD)
|
||||
/
|
||||
/ Also behavior of string I/O functions will be affected by this option.
|
||||
/ When LFN is not enabled, this option has no effect. */
|
||||
|
||||
|
||||
#define FF_LFN_BUF 255
|
||||
#define FF_SFN_BUF 12
|
||||
/* This set of options defines size of file name members in the FILINFO structure
|
||||
/ which is used to read out directory items. These values should be suffcient for
|
||||
/ the file names to read. The maximum possible length of the read file name depends
|
||||
/ on character encoding. When LFN is not enabled, these options have no effect. */
|
||||
|
||||
|
||||
#define FF_STRF_ENCODE 3
|
||||
/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(),
|
||||
/ f_putc(), f_puts and f_printf() convert the character encoding in it.
|
||||
/ This option selects assumption of character encoding ON THE FILE to be
|
||||
/ read/written via those functions.
|
||||
/
|
||||
/ 0: ANSI/OEM in current CP
|
||||
/ 1: Unicode in UTF-16LE
|
||||
/ 2: Unicode in UTF-16BE
|
||||
/ 3: Unicode in UTF-8
|
||||
*/
|
||||
|
||||
|
||||
#define FF_FS_RPATH 0
|
||||
/* This option configures support for relative path.
|
||||
/
|
||||
/ 0: Disable relative path and remove related functions.
|
||||
/ 1: Enable relative path. f_chdir() and f_chdrive() are available.
|
||||
/ 2: f_getcwd() function is available in addition to 1.
|
||||
*/
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Drive/Volume Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_VOLUMES 1
|
||||
/* Number of volumes (logical drives) to be used. (1-10) */
|
||||
|
||||
|
||||
#define FF_STR_VOLUME_ID 0
|
||||
#define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
|
||||
/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
|
||||
/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
|
||||
/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
|
||||
/ logical drives. Number of items must not be less than FF_VOLUMES. Valid
|
||||
/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
|
||||
/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
|
||||
/ not defined, a user defined volume string table needs to be defined as:
|
||||
/
|
||||
/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
|
||||
*/
|
||||
|
||||
|
||||
#define FF_MULTI_PARTITION 0
|
||||
/* This option switches support for multiple volumes on the physical drive.
|
||||
/ By default (0), each logical drive number is bound to the same physical drive
|
||||
/ number and only an FAT volume found on the physical drive will be mounted.
|
||||
/ When this function is enabled (1), each logical drive number can be bound to
|
||||
/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
|
||||
/ funciton will be available. */
|
||||
|
||||
|
||||
#define FF_MIN_SS 512
|
||||
#define FF_MAX_SS 512
|
||||
/* This set of options configures the range of sector size to be supported. (512,
|
||||
/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
|
||||
/ harddisk. But a larger value may be required for on-board flash memory and some
|
||||
/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
|
||||
/ for variable sector size mode and disk_ioctl() function needs to implement
|
||||
/ GET_SECTOR_SIZE command. */
|
||||
|
||||
|
||||
#define FF_USE_TRIM 0
|
||||
/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
|
||||
/ To enable Trim function, also CTRL_TRIM command should be implemented to the
|
||||
/ disk_ioctl() function. */
|
||||
|
||||
|
||||
#define FF_FS_NOFSINFO 0
|
||||
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
|
||||
/ option, and f_getfree() function at first time after volume mount will force
|
||||
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
|
||||
/
|
||||
/ bit0=0: Use free cluster count in the FSINFO if available.
|
||||
/ bit0=1: Do not trust free cluster count in the FSINFO.
|
||||
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
|
||||
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ System Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_FS_TINY 0
|
||||
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
|
||||
/ At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
|
||||
/ Instead of private sector buffer eliminated from the file object, common sector
|
||||
/ buffer in the filesystem object (FATFS) is used for the file data transfer. */
|
||||
|
||||
|
||||
#define FF_FS_EXFAT 0
|
||||
/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
|
||||
/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
|
||||
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
|
||||
|
||||
|
||||
#define FF_FS_NORTC 1
|
||||
#define FF_NORTC_MON 3
|
||||
#define FF_NORTC_MDAY 22
|
||||
#define FF_NORTC_YEAR 2008
|
||||
/* The option FF_FS_NORTC switches timestamp functiton. If the system does not have
|
||||
/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
|
||||
/ the timestamp function. Every object modified by FatFs will have a fixed timestamp
|
||||
/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
|
||||
/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
|
||||
/ added to the project to read current time form real-time clock. FF_NORTC_MON,
|
||||
/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
|
||||
/ These options have no effect at read-only configuration (FF_FS_READONLY = 1). */
|
||||
|
||||
|
||||
#define FF_FS_LOCK 0
|
||||
/* The option FF_FS_LOCK switches file lock function to control duplicated file open
|
||||
/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
|
||||
/ is 1.
|
||||
/
|
||||
/ 0: Disable file lock function. To avoid volume corruption, application program
|
||||
/ should avoid illegal open, remove and rename to the open objects.
|
||||
/ >0: Enable file lock function. The value defines how many files/sub-directories
|
||||
/ can be opened simultaneously under file lock control. Note that the file
|
||||
/ lock control is independent of re-entrancy. */
|
||||
|
||||
|
||||
/* #include <somertos.h> // O/S definitions */
|
||||
#define FF_FS_REENTRANT 0
|
||||
#define FF_FS_TIMEOUT 1000
|
||||
#define FF_SYNC_t HANDLE
|
||||
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
|
||||
/ module itself. Note that regardless of this option, file access to different
|
||||
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
|
||||
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
|
||||
/ to the same volume is under control of this function.
|
||||
/
|
||||
/ 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect.
|
||||
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
|
||||
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
|
||||
/ function, must be added to the project. Samples are available in
|
||||
/ option/syscall.c.
|
||||
/
|
||||
/ The FF_FS_TIMEOUT defines timeout period in unit of time tick.
|
||||
/ The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
|
||||
/ SemaphoreHandle_t and etc. A header file for O/S definitions needs to be
|
||||
/ included somewhere in the scope of ff.h. */
|
||||
|
||||
|
||||
|
||||
/*--- End of configuration options ---*/
|
||||
170
src/fileLauncher/fatFs/ffsystem.c
Normal file
170
src/fileLauncher/fatFs/ffsystem.c
Normal file
@@ -0,0 +1,170 @@
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Sample Code of OS Dependent Functions for FatFs */
|
||||
/* (C)ChaN, 2018 */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "ff.h"
|
||||
|
||||
|
||||
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Allocate a memory block */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */
|
||||
UINT msize /* Number of bytes to allocate */
|
||||
)
|
||||
{
|
||||
return malloc(msize); /* Allocate a new memory block with POSIX API */
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Free a memory block */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void ff_memfree (
|
||||
void* mblock /* Pointer to the memory block to free (nothing to do if null) */
|
||||
)
|
||||
{
|
||||
free(mblock); /* Free the memory block with POSIX API */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if FF_FS_REENTRANT /* Mutal exclusion */
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Create a Synchronization Object */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called in f_mount() function to create a new
|
||||
/ synchronization object for the volume, such as semaphore and mutex.
|
||||
/ When a 0 is returned, the f_mount() function fails with FR_INT_ERR.
|
||||
*/
|
||||
|
||||
//const osMutexDef_t Mutex[FF_VOLUMES]; /* Table of CMSIS-RTOS mutex */
|
||||
|
||||
|
||||
int ff_cre_syncobj ( /* 1:Function succeeded, 0:Could not create the sync object */
|
||||
BYTE vol, /* Corresponding volume (logical drive number) */
|
||||
FF_SYNC_t* sobj /* Pointer to return the created sync object */
|
||||
)
|
||||
{
|
||||
/* Win32 */
|
||||
*sobj = CreateMutex(NULL, FALSE, NULL);
|
||||
return (int)(*sobj != INVALID_HANDLE_VALUE);
|
||||
|
||||
/* uITRON */
|
||||
// T_CSEM csem = {TA_TPRI,1,1};
|
||||
// *sobj = acre_sem(&csem);
|
||||
// return (int)(*sobj > 0);
|
||||
|
||||
/* uC/OS-II */
|
||||
// OS_ERR err;
|
||||
// *sobj = OSMutexCreate(0, &err);
|
||||
// return (int)(err == OS_NO_ERR);
|
||||
|
||||
/* FreeRTOS */
|
||||
// *sobj = xSemaphoreCreateMutex();
|
||||
// return (int)(*sobj != NULL);
|
||||
|
||||
/* CMSIS-RTOS */
|
||||
// *sobj = osMutexCreate(&Mutex[vol]);
|
||||
// return (int)(*sobj != NULL);
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Delete a Synchronization Object */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called in f_mount() function to delete a synchronization
|
||||
/ object that created with ff_cre_syncobj() function. When a 0 is returned,
|
||||
/ the f_mount() function fails with FR_INT_ERR.
|
||||
*/
|
||||
|
||||
int ff_del_syncobj ( /* 1:Function succeeded, 0:Could not delete due to an error */
|
||||
FF_SYNC_t sobj /* Sync object tied to the logical drive to be deleted */
|
||||
)
|
||||
{
|
||||
/* Win32 */
|
||||
return (int)CloseHandle(sobj);
|
||||
|
||||
/* uITRON */
|
||||
// return (int)(del_sem(sobj) == E_OK);
|
||||
|
||||
/* uC/OS-II */
|
||||
// OS_ERR err;
|
||||
// OSMutexDel(sobj, OS_DEL_ALWAYS, &err);
|
||||
// return (int)(err == OS_NO_ERR);
|
||||
|
||||
/* FreeRTOS */
|
||||
// vSemaphoreDelete(sobj);
|
||||
// return 1;
|
||||
|
||||
/* CMSIS-RTOS */
|
||||
// return (int)(osMutexDelete(sobj) == osOK);
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Request Grant to Access the Volume */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called on entering file functions to lock the volume.
|
||||
/ When a 0 is returned, the file function fails with FR_TIMEOUT.
|
||||
*/
|
||||
|
||||
int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */
|
||||
FF_SYNC_t sobj /* Sync object to wait */
|
||||
)
|
||||
{
|
||||
/* Win32 */
|
||||
return (int)(WaitForSingleObject(sobj, FF_FS_TIMEOUT) == WAIT_OBJECT_0);
|
||||
|
||||
/* uITRON */
|
||||
// return (int)(wai_sem(sobj) == E_OK);
|
||||
|
||||
/* uC/OS-II */
|
||||
// OS_ERR err;
|
||||
// OSMutexPend(sobj, FF_FS_TIMEOUT, &err));
|
||||
// return (int)(err == OS_NO_ERR);
|
||||
|
||||
/* FreeRTOS */
|
||||
// return (int)(xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE);
|
||||
|
||||
/* CMSIS-RTOS */
|
||||
// return (int)(osMutexWait(sobj, FF_FS_TIMEOUT) == osOK);
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Release Grant to Access the Volume */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called on leaving file functions to unlock the volume.
|
||||
*/
|
||||
|
||||
void ff_rel_grant (
|
||||
FF_SYNC_t sobj /* Sync object to be signaled */
|
||||
)
|
||||
{
|
||||
/* Win32 */
|
||||
ReleaseMutex(sobj);
|
||||
|
||||
/* uITRON */
|
||||
// sig_sem(sobj);
|
||||
|
||||
/* uC/OS-II */
|
||||
// OSMutexPost(sobj);
|
||||
|
||||
/* FreeRTOS */
|
||||
// xSemaphoreGive(sobj);
|
||||
|
||||
/* CMSIS-RTOS */
|
||||
// osMutexRelease(sobj);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
15597
src/fileLauncher/fatFs/ffunicode.c
Normal file
15597
src/fileLauncher/fatFs/ffunicode.c
Normal file
File diff suppressed because it is too large
Load Diff
303
src/fileLauncher/launcher.c
Normal file
303
src/fileLauncher/launcher.c
Normal file
@@ -0,0 +1,303 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "../emulator.h"
|
||||
#include "../portability.h"
|
||||
#include "launcher.h"
|
||||
#include "fatFs/ff.h"
|
||||
|
||||
|
||||
bool launcherSaveSdCardImage;
|
||||
|
||||
|
||||
static void launcherInstallPassMeM515(void){
|
||||
//works like PassMe on a DS, boots from a different slot
|
||||
}
|
||||
|
||||
#if defined(EMU_SUPPORT_PALM_OS5)
|
||||
static void launcherInstallPassMeTungstenC(void){
|
||||
//works like PassMe on a DS, boots from a different slot
|
||||
}
|
||||
#endif
|
||||
|
||||
static const char* launcherGetFileExtension(uint8_t fileType){
|
||||
switch(fileType){
|
||||
case LAUNCHER_FILE_TYPE_PRC:
|
||||
return "PRC";
|
||||
|
||||
case LAUNCHER_FILE_TYPE_PDB:
|
||||
return "PDB";
|
||||
|
||||
case LAUNCHER_FILE_TYPE_PQA:
|
||||
return "PQA";
|
||||
|
||||
default:
|
||||
return "000";
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t launcherMakeBootRecord(const char* appName){
|
||||
//writes BOOT.TXT to the PASSME of the SD card, the application in the PassMe image will run the application with the ID listed in this file after everything is copyed over
|
||||
//type is always 'appl', creator is the 4 letter app name
|
||||
FIL record;
|
||||
FRESULT result;
|
||||
uint32_t written;
|
||||
|
||||
result = f_open(&record, "0/PASSME/BOOT.TXT", FA_WRITE | FA_CREATE_ALWAYS);
|
||||
if(result != FR_OK)
|
||||
return EMU_ERROR_UNKNOWN;
|
||||
|
||||
result = f_write(&record, appName, 4, &written);
|
||||
if(result != FR_OK || written != 4)
|
||||
return EMU_ERROR_UNKNOWN;
|
||||
|
||||
result = f_close(&record);
|
||||
if(result != FR_OK)
|
||||
return EMU_ERROR_UNKNOWN;
|
||||
|
||||
return EMU_ERROR_NONE;
|
||||
}
|
||||
|
||||
static uint32_t launcherCopyPrcPdbPqaToSdCard(launcher_file_t* file, uint32_t* fileId){
|
||||
FIL application;
|
||||
char name[FF_MAX_LFN];
|
||||
FRESULT result;
|
||||
uint32_t written;
|
||||
|
||||
sprintf(name, "0/PASSME/%d.%s", *fileId, launcherGetFileExtension(file->type));
|
||||
|
||||
result = f_open(&application, name, FA_WRITE | FA_CREATE_ALWAYS);
|
||||
if(result != FR_OK)
|
||||
return EMU_ERROR_UNKNOWN;
|
||||
|
||||
result = f_write(&application, file->fileData, file->fileSize, &written);
|
||||
if(result != FR_OK || written != file->fileSize)
|
||||
return EMU_ERROR_UNKNOWN;
|
||||
|
||||
result = f_close(&application);
|
||||
if(result != FR_OK)
|
||||
return EMU_ERROR_UNKNOWN;
|
||||
|
||||
//set boot record if this is the boot application
|
||||
if(file->boot){
|
||||
uint32_t error;
|
||||
|
||||
//cant read the name, not a real app
|
||||
if(file->fileSize < 0x40 + 4)
|
||||
return EMU_ERROR_INVALID_PARAMETER;
|
||||
|
||||
//the app name is at 0x40, dont know if this applys to pqa or not
|
||||
error = launcherMakeBootRecord(file->fileData + 0x40);
|
||||
if(error != EMU_ERROR_NONE)
|
||||
return error;
|
||||
}
|
||||
|
||||
(*fileId)++;
|
||||
return EMU_ERROR_NONE;
|
||||
}
|
||||
|
||||
static uint32_t launcherSetupSdCardImageFromApps(launcher_file_t* files, uint32_t fileCount){
|
||||
uint32_t fileId = 0;//first file is named 0.p**, next is 1.p**, and the name continues going up with the file count
|
||||
FRESULT result;
|
||||
uint32_t error;
|
||||
uint32_t index;
|
||||
|
||||
//setup filesystem
|
||||
result = f_mkfs("0", FM_FAT, 4096, NULL, 0);
|
||||
if(result != FR_OK)
|
||||
return EMU_ERROR_UNKNOWN;
|
||||
|
||||
//setup directory
|
||||
result = f_mkdir("0/PASSME");
|
||||
if(result != FR_OK)
|
||||
return EMU_ERROR_UNKNOWN;
|
||||
|
||||
for(index = 0; index < fileCount; index++){
|
||||
switch(files[index].type){
|
||||
case LAUNCHER_FILE_TYPE_PRC:
|
||||
case LAUNCHER_FILE_TYPE_PDB:
|
||||
case LAUNCHER_FILE_TYPE_PQA:
|
||||
error = launcherCopyPrcPdbPqaToSdCard(&files[index], &fileId);
|
||||
break;
|
||||
|
||||
case LAUNCHER_FILE_TYPE_NONE:
|
||||
case LAUNCHER_FILE_TYPE_IMG:
|
||||
default:
|
||||
error = EMU_ERROR_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
if(error != EMU_ERROR_NONE)
|
||||
return error;
|
||||
}
|
||||
|
||||
return EMU_ERROR_NONE;
|
||||
}
|
||||
|
||||
void launcherGetSdCardInfoFromInfoFile(launcher_file_t* file, sd_card_info_t* returnValue){
|
||||
//parse a small binary file with the extra SD card data
|
||||
uint32_t offset = 0;
|
||||
|
||||
//clear everything
|
||||
memset(returnValue, 0x00, sizeof(sd_card_info_t));
|
||||
|
||||
//csd
|
||||
if(file->infoSize < offset + 16)
|
||||
return;
|
||||
memcpy(returnValue->csd, file->infoData + offset, 16);
|
||||
offset += 16;
|
||||
|
||||
//cid
|
||||
if(file->infoSize < offset + 16)
|
||||
return;
|
||||
memcpy(returnValue->cid, file->infoData + offset, 16);
|
||||
offset += 16;
|
||||
|
||||
//scr
|
||||
if(file->infoSize < offset + 8)
|
||||
return;
|
||||
memcpy(returnValue->scr, file->infoData + offset, 8);
|
||||
offset += 8;
|
||||
|
||||
//ocr
|
||||
if(file->infoSize < offset + sizeof(uint32_t))
|
||||
return;
|
||||
returnValue->ocr = readStateValue32(file->infoData + offset);
|
||||
offset += sizeof(uint32_t);
|
||||
|
||||
//writeProtectSwitch
|
||||
if(file->infoSize < offset + sizeof(uint8_t))
|
||||
return;
|
||||
returnValue->writeProtectSwitch = readStateValue8(file->infoData + offset);
|
||||
offset += sizeof(uint8_t);
|
||||
|
||||
//this can keep growing with new values but all the current values are fixed!!
|
||||
}
|
||||
|
||||
uint32_t launcherLaunch(launcher_file_t* files, uint32_t fileCount, uint8_t* sramData, uint32_t sramSize, uint8_t* sdCardData, uint32_t sdCardSize){
|
||||
bool applicationFileHasBeenLoaded = false;
|
||||
bool cardImageHasBeenLoaded = false;
|
||||
bool bootFileExists = false;
|
||||
uint32_t bootFileNum;
|
||||
uint32_t totalSize = 0;
|
||||
bool success;
|
||||
uint32_t error;
|
||||
uint32_t index;
|
||||
|
||||
for(index = 0; index < fileCount; index++){
|
||||
//cant load anything else if a card image has been loaded
|
||||
if(cardImageHasBeenLoaded)
|
||||
return EMU_ERROR_INVALID_PARAMETER;
|
||||
|
||||
//cant load a card image if an app has been loaded already
|
||||
if(applicationFileHasBeenLoaded && files[index].type == LAUNCHER_FILE_TYPE_IMG)
|
||||
return EMU_ERROR_INVALID_PARAMETER;
|
||||
|
||||
if(files[index].type == LAUNCHER_FILE_TYPE_IMG)
|
||||
cardImageHasBeenLoaded = true;
|
||||
else
|
||||
applicationFileHasBeenLoaded = true;
|
||||
|
||||
//there must be exactly 1 boot file
|
||||
if(files[index].boot){
|
||||
if(!bootFileExists){
|
||||
bootFileExists = true;
|
||||
bootFileNum = index;
|
||||
}
|
||||
else{
|
||||
return EMU_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
totalSize += files[index].fileSize;
|
||||
}
|
||||
|
||||
if(!bootFileExists)
|
||||
return EMU_ERROR_INVALID_PARAMETER;
|
||||
|
||||
//in case the installed apps store anything on the SD card
|
||||
launcherSaveSdCardImage = true;
|
||||
|
||||
//in case the emulator is currently running with an SD card inserted
|
||||
emulatorEjectSdCard();
|
||||
|
||||
//make the card image or just copy it over
|
||||
if(cardImageHasBeenLoaded){
|
||||
if(files[bootFileNum].infoData){
|
||||
//with info file
|
||||
sd_card_info_t sdInfo;
|
||||
|
||||
//get SD info from file
|
||||
launcherGetSdCardInfoFromInfoFile(&files[bootFileNum], &sdInfo);
|
||||
|
||||
//load card image
|
||||
error = emulatorInsertSdCard(files[bootFileNum].fileData, files[bootFileNum].fileSize, &sdInfo);
|
||||
if(error != EMU_ERROR_NONE)
|
||||
return error;
|
||||
|
||||
//dont save read only card images
|
||||
if(sdInfo.writeProtectSwitch)
|
||||
launcherSaveSdCardImage = false;
|
||||
}
|
||||
else{
|
||||
//without info file
|
||||
error = emulatorInsertSdCard(files[bootFileNum].fileData, files[bootFileNum].fileSize, NULL);
|
||||
if(error != EMU_ERROR_NONE)
|
||||
return error;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(sdCardData){
|
||||
//use existing SD card image
|
||||
error = emulatorInsertSdCard(sdCardData, sdCardSize, NULL);
|
||||
if(error != EMU_ERROR_NONE)
|
||||
return error;
|
||||
}
|
||||
else{
|
||||
//load apps to new SD card image
|
||||
error = emulatorInsertSdCard(NULL, totalSize, NULL);
|
||||
if(error != EMU_ERROR_NONE)
|
||||
return error;
|
||||
|
||||
error = launcherSetupSdCardImageFromApps(files, fileCount);
|
||||
if(error != EMU_ERROR_NONE)
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
if(sramData){
|
||||
//use the existing SRAM file
|
||||
emulatorLoadRam(sramData, sramSize);
|
||||
}
|
||||
else{
|
||||
//install a pre specified RAM image for the current device(m515 or Tungsten C)needs to be decompressed into palmRam here
|
||||
#if defined(EMU_SUPPORT_PALM_OS5)
|
||||
if(palmEmulatingTungstenC)
|
||||
launcherInstallPassMeTungstenC();
|
||||
else
|
||||
#endif
|
||||
launcherInstallPassMeM515();
|
||||
}
|
||||
|
||||
emulatorSoftReset();
|
||||
|
||||
/*
|
||||
//execute frames until launch is completed(or failed with a time out)
|
||||
success = false;
|
||||
palmEmuFeatures.value = 'RUN0';
|
||||
for(index = 0; index < EMU_FPS * 10; index++){
|
||||
emulatorRunFrame();
|
||||
if(palmEmuFeatures.value == 'STOP'){
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//timed out
|
||||
if(!success)
|
||||
return EMU_ERROR_RESOURCE_LOCKED;
|
||||
*/
|
||||
|
||||
//worked
|
||||
return EMU_ERROR_NONE;
|
||||
}
|
||||
59
src/fileLauncher/launcher.h
Normal file
59
src/fileLauncher/launcher.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef LAUNCHER_H
|
||||
#define LAUNCHER_H
|
||||
//this is the only header a frontend needs to include from the launcher
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "../emulator.h"
|
||||
|
||||
enum{
|
||||
LAUNCHER_FILE_TYPE_NONE = 0,
|
||||
LAUNCHER_FILE_TYPE_PRC,
|
||||
LAUNCHER_FILE_TYPE_PDB,
|
||||
LAUNCHER_FILE_TYPE_PQA,
|
||||
LAUNCHER_FILE_TYPE_IMG
|
||||
};
|
||||
|
||||
typedef struct{
|
||||
uint8_t type;//file type
|
||||
bool boot;//if set will be the application that is launched
|
||||
uint8_t* fileData;
|
||||
uint32_t fileSize;
|
||||
uint8_t* infoData;//only used with LAUNCHER_FILE_TYPE_IMG right now
|
||||
uint32_t infoSize;
|
||||
}launcher_file_t;
|
||||
|
||||
extern bool launcherSaveSdCardImage;//false if loading a read only SD card image
|
||||
|
||||
/*
|
||||
the launcher is called after emulatorInit when its enabled
|
||||
the order is:
|
||||
launcher_file_t* files[...];
|
||||
uint32_t fileCount;
|
||||
uint8_t* saveData = NULL;
|
||||
uint32_t saveSize = 0;
|
||||
uint8_t* oldSdCardData = NULL;
|
||||
uint32_t oldSdCardSize = 0;
|
||||
uint32_t error;
|
||||
|
||||
error = emulatorInit(romFileData, romFileSize, bootloaderFileData(if m515), bootloaderFileSize(if m515), features | FEATURE_LAUNCH_APP);
|
||||
if(error)
|
||||
return error;
|
||||
error = launcherLaunch(files, fileCount, saveData, saveSize, oldSdCardData, oldSdCardSize);//this can fail building the SD card image
|
||||
if(error)
|
||||
return error;
|
||||
//its now safe to call emulatorFrame for frames
|
||||
*/
|
||||
//if first launch NULL should be passed for sramData and sdCardData
|
||||
uint32_t launcherLaunch(launcher_file_t* files, uint32_t fileCount, uint8_t* sramData, uint32_t sramSize, uint8_t* sdCardData, uint32_t sdCardSize);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
20
src/fileLauncher/readme.md
Normal file
20
src/fileLauncher/readme.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Automatic file launcher for Mu
|
||||
|
||||
## This module is completely optional!!!
|
||||
By default it is not compiled in with the emu, to build it in define EMU_HAVE_FILE_LAUNCHER.
|
||||
|
||||
This along with an internal program allows booting .prc, .pqa and .img files, .img files can be passed with an optional .info file to specify any attributes not stored in the flash memory.
|
||||
|
||||
There is also the option to load multiple files at the same time and specify the boot file.
|
||||
|
||||
The supported file types are:
|
||||
* .prc(bootable)
|
||||
* .pdb(not bootable)
|
||||
* .pqa(bootable)
|
||||
* .img(bootable)
|
||||
* .info + .img(bootable)
|
||||
|
||||
All file types can be mixed, except .img files.
|
||||
.img files can not be loaded with any other file types or other .img files.
|
||||
|
||||
NOTE: Some Palm apps would name prc files with the ".pdb" extension or pdb files with the ".prc" extension, for this reason they are being treated the same in the boot code, attempting to boot an actual pdb will still not work.
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "emulator.h"
|
||||
#include "portability.h"
|
||||
#include "dbvzRegisters.h"
|
||||
#include "dbvz.h"
|
||||
#include "m515Bus.h"
|
||||
#include "m68k/m68kcpu.h"
|
||||
|
||||
@@ -33,7 +33,7 @@ void flx68000PcLongJump(uint32_t newPc){
|
||||
|
||||
case DBVZ_CHIP_REGISTERS:
|
||||
//needed for when EMU_NO_SAFETY is set and a function is run in the sandbox
|
||||
dataBufferHost = (uintptr_t)palmReg;
|
||||
dataBufferHost = (uintptr_t)dbvzReg;
|
||||
dataBufferGuest = DBVZ_BANK_ADDRESS(DBVZ_START_BANK(newPc));
|
||||
windowSize = DBVZ_REG_SIZE;
|
||||
break;
|
||||
@@ -42,7 +42,7 @@ void flx68000PcLongJump(uint32_t newPc){
|
||||
memBase = dataBufferHost - dataBufferGuest - windowSize * ((newPc - dataBufferGuest) / windowSize);
|
||||
}
|
||||
|
||||
//everything must be 16 bit aligned(accept 8 bit accesses) due to 68k unaligned access rules,
|
||||
//everything must be 16 bit aligned(except 8 bit accesses) due to 68k unaligned access rules,
|
||||
//32 bit reads are 2 16 bit reads because on some platforms 32 bit reads that arnt on 32 bit boundrys will crash the program
|
||||
#if defined(EMU_BIG_ENDIAN)
|
||||
uint16_t m68k_read_immediate_16(uint32_t address){
|
||||
@@ -79,7 +79,7 @@ uint32_t m68k_read_pcrelative_32(uint32_t address){
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void flx68000Init(void){
|
||||
void flx68000Reset(void){
|
||||
static bool inited = false;
|
||||
|
||||
if(!inited){
|
||||
@@ -90,11 +90,7 @@ void flx68000Init(void){
|
||||
|
||||
inited = true;
|
||||
}
|
||||
}
|
||||
|
||||
void flx68000Reset(void){
|
||||
dbvzResetRegisters();
|
||||
dbvzResetAddressSpace();//address space must be reset after hardware registers because it is dependent on them
|
||||
m68k_pulse_reset();
|
||||
}
|
||||
|
||||
@@ -249,23 +245,8 @@ void flx68000LoadStateFinished(void){
|
||||
#endif
|
||||
}
|
||||
|
||||
void flx68000Execute(void){
|
||||
double cyclesRemaining = dbvzSysclksPerClk32;
|
||||
|
||||
dbvzBeginClk32();
|
||||
|
||||
while(cyclesRemaining >= 1.0){
|
||||
double sysclks = dMin(cyclesRemaining, EMU_SYSCLK_PRECISION);
|
||||
int32_t cpuCycles = sysclks * pctlrCpuClockDivider * palmClockMultiplier;
|
||||
|
||||
if(cpuCycles > 0)
|
||||
m68k_execute(cpuCycles);
|
||||
dbvzAddSysclks(sysclks);
|
||||
|
||||
cyclesRemaining -= sysclks;
|
||||
}
|
||||
|
||||
dbvzEndClk32();
|
||||
void flx68000Execute(int32_t cycles){
|
||||
m68k_execute(cycles);
|
||||
}
|
||||
|
||||
void flx68000SetIrq(uint8_t irqLevel){
|
||||
@@ -289,10 +270,6 @@ uint32_t flx68000GetRegister(uint8_t reg){
|
||||
return m68k_get_reg(NULL, reg);
|
||||
}
|
||||
|
||||
uint32_t flx68000GetPc(void){
|
||||
return m68k_get_reg(NULL, M68K_REG_PPC);
|
||||
}
|
||||
|
||||
uint64_t flx68000ReadArbitraryMemory(uint32_t address, uint8_t size){
|
||||
uint64_t data = UINT64_MAX;//invalid access
|
||||
|
||||
|
||||
@@ -4,20 +4,19 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
void flx68000Init(void);
|
||||
void flx68000Reset(void);
|
||||
uint32_t flx68000StateSize(void);
|
||||
void flx68000SaveState(uint8_t* data);
|
||||
void flx68000LoadState(uint8_t* data);
|
||||
void flx68000LoadStateFinished(void);
|
||||
|
||||
void flx68000Execute(void);//runs the CPU for 1 CLK32 pulse
|
||||
void flx68000Execute(int32_t cycles);
|
||||
void flx68000SetIrq(uint8_t irqLevel);
|
||||
bool flx68000IsSupervisor(void);
|
||||
void flx68000BusError(uint32_t address, bool isWrite);
|
||||
|
||||
uint32_t flx68000GetRegister(uint8_t reg);//only for debugging
|
||||
uint32_t flx68000GetPc(void);//only for debugging
|
||||
#define flx68000GetPc() flx68000GetRegister(29/*M68K_REG_PPC*/)//only for debugging
|
||||
uint64_t flx68000ReadArbitraryMemory(uint32_t address, uint8_t size);//only for debugging
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "emulator.h"
|
||||
#include "dbvzRegisters.h"
|
||||
#include "dbvz.h"
|
||||
#include "expansionHardware.h"
|
||||
#include "m515Bus.h"
|
||||
#include "portability.h"
|
||||
@@ -15,18 +15,18 @@
|
||||
uint8_t dbvzBankType[DBVZ_TOTAL_MEMORY_BANKS];
|
||||
|
||||
|
||||
//RAM accesses
|
||||
static uint8_t ramRead8(uint32_t address){return BUFFER_READ_8(palmRam, address, dbvzChipSelects[DBVZ_CHIP_DX_RAM].mask);}
|
||||
static uint16_t ramRead16(uint32_t address){return BUFFER_READ_16(palmRam, address, dbvzChipSelects[DBVZ_CHIP_DX_RAM].mask);}
|
||||
static uint32_t ramRead32(uint32_t address){return BUFFER_READ_32(palmRam, address, dbvzChipSelects[DBVZ_CHIP_DX_RAM].mask);}
|
||||
static void ramWrite8(uint32_t address, uint8_t value){BUFFER_WRITE_8(palmRam, address, dbvzChipSelects[DBVZ_CHIP_DX_RAM].mask, value);}
|
||||
static void ramWrite16(uint32_t address, uint16_t value){BUFFER_WRITE_16(palmRam, address, dbvzChipSelects[DBVZ_CHIP_DX_RAM].mask, value);}
|
||||
static void ramWrite32(uint32_t address, uint32_t value){BUFFER_WRITE_32(palmRam, address, dbvzChipSelects[DBVZ_CHIP_DX_RAM].mask, value);}
|
||||
|
||||
//ROM accesses
|
||||
static uint8_t romRead8(uint32_t address){return BUFFER_READ_8(palmRom, address, dbvzChipSelects[DBVZ_CHIP_A0_ROM].mask);}
|
||||
static uint16_t romRead16(uint32_t address){return BUFFER_READ_16(palmRom, address, dbvzChipSelects[DBVZ_CHIP_A0_ROM].mask);}
|
||||
static uint32_t romRead32(uint32_t address){return BUFFER_READ_32(palmRom, address, dbvzChipSelects[DBVZ_CHIP_A0_ROM].mask);}
|
||||
static uint8_t romRead8(uint32_t address){return M68K_BUFFER_READ_8(palmRom, address, dbvzChipSelects[DBVZ_CHIP_A0_ROM].mask);}
|
||||
static uint16_t romRead16(uint32_t address){return M68K_BUFFER_READ_16(palmRom, address, dbvzChipSelects[DBVZ_CHIP_A0_ROM].mask);}
|
||||
static uint32_t romRead32(uint32_t address){return M68K_BUFFER_READ_32(palmRom, address, dbvzChipSelects[DBVZ_CHIP_A0_ROM].mask);}
|
||||
|
||||
//RAM accesses
|
||||
static uint8_t ramRead8(uint32_t address){return M68K_BUFFER_READ_8(palmRam, address, dbvzChipSelects[DBVZ_CHIP_DX_RAM].mask);}
|
||||
static uint16_t ramRead16(uint32_t address){return M68K_BUFFER_READ_16(palmRam, address, dbvzChipSelects[DBVZ_CHIP_DX_RAM].mask);}
|
||||
static uint32_t ramRead32(uint32_t address){return M68K_BUFFER_READ_32(palmRam, address, dbvzChipSelects[DBVZ_CHIP_DX_RAM].mask);}
|
||||
static void ramWrite8(uint32_t address, uint8_t value){M68K_BUFFER_WRITE_8(palmRam, address, dbvzChipSelects[DBVZ_CHIP_DX_RAM].mask, value);}
|
||||
static void ramWrite16(uint32_t address, uint16_t value){M68K_BUFFER_WRITE_16(palmRam, address, dbvzChipSelects[DBVZ_CHIP_DX_RAM].mask, value);}
|
||||
static void ramWrite32(uint32_t address, uint32_t value){M68K_BUFFER_WRITE_32(palmRam, address, dbvzChipSelects[DBVZ_CHIP_DX_RAM].mask, value);}
|
||||
|
||||
//SED1376 accesses
|
||||
static uint8_t sed1376Read8(uint32_t address){
|
||||
@@ -35,7 +35,7 @@ static uint8_t sed1376Read8(uint32_t address){
|
||||
return 0x00;
|
||||
#endif
|
||||
if(address & SED1376_MR_BIT)
|
||||
return BUFFER_READ_8_BIG_ENDIAN(sed1376Ram, address, dbvzChipSelects[DBVZ_CHIP_B0_SED].mask);
|
||||
return M68K_BUFFER_READ_8_BIG_ENDIAN(sed1376Ram, address, dbvzChipSelects[DBVZ_CHIP_B0_SED].mask);
|
||||
else
|
||||
return sed1376GetRegister(address & dbvzChipSelects[DBVZ_CHIP_B0_SED].mask);
|
||||
}
|
||||
@@ -45,7 +45,7 @@ static uint16_t sed1376Read16(uint32_t address){
|
||||
return 0x0000;
|
||||
#endif
|
||||
if(address & SED1376_MR_BIT)
|
||||
return BUFFER_READ_16_BIG_ENDIAN(sed1376Ram, address, dbvzChipSelects[DBVZ_CHIP_B0_SED].mask);
|
||||
return M68K_BUFFER_READ_16_BIG_ENDIAN(sed1376Ram, address, dbvzChipSelects[DBVZ_CHIP_B0_SED].mask);
|
||||
else
|
||||
return sed1376GetRegister(address & dbvzChipSelects[DBVZ_CHIP_B0_SED].mask);
|
||||
}
|
||||
@@ -55,25 +55,25 @@ static uint32_t sed1376Read32(uint32_t address){
|
||||
return 0x00000000;
|
||||
#endif
|
||||
if(address & SED1376_MR_BIT)
|
||||
return BUFFER_READ_32_BIG_ENDIAN(sed1376Ram, address, dbvzChipSelects[DBVZ_CHIP_B0_SED].mask);
|
||||
return M68K_BUFFER_READ_32_BIG_ENDIAN(sed1376Ram, address, dbvzChipSelects[DBVZ_CHIP_B0_SED].mask);
|
||||
else
|
||||
return sed1376GetRegister(address & dbvzChipSelects[DBVZ_CHIP_B0_SED].mask);
|
||||
}
|
||||
static void sed1376Write8(uint32_t address, uint8_t value){
|
||||
if(address & SED1376_MR_BIT)
|
||||
BUFFER_WRITE_8_BIG_ENDIAN(sed1376Ram, address, dbvzChipSelects[DBVZ_CHIP_B0_SED].mask, value);
|
||||
M68K_BUFFER_WRITE_8_BIG_ENDIAN(sed1376Ram, address, dbvzChipSelects[DBVZ_CHIP_B0_SED].mask, value);
|
||||
else
|
||||
sed1376SetRegister(address & dbvzChipSelects[DBVZ_CHIP_B0_SED].mask, value);
|
||||
}
|
||||
static void sed1376Write16(uint32_t address, uint16_t value){
|
||||
if(address & SED1376_MR_BIT)
|
||||
BUFFER_WRITE_16_BIG_ENDIAN(sed1376Ram, address, dbvzChipSelects[DBVZ_CHIP_B0_SED].mask, value);
|
||||
M68K_BUFFER_WRITE_16_BIG_ENDIAN(sed1376Ram, address, dbvzChipSelects[DBVZ_CHIP_B0_SED].mask, value);
|
||||
else
|
||||
sed1376SetRegister(address & dbvzChipSelects[DBVZ_CHIP_B0_SED].mask, value);
|
||||
}
|
||||
static void sed1376Write32(uint32_t address, uint32_t value){
|
||||
if(address & SED1376_MR_BIT)
|
||||
BUFFER_WRITE_32_BIG_ENDIAN(sed1376Ram, address, dbvzChipSelects[DBVZ_CHIP_B0_SED].mask, value);
|
||||
M68K_BUFFER_WRITE_32_BIG_ENDIAN(sed1376Ram, address, dbvzChipSelects[DBVZ_CHIP_B0_SED].mask, value);
|
||||
else
|
||||
sed1376SetRegister(address & dbvzChipSelects[DBVZ_CHIP_B0_SED].mask, value);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#define DBVZ_END_BANK(address, size) (DBVZ_START_BANK(address) + DBVZ_NUM_BANKS(size) - 1)
|
||||
#define DBVZ_BANK_IN_RANGE(bank, address, size) ((bank) >= DBVZ_START_BANK(address) && (bank) <= DBVZ_END_BANK(address, size))
|
||||
#define DBVZ_BANK_ADDRESS(bank) ((bank) << DBVZ_BANK_SCOOT)
|
||||
#define DBVZ_TOTAL_MEMORY_BANKS (1 << (32 - DBVZ_BANK_SCOOT))//0x40000 banks for BANK_SCOOT = 14
|
||||
#define DBVZ_TOTAL_MEMORY_BANKS (1 << (32 - DBVZ_BANK_SCOOT))//0x40000 banks for *_BANK_SCOOT = 14
|
||||
|
||||
//chip addresses and sizes
|
||||
//after boot RAM is at 0x00000000,
|
||||
@@ -19,13 +19,45 @@
|
||||
//and the SED1376 is at 0x1FF80000(+ 0x20000 for framebuffer)
|
||||
#define DBVZ_EMUCS_START_ADDRESS 0xFFFC0000
|
||||
#define DBVZ_REG_START_ADDRESS 0xFFFFF000
|
||||
#define M515_RAM_SIZE (16 * 0x100000)//16mb RAM
|
||||
#define M515_ROM_SIZE (4 * 0x100000)//4mb ROM
|
||||
#define M515_RAM_SIZE (16 * 0x100000)//16mb RAM
|
||||
#define DBVZ_EMUCS_SIZE 0x20000
|
||||
#define DBVZ_REG_SIZE 0x1000//is actually 0xE00 without bootloader
|
||||
#define DBVZ_BOOTLOADER_SIZE 0x200
|
||||
#define SED1376_MR_BIT 0x20000
|
||||
|
||||
//buffers
|
||||
//the read/write stuff looks messy here but makes the memory access functions alot cleaner
|
||||
#if defined(EMU_BIG_ENDIAN)
|
||||
//memory layout is the same as the Palm m515, just cast to pointer and access, 32 bit accesses are split to prevent unaligned access issues
|
||||
#define M68K_BUFFER_READ_8(segment, accessAddress, mask) (*(uint8_t*)(segment + ((accessAddress) & (mask))))
|
||||
#define M68K_BUFFER_READ_16(segment, accessAddress, mask) (*(uint16_t*)(segment + ((accessAddress) & (mask))))
|
||||
#define M68K_BUFFER_READ_32(segment, accessAddress, mask) (*(uint16_t*)(segment + ((accessAddress) & (mask))) << 16 | *(uint16_t*)(segment + ((accessAddress) + 2 & (mask))))
|
||||
#define M68K_BUFFER_WRITE_8(segment, accessAddress, mask, value) (*(uint8_t*)(segment + ((accessAddress) & (mask))) = (value))
|
||||
#define M68K_BUFFER_WRITE_16(segment, accessAddress, mask, value) (*(uint16_t*)(segment + ((accessAddress) & (mask))) = (value))
|
||||
#define M68K_BUFFER_WRITE_32(segment, accessAddress, mask, value) (*(uint16_t*)(segment + ((accessAddress) & (mask))) = (value) >> 16 , *(uint16_t*)(segment + ((accessAddress) + 2 & (mask))) = (value) & 0xFFFF)
|
||||
#define M68K_BUFFER_READ_8_BIG_ENDIAN M68K_BUFFER_READ_8
|
||||
#define M68K_BUFFER_READ_16_BIG_ENDIAN M68K_BUFFER_READ_16
|
||||
#define M68K_BUFFER_READ_32_BIG_ENDIAN M68K_BUFFER_READ_32
|
||||
#define M68K_BUFFER_WRITE_8_BIG_ENDIAN M68K_BUFFER_WRITE_8
|
||||
#define M68K_BUFFER_WRITE_16_BIG_ENDIAN M68K_BUFFER_WRITE_16
|
||||
#define M68K_BUFFER_WRITE_32_BIG_ENDIAN M68K_BUFFER_WRITE_32
|
||||
#else
|
||||
//memory layout is different from the Palm m515, optimize for opcode fetches(16 bit reads)
|
||||
#define M68K_BUFFER_READ_8(segment, accessAddress, mask) (*(uint8_t*)(segment + ((accessAddress) & (mask) ^ 1)))
|
||||
#define M68K_BUFFER_READ_16(segment, accessAddress, mask) (*(uint16_t*)(segment + ((accessAddress) & (mask))))
|
||||
#define M68K_BUFFER_READ_32(segment, accessAddress, mask) (*(uint16_t*)(segment + ((accessAddress) & (mask))) << 16 | *(uint16_t*)(segment + ((accessAddress) + 2 & (mask))))
|
||||
#define M68K_BUFFER_WRITE_8(segment, accessAddress, mask, value) (*(uint8_t*)(segment + ((accessAddress) & (mask) ^ 1)) = (value))
|
||||
#define M68K_BUFFER_WRITE_16(segment, accessAddress, mask, value) (*(uint16_t*)(segment + ((accessAddress) & (mask))) = (value))
|
||||
#define M68K_BUFFER_WRITE_32(segment, accessAddress, mask, value) (*(uint16_t*)(segment + ((accessAddress) & (mask))) = (value) >> 16 , *(uint16_t*)(segment + ((accessAddress) + 2 & (mask))) = (value) & 0xFFFF)
|
||||
#define M68K_BUFFER_READ_8_BIG_ENDIAN(segment, accessAddress, mask) (segment[(accessAddress) & (mask)])
|
||||
#define M68K_BUFFER_READ_16_BIG_ENDIAN(segment, accessAddress, mask) (segment[(accessAddress) & (mask)] << 8 | segment[(accessAddress) + 1 & (mask)])
|
||||
#define M68K_BUFFER_READ_32_BIG_ENDIAN(segment, accessAddress, mask) (segment[(accessAddress) & (mask)] << 24 | segment[(accessAddress) + 1 & (mask)] << 16 | segment[(accessAddress) + 2 & (mask)] << 8 | segment[(accessAddress) + 3 & (mask)])
|
||||
#define M68K_BUFFER_WRITE_8_BIG_ENDIAN(segment, accessAddress, mask, value) (segment[(accessAddress) & (mask)] = (value))
|
||||
#define M68K_BUFFER_WRITE_16_BIG_ENDIAN(segment, accessAddress, mask, value) (segment[(accessAddress) & (mask)] = (value) >> 8, segment[(accessAddress) + 1 & (mask)] = (value) & 0xFF)
|
||||
#define M68K_BUFFER_WRITE_32_BIG_ENDIAN(segment, accessAddress, mask, value) (segment[(accessAddress) & (mask)] = (value) >> 24, segment[(accessAddress) + 1 & (mask)] = ((value) >> 16) & 0xFF, segment[(accessAddress) + 2 & (mask)] = ((value) >> 8) & 0xFF, segment[(accessAddress) + 3 & (mask)] = (value) & 0xFF)
|
||||
#endif
|
||||
|
||||
extern uint8_t dbvzBankType[];
|
||||
|
||||
void dbvzSetRegisterXXFFAccessMode(void);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
EMU_DEFINES :=
|
||||
EMU_SOURCES_C := $(EMU_PATH)/emulator.c \
|
||||
$(EMU_PATH)/m515Bus.c \
|
||||
$(EMU_PATH)/dbvzRegisters.c \
|
||||
$(EMU_PATH)/dbvz.c \
|
||||
$(EMU_PATH)/flx68000.c \
|
||||
$(EMU_PATH)/sed1376.c \
|
||||
$(EMU_PATH)/ads7846.c \
|
||||
@@ -17,8 +17,67 @@ EMU_SOURCES_C := $(EMU_PATH)/emulator.c \
|
||||
$(EMU_PATH)/m68k/m68kopac.c \
|
||||
$(EMU_PATH)/m68k/m68kdasm.c \
|
||||
$(EMU_PATH)/m68k/m68kcpu.c
|
||||
EMU_SOURCES_CXX :=
|
||||
EMU_SOURCES_ASM :=
|
||||
|
||||
ifeq ($(EMU_HAVE_FILE_LAUNCHER), 1)
|
||||
EMU_SOURCES_C += $(EMU_PATH)/fileLauncher/fatFs/diskio.c \
|
||||
$(EMU_PATH)/src/fileLauncher/fatFs/ff.c \
|
||||
$(EMU_PATH)/src/fileLauncher/fatFs/ffsystem.c \
|
||||
$(EMU_PATH)/src/fileLauncher/fatFs/ffunicode.c \
|
||||
$(EMU_PATH)/src/fileLauncher/launcher.c
|
||||
endif
|
||||
|
||||
ifeq ($(EMU_SUPPORT_PALM_OS5), 1)
|
||||
# ARM emulator uses C++11 so it must be supported and enabled if Palm OS 5 support is enabled
|
||||
EMU_DEFINES += -DEMU_SUPPORT_PALM_OS5
|
||||
EMU_DEFINES += -DSUPPORT_LINUX # forces the dynarec to use accurate mode and disable Nspire OS hacks
|
||||
EMU_SOURCES_C += $(EMU_PATH)/pxa255/pxa255_mem.c \
|
||||
$(EMU_PATH)/pxa255/pxa255_DMA.c \
|
||||
$(EMU_PATH)/pxa255/pxa255_DSP.c \
|
||||
$(EMU_PATH)/pxa255/pxa255_GPIO.c \
|
||||
$(EMU_PATH)/pxa255/pxa255_IC.c \
|
||||
$(EMU_PATH)/pxa255/pxa255_LCD.c \
|
||||
$(EMU_PATH)/pxa255/pxa255_PwrClk.c \
|
||||
$(EMU_PATH)/pxa255/pxa255_RTC.c \
|
||||
$(EMU_PATH)/pxa255/pxa255_TIMR.c \
|
||||
$(EMU_PATH)/pxa255/pxa255_UART.c \
|
||||
$(EMU_PATH)/pxa255/pxa255.c \
|
||||
$(EMU_PATH)/armv5te/emuVarPool.c \
|
||||
$(EMU_PATH)/armv5te/mem.c \
|
||||
$(EMU_PATH)/armv5te/mmu.c \
|
||||
$(EMU_PATH)/tungstenCBus.c
|
||||
EMU_SOURCES_CXX += $(EMU_PATH)/armv5te/arm_interpreter.cpp \
|
||||
$(EMU_PATH)/armv5te/thumb_interpreter.cpp \
|
||||
$(EMU_PATH)/armv5te/cpu.cpp \
|
||||
$(EMU_PATH)/armv5te/coproc.cpp
|
||||
|
||||
ifeq ($(EMU_OS), windows)
|
||||
EMU_SOURCES_C += $(EMU_PATH)/armv5te/os/os-win32.c
|
||||
else ifeq ($(EMU_OS), linux)
|
||||
EMU_SOURCES_C += $(EMU_PATH)/armv5te/os/os-linux.c
|
||||
endif
|
||||
|
||||
ifeq ($(EMU_NO_DYNAREC), 1)
|
||||
EMU_DEFINES += -DNO_TRANSLATION
|
||||
EMU_SOURCES_C += $(EMU_PATH)/armv5te/asmcode.c
|
||||
else
|
||||
ifeq ($(EMU_ARCH), x86_32)
|
||||
EMU_SOURCES_C += $(EMU_PATH)/armv5te/translate_x86.c
|
||||
EMU_SOURCES_ASM += $(EMU_PATH)/armv5te/asmcode_x86.S
|
||||
else ifeq ($(EMU_ARCH), x86_64)
|
||||
EMU_SOURCES_C += $(EMU_PATH)/armv5te/translate_x86_64.c \
|
||||
$(EMU_PATH)/armv5te/asmcode.c
|
||||
EMU_SOURCES_ASM += $(EMU_PATH)/armv5te/asmcode_x86_64.S
|
||||
else ifeq ($(EMU_ARCH), armv7)
|
||||
EMU_SOURCES_C += $(EMU_PATH)/armv5te/asmcode.c
|
||||
EMU_SOURCES_CXX += $(EMU_PATH)/armv5te/translate_arm.cpp
|
||||
EMU_SOURCES_ASM += $(EMU_PATH)/armv5te/asmcode_arm.S
|
||||
else ifeq ($(EMU_ARCH), armv8)
|
||||
EMU_SOURCES_C += $(EMU_PATH)/armv5te/asmcode.c
|
||||
EMU_SOURCES_CXX += $(EMU_PATH)/armv5te/translate_aarch64.cpp
|
||||
EMU_SOURCES_ASM += $(EMU_PATH)/armv5te/asmcode_aarch64.S
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(EMU_SUPPORT_PALM_OS5),1)
|
||||
EMU_DEFINES += -DEMU_SUPPORT_PALM_OS5
|
||||
# EMU_SOURCES_C +=
|
||||
endif
|
||||
@@ -4,38 +4,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
//endian/buffers
|
||||
//the read/write stuff looks messy here but makes the memory access functions alot cleaner
|
||||
#if defined(EMU_BIG_ENDIAN)
|
||||
//memory layout is the same as the Palm m515, just cast to pointer and access, 32 bit accesses are split to prevent unaligned access issues
|
||||
#define BUFFER_READ_8(segment, accessAddress, mask) (*(uint8_t*)(segment + ((accessAddress) & (mask))))
|
||||
#define BUFFER_READ_16(segment, accessAddress, mask) (*(uint16_t*)(segment + ((accessAddress) & (mask))))
|
||||
#define BUFFER_READ_32(segment, accessAddress, mask) (*(uint16_t*)(segment + ((accessAddress) & (mask))) << 16 | *(uint16_t*)(segment + ((accessAddress) + 2 & (mask))))
|
||||
#define BUFFER_WRITE_8(segment, accessAddress, mask, value) (*(uint8_t*)(segment + ((accessAddress) & (mask))) = (value))
|
||||
#define BUFFER_WRITE_16(segment, accessAddress, mask, value) (*(uint16_t*)(segment + ((accessAddress) & (mask))) = (value))
|
||||
#define BUFFER_WRITE_32(segment, accessAddress, mask, value) (*(uint16_t*)(segment + ((accessAddress) & (mask))) = (value) >> 16 , *(uint16_t*)(segment + ((accessAddress) + 2 & (mask))) = (value) & 0xFFFF)
|
||||
#define BUFFER_READ_8_BIG_ENDIAN BUFFER_READ_8
|
||||
#define BUFFER_READ_16_BIG_ENDIAN BUFFER_READ_16
|
||||
#define BUFFER_READ_32_BIG_ENDIAN BUFFER_READ_32
|
||||
#define BUFFER_WRITE_8_BIG_ENDIAN BUFFER_WRITE_8
|
||||
#define BUFFER_WRITE_16_BIG_ENDIAN BUFFER_WRITE_16
|
||||
#define BUFFER_WRITE_32_BIG_ENDIAN BUFFER_WRITE_32
|
||||
#else
|
||||
//memory layout is different from the Palm m515, optimize for opcode fetches(16 bit reads)
|
||||
#define BUFFER_READ_8(segment, accessAddress, mask) (*(uint8_t*)(segment + ((accessAddress) & (mask) ^ 1)))
|
||||
#define BUFFER_READ_16(segment, accessAddress, mask) (*(uint16_t*)(segment + ((accessAddress) & (mask))))
|
||||
#define BUFFER_READ_32(segment, accessAddress, mask) (*(uint16_t*)(segment + ((accessAddress) & (mask))) << 16 | *(uint16_t*)(segment + ((accessAddress) + 2 & (mask))))
|
||||
#define BUFFER_WRITE_8(segment, accessAddress, mask, value) (*(uint8_t*)(segment + ((accessAddress) & (mask) ^ 1)) = (value))
|
||||
#define BUFFER_WRITE_16(segment, accessAddress, mask, value) (*(uint16_t*)(segment + ((accessAddress) & (mask))) = (value))
|
||||
#define BUFFER_WRITE_32(segment, accessAddress, mask, value) (*(uint16_t*)(segment + ((accessAddress) & (mask))) = (value) >> 16 , *(uint16_t*)(segment + ((accessAddress) + 2 & (mask))) = (value) & 0xFFFF)
|
||||
#define BUFFER_READ_8_BIG_ENDIAN(segment, accessAddress, mask) (segment[(accessAddress) & (mask)])
|
||||
#define BUFFER_READ_16_BIG_ENDIAN(segment, accessAddress, mask) (segment[(accessAddress) & (mask)] << 8 | segment[(accessAddress) + 1 & (mask)])
|
||||
#define BUFFER_READ_32_BIG_ENDIAN(segment, accessAddress, mask) (segment[(accessAddress) & (mask)] << 24 | segment[(accessAddress) + 1 & (mask)] << 16 | segment[(accessAddress) + 2 & (mask)] << 8 | segment[(accessAddress) + 3 & (mask)])
|
||||
#define BUFFER_WRITE_8_BIG_ENDIAN(segment, accessAddress, mask, value) (segment[(accessAddress) & (mask)] = (value))
|
||||
#define BUFFER_WRITE_16_BIG_ENDIAN(segment, accessAddress, mask, value) (segment[(accessAddress) & (mask)] = (value) >> 8, segment[(accessAddress) + 1 & (mask)] = (value) & 0xFF)
|
||||
#define BUFFER_WRITE_32_BIG_ENDIAN(segment, accessAddress, mask, value) (segment[(accessAddress) & (mask)] = (value) >> 24, segment[(accessAddress) + 1 & (mask)] = ((value) >> 16) & 0xFF, segment[(accessAddress) + 2 & (mask)] = ((value) >> 8) & 0xFF, segment[(accessAddress) + 3 & (mask)] = (value) & 0xFF)
|
||||
#endif
|
||||
|
||||
//endian
|
||||
#define SWAP_16(x) ((uint16_t)((((uint16_t)(x) & 0x00FF) << 8) | (((uint16_t)(x) & 0xFF00) >> 8)))
|
||||
#define SWAP_32(x) ((uint32_t)((((uint32_t)(x) & 0x000000FF) << 24) | (((uint32_t)(x) & 0x0000FF00) << 8) | (((uint32_t)(x) & 0x00FF0000) >> 8) | (((uint32_t)(x) & 0xFF000000) >> 24)))
|
||||
#define SWAP_64(x) ((((uint64_t)(x) & UINT64_C(0x00000000000000FF)) << 56) | (((uint64_t)(x) & UINT64_C(0x000000000000FF00)) << 40) | (((uint64_t)(x) & UINT64_C(0x0000000000FF0000)) << 24) | (((uint64_t)(x) & UINT64_C(0x00000000FF000000)) << 8) | (((uint64_t)(x) & UINT64_C(0x000000FF00000000)) >> 8) | (((uint64_t)(x) & UINT64_C(0x0000FF0000000000)) >> 24) | (((uint64_t)(x) & UINT64_C(0x00FF000000000000)) >> 40) | (((uint64_t)(x) & UINT64_C(0xFF00000000000000)) >> 56))
|
||||
@@ -64,6 +33,33 @@ static inline void swap16BufferIfBig(uint8_t* buffer, uint32_t count){
|
||||
#endif
|
||||
}
|
||||
|
||||
//custom operators
|
||||
#define SIZEOF_BITS(value) (sizeof(value) * 8)
|
||||
|
||||
static inline uintmax_t fillBottomWith0s(uintmax_t value, uint8_t count){
|
||||
return value & UINTMAX_MAX << count;
|
||||
}
|
||||
|
||||
static inline uintmax_t fillTopWith0s(uintmax_t value, uint8_t count){
|
||||
return value & UINTMAX_MAX >> count;
|
||||
}
|
||||
|
||||
static inline uintmax_t fillBottomWith1s(uintmax_t value, uint8_t count){
|
||||
return value | (UINTMAX_MAX >> SIZEOF_BITS(uintmax_t) - count);
|
||||
}
|
||||
|
||||
static inline uintmax_t fillTopWith1s(uintmax_t value, uint8_t count){
|
||||
return value | (UINTMAX_MAX << SIZEOF_BITS(uintmax_t) - count);
|
||||
}
|
||||
|
||||
static inline uintmax_t leftShiftUse1s(uintmax_t value, uint8_t count){
|
||||
return fillBottomWith1s(value << count, count);
|
||||
}
|
||||
|
||||
static inline uintmax_t rightShiftUse1s(uintmax_t value, uint8_t count){
|
||||
return fillTopWith1s(value >> count, count);
|
||||
}
|
||||
|
||||
//threads
|
||||
#if defined(EMU_MULTITHREADED)
|
||||
#define PRAGMA_STRINGIFY(x) _Pragma(#x)
|
||||
@@ -74,135 +70,55 @@ static inline void swap16BufferIfBig(uint8_t* buffer, uint32_t count){
|
||||
#define MULTITHREAD_DOUBLE_LOOP(x, y)
|
||||
#endif
|
||||
|
||||
//pipeline
|
||||
#if defined(EMU_MANAGE_HOST_CPU_PIPELINE)
|
||||
#define unlikely(x) __builtin_expect(!!(x), false)
|
||||
#define likely(x) __builtin_expect(!!(x), true)
|
||||
#define likely_equal(x, y) __builtin_expect((x), (y))
|
||||
#else
|
||||
#define unlikely(x) x
|
||||
#define likely(x) x
|
||||
#define likely_equal(x, y) x
|
||||
#endif
|
||||
|
||||
//range capping
|
||||
static inline uint8_t u8Min(uint8_t x, uint8_t y){
|
||||
static inline uintmax_t uintMin(uintmax_t x, uintmax_t y){
|
||||
return x < y ? x : y;
|
||||
}
|
||||
|
||||
static inline uint8_t u8Max(uint8_t x, uint8_t y){
|
||||
static inline uintmax_t uintMax(uintmax_t x, uintmax_t y){
|
||||
return x > y ? x : y;
|
||||
}
|
||||
|
||||
static inline uint8_t u8Clamp(uint8_t low, uint8_t value, uint8_t high){
|
||||
static inline uintmax_t uintClamp(uintmax_t low, uintmax_t value, uintmax_t high){
|
||||
//low must always be less than high!
|
||||
return u8Max(low, u8Min(value, high));
|
||||
return uintMax(low, uintMin(value, high));
|
||||
}
|
||||
|
||||
static inline uint16_t u16Min(uint16_t x, uint16_t y){
|
||||
static inline intmax_t intMin(intmax_t x, intmax_t y){
|
||||
return x < y ? x : y;
|
||||
}
|
||||
|
||||
static inline uint16_t u16Max(uint16_t x, uint16_t y){
|
||||
static inline intmax_t intMax(intmax_t x, intmax_t y){
|
||||
return x > y ? x : y;
|
||||
}
|
||||
|
||||
static inline uint16_t u16Clamp(uint16_t low, uint16_t value, uint16_t high){
|
||||
static inline intmax_t intClamp(intmax_t low, intmax_t value, intmax_t high){
|
||||
//low must always be less than high!
|
||||
return u16Max(low, u16Min(value, high));
|
||||
return intMax(low, intMin(value, high));
|
||||
}
|
||||
|
||||
static inline uint32_t u32Min(uint32_t x, uint32_t y){
|
||||
static inline double floatMin(double x, double y){
|
||||
return x < y ? x : y;
|
||||
}
|
||||
|
||||
static inline uint32_t u32Max(uint32_t x, uint32_t y){
|
||||
static inline double floatMax(double x, double y){
|
||||
return x > y ? x : y;
|
||||
}
|
||||
|
||||
static inline uint32_t u32Clamp(uint32_t low, uint32_t value, uint32_t high){
|
||||
static inline double floatClamp(double low, double value, double high){
|
||||
//low must always be less than high!
|
||||
return u32Max(low, u32Min(value, high));
|
||||
}
|
||||
|
||||
static inline uint64_t u64Min(uint64_t x, uint64_t y){
|
||||
return x < y ? x : y;
|
||||
}
|
||||
|
||||
static inline uint64_t u64Max(uint64_t x, uint64_t y){
|
||||
return x > y ? x : y;
|
||||
}
|
||||
|
||||
static inline uint64_t u64Clamp(uint64_t low, uint64_t value, uint64_t high){
|
||||
//low must always be less than high!
|
||||
return u64Max(low, u64Min(value, high));
|
||||
}
|
||||
|
||||
static inline int8_t s8Min(int8_t x, int8_t y){
|
||||
return x < y ? x : y;
|
||||
}
|
||||
|
||||
static inline int8_t s8Max(int8_t x, int8_t y){
|
||||
return x > y ? x : y;
|
||||
}
|
||||
|
||||
static inline int8_t s8Clamp(int8_t low, int8_t value, int8_t high){
|
||||
//low must always be less than high!
|
||||
return s8Max(low, s8Min(value, high));
|
||||
}
|
||||
|
||||
static inline int16_t s16Min(int16_t x, int16_t y){
|
||||
return x < y ? x : y;
|
||||
}
|
||||
|
||||
static inline int16_t s16Max(int16_t x, int16_t y){
|
||||
return x > y ? x : y;
|
||||
}
|
||||
|
||||
static inline int16_t s16Clamp(int16_t low, int16_t value, int16_t high){
|
||||
//low must always be less than high!
|
||||
return s16Max(low, s16Min(value, high));
|
||||
}
|
||||
|
||||
static inline int32_t s32Min(int32_t x, int32_t y){
|
||||
return x < y ? x : y;
|
||||
}
|
||||
|
||||
static inline int32_t s32Max(int32_t x, int32_t y){
|
||||
return x > y ? x : y;
|
||||
}
|
||||
|
||||
static inline int32_t s32Clamp(int32_t low, int32_t value, int32_t high){
|
||||
//low must always be less than high!
|
||||
return s32Max(low, s32Min(value, high));
|
||||
}
|
||||
|
||||
static inline int64_t s64Min(int64_t x, int64_t y){
|
||||
return x < y ? x : y;
|
||||
}
|
||||
|
||||
static inline int64_t s64Max(int64_t x, int64_t y){
|
||||
return x > y ? x : y;
|
||||
}
|
||||
|
||||
static inline int64_t s64Clamp(int64_t low, int64_t value, int64_t high){
|
||||
//low must always be less than high!
|
||||
return s64Max(low, s64Min(value, high));
|
||||
}
|
||||
|
||||
static inline float fMin(float x, float y){
|
||||
return x < y ? x : y;
|
||||
}
|
||||
|
||||
static inline float fMax(float x, float y){
|
||||
return x > y ? x : y;
|
||||
}
|
||||
|
||||
static inline float fClamp(float low, float value, float high){
|
||||
//low must always be less than high!
|
||||
return fMax(low, fMin(value, high));
|
||||
}
|
||||
|
||||
static inline double dMin(double x, double y){
|
||||
return x < y ? x : y;
|
||||
}
|
||||
|
||||
static inline double dMax(double x, double y){
|
||||
return x > y ? x : y;
|
||||
}
|
||||
|
||||
static inline double dClamp(double low, double value, double high){
|
||||
//low must always be less than high!
|
||||
return dMax(low, dMin(value, high));
|
||||
return floatMax(low, floatMin(value, high));
|
||||
}
|
||||
|
||||
//float platform safety
|
||||
|
||||
246
src/pxa255/pxa255.c
Normal file
246
src/pxa255/pxa255.c
Normal file
@@ -0,0 +1,246 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "pxa255_DMA.h"
|
||||
#include "pxa255_DSP.h"
|
||||
#include "pxa255_GPIO.h"
|
||||
#include "pxa255_IC.h"
|
||||
#include "pxa255_LCD.h"
|
||||
#include "pxa255_PwrClk.h"
|
||||
#include "pxa255_RTC.h"
|
||||
#include "pxa255_TIMR.h"
|
||||
#include "pxa255_UART.h"
|
||||
#include "../armv5te/cpu.h"
|
||||
#include "../armv5te/emu.h"
|
||||
#include "../armv5te/mem.h"
|
||||
#include "../armv5te/os/os.h"
|
||||
#include "../armv5te/translate.h"
|
||||
#include "../tungstenCBus.h"
|
||||
#include "../emulator.h"
|
||||
|
||||
|
||||
#define PXA255_IO_BASE 0x40000000
|
||||
#define PXA255_MEMCTRL_BASE 0x48000000
|
||||
|
||||
#define PXA255_TIMER_TICKS_PER_FRAME (TUNGSTEN_C_CPU_CRYSTAL_FREQUENCY / EMU_FPS)
|
||||
|
||||
|
||||
uint16_t* pxa255Framebuffer;
|
||||
static Pxa255ic pxa255Ic;
|
||||
static Pxa255lcd pxa255Lcd;
|
||||
static Pxa255timr pxa255Timer;
|
||||
|
||||
|
||||
#include "pxa255Accessors.c.h"
|
||||
|
||||
bool pxa255Init(uint8_t** returnRom, uint8_t** returnRam){
|
||||
uint32_t mem_offset = 0;
|
||||
uint8_t i;
|
||||
|
||||
//enable dynarec if available
|
||||
do_translate = true;
|
||||
|
||||
mem_and_flags = os_reserve(MEM_MAXSIZE * 2);
|
||||
if(!mem_and_flags)
|
||||
return false;
|
||||
|
||||
addr_cache_init();
|
||||
memset(mem_areas, 0x00, sizeof(mem_areas));
|
||||
|
||||
//regions
|
||||
//ROM
|
||||
mem_areas[0].base = PXA255_ROM_START_ADDRESS;
|
||||
mem_areas[0].size = TUNGSTEN_C_ROM_SIZE;
|
||||
mem_areas[0].ptr = mem_and_flags + mem_offset;
|
||||
mem_offset += TUNGSTEN_C_ROM_SIZE;
|
||||
|
||||
//RAM
|
||||
mem_areas[1].base = PXA255_RAM_START_ADDRESS;
|
||||
mem_areas[1].size = TUNGSTEN_C_RAM_SIZE;
|
||||
mem_areas[1].ptr = mem_and_flags + mem_offset;
|
||||
mem_offset += TUNGSTEN_C_RAM_SIZE;
|
||||
|
||||
//memory regions that are not directly mapped to a buffer are not added to mem_areas
|
||||
//adding them will cause SIGSEGVs
|
||||
|
||||
//accessors
|
||||
//default
|
||||
for(i = 0; i < PXA255_TOTAL_MEMORY_BANKS; i++){
|
||||
// will fallback to bad_* on non-memory addresses
|
||||
read_byte_map[i] = memory_read_byte;
|
||||
read_half_map[i] = memory_read_half;
|
||||
read_word_map[i] = memory_read_word;
|
||||
write_byte_map[i] = memory_write_byte;
|
||||
write_half_map[i] = memory_write_half;
|
||||
write_word_map[i] = memory_write_word;
|
||||
}
|
||||
|
||||
//PCMCIA0
|
||||
for(i = PXA255_START_BANK(PXA255_PCMCIA0_START_ADDRESS); i <= PXA255_END_BANK(PXA255_PCMCIA0_START_ADDRESS, PXA255_PCMCIA0_SIZE); i++){
|
||||
read_byte_map[i] = pxa255_pcmcia0_read_byte;
|
||||
read_half_map[i] = pxa255_pcmcia0_read_half;
|
||||
read_word_map[i] = pxa255_pcmcia0_read_word;
|
||||
write_byte_map[i] = pxa255_pcmcia0_write_byte;
|
||||
write_half_map[i] = pxa255_pcmcia0_write_half;
|
||||
write_word_map[i] = pxa255_pcmcia0_write_word;
|
||||
}
|
||||
|
||||
//PCMCIA1
|
||||
for(i = PXA255_START_BANK(PXA255_PCMCIA1_START_ADDRESS); i <= PXA255_END_BANK(PXA255_PCMCIA1_START_ADDRESS, PXA255_PCMCIA1_SIZE); i++){
|
||||
read_byte_map[i] = pxa255_pcmcia1_read_byte;
|
||||
read_half_map[i] = pxa255_pcmcia1_read_half;
|
||||
read_word_map[i] = pxa255_pcmcia1_read_word;
|
||||
write_byte_map[i] = pxa255_pcmcia1_write_byte;
|
||||
write_half_map[i] = pxa255_pcmcia1_write_half;
|
||||
write_word_map[i] = pxa255_pcmcia1_write_word;
|
||||
}
|
||||
|
||||
//IO
|
||||
read_byte_map[PXA255_START_BANK(PXA255_IO_BASE)] = pxa255_io_read_byte;
|
||||
read_half_map[PXA255_START_BANK(PXA255_IO_BASE)] = bad_read_half;
|
||||
read_word_map[PXA255_START_BANK(PXA255_IO_BASE)] = pxa255_io_read_word;
|
||||
write_byte_map[PXA255_START_BANK(PXA255_IO_BASE)] = pxa255_io_write_byte;
|
||||
write_half_map[PXA255_START_BANK(PXA255_IO_BASE)] = bad_write_half;
|
||||
write_word_map[PXA255_START_BANK(PXA255_IO_BASE)] = pxa255_io_write_word;
|
||||
|
||||
//LCD
|
||||
read_byte_map[PXA255_START_BANK(PXA255_LCD_BASE)] = bad_read_byte;
|
||||
read_half_map[PXA255_START_BANK(PXA255_LCD_BASE)] = bad_read_half;
|
||||
read_word_map[PXA255_START_BANK(PXA255_LCD_BASE)] = pxa255_lcd_read_word;
|
||||
write_byte_map[PXA255_START_BANK(PXA255_LCD_BASE)] = bad_write_byte;
|
||||
write_half_map[PXA255_START_BANK(PXA255_LCD_BASE)] = bad_write_half;
|
||||
write_word_map[PXA255_START_BANK(PXA255_LCD_BASE)] = pxa255_lcd_write_word;
|
||||
|
||||
//MEMCTRL
|
||||
read_byte_map[PXA255_START_BANK(PXA255_MEMCTRL_BASE)] = bad_read_byte;
|
||||
read_half_map[PXA255_START_BANK(PXA255_MEMCTRL_BASE)] = bad_read_half;
|
||||
read_word_map[PXA255_START_BANK(PXA255_MEMCTRL_BASE)] = pxa255_memctrl_read_word;
|
||||
write_byte_map[PXA255_START_BANK(PXA255_MEMCTRL_BASE)] = bad_write_byte;
|
||||
write_half_map[PXA255_START_BANK(PXA255_MEMCTRL_BASE)] = bad_write_half;
|
||||
write_word_map[PXA255_START_BANK(PXA255_MEMCTRL_BASE)] = pxa255_memctrl_write_word;
|
||||
|
||||
//set up CPU hardware
|
||||
pxa255icInit(&pxa255Ic);
|
||||
pxa255lcdInit(&pxa255Lcd, &pxa255Ic);
|
||||
pxa255timrInit(&pxa255Timer, &pxa255Ic);
|
||||
|
||||
*returnRom = mem_areas[0].ptr;
|
||||
*returnRam = mem_areas[1].ptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void pxa255Deinit(void){
|
||||
if(mem_and_flags){
|
||||
// translation_table uses absolute addresses
|
||||
flush_translations();
|
||||
memset(mem_areas, 0, sizeof(mem_areas));
|
||||
os_free(mem_and_flags, MEM_MAXSIZE * 2);
|
||||
mem_and_flags = NULL;
|
||||
}
|
||||
|
||||
addr_cache_deinit();
|
||||
}
|
||||
|
||||
void pxa255Reset(void){
|
||||
/*
|
||||
static void emu_reset()
|
||||
{
|
||||
memset(mem_areas[1].ptr, 0, mem_areas[1].size);
|
||||
|
||||
memset(&arm, 0, sizeof arm);
|
||||
arm.control = 0x00050078;
|
||||
arm.cpsr_low28 = MODE_SVC | 0xC0;
|
||||
cpu_events &= EVENT_DEBUG_STEP;
|
||||
|
||||
sched_reset();
|
||||
sched.items[SCHED_THROTTLE].clock = CLOCK_27M;
|
||||
sched.items[SCHED_THROTTLE].proc = throttle_interval_event;
|
||||
|
||||
memory_reset();
|
||||
}
|
||||
*/
|
||||
|
||||
memset(&arm, 0, sizeof arm);
|
||||
arm.control = 0x00050078;
|
||||
arm.cpsr_low28 = MODE_SVC | 0xC0;
|
||||
cycle_count_delta = 0;
|
||||
cpu_events = 0;
|
||||
//cpu_events &= EVENT_DEBUG_STEP;
|
||||
|
||||
//PC starts at 0x00000000, the first opcode for Palm OS 5 is a jump
|
||||
}
|
||||
|
||||
void pxa255SetRtc(uint16_t days, uint8_t hours, uint8_t minutes, uint8_t seconds){
|
||||
//TODO: make this do something
|
||||
}
|
||||
|
||||
uint32_t pxa255StateSize(void){
|
||||
uint32_t size = 0;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void pxa255SaveState(uint8_t* data){
|
||||
uint32_t offset = 0;
|
||||
|
||||
}
|
||||
|
||||
void pxa255LoadState(uint8_t* data){
|
||||
uint32_t offset = 0;
|
||||
|
||||
}
|
||||
|
||||
void pxa255Execute(void){
|
||||
#if OS_HAS_PAGEFAULT_HANDLER
|
||||
os_exception_frame_t seh_frame = { NULL, NULL };
|
||||
|
||||
os_faulthandler_arm(&seh_frame);
|
||||
#endif
|
||||
|
||||
//TODO: need to set cycle_count_delta with the amount of opcodes to run
|
||||
cycle_count_delta = -500;//just a test value
|
||||
|
||||
// clang segfaults with that, for an iOS build :(
|
||||
#ifndef NO_SETJMP
|
||||
// Workaround for LLVM bug #18974
|
||||
while(__builtin_setjmp(restart_after_exception)){};
|
||||
#endif
|
||||
|
||||
exiting = false;//exiting is never set to true, maybe I should remove it?
|
||||
while (!exiting && cycle_count_delta < 0) {
|
||||
if (cpu_events & (EVENT_FIQ | EVENT_IRQ)) {
|
||||
// Align PC in case the interrupt occurred immediately after a jump
|
||||
if (arm.cpsr_low28 & 0x20)
|
||||
arm.reg[15] &= ~1;
|
||||
else
|
||||
arm.reg[15] &= ~3;
|
||||
|
||||
if (cpu_events & EVENT_WAITING)
|
||||
arm.reg[15] += 4; // Skip over wait instruction
|
||||
|
||||
arm.reg[15] += 4;
|
||||
cpu_exception((cpu_events & EVENT_FIQ) ? EX_FIQ : EX_IRQ);
|
||||
}
|
||||
cpu_events &= ~EVENT_WAITING;//this might need to be move above?
|
||||
|
||||
if (arm.cpsr_low28 & 0x20)
|
||||
cpu_thumb_loop();
|
||||
else
|
||||
cpu_arm_loop();
|
||||
}
|
||||
|
||||
#if OS_HAS_PAGEFAULT_HANDLER
|
||||
os_faulthandler_unarm(&seh_frame);
|
||||
#endif
|
||||
|
||||
//render
|
||||
pxa255lcdFrame(&pxa255Lcd);
|
||||
|
||||
//TODO: this needs to run at 3.6864 MHz
|
||||
//pxa255timrTick(&pxa255Timer);
|
||||
}
|
||||
|
||||
uint32_t pxa255GetRegister(uint8_t reg){
|
||||
return reg_pc(reg);
|
||||
}
|
||||
24
src/pxa255/pxa255.h
Normal file
24
src/pxa255/pxa255.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef PXA255_H
|
||||
#define PXA255_H
|
||||
|
||||
//this is the main module for the PXA255, with all the files it may be kind of confusing
|
||||
//this is the only file from this directory that the emu should interface with
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
uint16_t* pxa255Framebuffer;
|
||||
|
||||
bool pxa255Init(uint8_t** returnRom, uint8_t** returnRam);
|
||||
void pxa255Deinit(void);
|
||||
void pxa255Reset(void);
|
||||
void pxa255SetRtc(uint16_t days, uint8_t hours, uint8_t minutes, uint8_t seconds);
|
||||
uint32_t pxa255StateSize(void);
|
||||
void pxa255SaveState(uint8_t* data);
|
||||
void pxa255LoadState(uint8_t* data);
|
||||
|
||||
void pxa255Execute(void);//runs the CPU for 1 frame
|
||||
|
||||
uint32_t pxa255GetRegister(uint8_t reg);//only for debugging
|
||||
|
||||
#endif
|
||||
128
src/pxa255/pxa255Accessors.c.h
Normal file
128
src/pxa255/pxa255Accessors.c.h
Normal file
@@ -0,0 +1,128 @@
|
||||
static uint8_t pxa255_io_read_byte(uint32_t addr){
|
||||
//is only vaild for UART stuff
|
||||
}
|
||||
|
||||
static uint32_t pxa255_io_read_word(uint32_t addr){
|
||||
uint32_t out;
|
||||
|
||||
switch(addr >> 16){
|
||||
case PXA255_CLOCK_MANAGER_BASE >> 16:
|
||||
case PXA255_POWER_MANAGER_BASE >> 16:
|
||||
case PXA255_DMA_BASE >> 16:
|
||||
case PXA255_GPIO_BASE >> 16:
|
||||
case PXA255_IC_BASE >> 16:
|
||||
case PXA255_RTC_BASE >> 16:
|
||||
case PXA255_TIMR_BASE >> 16:
|
||||
case PXA255_FFUART_BASE >> 16:
|
||||
case PXA255_BTUART_BASE >> 16:
|
||||
case PXA255_STUART_BASE >> 16:
|
||||
//need to implement these
|
||||
debugLog("Unimplemented 32 bit PXA255 register read:0x%08X\n", addr);
|
||||
return 0x00000000;
|
||||
|
||||
default:
|
||||
debugLog("Invalid 32 bit PXA255 register read:0x%08X\n", addr);
|
||||
return 0x00000000;
|
||||
}
|
||||
}
|
||||
|
||||
static void pxa255_io_write_byte(uint32_t addr, uint8_t value){
|
||||
//is only vaild for UART stuff
|
||||
}
|
||||
|
||||
static void pxa255_io_write_word(uint32_t addr, uint32_t value){
|
||||
switch(addr >> 16){
|
||||
case PXA255_CLOCK_MANAGER_BASE >> 16:
|
||||
case PXA255_POWER_MANAGER_BASE >> 16:
|
||||
case PXA255_DMA_BASE >> 16:
|
||||
case PXA255_GPIO_BASE >> 16:
|
||||
case PXA255_IC_BASE >> 16:
|
||||
case PXA255_RTC_BASE >> 16:
|
||||
case PXA255_TIMR_BASE >> 16:
|
||||
case PXA255_FFUART_BASE >> 16:
|
||||
case PXA255_BTUART_BASE >> 16:
|
||||
case PXA255_STUART_BASE >> 16:
|
||||
//need to implement these
|
||||
debugLog("Unimplemented 32 bit PXA255 register write:0x%08X, value:0x%08X\n", addr, value);
|
||||
return;
|
||||
|
||||
default:
|
||||
debugLog("Invalid 32 bit PXA255 register write:0x%08X, value:0x%08X\n", addr, value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t pxa255_lcd_read_word(uint32_t addr){
|
||||
uint32_t out;
|
||||
pxa255lcdPrvMemAccessF(&pxa255Lcd, addr, 4, false, &out);
|
||||
debugLog("32 bit PXA255 LCD register read:0x%08X\n", addr);
|
||||
return out;
|
||||
}
|
||||
|
||||
static void pxa255_lcd_write_word(uint32_t addr, uint32_t value){
|
||||
pxa255lcdPrvMemAccessF(&pxa255Lcd, addr, 4, true, &value);
|
||||
debugLog("32 bit PXA255 LCD register write:0x%08X, value:0x%08X\n", addr, value);
|
||||
}
|
||||
|
||||
static uint32_t pxa255_memctrl_read_word(uint32_t addr){
|
||||
debugLog("32 bit PXA255 MEMCTRL register read:0x%08X\n", addr);
|
||||
return 0x00000000;
|
||||
}
|
||||
|
||||
static void pxa255_memctrl_write_word(uint32_t addr, uint32_t value){
|
||||
debugLog("32 bit PXA255 MEMCTRL register write:0x%08X, value:0x%08X\n", addr, value);
|
||||
}
|
||||
|
||||
static uint8_t pxa255_pcmcia0_read_byte(uint32_t addr){
|
||||
debugLog("PCMCIA0 8 bit read:0x%08X\n", addr);
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
static uint16_t pxa255_pcmcia0_read_half(uint32_t addr){
|
||||
debugLog("PCMCIA0 16 bit read:0x%08X\n", addr);
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
static uint32_t pxa255_pcmcia0_read_word(uint32_t addr){
|
||||
debugLog("PCMCIA0 32 bit read:0x%08X\n", addr);
|
||||
return 0x00000000;
|
||||
}
|
||||
|
||||
static void pxa255_pcmcia0_write_byte(uint32_t addr, uint8_t value){
|
||||
debugLog("PCMCIA0 8 bit write:0x%08X, value:0x%02X\n", addr, value);
|
||||
}
|
||||
|
||||
static void pxa255_pcmcia0_write_half(uint32_t addr, uint16_t value){
|
||||
debugLog("PCMCIA0 16 bit write:0x%08X, value:0x%04X\n", addr, value);
|
||||
}
|
||||
|
||||
static void pxa255_pcmcia0_write_word(uint32_t addr, uint32_t value){
|
||||
debugLog("PCMCIA0 32 bit write:0x%08X, value:0x%08X\n", addr, value);
|
||||
}
|
||||
|
||||
static uint8_t pxa255_pcmcia1_read_byte(uint32_t addr){
|
||||
debugLog("PCMCIA1 8 bit read:0x%08X\n", addr);
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
static uint16_t pxa255_pcmcia1_read_half(uint32_t addr){
|
||||
debugLog("PCMCIA1 16 bit read:0x%08X\n", addr);
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
static uint32_t pxa255_pcmcia1_read_word(uint32_t addr){
|
||||
debugLog("PCMCIA1 32 bit read:0x%08X\n", addr);
|
||||
return 0x00000000;
|
||||
}
|
||||
|
||||
static void pxa255_pcmcia1_write_byte(uint32_t addr, uint8_t value){
|
||||
debugLog("PCMCIA1 8 bit write:0x%08X, value:0x%02X\n", addr, value);
|
||||
}
|
||||
|
||||
static void pxa255_pcmcia1_write_half(uint32_t addr, uint16_t value){
|
||||
debugLog("PCMCIA1 16 bit write:0x%08X, value:0x%04X\n", addr, value);
|
||||
}
|
||||
|
||||
static void pxa255_pcmcia1_write_word(uint32_t addr, uint32_t value){
|
||||
debugLog("PCMCIA1 32 bit write:0x%08X, value:0x%08X\n", addr, value);
|
||||
}
|
||||
100
src/pxa255/pxa255_CPU.h
Normal file
100
src/pxa255/pxa255_CPU.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#ifndef _CPU_H_
|
||||
#define _CPU_H_
|
||||
|
||||
#include "pxa255_types.h"
|
||||
#include "../armv5te/cpu.h"
|
||||
|
||||
struct ArmCpu;
|
||||
|
||||
typedef Boolean (*ArmCoprocRegXferF) (struct ArmCpu* cpu, void* userData, Boolean two/* MCR2/MRC2 ? */, Boolean MRC, UInt8 op1, UInt8 Rx, UInt8 CRn, UInt8 CRm, UInt8 op2);
|
||||
typedef Boolean (*ArmCoprocDatProcF) (struct ArmCpu* cpu, void* userData, Boolean two/* CDP2 ? */, UInt8 op1, UInt8 CRd, UInt8 CRn, UInt8 CRm, UInt8 op2);
|
||||
typedef Boolean (*ArmCoprocMemAccsF) (struct ArmCpu* cpu, void* userData, Boolean two /* LDC2/STC2 ? */, Boolean N, Boolean store, UInt8 CRd, UInt32 addr, UInt8* option /* NULL if none */);
|
||||
typedef Boolean (*ArmCoprocTwoRegF) (struct ArmCpu* cpu, void* userData, Boolean MRRC, UInt8 op, UInt8 Rd, UInt8 Rn, UInt8 CRm);
|
||||
|
||||
typedef Boolean (*ArmCpuMemF) (struct ArmCpu* cpu, void* buf, UInt32 vaddr, UInt8 size, Boolean write, Boolean priviledged, UInt8* fsr); //read/write
|
||||
typedef Boolean (*ArmCpuHypercall) (struct ArmCpu* cpu); //return true if handled
|
||||
typedef void (*ArmCpuEmulErr) (struct ArmCpu* cpu, const char* err_str);
|
||||
|
||||
typedef void (*ArmSetFaultAdrF) (struct ArmCpu* cpu, UInt32 adr, UInt8 faultStatus);
|
||||
|
||||
/*
|
||||
|
||||
coprocessors:
|
||||
|
||||
0 - DSP (pxa only)
|
||||
0, 1 - WMMX (pxa only)
|
||||
11 - VFP (arm standard)
|
||||
15 - system control (arm standard)
|
||||
*/
|
||||
|
||||
|
||||
typedef struct{
|
||||
|
||||
ArmCoprocRegXferF regXfer;
|
||||
ArmCoprocDatProcF dataProcessing;
|
||||
ArmCoprocMemAccsF memAccess;
|
||||
ArmCoprocTwoRegF twoRegF;
|
||||
void* userData;
|
||||
|
||||
}ArmCoprocessor;
|
||||
|
||||
typedef struct{
|
||||
|
||||
UInt32 R13, R14;
|
||||
UInt32 SPSR; //usr mode doesn't have an SPSR
|
||||
}ArmBankedRegs;
|
||||
|
||||
|
||||
typedef struct ArmCpu{
|
||||
|
||||
UInt32 regs[16]; //current active regs as per current mode
|
||||
UInt32 CPSR, SPSR;
|
||||
|
||||
ArmBankedRegs bank_usr; //usr regs when in another mode
|
||||
ArmBankedRegs bank_svc; //svc regs when in another mode
|
||||
ArmBankedRegs bank_abt; //abt regs when in another mode
|
||||
ArmBankedRegs bank_und; //und regs when in another mode
|
||||
ArmBankedRegs bank_irq; //irq regs when in another mode
|
||||
ArmBankedRegs bank_fiq; //fiq regs when in another mode
|
||||
UInt32 extra_regs[5]; //fiq regs when not in fiq mode, usr regs when in fiq mode. R8-12
|
||||
|
||||
UInt16 waitingIrqs;
|
||||
UInt16 waitingFiqs;
|
||||
UInt16 CPAR;
|
||||
|
||||
ArmCoprocessor coproc[16]; //coprocessors
|
||||
|
||||
// various other cpu config options
|
||||
UInt32 vectorBase; //address of vector base
|
||||
|
||||
ArmCpuMemF memF;
|
||||
ArmCpuEmulErr emulErrF;
|
||||
ArmCpuHypercall hypercallF;
|
||||
ArmSetFaultAdrF setFaultAdrF;
|
||||
|
||||
void* userData; //shared by all callbacks
|
||||
}ArmCpu;
|
||||
|
||||
//UInt32 cpuGetRegExternal(ArmCpu* cpu, UInt8 reg);
|
||||
//void cpuSetReg(ArmCpu* cpu, UInt8 reg, UInt32 val);
|
||||
#define cpuGetRegExternal(x, regNum) reg(regNum)
|
||||
#define cpuSetReg(x, regNum, value) set_reg(regNum, value)
|
||||
|
||||
//void cpuIrq(ArmCpu* cpu, Boolean fiq, Boolean raise); //unraise when acknowledged
|
||||
/*
|
||||
static inline void cpuIrq(ArmCpu* cpu, Boolean fiq, Boolean raise){
|
||||
if(raise)
|
||||
cpu_exception(fiq ? EX_FIQ : EX_IRQ);
|
||||
//TODO: may need to fix not doing anything when cleared
|
||||
}
|
||||
*/
|
||||
#define cpuIrq(x, fiq, raise) if(raise)cpu_exception(fiq ? EX_FIQ : EX_IRQ)
|
||||
|
||||
//void cpuCoprocessorRegister(ArmCpu* cpu, UInt8 cpNum, ArmCoprocessor* coproc);
|
||||
//void cpuCoprocessorUnregister(ArmCpu* cpu, UInt8 cpNum);
|
||||
#define cpuCoprocessorRegister(x, y, z)
|
||||
#define cpuCoprocessorUnregister(x, y)
|
||||
//TODO: may need to actually implement these
|
||||
|
||||
#endif
|
||||
|
||||
116
src/pxa255/pxa255_DMA.c
Normal file
116
src/pxa255/pxa255_DMA.c
Normal file
@@ -0,0 +1,116 @@
|
||||
#include "pxa255_DMA.h"
|
||||
#include "pxa255_mem.h"
|
||||
|
||||
#define REG_DAR 0
|
||||
#define REG_SAR 1
|
||||
#define REG_TAR 2
|
||||
#define REG_CR 3
|
||||
#define REG_CSR 4
|
||||
|
||||
|
||||
static void pxa255dmaPrvChannelRegWrite(_UNUSED_ Pxa255dma* dma, UInt8 channel, UInt8 reg, UInt32 val){
|
||||
|
||||
if(val){ //we start with zeros, so non-zero writes are all we care about
|
||||
|
||||
const char* regs[] = {"DADDR", "SADDR", "TADDR", "CR", "CSR"};
|
||||
|
||||
err_str("dma: writes unimpl!");
|
||||
// err_str("PXA255 dma engine: writes unimpl! (writing 0x");
|
||||
// err_hex(val);
|
||||
// err_str(" to channel ");
|
||||
// err_dec(channel);
|
||||
// err_str(" reg ");
|
||||
// err_str(regs[reg]);
|
||||
// err_str(". Halting.\r\n");
|
||||
while(1);
|
||||
}
|
||||
}
|
||||
|
||||
static UInt32 pxa255dmaPrvChannelRegRead(_UNUSED_ Pxa255dma* dma, _UNUSED_ UInt8 channel, _UNUSED_ UInt8 reg){
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Boolean pxa255dmaPrvMemAccessF(void* userData, UInt32 pa, UInt8 size, Boolean write, void* buf){
|
||||
|
||||
Pxa255dma* dma = userData;
|
||||
UInt8 reg, set;
|
||||
UInt32 val = 0;
|
||||
|
||||
if(size != 4) {
|
||||
err_str(__FILE__ ": Unexpected ");
|
||||
// err_str(write ? "write" : "read");
|
||||
// err_str(" of ");
|
||||
// err_dec(size);
|
||||
// err_str(" bytes to 0x");
|
||||
// err_hex(pa);
|
||||
// err_str("\r\n");
|
||||
return true; //we do not support non-word accesses
|
||||
}
|
||||
|
||||
pa = (pa - PXA255_DMA_BASE) >> 2;
|
||||
|
||||
if(write){
|
||||
val = *(UInt32*)buf;
|
||||
|
||||
switch(pa >> 6){ //weird, but quick way to avoide repeated if-then-elses. this is faster
|
||||
case 0:
|
||||
if(pa < 16){
|
||||
reg = REG_CSR;
|
||||
set = pa;
|
||||
pxa255dmaPrvChannelRegWrite(dma, set, reg, val);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
pa -= 64;
|
||||
if(pa < 40) dma->CMR[pa] = val;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
pa -= 128;
|
||||
set = pa >> 2;
|
||||
reg = pa & 3;
|
||||
pxa255dmaPrvChannelRegWrite(dma, set, reg, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else{
|
||||
switch(pa >> 6){ //weird, but quick way to avoide repeated if-then-elses. this is faster
|
||||
case 0:
|
||||
if(pa < 16){
|
||||
reg = REG_CSR;
|
||||
set = pa;
|
||||
val = pxa255dmaPrvChannelRegRead(dma, set, reg);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
pa -= 64;
|
||||
if(pa < 40) val = dma->CMR[pa];
|
||||
break;
|
||||
|
||||
case 2:
|
||||
pa -= 128;
|
||||
set = pa >> 2;
|
||||
reg = pa & 3;
|
||||
val = pxa255dmaPrvChannelRegRead(dma, set, reg);
|
||||
break;
|
||||
}
|
||||
|
||||
*(UInt32*)buf = val;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Boolean pxa255dmaInit(Pxa255dma* dma, ArmMem* physMem, Pxa255ic* ic){
|
||||
|
||||
__mem_zero(dma, sizeof(Pxa255dma));
|
||||
dma->ic = ic;
|
||||
dma->mem = physMem;
|
||||
|
||||
return memRegionAdd(physMem, PXA255_DMA_BASE, PXA255_DMA_SIZE, pxa255dmaPrvMemAccessF, dma);
|
||||
}
|
||||
42
src/pxa255/pxa255_DMA.h
Normal file
42
src/pxa255/pxa255_DMA.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef _PXA255_DMA_H_
|
||||
#define _PXA255_DMA_H_
|
||||
|
||||
#include "pxa255_mem.h"
|
||||
#include "pxa255_CPU.h"
|
||||
#include "pxa255_IC.h"
|
||||
|
||||
/*
|
||||
PXA255 OS DMA controller
|
||||
|
||||
*/
|
||||
|
||||
#define PXA255_DMA_BASE 0x40000000UL
|
||||
#define PXA255_DMA_SIZE 0x00001000UL
|
||||
|
||||
typedef struct{
|
||||
|
||||
UInt32 DAR; //descriptor address register
|
||||
UInt32 SAR; //source address register
|
||||
UInt32 TAR; //target address register
|
||||
UInt32 CR; //command register
|
||||
UInt32 CSR; //control and status register
|
||||
|
||||
}Pxa255dmaChannel;
|
||||
|
||||
typedef struct{
|
||||
|
||||
Pxa255ic* ic;
|
||||
ArmMem* mem;
|
||||
|
||||
UInt16 DINT;
|
||||
Pxa255dmaChannel channels[16];
|
||||
UInt8 CMR[40]; //channel map registers [ we store lower 8 bits only :-) ]
|
||||
|
||||
}Pxa255dma;
|
||||
|
||||
|
||||
|
||||
Boolean pxa255dmaInit(Pxa255dma* gpio, ArmMem* physMem, Pxa255ic* ic);
|
||||
|
||||
#endif
|
||||
|
||||
81
src/pxa255/pxa255_DSP.c
Normal file
81
src/pxa255/pxa255_DSP.c
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "pxa255_math64.h"
|
||||
#include "pxa255_DSP.h"
|
||||
|
||||
|
||||
|
||||
|
||||
Boolean pxa255dspAccess(struct ArmCpu* cpu, void* userData, Boolean MRRC, UInt8 op, UInt8 RdLo, UInt8 RdHi, UInt8 acc){
|
||||
|
||||
Pxa255dsp* dsp = userData;
|
||||
|
||||
if(acc != 0 || op != 0) return false; //bad encoding
|
||||
|
||||
if(MRRC){ //MRA: read acc0
|
||||
|
||||
cpuSetReg(cpu, RdLo, u64_64_to_32(dsp->acc0));
|
||||
cpuSetReg(cpu, RdHi, (UInt8)u64_get_hi(dsp->acc0));
|
||||
}
|
||||
else{ //MAR: write acc0
|
||||
|
||||
dsp->acc0 = u64_from_halves(cpuGetRegExternal(cpu, RdHi) & 0xFF, cpuGetRegExternal(cpu, RdLo));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Boolean pxa255dspOp(struct ArmCpu* cpu, void* userData, Boolean two/* MCR2/MRC2 ? */, Boolean MRC, UInt8 op1, UInt8 Rs, UInt8 opcode_3, UInt8 Rm, UInt8 acc){
|
||||
|
||||
Pxa255dsp* dsp = userData;
|
||||
UInt64 addend = u64_zero();
|
||||
UInt32 Vs, Vm;
|
||||
|
||||
if(op1 != 1 || two || MRC || acc != 0) return false; //bad encoding
|
||||
|
||||
Vs = cpuGetRegExternal(cpu, Rs);
|
||||
Vm = cpuGetRegExternal(cpu, Rm);
|
||||
|
||||
switch(opcode_3 >> 2){
|
||||
|
||||
case 0: //MIA
|
||||
addend = u64_smul3232(Vm, Vs);
|
||||
break;
|
||||
|
||||
case 1: //invalid
|
||||
return false;
|
||||
|
||||
case 2: //MIAPH
|
||||
addend = u64_smul3232((Int32)((Int16)(Vm >> 0)), (Int32)((Int16)(Vs >> 0)));
|
||||
addend = u64_add(addend, u64_smul3232((Int32)((Int16)(Vm >> 16)), (Int32)((Int16)(Vs >> 16))));
|
||||
break;
|
||||
|
||||
case 3: //MIAxy
|
||||
if(opcode_3 & 2) Vm >>= 16; //X set
|
||||
if(opcode_3 & 1) Vs >>= 16; //Y set
|
||||
addend = u64_smul3232((Int32)((Int16)Vm), (Int32)((Int16)Vs));
|
||||
break;
|
||||
}
|
||||
|
||||
dsp->acc0 = u64_and(u64_add(dsp->acc0, addend), u64_from_halves(0xFF, 0xFFFFFFFFUL));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Boolean pxa255dspInit(Pxa255dsp* dsp, ArmCpu* cpu){
|
||||
|
||||
ArmCoprocessor cp;
|
||||
|
||||
|
||||
__mem_zero(dsp, sizeof(Pxa255dsp));
|
||||
|
||||
cp.regXfer = pxa255dspOp;
|
||||
cp.dataProcessing = NULL;
|
||||
cp.memAccess = NULL;
|
||||
cp.twoRegF = pxa255dspAccess;
|
||||
cp.userData = dsp;
|
||||
|
||||
cpuCoprocessorRegister(cpu, 0, &cp);
|
||||
|
||||
return true;
|
||||
}
|
||||
20
src/pxa255/pxa255_DSP.h
Normal file
20
src/pxa255/pxa255_DSP.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef _PXA255_DSP_H_
|
||||
#define _PXA255_DSP_H_
|
||||
|
||||
#include "pxa255_mem.h"
|
||||
#include "pxa255_CPU.h"
|
||||
|
||||
|
||||
|
||||
typedef struct{
|
||||
|
||||
UInt64 acc0;
|
||||
|
||||
}Pxa255dsp;
|
||||
|
||||
|
||||
|
||||
Boolean pxa255dspInit(Pxa255dsp* dsp, ArmCpu* cpu);
|
||||
|
||||
|
||||
#endif
|
||||
206
src/pxa255/pxa255_GPIO.c
Normal file
206
src/pxa255/pxa255_GPIO.c
Normal file
@@ -0,0 +1,206 @@
|
||||
#include "pxa255_GPIO.h"
|
||||
#include "pxa255_mem.h"
|
||||
|
||||
|
||||
static void pxa255gpioPrvRecalcValues(Pxa255gpio* gpio, UInt32 which){
|
||||
|
||||
UInt8 i;
|
||||
UInt32 val, bit;
|
||||
|
||||
val = gpio->dirs[which];
|
||||
gpio->levels[which] = (gpio->latches[which] & val) | (gpio->inputs[which] & (~val));
|
||||
|
||||
val = 3;
|
||||
bit = 1;
|
||||
for(i = 0 ; i < 16; i++, val <<= 2, bit <<= 1) if(gpio->AFRs[(which << 1) + 0] & val) gpio->levels[which] &=~ bit; //all AFRs read as zero to CPU
|
||||
for(i = 16; i < 32; i++, val <<= 2, bit <<= 1) if(gpio->AFRs[(which << 1) + 1] & val) gpio->levels[which] &=~ bit; //all AFRs read as zero to CPU
|
||||
}
|
||||
|
||||
static void pxa255gpioPrvRecalcIntrs(Pxa255gpio* gpio){
|
||||
|
||||
pxa255icInt(gpio->ic, PXA255_I_GPIO_all, gpio->levels[1] || gpio->levels[2] || (gpio->levels[0] &~ 3));
|
||||
pxa255icInt(gpio->ic, PXA255_I_GPIO_1, (gpio->levels[0] & 2) != 0);
|
||||
pxa255icInt(gpio->ic, PXA255_I_GPIO_0, (gpio->levels[0] & 1) != 0);
|
||||
}
|
||||
|
||||
static Boolean pxa255gpioPrvMemAccessF(void* userData, UInt32 pa, UInt8 size, Boolean write, void* buf){
|
||||
|
||||
Pxa255gpio* gpio = userData;
|
||||
UInt32 val = 0;
|
||||
|
||||
if(size != 4) {
|
||||
err_str(__FILE__ ": Unexpected ");
|
||||
// err_str(write ? "write" : "read");
|
||||
// err_str(" of ");
|
||||
// err_dec(size);
|
||||
// err_str(" bytes to 0x");
|
||||
// err_hex(pa);
|
||||
// err_str("\r\n");
|
||||
return true; //we do not support non-word accesses
|
||||
}
|
||||
|
||||
pa = (pa - PXA255_GPIO_BASE) >> 2;
|
||||
|
||||
if(write){
|
||||
val = *(UInt32*)buf;
|
||||
|
||||
switch(pa){
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
pa -= 3;
|
||||
gpio->dirs[pa] = val;
|
||||
goto recalc;
|
||||
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
pa -= 6;
|
||||
gpio->levels[pa] |= val;
|
||||
goto recalc;
|
||||
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
pa -= 9;
|
||||
gpio->levels[pa] &=~ val;
|
||||
goto recalc;
|
||||
|
||||
case 12:
|
||||
case 13:
|
||||
case 14:
|
||||
gpio->riseDet[pa - 12] = val;
|
||||
break;
|
||||
|
||||
case 15:
|
||||
case 16:
|
||||
case 17:
|
||||
gpio->fallDet[pa - 15] = val;
|
||||
break;
|
||||
|
||||
case 18:
|
||||
case 19:
|
||||
case 20:
|
||||
gpio->detStatus[pa - 18] &=~ val;
|
||||
goto trigger_intrs;
|
||||
|
||||
case 21:
|
||||
case 22:
|
||||
case 23:
|
||||
case 24:
|
||||
case 25:
|
||||
case 26:
|
||||
val = gpio->AFRs[pa - 21];
|
||||
goto recalc;
|
||||
}
|
||||
|
||||
goto done;
|
||||
|
||||
recalc:
|
||||
pxa255gpioPrvRecalcValues(gpio, pa);
|
||||
|
||||
trigger_intrs:
|
||||
pxa255gpioPrvRecalcIntrs(gpio);
|
||||
}
|
||||
else{
|
||||
switch(pa){
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
val = gpio->levels[pa - 0];
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
val = gpio->dirs[pa - 3];
|
||||
break;
|
||||
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
val = 0;
|
||||
break;
|
||||
|
||||
case 12:
|
||||
case 13:
|
||||
case 14:
|
||||
val = gpio->riseDet[pa - 12];
|
||||
break;
|
||||
|
||||
case 15:
|
||||
case 16:
|
||||
case 17:
|
||||
val = gpio->fallDet[pa - 15];
|
||||
break;
|
||||
|
||||
case 18:
|
||||
case 19:
|
||||
case 20:
|
||||
val = gpio->detStatus[pa - 18];
|
||||
break;
|
||||
|
||||
case 21:
|
||||
case 22:
|
||||
case 23:
|
||||
case 24:
|
||||
case 25:
|
||||
case 26:
|
||||
val = gpio->AFRs[pa - 21];
|
||||
break;
|
||||
|
||||
}
|
||||
*(UInt32*)buf = val;
|
||||
}
|
||||
|
||||
done:
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Boolean pxa255gpioInit(Pxa255gpio* gpio, ArmMem* physMem, Pxa255ic* ic){
|
||||
|
||||
__mem_zero(gpio, sizeof(Pxa255gpio));
|
||||
gpio->ic = ic;
|
||||
|
||||
return memRegionAdd(physMem, PXA255_GPIO_BASE, PXA255_GPIO_SIZE, pxa255gpioPrvMemAccessF, gpio);
|
||||
}
|
||||
|
||||
void pxa255gpioSetState(Pxa255gpio* gpio, UInt8 gpioNum, Boolean on){
|
||||
|
||||
UInt32 set = gpioNum >> 5;
|
||||
UInt32 v = 1UL << (gpioNum & 0x1F);
|
||||
UInt32* p;
|
||||
|
||||
if(gpioNum >= 85) return;
|
||||
|
||||
p = gpio->inputs + set;
|
||||
if(on) *p |= v;
|
||||
else *p &=~ v;
|
||||
|
||||
pxa255gpioPrvRecalcValues(gpio, set);
|
||||
pxa255gpioPrvRecalcIntrs(gpio);
|
||||
}
|
||||
|
||||
UInt8 pxa255gpioGetState(Pxa255gpio* gpio, UInt8 gpioNum){
|
||||
|
||||
UInt32 sSet = gpioNum >> 5;
|
||||
UInt32 bSet = gpioNum >> 4;
|
||||
UInt32 sV = 1UL << (gpioNum & 0x1F);
|
||||
UInt32 bV = 3UL << (gpioNum & 0x0F);
|
||||
|
||||
|
||||
if(gpioNum >= 85) return PXA255_GPIO_NOT_PRESENT;
|
||||
if(gpio->AFRs[bSet] & bV) return ((gpio->AFRs[bSet] & bV) >> (gpioNum & 0x0F)) + PXA255_GPIO_AFR1;
|
||||
if(gpio->dirs[sSet] & sV) return (gpio->latches[sSet] & sV) ? PXA255_GPIO_HIGH : PXA255_GPIO_LOW;
|
||||
return PXA255_GPIO_HiZ;
|
||||
}
|
||||
47
src/pxa255/pxa255_GPIO.h
Normal file
47
src/pxa255/pxa255_GPIO.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef _PXA255_GPIO_H_
|
||||
#define _PXA255_GPIO_H_
|
||||
|
||||
#include "pxa255_mem.h"
|
||||
#include "pxa255_CPU.h"
|
||||
#include "pxa255_IC.h"
|
||||
|
||||
/*
|
||||
PXA255 OS GPIO controller
|
||||
|
||||
*/
|
||||
|
||||
#define PXA255_GPIO_BASE 0x40E00000UL
|
||||
#define PXA255_GPIO_SIZE 0x00001000UL
|
||||
|
||||
|
||||
typedef struct{
|
||||
|
||||
Pxa255ic* ic;
|
||||
|
||||
UInt32 latches[3]; //what pxa wants to be outputting
|
||||
UInt32 inputs[3]; //what pxa is receiving [only set by the pxa255gpioSetState() API]
|
||||
UInt32 levels[3]; //what pxa sees (it differs from above for IN pins)
|
||||
UInt32 dirs[3]; //1 = output
|
||||
UInt32 riseDet[3]; //1 = rise detect
|
||||
UInt32 fallDet[3]; //1 = fall detect
|
||||
UInt32 detStatus[3]; //1 = detect happened
|
||||
UInt32 AFRs[6]; //1, 2, 3 = alt funcs
|
||||
|
||||
}Pxa255gpio;
|
||||
|
||||
#define PXA255_GPIO_LOW 0 //these values make it look like all HiZ, AFR, and nonexistent pins have pullups to those who dumbly assume gpioGEt returns a boolean
|
||||
#define PXA255_GPIO_HIGH 1
|
||||
#define PXA255_GPIO_HiZ 2
|
||||
#define PXA255_GPIO_AFR1 3
|
||||
#define PXA255_GPIO_AFR2 4
|
||||
#define PXA255_GPIO_AFR3 5
|
||||
#define PXA255_GPIO_NOT_PRESENT 6
|
||||
|
||||
Boolean pxa255gpioInit(Pxa255gpio* gpio, ArmMem* physMem, Pxa255ic* ic);
|
||||
|
||||
//for external use :)
|
||||
UInt8 pxa255gpioGetState(Pxa255gpio* gpio, UInt8 gpioNum);
|
||||
void pxa255gpioSetState(Pxa255gpio* gpio, UInt8 gpioNum, Boolean on); //we can only set value (and only of input pins), not direction
|
||||
|
||||
#endif
|
||||
|
||||
102
src/pxa255/pxa255_IC.c
Normal file
102
src/pxa255/pxa255_IC.c
Normal file
@@ -0,0 +1,102 @@
|
||||
#include "pxa255_IC.h"
|
||||
#include "pxa255_mem.h"
|
||||
|
||||
|
||||
static void pxa255icPrvHandleChanges(Pxa255ic* ic){
|
||||
|
||||
Boolean nowIrq, nowFiq;
|
||||
UInt32 unmasked = ic->ICPR & ic->ICMR;
|
||||
|
||||
nowFiq = (unmasked & ic->ICLR) != 0;
|
||||
nowIrq = (unmasked & ~ic->ICLR) != 0;
|
||||
|
||||
if(nowFiq != ic->wasFiq) cpuIrq(ic->cpu, true, nowFiq);
|
||||
if(nowIrq != ic->wasIrq) cpuIrq(ic->cpu, false, nowIrq);
|
||||
|
||||
ic->wasFiq = nowFiq;
|
||||
ic->wasIrq = nowIrq;
|
||||
}
|
||||
|
||||
static Boolean pxa255icPrvMemAccessF(void* userData, UInt32 pa, UInt8 size, Boolean write, void* buf){
|
||||
|
||||
Pxa255ic* ic = userData;
|
||||
UInt32 val = 0;
|
||||
|
||||
if(size != 4) {
|
||||
err_str(__FILE__ ": Unexpected ");
|
||||
// err_str(write ? "write" : "read");
|
||||
// err_str(" of ");
|
||||
// err_dec(size);
|
||||
// err_str(" bytes to 0x");
|
||||
// err_hex(pa);
|
||||
// err_str("\r\n");
|
||||
return true; //we do not support non-word accesses
|
||||
}
|
||||
|
||||
pa = (pa - PXA255_IC_BASE) >> 2;
|
||||
|
||||
if(write){
|
||||
if(pa == 1) ic->ICMR = *(UInt32*)buf;
|
||||
else if(pa == 2) ic->ICLR = *(UInt32*)buf;
|
||||
else if(pa == 5) ic->ICCR = *(UInt32*)buf;
|
||||
else return true;
|
||||
pxa255icPrvHandleChanges(ic);
|
||||
}
|
||||
else{
|
||||
switch(pa){
|
||||
|
||||
case 0:
|
||||
val = ic->ICPR & ic->ICMR & ~ic->ICLR;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
val = ic->ICMR;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
val = ic->ICLR;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
val = ic->ICPR & ic->ICMR & ic->ICLR;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
val = ic->ICPR;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
val = ic->ICCR;
|
||||
break;
|
||||
|
||||
}
|
||||
*(UInt32*)buf = val;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void pxa255icInit(Pxa255ic* ic){
|
||||
|
||||
__mem_zero(ic, sizeof(Pxa255ic));
|
||||
}
|
||||
|
||||
|
||||
void pxa255icInt(Pxa255ic* ic, UInt8 intNum, Boolean raise){ //interrupt caused by emulated hardware
|
||||
|
||||
UInt32 old_, new_;
|
||||
|
||||
old_ = new_ = ic->ICPR;
|
||||
|
||||
if(raise) new_ |= (1UL << intNum);
|
||||
else new_ &=~ (1UL << intNum);
|
||||
|
||||
if(new_ != old_){
|
||||
ic->ICPR = new_;
|
||||
pxa255icPrvHandleChanges(ic);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
63
src/pxa255/pxa255_IC.h
Normal file
63
src/pxa255/pxa255_IC.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#ifndef _PXA255_IC_H_
|
||||
#define _PXA255_IC_H_
|
||||
|
||||
#include "pxa255_mem.h"
|
||||
#include "pxa255_CPU.h"
|
||||
#include <stdio.h>
|
||||
|
||||
/*
|
||||
PXA255 interrupt controller
|
||||
|
||||
PURRPOSE: raises IRQ, FIQ as needed
|
||||
|
||||
*/
|
||||
|
||||
#define PXA255_IC_BASE 0x40D00000UL
|
||||
#define PXA255_IC_SIZE 0x00010000UL
|
||||
|
||||
|
||||
#define PXA255_I_RTC_ALM 31
|
||||
#define PXA255_I_RTC_HZ 30
|
||||
#define PXA255_I_TIMR3 29
|
||||
#define PXA255_I_TIMR2 28
|
||||
#define PXA255_I_TIMR1 27
|
||||
#define PXA255_I_TIMR0 26
|
||||
#define PXA255_I_DMA 25
|
||||
#define PXA255_I_SSP 24
|
||||
#define PXA255_I_MMC 23
|
||||
#define PXA255_I_FFUART 22
|
||||
#define PXA255_I_BTUART 21
|
||||
#define PXA255_I_STUART 20
|
||||
#define PXA255_I_ICP 19
|
||||
#define PXA255_I_I2C 18
|
||||
#define PXA255_I_LCD 17
|
||||
#define PXA255_I_NET_SSP 16
|
||||
#define PXA255_I_AC97 14
|
||||
#define PXA255_I_I2S 13
|
||||
#define PXA255_I_PMU 12
|
||||
#define PXA255_I_USB 11
|
||||
#define PXA255_I_GPIO_all 10
|
||||
#define PXA255_I_GPIO_1 9
|
||||
#define PXA255_I_GPIO_0 8
|
||||
#define PXA255_I_HWUART 7
|
||||
|
||||
|
||||
typedef struct{
|
||||
|
||||
ArmCpu* cpu;
|
||||
|
||||
UInt32 ICMR; //Mask Register
|
||||
UInt32 ICLR; //Level Register
|
||||
UInt32 ICCR; //Control Register
|
||||
UInt32 ICPR; //Pending register
|
||||
|
||||
Boolean wasIrq, wasFiq;
|
||||
|
||||
}Pxa255ic;
|
||||
|
||||
void pxa255icInit(Pxa255ic* ic);
|
||||
void pxa255icInt(Pxa255ic* ic, UInt8 intNum, Boolean raise); //interrupt caused by emulated hardware/ interrupt handled by guest
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
361
src/pxa255/pxa255_LCD.c
Normal file
361
src/pxa255/pxa255_LCD.c
Normal file
@@ -0,0 +1,361 @@
|
||||
#include "pxa255_LCD.h"
|
||||
#include "pxa255_mem.h"
|
||||
#include "pxa255.h"
|
||||
#include "../armv5te/mem.h"
|
||||
|
||||
#define UNMASKABLE_INTS 0x7C8E
|
||||
|
||||
static void pxa255lcdPrvUpdateInts(Pxa255lcd* lcd){
|
||||
|
||||
UInt16 ints = lcd->lcsr & lcd->intMask;
|
||||
|
||||
if((ints && !lcd->intWasPending) || (!ints && lcd->intWasPending)){
|
||||
|
||||
lcd->intWasPending = !!ints;
|
||||
pxa255icInt(lcd->ic, PXA255_I_LCD, !!ints);
|
||||
}
|
||||
}
|
||||
|
||||
Boolean pxa255lcdPrvMemAccessF(void* userData, UInt32 pa, UInt8 size, Boolean write, void* buf){
|
||||
|
||||
Pxa255lcd* lcd = userData;
|
||||
UInt32 val = 0;
|
||||
UInt16 v16;
|
||||
|
||||
if(size != 4) {
|
||||
err_str(__FILE__ ": Unexpected ");
|
||||
// err_str(write ? "write" : "read");
|
||||
// err_str(" of ");
|
||||
// err_dec(size);
|
||||
// err_str(" bytes to 0x");
|
||||
// err_hex(pa);
|
||||
// err_str("\r\n");
|
||||
return true; //we do not support non-word accesses
|
||||
}
|
||||
|
||||
pa = (pa - PXA255_LCD_BASE) >> 2;
|
||||
|
||||
if(write){
|
||||
val = *(UInt32*)buf;
|
||||
|
||||
switch(pa){
|
||||
case 0:
|
||||
if((lcd->lccr0 ^ val) & 0x0001){ //something changed about enablement - handle it
|
||||
|
||||
lcd->enbChanged = 1;
|
||||
}
|
||||
lcd->lccr0 = val;
|
||||
//recalc intMask
|
||||
v16 = UNMASKABLE_INTS;
|
||||
if(val & 0x00200000UL){ //output fifo underrun
|
||||
v16 |= 0x0040;
|
||||
}
|
||||
if(val & 0x00100000UL){ //branch int
|
||||
v16 |= 0x0200;
|
||||
}
|
||||
if(val & 0x00000400UL){ //quick disable
|
||||
v16 |= 0x0001;
|
||||
}
|
||||
if(val & 0x00000040UL){ //end of frame
|
||||
v16 |= 0x0080;
|
||||
}
|
||||
if(val & 0x00000020UL){ //input fifo underrun
|
||||
v16 |= 0x0030;
|
||||
}
|
||||
if(val & 0x00000010UL){ //start of frame
|
||||
v16 |= 0x0002;
|
||||
}
|
||||
lcd->intMask = v16;
|
||||
pxa255lcdPrvUpdateInts(lcd);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
lcd->lccr1 = val;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
lcd->lccr2 = val;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
lcd->lccr3 = val;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
lcd->fbr0 = val;
|
||||
break;
|
||||
|
||||
case 9:
|
||||
lcd->fbr1 = val;
|
||||
break;
|
||||
|
||||
case 14:
|
||||
lcd->lcsr &=~ val;
|
||||
pxa255lcdPrvUpdateInts(lcd);
|
||||
break;
|
||||
|
||||
case 15:
|
||||
lcd->liicr = val;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
lcd->trgbr = val;
|
||||
break;
|
||||
|
||||
case 17:
|
||||
lcd->tcr = val;
|
||||
break;
|
||||
|
||||
case 128:
|
||||
lcd->fdadr0 = val;
|
||||
break;
|
||||
|
||||
case 132:
|
||||
lcd->fdadr1 = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else{
|
||||
switch(pa){
|
||||
case 0:
|
||||
val = lcd->lccr0;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
val = lcd->lccr1;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
val = lcd->lccr2;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
val = lcd->lccr3;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
val = lcd->fbr0;
|
||||
break;
|
||||
|
||||
case 9:
|
||||
val = lcd->fbr1;
|
||||
break;
|
||||
|
||||
case 14:
|
||||
val = lcd->lcsr;
|
||||
break;
|
||||
|
||||
case 15:
|
||||
val = lcd->liicr;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
val = lcd->trgbr;
|
||||
break;
|
||||
|
||||
case 17:
|
||||
val = lcd->tcr;
|
||||
break;
|
||||
|
||||
case 128:
|
||||
val = lcd->fdadr0;
|
||||
break;
|
||||
|
||||
case 129:
|
||||
val = lcd->fsadr0;
|
||||
break;
|
||||
|
||||
case 130:
|
||||
val = lcd->fidr0;
|
||||
break;
|
||||
|
||||
case 131:
|
||||
val = lcd->ldcmd0;
|
||||
break;
|
||||
|
||||
case 132:
|
||||
val = lcd->fdadr1;
|
||||
break;
|
||||
|
||||
case 133:
|
||||
val = lcd->fsadr1;
|
||||
break;
|
||||
|
||||
case 134:
|
||||
val = lcd->fidr1;
|
||||
break;
|
||||
|
||||
case 135:
|
||||
val = lcd->ldcmd1;
|
||||
break;
|
||||
}
|
||||
*(UInt32*)buf = val;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define pxa255PrvGetWord(x, addr) mmio_read_word(addr)
|
||||
|
||||
static void pxa255LcdPrvDma(Pxa255lcd* lcd, void* dest, UInt32 addr, UInt32 len){
|
||||
|
||||
UInt32 t;
|
||||
UInt8* d = dest;
|
||||
|
||||
//we assume aligntment here both on part of dest and of addr
|
||||
|
||||
while(len){
|
||||
|
||||
t = pxa255PrvGetWord(lcd, addr);
|
||||
if(len--) *d++ = t;
|
||||
if(len--) *d++ = t >> 8;
|
||||
if(len--) *d++ = t >> 16;
|
||||
if(len--) *d++ = t >> 24;
|
||||
addr += 4;
|
||||
}
|
||||
}
|
||||
|
||||
static _INLINE_ void pxa255LcdScreenDataPixel(Pxa255lcd* lcd, UInt8* buf){
|
||||
static UInt16 pixelX = 0;
|
||||
static UInt16 pixelY = 0;
|
||||
|
||||
if(pixelX == 640){
|
||||
pixelY++;
|
||||
pixelX = 0;
|
||||
}
|
||||
|
||||
if(pixelY == 480){
|
||||
pixelY = 0;
|
||||
pixelX = 0;
|
||||
}
|
||||
|
||||
if(pixelX < 320 && pixelY < 320)
|
||||
pxa255Framebuffer[pixelY * 320 + pixelX] = buf[0] || buf[1] << 8;
|
||||
}
|
||||
|
||||
static void pxa255LcdScreenDataDma(Pxa255lcd* lcd, UInt32 addr/*PA*/, UInt32 len){
|
||||
UInt8 data[4];
|
||||
UInt32 i, j;
|
||||
void* ptr;
|
||||
|
||||
len /= 4;
|
||||
while(len--){
|
||||
pxa255LcdPrvDma(lcd, data, addr, 4);
|
||||
addr += 4;
|
||||
|
||||
switch((lcd->lccr3 >> 24) & 7){
|
||||
|
||||
case 0: //1BPP
|
||||
|
||||
ptr = lcd->palette + ((data[i] >> j) & 1) * 2;
|
||||
for(i = 0; i < 4; i += 1) for(j = 0; j < 8; j += 1) pxa255LcdScreenDataPixel(lcd, ptr);
|
||||
break;
|
||||
|
||||
case 1: //2BPP
|
||||
|
||||
ptr = lcd->palette + ((data[i] >> j) & 3) * 2;
|
||||
for(i = 0; i < 4; i += 1) for(j = 0; j < 8; j += 2) pxa255LcdScreenDataPixel(lcd, ptr);
|
||||
break;
|
||||
|
||||
case 2: //4BPP
|
||||
|
||||
ptr = lcd->palette + ((data[i] >> j) & 15) * 2;
|
||||
for(i = 0; i < 4; i += 1) for(j = 0; j < 8; j += 4) pxa255LcdScreenDataPixel(lcd, ptr);
|
||||
break;
|
||||
|
||||
case 3: //8BPP
|
||||
|
||||
ptr = lcd->palette + (data[i] * 2);
|
||||
for(i = 0; i < 4; i += 1) pxa255LcdScreenDataPixel(lcd, ptr);
|
||||
break;
|
||||
|
||||
case 4: //16BPP
|
||||
|
||||
for(i = 0; i < 4; i +=2 ) pxa255LcdScreenDataPixel(lcd, data + i);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
;//BAD
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pxa255lcdFrame(Pxa255lcd* lcd){
|
||||
//every other call starts a frame, the others end one [this generates spacing between interrupts so as to not confuse guest OS]
|
||||
|
||||
if(lcd->enbChanged){
|
||||
|
||||
if(lcd->lccr0 & 0x0001){ //just got enabled
|
||||
|
||||
//TODO: perhaps check settings?
|
||||
}
|
||||
else{ // we just got quick disabled - kill current frame and do no more
|
||||
|
||||
lcd->lcsr |= 0x0080; //quick disable happened
|
||||
lcd->state = LCD_STATE_IDLE;
|
||||
}
|
||||
lcd->enbChanged = false;
|
||||
}
|
||||
|
||||
if(lcd->lccr0 & 0x0001){ //enabled - do a frame
|
||||
|
||||
UInt32 descrAddr, len;
|
||||
|
||||
switch(lcd->state){
|
||||
|
||||
case LCD_STATE_IDLE:
|
||||
|
||||
if(lcd->fbr0 & 1){ //branch
|
||||
|
||||
lcd->fbr0 &=~ 1UL;
|
||||
if(lcd->fbr0 & 2) lcd->lcsr |= 0x0200;
|
||||
descrAddr = lcd->fbr0 &~ 0xFUL;
|
||||
} else descrAddr = lcd->fdadr0;
|
||||
lcd->fdadr0 = pxa255PrvGetWord(lcd, descrAddr + 0);
|
||||
lcd->fsadr0 = pxa255PrvGetWord(lcd, descrAddr + 4);
|
||||
lcd->fidr0 = pxa255PrvGetWord(lcd, descrAddr + 8);
|
||||
lcd->ldcmd0 = pxa255PrvGetWord(lcd, descrAddr + 12);
|
||||
|
||||
lcd->state = LCD_STATE_DMA_0_START;
|
||||
break;
|
||||
|
||||
case LCD_STATE_DMA_0_START:
|
||||
|
||||
if(lcd->ldcmd0 & 0x00400000UL) lcd->lcsr |= 0x0002; //set SOF is DMA 0 started
|
||||
len = lcd->ldcmd0 & 0x000FFFFFUL;
|
||||
|
||||
if(lcd->ldcmd0 & 0x04000000UL){ //pallette data
|
||||
|
||||
if(len > sizeof(lcd->palette)){
|
||||
|
||||
len = sizeof(lcd->palette);
|
||||
|
||||
pxa255LcdPrvDma(lcd, lcd->palette, lcd->fsadr0, len);
|
||||
}
|
||||
}
|
||||
else{
|
||||
|
||||
lcd->frameNum++;
|
||||
if(!(lcd->frameNum & 63)) pxa255LcdScreenDataDma(lcd, lcd->fsadr0, len);
|
||||
}
|
||||
|
||||
lcd->state = LCD_STATE_DMA_0_END;
|
||||
break;
|
||||
|
||||
case LCD_STATE_DMA_0_END:
|
||||
|
||||
if(lcd->ldcmd0 & 0x00200000UL) lcd->lcsr |= 0x0100; //set EOF is DMA 0 finished
|
||||
lcd->state = LCD_STATE_IDLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pxa255lcdPrvUpdateInts(lcd);
|
||||
}
|
||||
|
||||
void pxa255lcdInit(Pxa255lcd* lcd, Pxa255ic* ic){
|
||||
__mem_zero(lcd, sizeof(Pxa255lcd));
|
||||
|
||||
lcd->ic = ic;
|
||||
lcd->intMask = UNMASKABLE_INTS;
|
||||
}
|
||||
55
src/pxa255/pxa255_LCD.h
Normal file
55
src/pxa255/pxa255_LCD.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#ifndef _PXA255_LCD_H_
|
||||
#define _PXA255_LCD_H_
|
||||
|
||||
#include "pxa255_mem.h"
|
||||
#include "pxa255_CPU.h"
|
||||
#include "pxa255_IC.h"
|
||||
|
||||
uint16_t* pxa255Framebuffer;
|
||||
|
||||
/*
|
||||
PXA255 OS LCD controller
|
||||
|
||||
PURRPOSE: it's nice to have a framebuffer
|
||||
|
||||
*/
|
||||
|
||||
#define PXA255_LCD_BASE 0x44000000UL
|
||||
#define PXA255_LCD_SIZE 0x00001000UL
|
||||
|
||||
|
||||
|
||||
|
||||
#define LCD_STATE_IDLE 0
|
||||
#define LCD_STATE_DMA_0_START 1
|
||||
#define LCD_STATE_DMA_0_END 2
|
||||
|
||||
typedef struct{
|
||||
|
||||
Pxa255ic* ic;
|
||||
|
||||
//registers
|
||||
UInt32 lccr0, lccr1, lccr2, lccr3, fbr0, fbr1, liicr, trgbr, tcr;
|
||||
UInt32 fdadr0, fsadr0, fidr0, ldcmd0;
|
||||
UInt32 fdadr1, fsadr1, fidr1, ldcmd1;
|
||||
UInt16 lcsr; //yes, 16-bit :)
|
||||
|
||||
//for our use
|
||||
UInt16 intMask;
|
||||
|
||||
UInt8 state : 6;
|
||||
UInt8 intWasPending : 1;
|
||||
UInt8 enbChanged : 1;
|
||||
|
||||
UInt8 palette[512];
|
||||
|
||||
UInt32 frameNum;
|
||||
|
||||
}Pxa255lcd;
|
||||
|
||||
Boolean pxa255lcdPrvMemAccessF(void* userData, UInt32 pa, UInt8 size, Boolean write, void* buf);
|
||||
void pxa255lcdInit(Pxa255lcd* lcd, Pxa255ic* ic);
|
||||
void pxa255lcdFrame(Pxa255lcd* lcd);
|
||||
|
||||
#endif
|
||||
|
||||
154
src/pxa255/pxa255_PwrClk.c
Normal file
154
src/pxa255/pxa255_PwrClk.c
Normal file
@@ -0,0 +1,154 @@
|
||||
#include "pxa255_PwrClk.h"
|
||||
|
||||
|
||||
static Boolean pxa255pwrClkPrvCoprocRegXferFunc(struct ArmCpu* cpu, void* userData, Boolean two, Boolean read, UInt8 op1, UInt8 Rx, UInt8 CRn, UInt8 CRm, UInt8 op2){
|
||||
|
||||
Pxa255pwrClk* pc = userData;
|
||||
UInt32 val = 0;
|
||||
|
||||
if(!read) val = cpuGetRegExternal(cpu, Rx);
|
||||
|
||||
if(CRm == 0 && op2 == 0 && op1 == 0 && !two){
|
||||
|
||||
switch(CRn){
|
||||
|
||||
case 6:
|
||||
if(read) val = 0;
|
||||
else{
|
||||
pc->turbo = (val & 1) != 0;
|
||||
if(val & 2){
|
||||
|
||||
err_str("Set speed mode");
|
||||
// err_str("(CCCR + cp14 reg6) to 0x");
|
||||
// err_hex(pc->CCCR);
|
||||
// err_str(", 0x");
|
||||
// err_hex(val);
|
||||
// err_str("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
case 7:
|
||||
if(read) val = pc->turbo ? 1 : 0;
|
||||
else if(val != 0){
|
||||
|
||||
// fprintf(stderr, "Someone tried to set processor power mode (cp14 reg7) to 0x%08lX\n", val);
|
||||
}
|
||||
goto success;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
success:
|
||||
|
||||
if(read) cpuSetReg(cpu, Rx, val);
|
||||
return true;
|
||||
}
|
||||
|
||||
static Boolean pxa255pwrClkPrvClockMgrMemAccessF(void* userData, UInt32 pa, UInt8 size, Boolean write, void* buf){
|
||||
|
||||
Pxa255pwrClk* pc = userData;
|
||||
UInt32 val = 0;
|
||||
|
||||
if(size != 4) {
|
||||
err_str(__FILE__ ": Unexpected ");
|
||||
// err_str(write ? "write" : "read");
|
||||
// err_str(" of ");
|
||||
// err_dec(size);
|
||||
// err_str(" bytes to 0x");
|
||||
// err_hex(pa);
|
||||
// err_str("\r\n");
|
||||
return true; //we do not support non-word accesses
|
||||
}
|
||||
|
||||
pa = (pa - PXA255_CLOCK_MANAGER_BASE) >> 2;
|
||||
|
||||
if(write) val = *(UInt32*)buf;
|
||||
|
||||
switch(pa){
|
||||
|
||||
case 0: //CCCR
|
||||
if(write) pc->CCCR = val;
|
||||
else val = pc->CCCR;
|
||||
break;
|
||||
|
||||
case 1: //CKEN
|
||||
if(write) pc->CKEN = val;
|
||||
else val = pc->CKEN;
|
||||
break;
|
||||
|
||||
case 2: //OSCR
|
||||
if(!write) val = pc->OSCR;
|
||||
//no writing to this register
|
||||
break;
|
||||
}
|
||||
|
||||
if(!write) *(UInt32*)buf = val;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static Boolean pxa255pwrClkPrvPowerMgrMemAccessF(void* userData, UInt32 pa, UInt8 size, Boolean write, void* buf){
|
||||
|
||||
Pxa255pwrClk* pc = userData;
|
||||
UInt32 val = 0;
|
||||
|
||||
if(size != 4) {
|
||||
err_str(__FILE__ ": Unexpected ");
|
||||
// err_str(write ? "write" : "read");
|
||||
// err_str(" of ");
|
||||
// err_dec(size);
|
||||
// err_str(" bytes to 0x");
|
||||
// err_hex(pa);
|
||||
// err_str("\r\n");
|
||||
return true; //we do not support non-word accesses
|
||||
}
|
||||
|
||||
pa = (pa - PXA255_POWER_MANAGER_BASE) >> 2;
|
||||
|
||||
if(write) val = *(UInt32*)buf;
|
||||
|
||||
if(pa < 13){
|
||||
|
||||
if(write) pc->pwrRegs[pa] = val;
|
||||
else val = pc->pwrRegs[pa];
|
||||
}
|
||||
|
||||
if(!write) *(UInt32*)buf = val;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Boolean pxa255pwrClkInit(Pxa255pwrClk* pc, ArmCpu* cpu, ArmMem* physMem){
|
||||
|
||||
ArmCoprocessor cp;
|
||||
Boolean ok = true;
|
||||
|
||||
__mem_zero(pc, sizeof(Pxa255pwrClk));
|
||||
|
||||
pc->cpu = cpu;
|
||||
pc->CCCR = 0x00000122UL; //set CCCR to almost default value (we use mult 32 not 27)
|
||||
pc->CKEN = 0x000179EFUL; //set CKEN to default value
|
||||
pc->OSCR = 0x00000003UL; //32KHz oscillator on and stable
|
||||
pc->pwrRegs[1] = 0x20; //set PSSR
|
||||
pc->pwrRegs[3] = 3; //set PWER
|
||||
pc->pwrRegs[4] = 3; //set PRER
|
||||
pc->pwrRegs[5] = 3; //set PFER
|
||||
pc->pwrRegs[12] = 1; //set RCSR
|
||||
|
||||
|
||||
cp.regXfer = pxa255pwrClkPrvCoprocRegXferFunc;
|
||||
cp.dataProcessing = NULL;
|
||||
cp.memAccess = NULL;
|
||||
cp.twoRegF = NULL;
|
||||
cp.userData = pc;
|
||||
|
||||
cpuCoprocessorRegister(cpu, 14, &cp);
|
||||
|
||||
ok = ok && memRegionAdd(physMem, PXA255_CLOCK_MANAGER_BASE, PXA255_CLOCK_MANAGER_SIZE, pxa255pwrClkPrvClockMgrMemAccessF, pc);
|
||||
ok = ok && memRegionAdd(physMem, PXA255_POWER_MANAGER_BASE, PXA255_POWER_MANAGER_SIZE, pxa255pwrClkPrvPowerMgrMemAccessF, pc);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
32
src/pxa255/pxa255_PwrClk.h
Normal file
32
src/pxa255/pxa255_PwrClk.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef _PXA255_PWR_CLK_H_
|
||||
#define _PXA255_PWR_CLK_H_
|
||||
|
||||
#include "pxa255_mem.h"
|
||||
#include "pxa255_CPU.h"
|
||||
|
||||
|
||||
|
||||
typedef struct{
|
||||
|
||||
ArmCpu* cpu;
|
||||
UInt32 CCCR, CKEN, OSCR; //clocks manager regs
|
||||
UInt32 pwrRegs[13]; //we care so little about these, we don't even name them
|
||||
Boolean turbo;
|
||||
|
||||
}Pxa255pwrClk;
|
||||
|
||||
|
||||
#define PXA255_CLOCK_MANAGER_BASE 0x41300000UL
|
||||
#define PXA255_CLOCK_MANAGER_SIZE 0x00001000UL
|
||||
|
||||
#define PXA255_POWER_MANAGER_BASE 0x40F00000UL
|
||||
#define PXA255_POWER_MANAGER_SIZE 0x00001000UL
|
||||
|
||||
|
||||
Boolean pxa255pwrClkInit(Pxa255pwrClk* pc, ArmCpu* cpu, ArmMem* physMem);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
112
src/pxa255/pxa255_RTC.c
Normal file
112
src/pxa255/pxa255_RTC.c
Normal file
@@ -0,0 +1,112 @@
|
||||
#include "pxa255_RTC.h"
|
||||
#include "pxa255_mem.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
|
||||
|
||||
static UInt32 rtcCurTime(void){
|
||||
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
return tv.tv_sec;
|
||||
}
|
||||
|
||||
void pxa255rtcPrvUpdate(Pxa255rtc* rtc){
|
||||
|
||||
UInt32 time = rtcCurTime();
|
||||
|
||||
if(rtc->lastSeenTime != time){ //do not triger alarm more than once per second please
|
||||
|
||||
if((rtc->RTSR & 0x4) && (time + rtc->RCNR_offset == rtc->RTAR)){ //check alarm
|
||||
rtc->RTSR |= 1;
|
||||
}
|
||||
if(rtc->RTSR & 0x8){ //send HZ interrupt
|
||||
rtc->RTSR |= 2;
|
||||
}
|
||||
}
|
||||
pxa255icInt(rtc->ic, PXA255_I_RTC_ALM, (rtc->RTSR & 1) != 0);
|
||||
pxa255icInt(rtc->ic, PXA255_I_RTC_HZ, (rtc->RTSR & 2) != 0);
|
||||
}
|
||||
|
||||
static Boolean pxa255rtcPrvMemAccessF(void* userData, UInt32 pa, UInt8 size, Boolean write, void* buf){
|
||||
|
||||
Pxa255rtc* rtc = userData;
|
||||
UInt32 val = 0;
|
||||
|
||||
if(size != 4) {
|
||||
err_str(__FILE__ ": Unexpected ");
|
||||
// err_str(write ? "write" : "read");
|
||||
// err_str(" of ");
|
||||
// err_dec(size);
|
||||
// err_str(" bytes to 0x");
|
||||
// err_hex(pa);
|
||||
// err_str("\r\n");
|
||||
return true; //we do not support non-word accesses
|
||||
}
|
||||
|
||||
pa = (pa - PXA255_RTC_BASE) >> 2;
|
||||
|
||||
if(write){
|
||||
val = *(UInt32*)buf;
|
||||
|
||||
switch(pa){
|
||||
case 0:
|
||||
rtc->RCNR_offset = rtcCurTime() - val;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
rtc->RTAR = val;
|
||||
pxa255rtcPrvUpdate(rtc);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
rtc->RTSR = (val &~ 3UL) | ((rtc->RTSR &~ val) & 3UL);
|
||||
pxa255rtcPrvUpdate(rtc);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if(!(rtc->RTTR & 0x80000000UL)) rtc->RTTR = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else{
|
||||
switch(pa){
|
||||
case 0:
|
||||
val = rtcCurTime() - rtc->RCNR_offset;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
val = rtc->RTAR;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
val = rtc->RTSR;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
val = rtc->RTTR;
|
||||
break;
|
||||
}
|
||||
*(UInt32*)buf = val;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Boolean pxa255rtcInit(Pxa255rtc* rtc, ArmMem* physMem, Pxa255ic* ic){
|
||||
|
||||
__mem_zero(rtc, sizeof(Pxa255rtc));
|
||||
rtc->ic = ic;
|
||||
rtc->RCNR_offset = 0;
|
||||
rtc->RTTR = 0x7FFF; //nice default value
|
||||
rtc->lastSeenTime = rtcCurTime();
|
||||
return memRegionAdd(physMem, PXA255_RTC_BASE, PXA255_RTC_SIZE, pxa255rtcPrvMemAccessF, rtc);
|
||||
}
|
||||
|
||||
void pxa255rtcUpdate(Pxa255rtc* rtc){
|
||||
pxa255rtcPrvUpdate(rtc);
|
||||
}
|
||||
37
src/pxa255/pxa255_RTC.h
Normal file
37
src/pxa255/pxa255_RTC.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef _PXA255_RTC_H_
|
||||
#define _PXA255_RTC_H_
|
||||
|
||||
#include "pxa255_mem.h"
|
||||
#include "pxa255_CPU.h"
|
||||
#include "pxa255_IC.h"
|
||||
|
||||
|
||||
/*
|
||||
PXA255 OS RTC controller
|
||||
|
||||
PURRPOSE: it's nice to know what time it is
|
||||
|
||||
*/
|
||||
|
||||
#define PXA255_RTC_BASE 0x40900000UL
|
||||
#define PXA255_RTC_SIZE 0x00001000UL
|
||||
|
||||
|
||||
typedef struct{
|
||||
|
||||
Pxa255ic* ic;
|
||||
|
||||
UInt32 RCNR_offset; //RTC counter offset from our local time
|
||||
UInt32 RTAR; //RTC alarm
|
||||
UInt32 RTSR; //RTC status
|
||||
UInt32 RTTR; //RTC trim - we ignore this alltogether
|
||||
UInt32 lastSeenTime; //for HZ interrupt
|
||||
|
||||
}Pxa255rtc;
|
||||
|
||||
Boolean pxa255rtcInit(Pxa255rtc* rtc, ArmMem* physMem, Pxa255ic* ic);
|
||||
void pxa255rtcUpdate(Pxa255rtc* rtc);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
122
src/pxa255/pxa255_TIMR.c
Normal file
122
src/pxa255/pxa255_TIMR.c
Normal file
@@ -0,0 +1,122 @@
|
||||
#include "pxa255_TIMR.h"
|
||||
#include "pxa255_mem.h"
|
||||
|
||||
|
||||
static void pxa255timrPrvRaiseLowerInts(Pxa255timr* timr){
|
||||
|
||||
pxa255icInt(timr->ic, PXA255_I_TIMR0, (timr->OSSR & 1) != 0);
|
||||
pxa255icInt(timr->ic, PXA255_I_TIMR1, (timr->OSSR & 2) != 0);
|
||||
pxa255icInt(timr->ic, PXA255_I_TIMR2, (timr->OSSR & 4) != 0);
|
||||
pxa255icInt(timr->ic, PXA255_I_TIMR3, (timr->OSSR & 8) != 0);
|
||||
}
|
||||
|
||||
static void pxa255timrPrvCheckMatch(Pxa255timr* timr, UInt8 idx){
|
||||
|
||||
UInt8 v = 1UL << idx;
|
||||
|
||||
if((timr->OSCR == timr->OSMR[idx]) && (timr->OIER & v)){
|
||||
timr->OSSR |= v;
|
||||
}
|
||||
}
|
||||
|
||||
static void pxa255timrPrvUpdate(Pxa255timr* timr){
|
||||
|
||||
pxa255timrPrvCheckMatch(timr, 0);
|
||||
pxa255timrPrvCheckMatch(timr, 1);
|
||||
pxa255timrPrvCheckMatch(timr, 2);
|
||||
pxa255timrPrvCheckMatch(timr, 3);
|
||||
}
|
||||
|
||||
static Boolean pxa255timrPrvMemAccessF(void* userData, UInt32 pa, UInt8 size, Boolean write, void* buf){
|
||||
|
||||
Pxa255timr* timr = userData;
|
||||
UInt32 val = 0;
|
||||
|
||||
if(size != 4) {
|
||||
err_str(__FILE__ ": Unexpected ");
|
||||
// err_str(write ? "write" : "read");
|
||||
// err_str(" of ");
|
||||
// err_dec(size);
|
||||
// err_str(" bytes to 0x");
|
||||
// err_hex(pa);
|
||||
// err_str("\r\n");
|
||||
return true; //we do not support non-word accesses
|
||||
}
|
||||
|
||||
pa = (pa - PXA255_TIMR_BASE) >> 2;
|
||||
|
||||
if(write){
|
||||
val = *(UInt32*)buf;
|
||||
|
||||
switch(pa){
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
timr->OSMR[pa] = val;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
timr->OSCR = val;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
timr->OSSR = timr->OSSR &~ val;
|
||||
pxa255timrPrvRaiseLowerInts(timr);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
timr->OWER = val;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
timr->OIER = val;
|
||||
pxa255timrPrvUpdate(timr);
|
||||
pxa255timrPrvRaiseLowerInts(timr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else{
|
||||
switch(pa){
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
val = timr->OSMR[pa];
|
||||
break;
|
||||
|
||||
case 4:
|
||||
val = timr->OSCR;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
val = timr->OSSR;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
val = timr->OWER;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
val = timr->OIER;
|
||||
break;
|
||||
}
|
||||
*(UInt32*)buf = val;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void pxa255timrInit(Pxa255timr* timr, Pxa255ic* ic){
|
||||
|
||||
__mem_zero(timr, sizeof(Pxa255timr));
|
||||
timr->ic = ic;
|
||||
}
|
||||
|
||||
void pxa255timrTick(Pxa255timr* timr){
|
||||
|
||||
timr->OSCR++;
|
||||
pxa255timrPrvUpdate(timr);
|
||||
pxa255timrPrvRaiseLowerInts(timr);
|
||||
}
|
||||
37
src/pxa255/pxa255_TIMR.h
Normal file
37
src/pxa255/pxa255_TIMR.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef _PXA255_TIMR_H_
|
||||
#define _PXA255_TIMR_H_
|
||||
|
||||
#include "pxa255_mem.h"
|
||||
#include "pxa255_CPU.h"
|
||||
#include "pxa255_IC.h"
|
||||
|
||||
|
||||
/*
|
||||
PXA255 OS timers controller
|
||||
|
||||
PURRPOSE: timers are useful for stuff :)
|
||||
|
||||
*/
|
||||
|
||||
#define PXA255_TIMR_BASE 0x40A00000UL
|
||||
#define PXA255_TIMR_SIZE 0x00010000UL
|
||||
|
||||
|
||||
typedef struct{
|
||||
|
||||
Pxa255ic* ic;
|
||||
|
||||
UInt32 OSMR[4]; //Match Register 0-3
|
||||
UInt32 OIER; //Interrupt Enable
|
||||
UInt32 OWER; //Watchdog enable
|
||||
UInt32 OSCR; //Counter Register
|
||||
UInt32 OSSR; //Status Register
|
||||
|
||||
}Pxa255timr;
|
||||
|
||||
void pxa255timrInit(Pxa255timr* timr, Pxa255ic* ic);
|
||||
void pxa255timrTick(Pxa255timr* timr);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
588
src/pxa255/pxa255_UART.c
Normal file
588
src/pxa255/pxa255_UART.c
Normal file
@@ -0,0 +1,588 @@
|
||||
#include "pxa255_UART.h"
|
||||
#include "pxa255_mem.h"
|
||||
|
||||
|
||||
|
||||
//TODO: signal handler for Ctl+C and send break to fifo :)
|
||||
//todo: recalc ints after eveyr write and every call to "process" from SoC
|
||||
|
||||
|
||||
#define UART_IER_DMAE 0x80 //DMA enable
|
||||
#define UART_IER_UUE 0x40 //Uart unit enable
|
||||
#define UART_IER_NRZE 0x20 //NRZI enable
|
||||
#define UART_IER_RTOIE 0x10 //transmit timeout interrupt enable
|
||||
#define UART_IER_MIE 0x08 //modem interrupt enable
|
||||
#define UART_IER_RLSE 0x04 //receiver line status interrupt enable
|
||||
#define UART_IER_TIE 0x02 //transmit data request interrupt enable
|
||||
#define UART_IER_RAVIE 0x01 //receiver data available interrupt enable
|
||||
|
||||
#define UART_IIR_FIFOES 0xC0 //fifo enable status
|
||||
#define UART_IIR_TOD 0x08 //character timout interrupt pending
|
||||
#define UART_IIR_RECV_ERR 0x06 //receive error(overrun, parity, framing, break)
|
||||
#define UART_IIR_RECV_DATA 0x04 //receive data available
|
||||
#define UART_IIR_RCV_TIMEOUT 0x0C //receive data in buffer and been a while since we've seen more
|
||||
#define UART_IIR_SEND_DATA 0x02 //transmit fifo requests data
|
||||
#define UART_IIR_MODEM_STAT 0x00 //modem lines changed state(CTS, DSR, DI, DCD)
|
||||
#define UART_IIR_NOINT 0x01 //no interrupt pending
|
||||
|
||||
|
||||
#define UART_FCR_ITL_MASK 0xC0 //mask for ITL part of FCR
|
||||
#define UART_FCR_ITL_1 0x00 //interrupt when >=1 byte in recv fifo
|
||||
#define UART_FCR_ITL_8 0x40 //interrupt when >=8 byte in recv fifo
|
||||
#define UART_FCR_ITL_16 0x80 //interrupt when >=16 byte in recv fifo
|
||||
#define UART_FCR_ITL_32 0xC0 //interrupt when >=32 byte in recv fifo
|
||||
#define UART_FCR_RESETTF 0x04 //reset tranmitter fifo
|
||||
#define UART_FCR_RESETRF 0x02 //reset receiver fifo
|
||||
#define UART_FCR_TRFIFOE 0x01 //transmit and receive fifo enable
|
||||
|
||||
#define UART_LCR_DLAB 0x80 //divisor latch access bit
|
||||
#define UART_LCR_SB 0x40 //send break
|
||||
#define UART_LCR_STKYP 0x20 //sticky parity (send parity bit but dont care what value)
|
||||
#define UART_LCR_EPS 0x10 //even parity select
|
||||
#define UART_LCR_PEN 0x08 //parity enable
|
||||
#define UART_LCR_STB 0x04 //stop bits (1 = 2, 0 = 1)
|
||||
#define UART_LCR_WLS_MASK 0x03 //mask for WLS values
|
||||
#define UART_LCR_WLS_8 0x03 //8 bit words
|
||||
#define UART_LCR_WLS_7 0x02 //7 bit words
|
||||
#define UART_LCR_WLS_6 0x01 //6 bit words
|
||||
#define UART_LCR_WLS_5 0x00 //5 bit words
|
||||
|
||||
#define UART_LSR_FIFOE 0x80 //fifo contails an error (framing, parity, or break)
|
||||
#define UART_LSR_TEMT 0x40 //tranmitter empty (shift reg is empty and no more byte sin fifo/no byte in holding reg)
|
||||
#define UART_LSR_TDRQ 0x20 //transmitter data request (see docs)
|
||||
#define UART_LSR_BI 0x10 //send when char at front of fifo (or in holding reg) was a break char (chr reads as zero by itself)
|
||||
#define UART_LSR_FE 0x08 //same as above, but for framing errors
|
||||
#define UART_LSR_PE 0x04 //dame as above, but for parity errors
|
||||
#define UART_LSR_OE 0x02 //recv fifo overran
|
||||
#define UART_LSR_DR 0x01 //byte received
|
||||
|
||||
#define UART_MCR_LOOP 0x10 //loop modem control lines (not full loopback)
|
||||
#define UART_MCR_OUT2 0x08 //when loop is 0 enables or disables UART interrupts
|
||||
#define UART_MCR_OUT1 0x04 //force RI to 1
|
||||
#define UART_MCR_RTS 0x02 //1 -> nRTS is 0
|
||||
#define UART_MCR_DTR 0x01 //0 -> nDTR is 0
|
||||
|
||||
#define UART_MSR_DCD 0x80
|
||||
#define UART_MSR_RI 0x40
|
||||
#define UART_MSR_DSR 0x20
|
||||
#define UART_MSR_CTS 0x10
|
||||
#define UART_MSR_DDCD 0x08 //dcd changed since last read
|
||||
#define UART_MSR_TERI 0x04 //ri has changed from 0 to 1 since last read
|
||||
#define UART_MSR_DDSR 0x02 //dsr changed since last read
|
||||
#define UART_MSR_DCTS 0x01 //cts changed since last read
|
||||
|
||||
|
||||
|
||||
static void pxa255uartPrvRecalc(Pxa255uart* uart);
|
||||
|
||||
|
||||
static void pxa255uartPrvIrq(Pxa255uart* uart, Boolean raise){
|
||||
|
||||
pxa255icInt(uart->ic, uart->irq, !(uart->MCR & UART_MCR_LOOP) && (uart->MCR & UART_MCR_OUT2) && raise/* only raise if ints are enabled */);
|
||||
}
|
||||
|
||||
static UInt16 pxa255uartPrvDefaultRead(_UNUSED_ void* userData){ //these are special funcs since they always get their own userData - the uart pointer :)
|
||||
|
||||
return UART_CHAR_NONE; //we read nothing..as always
|
||||
}
|
||||
|
||||
static void pxa255uartPrvDefaultWrite(_UNUSED_ UInt16 chr, _UNUSED_ void* userData){ //these are special funcs since they always get their own userData - the uart pointer :)
|
||||
|
||||
//nothing to do here
|
||||
}
|
||||
|
||||
static UInt16 pxa255uartPrvGetchar(Pxa255uart* uart){
|
||||
|
||||
Pxa255UartReadF func = uart->readF;
|
||||
void* data = (func == pxa255uartPrvDefaultRead) ? uart : uart->accessFuncsData;
|
||||
|
||||
return func(data);
|
||||
}
|
||||
|
||||
static void pxa255uartPrvPutchar(Pxa255uart* uart, UInt16 chr){
|
||||
|
||||
Pxa255UartWriteF func = uart->writeF;
|
||||
void* data = (func == pxa255uartPrvDefaultWrite) ? uart : uart->accessFuncsData;
|
||||
|
||||
func(chr, data);
|
||||
}
|
||||
|
||||
UInt8 pxa255uartPrvFifoUsed(UartFifo* fifo){ //return num spots used
|
||||
|
||||
UInt8 v;
|
||||
|
||||
if(fifo->read == UART_FIFO_EMPTY) return 0;
|
||||
v = fifo->write + UART_FIFO_DEPTH - fifo->read;
|
||||
if(v > UART_FIFO_DEPTH) v -=UART_FIFO_DEPTH;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void pxa255uartPrvFifoFlush(UartFifo* fifo){
|
||||
|
||||
fifo->read = UART_FIFO_EMPTY;
|
||||
fifo->write = UART_FIFO_EMPTY;
|
||||
}
|
||||
|
||||
Boolean pxa255uartPrvFifoPut(UartFifo* fifo, UInt16 val){ //return success
|
||||
|
||||
if(fifo->read == UART_FIFO_EMPTY){
|
||||
|
||||
fifo->read = 0;
|
||||
fifo->write = 1;
|
||||
fifo->buf[0] = val;
|
||||
}
|
||||
else if(fifo->read != fifo->write){ //only if not full
|
||||
|
||||
fifo->buf[fifo->write++] = val;
|
||||
if(fifo->write == UART_FIFO_DEPTH) fifo->write = 0;
|
||||
}
|
||||
else return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
UInt16 pxa255uartPrvFifoGet(UartFifo* fifo){
|
||||
|
||||
UInt16 ret;
|
||||
|
||||
if(fifo->read == UART_FIFO_EMPTY){
|
||||
|
||||
ret = 0xFFFF; //why not?
|
||||
}
|
||||
else{
|
||||
|
||||
ret = fifo->buf[fifo->read++];
|
||||
if(fifo->read == UART_FIFO_DEPTH) fifo->read = 0;
|
||||
|
||||
if(fifo->read == fifo->write){ //it is now empty
|
||||
|
||||
fifo->read = UART_FIFO_EMPTY;
|
||||
fifo->write = UART_FIFO_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
UInt16 pxa255uartPrvFifoPeekNth(UartFifo* fifo, UInt8 n){
|
||||
|
||||
UInt16 ret;
|
||||
|
||||
|
||||
if(fifo->read == UART_FIFO_EMPTY){
|
||||
|
||||
ret = 0xFFFF; //why not?
|
||||
}
|
||||
else{
|
||||
|
||||
n += fifo->read;
|
||||
if(n >= UART_FIFO_DEPTH) n-= UART_FIFO_DEPTH;
|
||||
ret = fifo->buf[n];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
UInt16 pxa255uartPrvFifoPeek(UartFifo* fifo){
|
||||
|
||||
return pxa255uartPrvFifoPeekNth(fifo, 0);
|
||||
}
|
||||
|
||||
|
||||
static void sendVal(Pxa255uart* uart, UInt16 v){
|
||||
|
||||
if(uart->LSR & UART_LSR_TEMT){ //if transmit, put in shift register immediately if it's idle
|
||||
|
||||
uart->transmitShift = v;
|
||||
uart->LSR &=~ UART_LSR_TEMT;
|
||||
}
|
||||
else if(uart->FCR & UART_FCR_TRFIFOE){ //put in tx fifo if in fifo mode
|
||||
|
||||
pxa255uartPrvFifoPut(&uart->TX, v);
|
||||
if(pxa255uartPrvFifoUsed(&uart->TX) > UART_FIFO_DEPTH / 2){ //we go went below half-full buffer - set appropriate bit...
|
||||
|
||||
uart->LSR &=~ UART_LSR_TDRQ;
|
||||
}
|
||||
}
|
||||
else if(uart->LSR & UART_LSR_TDRQ){ //send without fifo if in polled mode
|
||||
|
||||
uart->transmitHolding = v;
|
||||
uart->LSR &=~ UART_LSR_TDRQ;
|
||||
}
|
||||
else{
|
||||
|
||||
//nothing to do - buffer is full so we ignore this request
|
||||
}
|
||||
}
|
||||
|
||||
static Boolean pxa255uartPrvMemAccessF(void* userData, UInt32 pa, UInt8 size, Boolean write, void* buf){
|
||||
|
||||
Pxa255uart* uart = userData;
|
||||
Boolean DLAB = (uart->LCR & UART_LCR_DLAB) != 0;
|
||||
Boolean recalcValues = false;
|
||||
UInt8 t, val = 0;
|
||||
|
||||
if(size != 4 && size != 1) {
|
||||
err_str(__FILE__ ": Unexpected ");
|
||||
// err_str(write ? "write" : "read");
|
||||
// err_str(" of ");
|
||||
// err_dec(size);
|
||||
// err_str(" bytes to 0x");
|
||||
// err_hex(pa);
|
||||
// err_str("\r\n");
|
||||
return true; //we do not support non-word accesses
|
||||
}
|
||||
|
||||
pa = (pa - uart->baseAddr) >> 2;
|
||||
|
||||
if(write){
|
||||
recalcValues = true;
|
||||
val = (size == 1) ? *(UInt8*)buf : *(UInt32*)buf;
|
||||
|
||||
switch(pa){
|
||||
case 0:
|
||||
if(DLAB){ //if DLAB - set "baudrate"...
|
||||
uart->DLL = val;
|
||||
recalcValues = false;
|
||||
}
|
||||
else{
|
||||
|
||||
sendVal(uart, val);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if(DLAB){
|
||||
|
||||
uart->DLH = val;
|
||||
recalcValues = false;
|
||||
}
|
||||
else{
|
||||
t = uart->IER ^ val;
|
||||
|
||||
if(t & UART_IER_DMAE){
|
||||
|
||||
err_str("pxa255UART: DMA mode cannot be enabled");
|
||||
t &=~ UART_IER_DMAE; //undo the change
|
||||
}
|
||||
|
||||
if(t & UART_IER_UUE){
|
||||
|
||||
if(val & UART_IER_UUE){
|
||||
|
||||
uart->LSR = UART_LSR_TEMT | UART_LSR_TDRQ;
|
||||
uart->MSR = UART_MSR_CTS;
|
||||
}
|
||||
}
|
||||
|
||||
uart->IER ^= t;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
t = uart->FCR ^ val;
|
||||
if(t & UART_FCR_TRFIFOE){
|
||||
if(val & UART_FCR_TRFIFOE){ //fifos are now on - perform other actions as requested
|
||||
|
||||
if(val & UART_FCR_RESETRF){
|
||||
|
||||
pxa255uartPrvFifoFlush(&uart->RX); //clear the RX fifo now
|
||||
}
|
||||
if(val & UART_FCR_RESETTF){
|
||||
|
||||
pxa255uartPrvFifoFlush(&uart->TX); //clear the TX fifo now
|
||||
uart->LSR = UART_LSR_TEMT | UART_LSR_TDRQ;
|
||||
}
|
||||
uart->IIR = UART_IIR_FIFOES |UART_IIR_NOINT;
|
||||
}
|
||||
else{
|
||||
pxa255uartPrvFifoFlush(&uart->TX);
|
||||
pxa255uartPrvFifoFlush(&uart->RX);
|
||||
uart->LSR = UART_LSR_TEMT | UART_LSR_TDRQ;
|
||||
uart->IIR = UART_IIR_NOINT;
|
||||
}
|
||||
}
|
||||
uart->FCR = val;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
t = uart->LCR ^ val;
|
||||
if(t & UART_LCR_SB){
|
||||
if(val & UART_LCR_SB){ //break set (tx line pulled low)
|
||||
|
||||
|
||||
}
|
||||
else{ //break cleared (tx line released)
|
||||
|
||||
sendVal(uart, UART_CHAR_BREAK);
|
||||
}
|
||||
}
|
||||
uart->LCR = val;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
uart->MCR = val;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
uart->SPR = val;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
uart->ISR = val;
|
||||
if(val & 3){
|
||||
err_str("UART: IrDA mode set on UART\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else{
|
||||
switch(pa){
|
||||
case 0:
|
||||
if(DLAB) val = uart->DLL;
|
||||
else if(!(uart->LSR & UART_LSR_DR)){ //no data-> too bad
|
||||
|
||||
val = 0;
|
||||
}
|
||||
else if(uart->FCR & UART_FCR_TRFIFOE){ //fifo mode -> read fifo
|
||||
|
||||
val = pxa255uartPrvFifoGet(&uart->RX);
|
||||
if(!pxa255uartPrvFifoUsed(&uart->RX)) uart->LSR &=~ UART_LSR_DR;
|
||||
recalcValues = true; //error bits might have changed
|
||||
}
|
||||
else{ //polled mode -> read rx polled reg
|
||||
|
||||
val = uart->receiveHolding;
|
||||
uart->LSR &=~ UART_LSR_DR;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if(DLAB) val = uart->DLH;
|
||||
else val = uart->IER;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
val = uart->IIR;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
val = uart->LCR;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
val = uart->MCR;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
val = uart->LSR;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
val = uart->MSR;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
val = uart->SPR;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
val = uart->ISR;
|
||||
break;
|
||||
}
|
||||
if(size == 1) *(UInt8*)buf = val;
|
||||
else *(UInt32*)buf = val;
|
||||
}
|
||||
|
||||
if(recalcValues) pxa255uartPrvRecalc(uart);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void pxa255uartSetFuncs(Pxa255uart* uart, Pxa255UartReadF readF, Pxa255UartWriteF writeF, void* userData){
|
||||
|
||||
if(!readF) readF = pxa255uartPrvDefaultRead; //these are special funcs since they get their own private data - the uart :)
|
||||
if(!writeF) writeF = pxa255uartPrvDefaultWrite;
|
||||
|
||||
uart->readF = readF;
|
||||
uart->writeF = writeF;
|
||||
uart->accessFuncsData = userData;
|
||||
}
|
||||
|
||||
Boolean pxa255uartInit(Pxa255uart* uart, ArmMem* physMem, Pxa255ic* ic, UInt32 baseAddr, UInt8 irq){
|
||||
|
||||
__mem_zero(uart, sizeof(Pxa255uart));
|
||||
uart->ic = ic;
|
||||
uart->irq = irq;
|
||||
uart->baseAddr = baseAddr;
|
||||
uart->IIR = UART_IIR_NOINT;
|
||||
uart->IER = UART_IER_UUE | UART_IER_NRZE; //uart on
|
||||
uart->LSR = UART_LSR_TEMT | UART_LSR_TDRQ;
|
||||
uart->MSR = UART_MSR_CTS;
|
||||
pxa255uartPrvFifoFlush(&uart->TX);
|
||||
pxa255uartPrvFifoFlush(&uart->RX);
|
||||
|
||||
|
||||
pxa255uartSetFuncs(uart, NULL, NULL, NULL);
|
||||
|
||||
return memRegionAdd(physMem, baseAddr, PXA255_UART_SIZE, pxa255uartPrvMemAccessF, uart);
|
||||
}
|
||||
|
||||
void pxa255uartProcess(Pxa255uart* uart){ //send and rceive up to one character
|
||||
|
||||
UInt8 t;
|
||||
UInt16 v;
|
||||
|
||||
//first process sending (if any)
|
||||
if(!(uart->LSR & UART_LSR_TEMT)){
|
||||
|
||||
pxa255uartPrvPutchar(uart, uart->transmitShift);
|
||||
|
||||
if(uart->FCR & UART_FCR_TRFIFOE){ //fifo mode
|
||||
|
||||
t = pxa255uartPrvFifoUsed(&uart->TX);
|
||||
|
||||
if(t--){
|
||||
|
||||
uart->transmitShift = pxa255uartPrvFifoGet(&uart->TX);
|
||||
if(t <= UART_FIFO_DEPTH / 2) uart->LSR |= UART_LSR_TDRQ; //above half full - clear TDRQ bit
|
||||
}
|
||||
else{
|
||||
|
||||
uart->LSR |= UART_LSR_TEMT;
|
||||
}
|
||||
}
|
||||
else if (uart->LSR & UART_LSR_TDRQ){
|
||||
|
||||
uart->LSR |= UART_LSR_TEMT;
|
||||
}
|
||||
else{
|
||||
|
||||
uart->transmitShift = uart->transmitHolding;
|
||||
uart->LSR |= UART_LSR_TDRQ;
|
||||
}
|
||||
}
|
||||
|
||||
//now process receiving
|
||||
v = pxa255uartPrvGetchar(uart);
|
||||
if(v != UART_CHAR_NONE){
|
||||
|
||||
uart->cyclesSinceRecv = 0;
|
||||
uart->LSR |= UART_LSR_DR;
|
||||
|
||||
if(uart->FCR & UART_FCR_TRFIFOE){ //fifo mode
|
||||
|
||||
if(!pxa255uartPrvFifoPut(&uart->RX, v)){
|
||||
|
||||
uart->LSR |= UART_LSR_OE;
|
||||
}
|
||||
}
|
||||
else{
|
||||
|
||||
if(uart->LSR & UART_LSR_DR) uart->LSR |= UART_LSR_OE;
|
||||
else uart->receiveHolding = v;
|
||||
}
|
||||
}
|
||||
else if(uart->cyclesSinceRecv <= 4){
|
||||
uart->cyclesSinceRecv++;
|
||||
}
|
||||
|
||||
pxa255uartPrvRecalc(uart);
|
||||
}
|
||||
|
||||
static void pxa255uartPrvRecalcCharBits(Pxa255uart* uart, UInt16 c){
|
||||
|
||||
if(c & UART_CHAR_BREAK) uart->LSR |= UART_LSR_BI;
|
||||
if(c & UART_CHAR_FRAME_ERR) uart->LSR |= UART_LSR_FE;
|
||||
if(c & UART_CHAR_PAR_ERR) uart->LSR |= UART_LSR_PE;
|
||||
}
|
||||
|
||||
static void pxa255uartPrvRecalc(Pxa255uart* uart){
|
||||
|
||||
Boolean errorSet = false;
|
||||
UInt8 v;
|
||||
|
||||
uart->LSR &=~ UART_LSR_FIFOE;
|
||||
uart->IIR &= UART_IIR_FIFOES; //clear all other bits...
|
||||
uart->LSR &=~ (UART_LSR_BI | UART_LSR_FE | UART_LSR_PE);
|
||||
|
||||
if(uart->FCR & UART_FCR_TRFIFOE){ //fifo mode
|
||||
|
||||
|
||||
//check rx fifo for errors
|
||||
for(v = 0; v < pxa255uartPrvFifoUsed(&uart->RX); v++){
|
||||
|
||||
if((pxa255uartPrvFifoPeekNth(&uart->RX, v) >> 8) && (uart->IER & UART_IER_RLSE)){
|
||||
|
||||
uart->LSR |= UART_LSR_FIFOE;
|
||||
uart->IIR |= UART_IIR_RECV_ERR;
|
||||
errorSet = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
v = pxa255uartPrvFifoUsed(&uart->RX);
|
||||
if(v){
|
||||
pxa255uartPrvRecalcCharBits(uart, pxa255uartPrvFifoPeek(&uart->RX));
|
||||
}
|
||||
|
||||
switch(uart->FCR & UART_FCR_ITL_MASK){
|
||||
|
||||
case UART_FCR_ITL_1:
|
||||
v = v >= 1;
|
||||
break;
|
||||
|
||||
case UART_FCR_ITL_8:
|
||||
v = v >= 8;
|
||||
break;
|
||||
|
||||
case UART_FCR_ITL_16:
|
||||
v = v >= 16;
|
||||
break;
|
||||
|
||||
case UART_FCR_ITL_32:
|
||||
v = v >= 32;
|
||||
break;
|
||||
}
|
||||
if(v && (uart->IER & UART_IER_RAVIE) && !errorSet){
|
||||
|
||||
errorSet = true;
|
||||
uart->IIR |= UART_IIR_RECV_DATA;
|
||||
}
|
||||
if(pxa255uartPrvFifoUsed(&uart->RX) && uart->cyclesSinceRecv >= 4 && (uart->IER & UART_IER_RAVIE) && !errorSet){
|
||||
|
||||
errorSet = true;
|
||||
uart->IIR |= UART_IIR_RCV_TIMEOUT;
|
||||
}
|
||||
}
|
||||
else{ //polling mode
|
||||
|
||||
UInt16 c = uart->receiveHolding;
|
||||
|
||||
if(uart->LSR & UART_LSR_DR){
|
||||
|
||||
pxa255uartPrvRecalcCharBits(uart, c);
|
||||
|
||||
if((c >> 8) && !errorSet && (uart->IER & UART_IER_RLSE)){
|
||||
|
||||
uart->IIR |= UART_IIR_RECV_ERR;
|
||||
errorSet = true;
|
||||
}
|
||||
else if(!errorSet && (uart->IER & UART_IER_RAVIE)){
|
||||
|
||||
uart->IIR |= UART_IIR_RECV_DATA;
|
||||
errorSet = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(uart->LSR & UART_LSR_TDRQ && !errorSet && (uart->IER & UART_IER_TIE)){
|
||||
|
||||
errorSet = true;
|
||||
uart->IIR |= UART_IIR_SEND_DATA;
|
||||
}
|
||||
|
||||
if(!errorSet) uart->IIR |= UART_IIR_NOINT;
|
||||
pxa255uartPrvIrq(uart, errorSet);
|
||||
}
|
||||
88
src/pxa255/pxa255_UART.h
Normal file
88
src/pxa255/pxa255_UART.h
Normal file
@@ -0,0 +1,88 @@
|
||||
#ifndef _PXA255_UART_H_
|
||||
#define _PXA255_UART_H_
|
||||
|
||||
#include "pxa255_mem.h"
|
||||
#include "pxa255_CPU.h"
|
||||
#include "pxa255_IC.h"
|
||||
|
||||
|
||||
/*
|
||||
PXA255 UARTs
|
||||
|
||||
PXA255 has three. they are identical, but at diff base addresses. this implements one. instanciate more than one of this struct to make all 3 work if needed.
|
||||
PURRPOSE: this is how linux talks to us :)
|
||||
|
||||
|
||||
by default we read nothing and write nowhere (buffer drains fast into nothingness)
|
||||
this can be changed by addidng appropriate callbacks
|
||||
|
||||
*/
|
||||
|
||||
#define PXA255_FFUART_BASE 0x40100000UL
|
||||
#define PXA255_BTUART_BASE 0x40200000UL
|
||||
#define PXA255_STUART_BASE 0x40700000UL
|
||||
#define PXA255_UART_SIZE 0x00010000UL
|
||||
|
||||
#define UART_FIFO_DEPTH 64
|
||||
|
||||
|
||||
#define UART_CHAR_BREAK 0x800
|
||||
#define UART_CHAR_FRAME_ERR 0x400
|
||||
#define UART_CHAR_PAR_ERR 0x200
|
||||
#define UART_CHAR_NONE 0x100
|
||||
|
||||
typedef UInt16 (*Pxa255UartReadF)(void* userData);
|
||||
typedef void (*Pxa255UartWriteF)(UInt16 chr, void* userData);
|
||||
|
||||
#define UART_FIFO_EMPTY 0xFF
|
||||
|
||||
typedef struct{
|
||||
|
||||
UInt8 read;
|
||||
UInt8 write;
|
||||
UInt16 buf[UART_FIFO_DEPTH];
|
||||
|
||||
}UartFifo;
|
||||
|
||||
typedef struct{
|
||||
|
||||
Pxa255ic* ic;
|
||||
UInt32 baseAddr;
|
||||
|
||||
Pxa255UartReadF readF;
|
||||
Pxa255UartWriteF writeF;
|
||||
void* accessFuncsData;
|
||||
|
||||
UartFifo TX, RX;
|
||||
|
||||
UInt16 transmitShift; //char currently "sending"
|
||||
UInt16 transmitHolding; //holding register for no-fifo mode
|
||||
|
||||
UInt16 receiveHolding; //char just received
|
||||
|
||||
UInt8 irq:5;
|
||||
UInt8 cyclesSinceRecv:3;
|
||||
|
||||
UInt8 IER; //interrupt enable register
|
||||
UInt8 IIR; //interrupt information register
|
||||
UInt8 FCR; //fifo control register
|
||||
UInt8 LCR; //line control register
|
||||
UInt8 LSR; //line status register
|
||||
UInt8 MCR; //modem control register
|
||||
UInt8 MSR; //modem status register
|
||||
UInt8 SPR; //scratchpad register
|
||||
UInt8 DLL; //divisor latch low
|
||||
UInt8 DLH; //divior latch high;
|
||||
UInt8 ISR; //infrared selection register
|
||||
|
||||
|
||||
|
||||
}Pxa255uart;
|
||||
|
||||
Boolean pxa255uartInit(Pxa255uart* uart, ArmMem* physMem, Pxa255ic* ic, UInt32 baseAddr, UInt8 irq);
|
||||
void pxa255uartProcess(Pxa255uart* uart); //write out data in TX fifo and read data into RX fifo
|
||||
|
||||
void pxa255uartSetFuncs(Pxa255uart* uart, Pxa255UartReadF readF, Pxa255UartWriteF writeF, void* userData);
|
||||
|
||||
#endif
|
||||
|
||||
25
src/pxa255/pxa255_math64.h
Normal file
25
src/pxa255/pxa255_math64.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef UARM_MATH_64_H
|
||||
#define UARM_MATH_64_H
|
||||
|
||||
#include "pxa255_types.h"
|
||||
|
||||
static inline UInt64 u64_from_halves(UInt32 hi, UInt32 lo) { return (((UInt64)hi) << 32ULL) | ((UInt64)lo); }
|
||||
static inline UInt64 u64_32_to_64(UInt32 v) { return (UInt64)v; }
|
||||
static inline UInt32 u64_64_to_32(UInt64 v) { return (UInt32)v; }
|
||||
static inline UInt32 u64_get_hi(UInt64 v) { return (UInt32)(v >> 32ULL); }
|
||||
static inline UInt64 u64_add(UInt64 a, UInt64 b) { return a + b; }
|
||||
static inline UInt64 u64_add32(UInt64 a, UInt32 b) { return a + (UInt64)b; }
|
||||
static inline UInt64 u64_umul3232(UInt32 a, UInt32 b) { return ((UInt64)a) * ((UInt64)b); } //sad but true: gcc has no u32xu32->64 multiply
|
||||
static inline UInt64 u64_smul3232(Int32 a, Int32 b) { return ((Int64)a) * ((Int64)b); } //sad but true: gcc has no s32xs32->64 multiply
|
||||
static inline UInt64 u64_shr(UInt64 a, unsigned bits) { return a >> (UInt64)bits; }
|
||||
static inline UInt64 u64_shl(UInt64 a, unsigned bits) { return a << (UInt64)bits; }
|
||||
static inline UInt64 u64_xtnd32(UInt64 a) { if(a & 0x80000000UL) a |= (((UInt64)-1) << 32ULL); return a; }
|
||||
static inline Boolean u64_isZero(UInt64 a) { return a == 0; }
|
||||
static inline UInt64 u64_inc(UInt64 a) { return a + 1ULL; }
|
||||
static inline UInt64 u64_and(UInt64 a, UInt64 b) { return a & b; }
|
||||
static inline UInt64 u64_zero(void) { return 0; }
|
||||
static inline UInt64 u64_sub(UInt64 a, UInt64 b) { return a - b; }
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
96
src/pxa255/pxa255_mem.c
Normal file
96
src/pxa255/pxa255_mem.c
Normal file
@@ -0,0 +1,96 @@
|
||||
#include "pxa255_mem.h"
|
||||
|
||||
|
||||
|
||||
void memInit(ArmMem* mem){
|
||||
|
||||
UInt8 i;
|
||||
|
||||
for(i = 0; i < MAX_MEM_REGIONS; i++){
|
||||
mem->regions[i].sz = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void memDeinit(_UNUSED_ ArmMem* mem){
|
||||
|
||||
//nothing here
|
||||
}
|
||||
|
||||
Boolean memRegionAdd(ArmMem* mem, UInt32 pa, UInt32 sz, ArmMemAccessF aF, void* uD){
|
||||
|
||||
UInt8 i;
|
||||
|
||||
//check for intersection with another region
|
||||
|
||||
for(i = 0; i < MAX_MEM_REGIONS; i++){
|
||||
|
||||
if(!mem->regions[i].sz) continue;
|
||||
if((mem->regions[i].pa <= pa && mem->regions[i].pa + mem->regions[i].sz > pa) || (pa <= mem->regions[i].pa && pa + sz > mem->regions[i].pa)){
|
||||
|
||||
return false; //intersection -> fail
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//find a free region and put it there
|
||||
|
||||
for(i = 0; i < MAX_MEM_REGIONS; i++){
|
||||
if(mem->regions[i].sz == 0){
|
||||
|
||||
mem->regions[i].pa = pa;
|
||||
mem->regions[i].sz = sz;
|
||||
mem->regions[i].aF = aF;
|
||||
mem->regions[i].uD = uD;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//fail miserably
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Boolean memRegionDel(ArmMem* mem, UInt32 pa, UInt32 sz){
|
||||
|
||||
UInt8 i;
|
||||
|
||||
for(i = 0; i < MAX_MEM_REGIONS; i++){
|
||||
if(mem->regions[i].pa == pa && mem->regions[i].sz ==sz){
|
||||
|
||||
mem->regions[i].sz = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Boolean memAccess(ArmMem* mem, UInt32 addr, UInt8 size, Boolean write, void* buf){
|
||||
|
||||
UInt8 i;
|
||||
|
||||
for(i = 0; i < MAX_MEM_REGIONS; i++){
|
||||
if(mem->regions[i].pa <= addr && mem->regions[i].pa + mem->regions[i].sz > addr){
|
||||
|
||||
return mem->regions[i].aF(mem->regions[i].uD, addr, size, write & 0x7F, buf);
|
||||
}
|
||||
}
|
||||
|
||||
if(!(write & 0x80)){ //high bit in write tells us to not print this error (used by gdb stub)
|
||||
|
||||
err_str("Memory ");
|
||||
err_str(write ? "write" : "read");
|
||||
err_str(" of ");
|
||||
err_dec(size);
|
||||
err_str(" bytes at physical addr 0x");
|
||||
err_hex(addr);
|
||||
err_str(" fails, halting\r\n");
|
||||
while(1);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user