mirror of
https://github.com/libretro/Mu.git
synced 2026-02-13 21:24:19 +00:00
Compare commits
12 Commits
1.3.2
...
tungstenT3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b39468a32 | ||
|
|
c33be1988d | ||
|
|
82aa0c6fc0 | ||
|
|
018ed97874 | ||
|
|
be43d3a39a | ||
|
|
ee822d2800 | ||
|
|
46918f8d0f | ||
|
|
5601f0c3d4 | ||
|
|
349ff5d92f | ||
|
|
be02f9b313 | ||
|
|
87f5312954 | ||
|
|
34426a791b |
@@ -1,5 +1,5 @@
|
||||
T3 locks up endlessly checking TSC2101 interrupts
|
||||
QT release build is broken
|
||||
|
||||
Fixed:
|
||||
Trys to turn the CPU off when it shouldnt(kernel was crashing, the MMU from firebird is not working)
|
||||
68K Palm OS randomly locks up for 1-2 seconds(caused by interrupt handling code not turning the CPU back on for masked interrupts when it should because of faulty interrupt cacheing, Caused by commit: 1567cbce674741bc13be92b0847f59a5cc68335a)
|
||||
132
bugs/fixed/fixedUnimplementedHardware11292019.txt
Normal file
132
bugs/fixed/fixedUnimplementedHardware11292019.txt
Normal file
@@ -0,0 +1,132 @@
|
||||
memory dumping dosent work for OS 5 yet
|
||||
Endian compatibility is broken(the CPU state needs to be standardized)
|
||||
RetroArch port crashes on exit(needed to check if environ_cb returned true, switched to libretro-common filestreams too)
|
||||
make the headers belong to the main file of the target chip being emulated for specs
|
||||
remove specs folder
|
||||
UART1 USTCNT1
|
||||
trying to beam anything will lock up the OS
|
||||
Qt dosent have a hybrid file/folder selector so apps will always have to be launched from folders for now
|
||||
File Installer isnt working yet
|
||||
Qt GUI dosent resize properly with 320x320 framebuffer
|
||||
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)
|
||||
(CPU)TCN2 is unimplemented and seems to be used by the same routines that turn the CPU off for sleep mode(it is implemented now and it wasn't the cause of the sleep mode issue)
|
||||
(CPU)SCR privilege violation doesnt trigger the same as on hardware(likely has to do with not emulating the unused chip selects)(I added those chip selects)
|
||||
(CPU)Trace T1 bit, should be unused by the OS and applications anyway(this was fixed long ago, don't know why it was still listed?)
|
||||
(SD Card)data blocks won't work properly when CRC checks are enabled
|
||||
(SD Card)the CRC of CSD and CID are invalid
|
||||
unemulated chip select(CSB1) privilege violation interrupts are not handled properly
|
||||
SD card can't be inserted in RetroArch port
|
||||
USB chip seems to share an interrupt with the SD card(the SD card interrupt is unimplemented at boot and triggers a printf over USB instead, if USB is not properly emulated that will cause a lock up)(enough of the USB is now emulated to prevent this)
|
||||
(SD Card)need to an input option for the write protect switch on the side of the SD card(will be fixed at input time since you can't flip it when in the slot)
|
||||
(SD Card)memory block ordering is broken
|
||||
(SD Card)block length is fixed at 512 right now(this is actually proper behavior for newer SDSC cards)
|
||||
(SD Card)need to dump valid SCR
|
||||
(SD Card)SEND_SCR may not be sending in the proper format(the return data format was not specified in the spec I read)(data packet format is working)
|
||||
SPI1(the SD card interface)(SPI1 master mode seems fully implemented now)
|
||||
SD card can't be read or written to by Palm OS(SPI transactions are going both ways now, some flash chip read commands are implemented too)
|
||||
(ADS7846)need to trigger a false PENIRQ interrupt on reading certain channels(this is being done now)
|
||||
(MakePalmBitmap)the small icons are corrupt
|
||||
(MakePalmBitmap)the non 16 bpp large icons are corrupt
|
||||
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)(IRQ2 doesnt change when SD card chip select is toggled regardless of wether the card is inserted)
|
||||
the "Fatal Error" dialog reset button doesnt work, may not be a bug, could just an be issue with certain errors needing a full reset(its error dependent, one bug proved the other bug wasn't actually a bug)
|
||||
don't know if flushing the PWM1 FIFO sets all the bytes to 0x00, or just sets the read pointer to the write pointer preserving the newest sample and making the size 0(readPtr = writePtr has significantly cleaner audio then 0ing it out though(they sound the same now, this just masked the actual issue))
|
||||
edge triggered INT* don't clear on write to ISR when masked in IMR(I needed to use (ints & ~IMR) not (ints & IMR) which only triggers disable interrupts while blocking active ones)
|
||||
PDKBEN also has a hack for power on(its been removed)
|
||||
On hardware PDIRQEG seems not to actually work at all(CPUID:0x57000000)(it does)
|
||||
may need to trigger an interrupt if a IMR masked interrupt becomes unmasked and its bit is still set in IPR(already doing that)
|
||||
power button must be pushed twice to turn the CPU back on(when debugging it works the first time, then must be pushed twice to turn off instead of on)(this also occurs in the RetroArch build)
|
||||
the power button double press issue may be because the button map I am using was taken from POSE source, the power button may be mapped to other locations on the button matrix than expected
|
||||
if a sound interrupt is triggered while the button interrupt is disabled the button interrupt will still trigger(in galax game)(seems to be an issue with FIFOAV always = true hack and how INT_PWM1 needed to be cleared on read or write)
|
||||
SED1376 16 bpp mode is broken and crushed to the top of the screen
|
||||
audio can max out the resampler buffer, the max 1 FIFO sample can play is around 2.16 minutes(CLK32 / period(257) / clockDivider(16) / prescaler(128) / repeat(8))(257 * 16 * 128 * 8 / 32768=128.5 seconds)(safety check added, >= 1 second duty cycle is useless and will just make annoying cracks anyway)
|
||||
PWM1 output value is not a direct range cast of 0<->255 to 0<->32767, its additive, see properPwmSineWave.png
|
||||
inductor dosent properly drain when PWM1 gets disabled
|
||||
PWM1 FIFOAV is always set true
|
||||
may need to force sound generation until buffer is adequately filled when sound is on(not possible, INT_PWM1 is masked)
|
||||
if a timer triggers more than once per CLK32(> 32768 times a second) it will lose any triggers after the first and get out of sync(still not perfect but resolution is much higher)
|
||||
Peripheral Control Register, timer cascade mode
|
||||
MakePalmIcon 8bpp bitmaps have corrupt palettes
|
||||
missing home screen icons
|
||||
EMUCS memory range is unemulated(used for emu registers if enabled, invalid access otherwise)
|
||||
port g data bit 2 may need to be 0 to access ADS7846(it is)
|
||||
pen input
|
||||
pulling chip select low when high state likely resets the ADS7846 bit stream(this is currently being done when the SPI bus is enabled)(resetting on disable, that would make more sense because the chip needs to be read/written instantly once chip select is pulled low)
|
||||
From ADS7846 datasheet:Chip Select Input. Controls conversion timing and enables the serial input/output register. (this means the chip timing is likely reset when cs goes low)
|
||||
the ADS7846 is currently being reset every time the SPI is disabled and reenabled(happens when ADS7846 chip select is turned off, not on SPI enable)
|
||||
Port G SPI stuff(ADS7846 chip select was on port g)
|
||||
Edge triggered IRQ* interrupt pins may actually read the value of the interrupt instead of the pin value(IRQ* reads the pin, INT* still hasn't been tested yet)
|
||||
ADS7846 channels are not implemented properly in the emulator(works now)
|
||||
ADS7846 Channels 3 and 4(they need to be scaled differently)(works now)
|
||||
CPU32 table lookup opcodes(there is no CPU32)
|
||||
IRQ2 seems to not even be hooked up in IDA(IRQ2 is setup after boot by the SD driver, the kernels IRQ2 driver only sends a debug message over USB or serial)
|
||||
framebuffer accesses can cause a buffer overflow(this was always the case, its not a new bug)(just increased the buffer to an address line maskable size)
|
||||
chipselect mirroring is wrong(CS*0 and CS*1 are considered continuous when mapped with more address space than memory, mirroring is currently [CS*0, CS*1, repeat alternating till the end of address space] it should be [CS*0, repeat CS*0 until half way through CS* address space, CS*1, repeat CS*1 until end of CS* address space])
|
||||
the second line on all the chip selects is not properly emulated, they should be empty and trigger a bus error(except CSD1, it seems to follow different rules) but currently mirror the first half
|
||||
USB chip address range
|
||||
USB chip may be swapped into address space with a GPIO and reconfiguring CSC chip select
|
||||
the CSC address space is never accessed, this may be because the DRAM controller takes it over
|
||||
CSC0 and CSA1 ranges overlap, something specifically stated as what not to do(the CSA1 pin is disabled though)(its actually CSC that is disabled)
|
||||
CSC is owned by CSD for DRAM, CSA1 controls USB access
|
||||
All edge triggered interrupts(currently always level triggered)
|
||||
interrupt control register(ICR) edge trigger selects
|
||||
port d IRQ* bits edge triggered interrupt mode is not emulated
|
||||
edgeTriggeredInterruptLastValue may need to be locked when PLL is off(just checking if PLL is on before triggering an interrupt in edge triggered mode)
|
||||
Edge triggered interrupt seem needed for SD card(interrupt is disabled after card is inserted)(don't know if they are needed, but they are implemented now)
|
||||
on changing ICR all port d interrupts and IRQ5 need to be refreshed
|
||||
SD card can't be inserted or removed
|
||||
SD card has no error handling in save/load state
|
||||
there was what appeared to be memory corruption because a byte was incrementing and decrementing for loading and saving the same state but it was just the precision cast from uint64_t > double > uint64_t made it increment by 1
|
||||
STOP opcode may be causing issues with the power button(it may be setting a separate CPU disable option)(not a bug)
|
||||
add 320*320 frame buffer silkscreen, 2xBRZ should be able to make 320*320 version of the 160*160 silkscreen(not hooked up but one has been created)
|
||||
Tango icons don't grow enough with a big window
|
||||
a reset caused by the watchdog may cause undefined behavior(it just resets, same as if a reset opcode where called)
|
||||
CSD and CSC chaining to create a 16mb address space from 4x4mb chunks(this is just completely wrong, the address lines are controlled by the DRAM module that was unemulated)
|
||||
RAM wait states(not happening)
|
||||
All of DRAMMC
|
||||
need to investigate SDRAM registers
|
||||
Since chipselects must be aligned to there memory size the start address doesnt need to be subtracted from each address calculation
|
||||
PENIRQ can be read even when PFSEL bit 1 is false
|
||||
early on I swapped RGB16 R and B, the LUT register addresses for red and blue were swapped though, red actually is the top 5 bits
|
||||
Allow IMR to mask interrupts after they are created
|
||||
ADC7846 temperature sensors
|
||||
ADC7846 dock sensor
|
||||
storage RAM protect(already done from fixing chip select bits, ROP bit set in CSD, verified with hardware test)
|
||||
interrupt control register(ICR) POL5
|
||||
port g backlight state readback
|
||||
Timer capture events, I don't think there possible though with the Palms hardware(there not possible, the power button LED occupies the TIN pin which is required for capture events)
|
||||
port b pin 6 is xored with the power detection on the alarm/power LED, if docked it turns the LED off, if not docked it turns the LED on
|
||||
touch interrupt seems to not use IRQ5(it does, theres a test for it now)
|
||||
port d seems to allow using special function pins as inputs while active unlike other ports, this needs to be verified(verified by MC68VZ328UM.pdf Page 10-15)
|
||||
CPU_RUN_MODE is not preserved on save state and could allow executing after an address error if a save and load are performed(CPU_RUN_MODE is not used when address error is disabled)
|
||||
Bootloader memory access
|
||||
need to check REFREQ clock frequency in RTCCTL
|
||||
PLLCR CLKEN being off should also disable the SED1376 but this would require a bank refresh on writing to PLLCR
|
||||
proper clearing of timer interrupts
|
||||
PCTLR should not divide palmCrystalCycles or dmaclksPerClk32 can't use it(removed palmCrystalCycles from dmaclksPerClk32)
|
||||
if PCTLR divides the PLL it may also affect DMACLK, SYSCLK and the timers(it doesnt)
|
||||
timers shouldn't allow comp bits to be cleared until read with a 1 in them and should remain in interrupt state until they are cleared(the interrupt state part is unverified)
|
||||
SED1376 byte and word swap bits
|
||||
qt play pause icon not working for emu control button
|
||||
SED1376 register access
|
||||
SED1376 picture in picture registers
|
||||
PCTLR, can divide CPU speed by 1<->31
|
||||
Palm OS usage of the "rte" instruction is incompatible with the 68020, switch to 68000 core
|
||||
System Control Register, 0xXXFFF000 all upper banks are registers mode
|
||||
Port D keyboard enable register
|
||||
(HW verified)PDPOL inverts interrupts by inverting PDDATA bits, the data register is affected by PDPOL
|
||||
Endian safe savestates
|
||||
PLL Control Register, Wake Select
|
||||
(Resolved by MC68VZ328UM_CORRECTIONS datasheet, its 0xA28, 16 bits)The data sheet says LRRA is 0xFFFFFA29 and 0xFFFFFA28 but its only 8 bits?
|
||||
(All except edge triggered INT0/1/2/3 restart PLL)Check if interrupts restart PLL when its disabled
|
||||
Disabling PLL also turns off general purpose timer 1 and 2 unless they are set to use CLK32 as there source
|
||||
All chip select registers
|
||||
The boot memory mapping where ROM starts at 0x00000000 and RAM is swapped in
|
||||
Bits 15 and 14 of the chip selects, currently only the upper 16 bits are taken into account
|
||||
Supervisor only chip select bits
|
||||
RAM location and size setting, Dragonball VZ can set the page mapping SDCTRL(SDCTRL only sets external physical pins not RAM mapping)
|
||||
355
bugs/fixed/idleModeStackTrace.txt
Normal file
355
bugs/fixed/idleModeStackTrace.txt
Normal file
@@ -0,0 +1,355 @@
|
||||
Using uARM CPU core!(printed 1 times)
|
||||
TSC2101 PINTDAV not fully implemented(printed 2 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register write:0x0004, value:0x038FF000(printed 1 times)
|
||||
Set speed modeMISSING "\n"(printed 1 times)
|
||||
Someone tried to set processor power mode (cp14 reg7) to 0x00000003, PC:0x000003F8(printed 1 times)
|
||||
Unimplimented PXA260 GPIO 36 set:1, PC:0x0000051C(printed 1 times)
|
||||
Unimplimented PXA260 GPIO 43 set:1, PC:0x0000051C(printed 1 times)
|
||||
Unimplimented PXA260 GPIO 45 set:1, PC:0x0000051C(printed 1 times)
|
||||
Unimplimented PXA260 GPIO 47 set:1, PC:0x0000051C(printed 1 times)
|
||||
Unimplimented PXA260 GPIO 48 set:1, PC:0x0000051C(printed 1 times)
|
||||
Unimplimented PXA260 GPIO 51 set:1, PC:0x0000051C(printed 1 times)
|
||||
Unimplimented PXA260 GPIO 52 set:1, PC:0x0000051C(printed 1 times)
|
||||
Unimplimented PXA260 GPIO 53 set:1, PC:0x0000051C(printed 1 times)
|
||||
Unimplimented PXA260 GPIO 57 set:1, PC:0x0000051C(printed 1 times)
|
||||
Unimplimented PXA260 GPIO 78 set:1, PC:0x0000051C(printed 1 times)
|
||||
Unimplimented PXA260 GPIO 79 set:1, PC:0x0000051C(printed 1 times)
|
||||
Unimplimented PXA260 GPIO 43 set:1, PC:0x0000053C(printed 1 times)
|
||||
Unimplimented PXA260 GPIO 45 set:4, PC:0x0000053C(printed 1 times)
|
||||
Unimplimented PXA260 GPIO 47 set:3, PC:0x0000053C(printed 1 times)
|
||||
Unimplimented PXA260 GPIO 78 set:4, PC:0x0000053C(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register read:0x0008(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register write:0x0008, value:0x23DA23D2(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register read:0x0008(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register write:0x000C, value:0x3FF1A441(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register read:0x000C(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register write:0x0010, value:0x7FF17FF1(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register read:0x0010(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register write:0x0014, value:0x00000001(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register read:0x0014(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register write:0x0028, value:0x00010504(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register read:0x0028(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register write:0x002C, value:0x00010504(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register read:0x002C(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register write:0x0030, value:0x00010504(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register read:0x0030(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register write:0x0034, value:0x00010504(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register read:0x0034(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register write:0x0038, value:0x00004715(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register read:0x0038(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register write:0x003C, value:0x00004715(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register read:0x003C(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register read:0x0004(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register write:0x0004, value:0x00000018(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register write:0x001C, value:0x00000000(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register read:0x0004(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register write:0x0004, value:0x00010018(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register read:0x0004(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register write:0x0004, value:0x00010018(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register write:0x0004, value:0x00018018(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register write:0x0000, value:0x00000AC8(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register write:0x0000, value:0x00000AC9(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register write:0x0040, value:0x00000000(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register read:0x0004(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register write:0x0004, value:0x00118018(printed 1 times)
|
||||
Turned MMU on(printed 1 times)
|
||||
Unknown coprocessor instruction MCR 0E071F16(printed 2 times)
|
||||
Unknown coprocessor instruction MCR 0E071FB2(printed 1024 times)
|
||||
Unimplimented PXA260 GPIO 52 set:3, PC:0x00001128(printed 1 times)
|
||||
Unimplimented PXA260 GPIO 52 set:3, PC:0x00001154(printed 1 times)
|
||||
Unknown coprocessor instruction MCR 0E071FB2(printed 2048 times)
|
||||
Unimplimented PXA260 GPIO 36 set:4, PC:0x2000636C(printed 1 times)
|
||||
Unimplemented TPS65010 register write, address:0x08, value:0x80, PC:0x20005B30(printed 10 times)
|
||||
Unimplemented TPS65010 register write, address:0x0D, value:0x00, PC:0x20005B30(printed 10 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register read:0x000C(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register write:0x000C, value:0x3FF187A9(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register read:0x000C(printed 1 times)
|
||||
Unimplimented PXA260 GPIO 78 set:4, PC:0x2000631C(printed 1 times)
|
||||
Unimplimented PXA260 GPIO 78 set:4, PC:0x20006328(printed 1 times)
|
||||
Unimplemented T3 SD chip write at address 0x0C, value:0x0014(printed 1 times)
|
||||
Unimplemented T3 SD chip read at address 0x0E(printed 1 times)
|
||||
Unimplemented T3 SD chip write at address 0x0C, value:0x0014(printed 1 times)
|
||||
Unimplemented T3 SD chip read at address 0x0E(printed 1 times)
|
||||
Unimplemented T3 SD chip read at address 0x0C(printed 1 times)
|
||||
TSC2101 PINTDAV not fully implemented(printed 2 times)
|
||||
TSC2101 scan set to 0x0(printed 1 times)
|
||||
TSC2101 PINTDAV not fully implemented(printed 2 times)
|
||||
TSC2101 config register writes not fully implemented(printed 1 times)
|
||||
TSC2101 PINTDAV not fully implemented(printed 2 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:1, value:0(printed 1 times)
|
||||
Called from address:0x200AC418(printed 1 times)
|
||||
Unknown coprocessor instruction MCR 0E071FB2(printed 16384 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "ReadGPIOPin", pin:36(printed 1 times)
|
||||
Called from address:0x200B86C4(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:36, value:0(printed 1 times)
|
||||
Called from address:0x200B86DC(printed 1 times)
|
||||
Unimplimented PXA260 GPIO 36 set:4, PC:0x200B1DC0(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "ReadGPIOPin", pin:35(printed 1 times)
|
||||
Called from address:0x200B866C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:35, value:1(printed 1 times)
|
||||
Called from address:0x200B8688(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:1, value:0(printed 1 times)
|
||||
Called from address:0x200AC7FC(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:1, value:0(printed 1 times)
|
||||
Called from address:0x200AC7FC(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:1, value:0(printed 1 times)
|
||||
Called from address:0x200AC7FC(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:1, value:0(printed 1 times)
|
||||
Called from address:0x200AC7FC(printed 1 times)
|
||||
Unimplemented TPS65010 register write, address:0x08, value:0x80, PC:0x200B5EFC(printed 10 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:1, value:0(printed 1 times)
|
||||
Called from address:0x200AC7FC(printed 1 times)
|
||||
Unimplemented TPS65010 register write, address:0x0D, value:0x20, PC:0x200B5EFC(printed 10 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register read:0x000C(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register write:0x000C, value:0x3FF187A9(printed 1 times)
|
||||
Unimplimented 32 bit PXA260 MEMCTRL register read:0x000C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:78, value:0(printed 1 times)
|
||||
Called from address:0x200B6224(printed 1 times)
|
||||
Unimplimented PXA260 GPIO 78 set:4, PC:0x200B1D70(printed 1 times)
|
||||
Unimplimented PXA260 GPIO 78 set:4, PC:0x200B1D7C(printed 1 times)
|
||||
Unimplemented T3 SD chip write at address 0x0C, value:0x0014(printed 1 times)
|
||||
Unimplemented T3 SD chip read at address 0x0E(printed 1 times)
|
||||
Unimplemented T3 SD chip write at address 0x0C, value:0x0014(printed 1 times)
|
||||
Unimplemented T3 SD chip read at address 0x0E(printed 1 times)
|
||||
Unimplemented T3 SD chip read at address 0x0C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:40, value:1(printed 1 times)
|
||||
Called from address:0x200B47CC(printed 1 times)
|
||||
TSC2101 PINTDAV not fully implemented(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:40, value:0(printed 1 times)
|
||||
Called from address:0x200B47E0(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:24, value:0(printed 1 times)
|
||||
Called from address:0x200B47BC(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:24, value:1(printed 1 times)
|
||||
Called from address:0x200B47BC(printed 1 times)
|
||||
TSC2101 PINTDAV not fully implemented(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:24, value:0(printed 1 times)
|
||||
Called from address:0x200B47BC(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:24, value:1(printed 1 times)
|
||||
Called from address:0x200B47BC(printed 1 times)
|
||||
TSC2101 scan set to 0x0(printed 1 times)
|
||||
TSC2101 PINTDAV not fully implemented(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:24, value:0(printed 1 times)
|
||||
Called from address:0x200B47BC(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:24, value:1(printed 1 times)
|
||||
Called from address:0x200B47BC(printed 1 times)
|
||||
TSC2101 PINTDAV not fully implemented(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:24, value:0(printed 1 times)
|
||||
Called from address:0x200B47BC(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:24, value:1(printed 1 times)
|
||||
Called from address:0x200B47BC(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:24, value:0(printed 1 times)
|
||||
Called from address:0x200B47BC(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:24, value:1(printed 1 times)
|
||||
Called from address:0x200B47BC(printed 1 times)
|
||||
TSC2101 config register writes not fully implemented(printed 1 times)
|
||||
TSC2101 PINTDAV not fully implemented(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:24, value:0(printed 1 times)
|
||||
Called from address:0x200B47BC(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:24, value:1(printed 1 times)
|
||||
Called from address:0x200B47BC(printed 1 times)
|
||||
TSC2101 PINTDAV not fully implemented(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:24, value:0(printed 1 times)
|
||||
Called from address:0x200B47BC(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:33, value:1(printed 1 times)
|
||||
Called from address:0x200AAA0C(printed 1 times)
|
||||
Called "ReadGPIOPin", pin:0(printed 1 times)
|
||||
Called from address:0x200AAA34(printed 1 times)
|
||||
Called "ReadGPIOPin", pin:10(printed 1 times)
|
||||
Called from address:0x200AAA34(printed 1 times)
|
||||
Called "ReadGPIOPin", pin:11(printed 1 times)
|
||||
Called from address:0x200AAA34(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:33, value:0(printed 1 times)
|
||||
Called from address:0x200AAA58(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:19, value:1(printed 1 times)
|
||||
Called from address:0x200AAA0C(printed 1 times)
|
||||
Called "ReadGPIOPin", pin:0(printed 1 times)
|
||||
Called from address:0x200AAA34(printed 1 times)
|
||||
Called "ReadGPIOPin", pin:10(printed 1 times)
|
||||
Called from address:0x200AAA34(printed 1 times)
|
||||
Called "ReadGPIOPin", pin:11(printed 1 times)
|
||||
Called from address:0x200AAA34(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:19, value:0(printed 1 times)
|
||||
Called from address:0x200AAA58(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:20, value:1(printed 1 times)
|
||||
Called from address:0x200AAA0C(printed 1 times)
|
||||
Called "ReadGPIOPin", pin:0(printed 1 times)
|
||||
Called from address:0x200AAA34(printed 1 times)
|
||||
Called "ReadGPIOPin", pin:10(printed 1 times)
|
||||
Called from address:0x200AAA34(printed 1 times)
|
||||
Called "ReadGPIOPin", pin:11(printed 1 times)
|
||||
Called from address:0x200AAA34(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:20, value:0(printed 1 times)
|
||||
Called from address:0x200AAA58(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:21, value:1(printed 1 times)
|
||||
Called from address:0x200AAA0C(printed 1 times)
|
||||
Called "ReadGPIOPin", pin:0(printed 1 times)
|
||||
Called from address:0x200AAA34(printed 1 times)
|
||||
Called "ReadGPIOPin", pin:10(printed 1 times)
|
||||
Called from address:0x200AAA34(printed 1 times)
|
||||
Called "ReadGPIOPin", pin:11(printed 1 times)
|
||||
Called from address:0x200AAA34(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:21, value:0(printed 1 times)
|
||||
Called from address:0x200AAA58(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:22, value:1(printed 1 times)
|
||||
Called from address:0x200AAA0C(printed 1 times)
|
||||
Called "ReadGPIOPin", pin:0(printed 1 times)
|
||||
Called from address:0x200AAA34(printed 1 times)
|
||||
Called "ReadGPIOPin", pin:10(printed 1 times)
|
||||
Called from address:0x200AAA34(printed 1 times)
|
||||
Called "ReadGPIOPin", pin:11(printed 1 times)
|
||||
Called from address:0x200AAA34(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:22, value:0(printed 1 times)
|
||||
Called from address:0x200AAA58(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:1, value:0(printed 1 times)
|
||||
Called from address:0x200AC7FC(printed 1 times)
|
||||
TPS65010 DEFGPIO read, PC:0x200B610C(printed 10 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Called "WriteGPIOPinInvertedValue", pin:45, value:0(printed 1 times)
|
||||
Called from address:0x200ADD3C(printed 1 times)
|
||||
Jumped to:0x200BA170(printed 1 times)
|
||||
Jumped to:0x200BA16A(printed 1 times)
|
||||
Jumped to:0x200BA174(printed 1 times)
|
||||
Jumped to:0x200BA170(printed 1 times)
|
||||
Jumped to:0x200BA16A(printed 1 times)
|
||||
Jumped to:0x200BA174(printed 1 times)
|
||||
Jumped to:0x200BA170(printed 1 times)
|
||||
Jumped to:0x200BA16A(printed 1 times)
|
||||
Jumped to:0x200BA174(printed 1 times)
|
||||
Jumped to:0x200BA170(printed 1 times)
|
||||
Jumped to:0x200BA16A(printed 1 times)
|
||||
Jumped to:0x200BA174(printed 1 times)
|
||||
Jumped to:0x200BA170(printed 1 times)
|
||||
Jumped to:0x200BA16A(printed 1 times)
|
||||
Jumped to:0x200BA174(printed 1 times)
|
||||
Jumped to:0x200BA170(printed 1 times)
|
||||
Jumped to:0x200BA16A(printed 1 times)
|
||||
Jumped to:0x200BA174(printed 1 times)
|
||||
Jumped to:0x200BA170(printed 1 times)
|
||||
Jumped to:0x200BA16A(printed 1 times)
|
||||
Jumped to:0x200BA174(printed 1 times)
|
||||
Jumped to:0x200BA170(printed 1 times)
|
||||
Jumped to:0x200BA16A(printed 1 times)
|
||||
Jumped to:0x200BA174(printed 1 times)
|
||||
Jumped to:0x200BA170(printed 1 times)
|
||||
Jumped to:0x200BA16A(printed 1 times)
|
||||
Jumped to:0x200BA174(printed 1 times)
|
||||
Jumped to:0x200BA170(printed 1 times)
|
||||
Jumped to:0x200BA16A(printed 1 times)
|
||||
Jumped to:0x200BA174(printed 1 times)
|
||||
Jumped to:0x200BA170(printed 1 times)
|
||||
Jumped to:0x200BA16A(printed 1 times)
|
||||
Jumped to:0x200BA174(printed 1 times)
|
||||
Jumped to:0x200BA170(printed 1 times)
|
||||
Jumped to:0x200BA16A(printed 1 times)
|
||||
Jumped to:0x200BA174(printed 1 times)
|
||||
Jumped to:0x200BA170(printed 1 times)
|
||||
Jumped to:0x200BA16A(printed 1 times)
|
||||
Jumped to:0x200BA174(printed 1 times)
|
||||
Jumped to:0x200BA170(printed 1 times)
|
||||
Jumped to:0x200BA16A(printed 1 times)
|
||||
Jumped to:0x200BA174(printed 1 times)
|
||||
Jumped to:0x200BA170(printed 1 times)
|
||||
Jumped to:0x200BA16A(printed 1 times)
|
||||
Jumped to:0x200BA192(printed 1 times)
|
||||
Jumped to:0x2009F5E0(printed 1 times)//WriteBootVectorTable
|
||||
Jumped to:0x200BA196(printed 1 times)
|
||||
Jumped to:0x200BBF3A(printed 1 times)
|
||||
Jumped to:0x2009B524(printed 1 times)
|
||||
Jumped to:0x2009B540(printed 1 times)
|
||||
Jumped to:0x200A678C(printed 1 times)
|
||||
Jumped to:0x200BBF12(printed 1 times)
|
||||
Jumped to:0x00000008(printed 1 times)
|
||||
Jumped to:0x200C0FC4(printed 1 times)//\
|
||||
Jumped to:0x200C0FF8(printed 1 times)//|SWIVector handler
|
||||
Jumped to:0x200C102C(printed 1 times)///
|
||||
Jumped to:0x200C1048(printed 1 times)
|
||||
Jumped to:0x200C2114(printed 1 times)
|
||||
Jumped to:0x200BA2A4(printed 1 times)
|
||||
Jumped to:0x2009F850(printed 1 times)
|
||||
Jumped to:0x200C1130(printed 1 times)
|
||||
Jumped to:0x2009F638(printed 1 times)//\CallsEnterIdleModeLayer0, purpose unknown, dosent lock up
|
||||
Jumped to:0x2009F6E8(printed 1 times)///
|
||||
Jumped to:0x2009F6EC(printed 1 times)
|
||||
Jumped to:0x200BBF26(printed 1 times)
|
||||
Jumped to:0x200A67DC(printed 1 times)
|
||||
Jumped to:0x2009B554(printed 1 times)
|
||||
Jumped to:0x200A72CC(printed 1 times)//trying to load some module here but never gets to it
|
||||
Jumped to:0x200C3018(printed 1 times)//KALMutexCreate jumptable entry
|
||||
Jumped to:0x200C1F54(printed 1 times)//KALMutexCreate function stub
|
||||
Jumped to:0x200BC542(printed 1 times)//KALMutexCreate SVC call stub
|
||||
Jumped to:0x00000008(printed 1 times)//SVC call made, this is where things start to go bad
|
||||
Jumped to:0x200C0FC4(printed 1 times)//\
|
||||
Jumped to:0x200C0FF8(printed 1 times)//|
|
||||
Jumped to:0x200C102C(printed 1 times)//|SWIVector handler
|
||||
Jumped to:0x200C1118(printed 1 times)//|
|
||||
Jumped to:0x200C2444(printed 1 times)///
|
||||
Jumped to:0x200BA66A(printed 1 times)//KALMutexCreate real function
|
||||
Jumped to:0x200BA67A(printed 1 times)//KALMutexCreate availability checking loop
|
||||
Jumped to:0x200BA610(printed 1 times)//\
|
||||
Jumped to:0x200BA622(printed 1 times)//|
|
||||
Jumped to:0x200BA62C(printed 1 times)//|
|
||||
Jumped to:0x200BA636(printed 1 times)//|InitMutex
|
||||
Jumped to:0x200BA658(printed 1 times)//|
|
||||
Jumped to:0x200BA61E(printed 1 times)///
|
||||
Jumped to:0x200BA6A4(printed 1 times)//return to KALMutexCreate
|
||||
Jumped to:0x200C1130(printed 1 times)//return to SWIVector handler
|
||||
Jumped to:0x2009F638(printed 1 times)//\CallsEnterIdleModeLayer0, purpose unknown
|
||||
Jumped to:0x2009F6A8(printed 1 times)//|
|
||||
Jumped to:0x2009F678(printed 1 times)///
|
||||
Jumped to:0x200C2820(printed 1 times)//EnterIdleMode
|
||||
Called "EnterIdleMode", took 395292 jumps to reach this function(printed 1 times)
|
||||
Called from address:0x2009F68C(printed 1 times)
|
||||
Someone tried to set processor power mode (cp14 reg7) to 0x00000001, PC:0x200C283C(printed 1 times)
|
||||
@@ -1,7 +0,0 @@
|
||||
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:
|
||||
Release at Christmas 17:00 GMT, 9:00 My time, 12:00 SquirrelJME devs time
|
||||
I post to r/emulation, Stephanie(SquirrelJME dev) posts to Twitter and Mastodon
|
||||
Sister projects with SquirrelJME: https://github.com/XerTheSquirrel/SquirrelJME
|
||||
@@ -60,139 +60,4 @@ T3 I2C currently has no ACK bits(should work without them its just inaccurate)(I
|
||||
T3 emulation attempts to set GPIO1(reset button) as an output
|
||||
GPIO lines from misc chips to CPU are not implemented
|
||||
PXA260 idle mode is unimplemented
|
||||
0x0E071F16 MCR P15, #0, R1, C7, C2, #5 is unimplemented along with several other unknown CP15 opcodes
|
||||
|
||||
|
||||
Fixed:
|
||||
memory dumping dosent work for OS 5 yet
|
||||
Endian compatibility is broken(the CPU state needs to be standardized)
|
||||
RetroArch port crashes on exit(needed to check if environ_cb returned true, switched to libretro-common filestreams too)
|
||||
make the headers belong to the main file of the target chip being emulated for specs
|
||||
remove specs folder
|
||||
UART1 USTCNT1
|
||||
trying to beam anything will lock up the OS
|
||||
Qt dosent have a hybrid file/folder selector so apps will always have to be launched from folders for now
|
||||
File Installer isnt working yet
|
||||
Qt GUI dosent resize properly with 320x320 framebuffer
|
||||
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)
|
||||
(CPU)TCN2 is unimplemented and seems to be used by the same routines that turn the CPU off for sleep mode(it is implemented now and it wasn't the cause of the sleep mode issue)
|
||||
(CPU)SCR privilege violation doesnt trigger the same as on hardware(likely has to do with not emulating the unused chip selects)(I added those chip selects)
|
||||
(CPU)Trace T1 bit, should be unused by the OS and applications anyway(this was fixed long ago, don't know why it was still listed?)
|
||||
(SD Card)data blocks won't work properly when CRC checks are enabled
|
||||
(SD Card)the CRC of CSD and CID are invalid
|
||||
unemulated chip select(CSB1) privilege violation interrupts are not handled properly
|
||||
SD card can't be inserted in RetroArch port
|
||||
USB chip seems to share an interrupt with the SD card(the SD card interrupt is unimplemented at boot and triggers a printf over USB instead, if USB is not properly emulated that will cause a lock up)(enough of the USB is now emulated to prevent this)
|
||||
(SD Card)need to an input option for the write protect switch on the side of the SD card(will be fixed at input time since you can't flip it when in the slot)
|
||||
(SD Card)memory block ordering is broken
|
||||
(SD Card)block length is fixed at 512 right now(this is actually proper behavior for newer SDSC cards)
|
||||
(SD Card)need to dump valid SCR
|
||||
(SD Card)SEND_SCR may not be sending in the proper format(the return data format was not specified in the spec I read)(data packet format is working)
|
||||
SPI1(the SD card interface)(SPI1 master mode seems fully implemented now)
|
||||
SD card can't be read or written to by Palm OS(SPI transactions are going both ways now, some flash chip read commands are implemented too)
|
||||
(ADS7846)need to trigger a false PENIRQ interrupt on reading certain channels(this is being done now)
|
||||
(MakePalmBitmap)the small icons are corrupt
|
||||
(MakePalmBitmap)the non 16 bpp large icons are corrupt
|
||||
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)(IRQ2 doesnt change when SD card chip select is toggled regardless of wether the card is inserted)
|
||||
the "Fatal Error" dialog reset button doesnt work, may not be a bug, could just an be issue with certain errors needing a full reset(its error dependent, one bug proved the other bug wasn't actually a bug)
|
||||
don't know if flushing the PWM1 FIFO sets all the bytes to 0x00, or just sets the read pointer to the write pointer preserving the newest sample and making the size 0(readPtr = writePtr has significantly cleaner audio then 0ing it out though(they sound the same now, this just masked the actual issue))
|
||||
edge triggered INT* don't clear on write to ISR when masked in IMR(I needed to use (ints & ~IMR) not (ints & IMR) which only triggers disable interrupts while blocking active ones)
|
||||
PDKBEN also has a hack for power on(its been removed)
|
||||
On hardware PDIRQEG seems not to actually work at all(CPUID:0x57000000)(it does)
|
||||
may need to trigger an interrupt if a IMR masked interrupt becomes unmasked and its bit is still set in IPR(already doing that)
|
||||
power button must be pushed twice to turn the CPU back on(when debugging it works the first time, then must be pushed twice to turn off instead of on)(this also occurs in the RetroArch build)
|
||||
the power button double press issue may be because the button map I am using was taken from POSE source, the power button may be mapped to other locations on the button matrix than expected
|
||||
if a sound interrupt is triggered while the button interrupt is disabled the button interrupt will still trigger(in galax game)(seems to be an issue with FIFOAV always = true hack and how INT_PWM1 needed to be cleared on read or write)
|
||||
SED1376 16 bpp mode is broken and crushed to the top of the screen
|
||||
audio can max out the resampler buffer, the max 1 FIFO sample can play is around 2.16 minutes(CLK32 / period(257) / clockDivider(16) / prescaler(128) / repeat(8))(257 * 16 * 128 * 8 / 32768=128.5 seconds)(safety check added, >= 1 second duty cycle is useless and will just make annoying cracks anyway)
|
||||
PWM1 output value is not a direct range cast of 0<->255 to 0<->32767, its additive, see properPwmSineWave.png
|
||||
inductor dosent properly drain when PWM1 gets disabled
|
||||
PWM1 FIFOAV is always set true
|
||||
may need to force sound generation until buffer is adequately filled when sound is on(not possible, INT_PWM1 is masked)
|
||||
if a timer triggers more than once per CLK32(> 32768 times a second) it will lose any triggers after the first and get out of sync(still not perfect but resolution is much higher)
|
||||
Peripheral Control Register, timer cascade mode
|
||||
MakePalmIcon 8bpp bitmaps have corrupt palettes
|
||||
missing home screen icons
|
||||
EMUCS memory range is unemulated(used for emu registers if enabled, invalid access otherwise)
|
||||
port g data bit 2 may need to be 0 to access ADS7846(it is)
|
||||
pen input
|
||||
pulling chip select low when high state likely resets the ADS7846 bit stream(this is currently being done when the SPI bus is enabled)(resetting on disable, that would make more sense because the chip needs to be read/written instantly once chip select is pulled low)
|
||||
From ADS7846 datasheet:Chip Select Input. Controls conversion timing and enables the serial input/output register. (this means the chip timing is likely reset when cs goes low)
|
||||
the ADS7846 is currently being reset every time the SPI is disabled and reenabled(happens when ADS7846 chip select is turned off, not on SPI enable)
|
||||
Port G SPI stuff(ADS7846 chip select was on port g)
|
||||
Edge triggered IRQ* interrupt pins may actually read the value of the interrupt instead of the pin value(IRQ* reads the pin, INT* still hasn't been tested yet)
|
||||
ADS7846 channels are not implemented properly in the emulator(works now)
|
||||
ADS7846 Channels 3 and 4(they need to be scaled differently)(works now)
|
||||
CPU32 table lookup opcodes(there is no CPU32)
|
||||
IRQ2 seems to not even be hooked up in IDA(IRQ2 is setup after boot by the SD driver, the kernels IRQ2 driver only sends a debug message over USB or serial)
|
||||
framebuffer accesses can cause a buffer overflow(this was always the case, its not a new bug)(just increased the buffer to an address line maskable size)
|
||||
chipselect mirroring is wrong(CS*0 and CS*1 are considered continuous when mapped with more address space than memory, mirroring is currently [CS*0, CS*1, repeat alternating till the end of address space] it should be [CS*0, repeat CS*0 until half way through CS* address space, CS*1, repeat CS*1 until end of CS* address space])
|
||||
the second line on all the chip selects is not properly emulated, they should be empty and trigger a bus error(except CSD1, it seems to follow different rules) but currently mirror the first half
|
||||
USB chip address range
|
||||
USB chip may be swapped into address space with a GPIO and reconfiguring CSC chip select
|
||||
the CSC address space is never accessed, this may be because the DRAM controller takes it over
|
||||
CSC0 and CSA1 ranges overlap, something specifically stated as what not to do(the CSA1 pin is disabled though)(its actually CSC that is disabled)
|
||||
CSC is owned by CSD for DRAM, CSA1 controls USB access
|
||||
All edge triggered interrupts(currently always level triggered)
|
||||
interrupt control register(ICR) edge trigger selects
|
||||
port d IRQ* bits edge triggered interrupt mode is not emulated
|
||||
edgeTriggeredInterruptLastValue may need to be locked when PLL is off(just checking if PLL is on before triggering an interrupt in edge triggered mode)
|
||||
Edge triggered interrupt seem needed for SD card(interrupt is disabled after card is inserted)(don't know if they are needed, but they are implemented now)
|
||||
on changing ICR all port d interrupts and IRQ5 need to be refreshed
|
||||
SD card can't be inserted or removed
|
||||
SD card has no error handling in save/load state
|
||||
there was what appeared to be memory corruption because a byte was incrementing and decrementing for loading and saving the same state but it was just the precision cast from uint64_t > double > uint64_t made it increment by 1
|
||||
STOP opcode may be causing issues with the power button(it may be setting a separate CPU disable option)(not a bug)
|
||||
add 320*320 frame buffer silkscreen, 2xBRZ should be able to make 320*320 version of the 160*160 silkscreen(not hooked up but one has been created)
|
||||
Tango icons don't grow enough with a big window
|
||||
a reset caused by the watchdog may cause undefined behavior(it just resets, same as if a reset opcode where called)
|
||||
CSD and CSC chaining to create a 16mb address space from 4x4mb chunks(this is just completely wrong, the address lines are controlled by the DRAM module that was unemulated)
|
||||
RAM wait states(not happening)
|
||||
All of DRAMMC
|
||||
need to investigate SDRAM registers
|
||||
Since chipselects must be aligned to there memory size the start address doesnt need to be subtracted from each address calculation
|
||||
PENIRQ can be read even when PFSEL bit 1 is false
|
||||
early on I swapped RGB16 R and B, the LUT register addresses for red and blue were swapped though, red actually is the top 5 bits
|
||||
Allow IMR to mask interrupts after they are created
|
||||
ADC7846 temperature sensors
|
||||
ADC7846 dock sensor
|
||||
storage RAM protect(already done from fixing chip select bits, ROP bit set in CSD, verified with hardware test)
|
||||
interrupt control register(ICR) POL5
|
||||
port g backlight state readback
|
||||
Timer capture events, I don't think there possible though with the Palms hardware(there not possible, the power button LED occupies the TIN pin which is required for capture events)
|
||||
port b pin 6 is xored with the power detection on the alarm/power LED, if docked it turns the LED off, if not docked it turns the LED on
|
||||
touch interrupt seems to not use IRQ5(it does, theres a test for it now)
|
||||
port d seems to allow using special function pins as inputs while active unlike other ports, this needs to be verified(verified by MC68VZ328UM.pdf Page 10-15)
|
||||
CPU_RUN_MODE is not preserved on save state and could allow executing after an address error if a save and load are performed(CPU_RUN_MODE is not used when address error is disabled)
|
||||
Bootloader memory access
|
||||
need to check REFREQ clock frequency in RTCCTL
|
||||
PLLCR CLKEN being off should also disable the SED1376 but this would require a bank refresh on writing to PLLCR
|
||||
proper clearing of timer interrupts
|
||||
PCTLR should not divide palmCrystalCycles or dmaclksPerClk32 can't use it(removed palmCrystalCycles from dmaclksPerClk32)
|
||||
if PCTLR divides the PLL it may also affect DMACLK, SYSCLK and the timers(it doesnt)
|
||||
timers shouldn't allow comp bits to be cleared until read with a 1 in them and should remain in interrupt state until they are cleared(the interrupt state part is unverified)
|
||||
SED1376 byte and word swap bits
|
||||
qt play pause icon not working for emu control button
|
||||
SED1376 register access
|
||||
SED1376 picture in picture registers
|
||||
PCTLR, can divide CPU speed by 1<->31
|
||||
Palm OS usage of the "rte" instruction is incompatible with the 68020, switch to 68000 core
|
||||
System Control Register, 0xXXFFF000 all upper banks are registers mode
|
||||
Port D keyboard enable register
|
||||
(HW verified)PDPOL inverts interrupts by inverting PDDATA bits, the data register is affected by PDPOL
|
||||
Endian safe savestates
|
||||
PLL Control Register, Wake Select
|
||||
(Resolved by MC68VZ328UM_CORRECTIONS datasheet, its 0xA28, 16 bits)The data sheet says LRRA is 0xFFFFFA29 and 0xFFFFFA28 but its only 8 bits?
|
||||
(All except edge triggered INT0/1/2/3 restart PLL)Check if interrupts restart PLL when its disabled
|
||||
Disabling PLL also turns off general purpose timer 1 and 2 unless they are set to use CLK32 as there source
|
||||
All chip select registers
|
||||
The boot memory mapping where ROM starts at 0x00000000 and RAM is swapped in
|
||||
Bits 15 and 14 of the chip selects, currently only the upper 16 bits are taken into account
|
||||
Supervisor only chip select bits
|
||||
RAM location and size setting, Dragonball VZ can set the page mapping SDCTRL(SDCTRL only sets external physical pins not RAM mapping)
|
||||
0x0E071F16 MCR P15, #0, R1, C7, C2, #5 is unimplemented along with several other unknown CP15 opcodes
|
||||
@@ -87,6 +87,7 @@ else ifneq ($(findstring MINGW,$(shell uname -a)),)
|
||||
endif
|
||||
|
||||
#CC = gcc
|
||||
#CXX = g++
|
||||
TARGET_NAME := mu
|
||||
LIBM = -lm
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ windows{
|
||||
QMAKE_LFLAGS += -fopenmp
|
||||
DEFINES += EMU_MULTITHREADED EMU_MANAGE_HOST_CPU_PIPELINE
|
||||
}
|
||||
CONFIG += cpu_x86_32 # TODO:this should be auto detected in the future
|
||||
}
|
||||
|
||||
macx{
|
||||
@@ -46,7 +45,6 @@ macx{
|
||||
ICON = macos/Mu.icns
|
||||
QMAKE_INFO_PLIST = macos/Info.plist
|
||||
DEFINES += EMU_MULTITHREADED EMU_MANAGE_HOST_CPU_PIPELINE
|
||||
CONFIG += cpu_x86_64 # Mac OS is only x86_64
|
||||
}
|
||||
|
||||
linux-g++{
|
||||
@@ -54,7 +52,6 @@ linux-g++{
|
||||
QMAKE_CXXFLAGS += -fopenmp
|
||||
QMAKE_LFLAGS += -fopenmp
|
||||
DEFINES += EMU_MULTITHREADED EMU_MANAGE_HOST_CPU_PIPELINE
|
||||
CONFIG += cpu_x86_64 # TODO:this should be auto detected in the future
|
||||
}
|
||||
|
||||
android{
|
||||
@@ -62,7 +59,6 @@ android{
|
||||
QMAKE_CXXFLAGS += -fopenmp
|
||||
QMAKE_LFLAGS += -fopenmp
|
||||
DEFINES += EMU_MULTITHREADED EMU_MANAGE_HOST_CPU_PIPELINE
|
||||
CONFIG += cpu_armv7 # TODO:this should be auto detected in the future
|
||||
}
|
||||
|
||||
|
||||
@@ -85,133 +81,7 @@ EMU_NO_SAFETY{
|
||||
DEFINES += EMU_NO_SAFETY
|
||||
}
|
||||
|
||||
support_palm_os5{
|
||||
DEFINES += EMU_SUPPORT_PALM_OS5 # the Qt build will not be supporting anything too slow to run OS 5
|
||||
DEFINES += SUPPORT_LINUX # forces the dynarec to use accurate mode and disable Nspire OS hacks
|
||||
|
||||
EMU_NO_SAFETY{
|
||||
# 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 \
|
||||
../../src/armv5te/uArm/CPU_2.c \
|
||||
../../src/armv5te/uArm/icache.c \
|
||||
../../src/armv5te/uArm/uArmGlue.cpp
|
||||
DEFINES += NO_TRANSLATION
|
||||
}
|
||||
|
||||
windows{
|
||||
SOURCES += \
|
||||
../../src/armv5te/os/os-win32.c
|
||||
}
|
||||
|
||||
macx|linux-g++|android{
|
||||
SOURCES += \
|
||||
../../src/armv5te/os/os-linux.c
|
||||
}
|
||||
|
||||
SOURCES += \
|
||||
../../src/pxa260/pxa260_DMA.c \
|
||||
../../src/pxa260/pxa260_DSP.c \
|
||||
../../src/pxa260/pxa260_GPIO.c \
|
||||
../../src/pxa260/pxa260_IC.c \
|
||||
../../src/pxa260/pxa260_LCD.c \
|
||||
../../src/pxa260/pxa260_PwrClk.c \
|
||||
../../src/pxa260/pxa260_RTC.c \
|
||||
../../src/pxa260/pxa260_TIMR.c \
|
||||
../../src/pxa260/pxa260_UART.c \
|
||||
../../src/pxa260/pxa260I2c.c \
|
||||
../../src/pxa260/pxa260Memctrl.c \
|
||||
../../src/pxa260/pxa260Timing.c \
|
||||
../../src/pxa260/pxa260Ssp.c \
|
||||
../../src/pxa260/pxa260Udc.c \
|
||||
../../src/pxa260/pxa260.c \
|
||||
../../src/armv5te/arm_interpreter.cpp \
|
||||
../../src/armv5te/cpu.cpp \
|
||||
../../src/armv5te/coproc.cpp \
|
||||
../../src/armv5te/disasm.c \
|
||||
../../src/armv5te/emuVarPool.c \
|
||||
../../src/armv5te/thumb_interpreter.cpp \
|
||||
../../src/armv5te/mem.c \
|
||||
../../src/armv5te/mmu.c \
|
||||
../../src/tps65010.c \
|
||||
../../src/tsc2101.c \
|
||||
../../src/w86l488.c
|
||||
|
||||
HEADERS += \
|
||||
../../src/pxa260/pxa260_CPU.h \
|
||||
../../src/pxa260/pxa260_DMA.h \
|
||||
../../src/pxa260/pxa260_DSP.h \
|
||||
../../src/pxa260/pxa260_GPIO.h \
|
||||
../../src/pxa260/pxa260_IC.h \
|
||||
../../src/pxa260/pxa260_LCD.h \
|
||||
../../src/pxa260/pxa260_PwrClk.h \
|
||||
../../src/pxa260/pxa260_RTC.h \
|
||||
../../src/pxa260/pxa260_TIMR.h \
|
||||
../../src/pxa260/pxa260_UART.h \
|
||||
../../src/pxa260/pxa260I2c.h \
|
||||
../../src/pxa260/pxa260Memctrl.h \
|
||||
../../src/pxa260/pxa260Timing.h \
|
||||
../../src/pxa260/pxa260Ssp.h \
|
||||
../../src/pxa260/pxa260Udc.h \
|
||||
../../src/pxa260/pxa260_types.h \
|
||||
../../src/pxa260/pxa260_math64.h \
|
||||
../../src/pxa260/pxa260Accessors.c.h \
|
||||
../../src/pxa260/pxa260.h \
|
||||
../../src/armv5te/os/os.h \
|
||||
../../src/armv5te/uArm/CPU_2.h \
|
||||
../../src/armv5te/uArm/icache.h \
|
||||
../../src/armv5te/uArm/uArmGlue.h \
|
||||
../../src/armv5te/asmcode.h \
|
||||
../../src/armv5te/bitfield.h \
|
||||
../../src/armv5te/cpu.h \
|
||||
../../src/armv5te/disasm.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/tungstenT3Bus.h \
|
||||
../../src/tps65010.h \
|
||||
../../src/tsc2101.h \
|
||||
../../src/w86l488.h
|
||||
}
|
||||
|
||||
DEFINES += EMU_SUPPORT_PALM_OS5 # the Qt build will not be supporting anything too slow to run OS 5
|
||||
CONFIG += c++11
|
||||
|
||||
INCLUDEPATH += $$PWD/qt-common/include
|
||||
@@ -231,9 +101,32 @@ SOURCES += \
|
||||
../../src/m68k/m68kopnz.c \
|
||||
../../src/m68k/m68kops.c \
|
||||
../../src/pdiUsbD12.c \
|
||||
../../src/pxa260/disasm.c \
|
||||
../../src/pxa260/pxa260.c \
|
||||
../../src/pxa260/pxa260I2c.c \
|
||||
../../src/pxa260/pxa260Memctrl.c \
|
||||
../../src/pxa260/pxa260Ssp.c \
|
||||
../../src/pxa260/pxa260Timing.c \
|
||||
../../src/pxa260/pxa260Udc.c \
|
||||
../../src/pxa260/pxa260_CPU.c \
|
||||
../../src/pxa260/pxa260_DMA.c \
|
||||
../../src/pxa260/pxa260_DSP.c \
|
||||
../../src/pxa260/pxa260_GPIO.c \
|
||||
../../src/pxa260/pxa260_IC.c \
|
||||
../../src/pxa260/pxa260_LCD.c \
|
||||
../../src/pxa260/pxa260_MMU.c \
|
||||
../../src/pxa260/pxa260_PwrClk.c \
|
||||
../../src/pxa260/pxa260_RTC.c \
|
||||
../../src/pxa260/pxa260_TIMR.c \
|
||||
../../src/pxa260/pxa260_UART.c \
|
||||
../../src/pxa260/pxa260_cp15.c \
|
||||
../../src/pxa260/pxa260_icache.c \
|
||||
../../src/sdCard.c \
|
||||
../../src/sed1376.c \
|
||||
../../src/silkscreen.c \
|
||||
../../src/tps65010.c \
|
||||
../../src/tsc2101.c \
|
||||
../../src/w86l488.c \
|
||||
debugviewer.cpp \
|
||||
emuwrapper.cpp \
|
||||
main.cpp \
|
||||
@@ -261,6 +154,29 @@ HEADERS += \
|
||||
../../src/pdiUsbD12.h \
|
||||
../../src/pdiUsbD12CommandNames.c.h \
|
||||
../../src/portability.h \
|
||||
../../src/pxa260/disasm.h \
|
||||
../../src/pxa260/pxa260.h \
|
||||
../../src/pxa260/pxa260Accessors.c.h \
|
||||
../../src/pxa260/pxa260I2c.h \
|
||||
../../src/pxa260/pxa260Memctrl.h \
|
||||
../../src/pxa260/pxa260Ssp.h \
|
||||
../../src/pxa260/pxa260Timing.h \
|
||||
../../src/pxa260/pxa260Udc.h \
|
||||
../../src/pxa260/pxa260_CPU.h \
|
||||
../../src/pxa260/pxa260_DMA.h \
|
||||
../../src/pxa260/pxa260_DSP.h \
|
||||
../../src/pxa260/pxa260_GPIO.h \
|
||||
../../src/pxa260/pxa260_IC.h \
|
||||
../../src/pxa260/pxa260_LCD.h \
|
||||
../../src/pxa260/pxa260_MMU.h \
|
||||
../../src/pxa260/pxa260_PwrClk.h \
|
||||
../../src/pxa260/pxa260_RTC.h \
|
||||
../../src/pxa260/pxa260_TIMR.h \
|
||||
../../src/pxa260/pxa260_UART.h \
|
||||
../../src/pxa260/pxa260_cp15.h \
|
||||
../../src/pxa260/pxa260_icache.h \
|
||||
../../src/pxa260/pxa260_math64.h \
|
||||
../../src/pxa260/pxa260_types.h \
|
||||
../../src/sdCard.h \
|
||||
../../src/sdCardAccessors.c.h \
|
||||
../../src/sdCardCommandNames.c.h \
|
||||
@@ -269,6 +185,10 @@ HEADERS += \
|
||||
../../src/sed1376Accessors.c.h \
|
||||
../../src/sed1376RegisterNames.c.h \
|
||||
../../src/silkscreen.h \
|
||||
../../src/tps65010.h \
|
||||
../../src/tsc2101.h \
|
||||
../../src/tungstenT3Bus.h \
|
||||
../../src/w86l488.h \
|
||||
debugviewer.h \
|
||||
emuwrapper.h \
|
||||
mainwindow.h \
|
||||
|
||||
@@ -17,6 +17,7 @@ DebugViewer::DebugViewer(QWidget* parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::DebugViewer){
|
||||
ui->setupUi(this);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
bitsPerEntry = 8;
|
||||
debugRadioButtonHandler();
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
extern "C"{
|
||||
#include "../../src/flx68000.h"
|
||||
#include "../../src/m68k/m68k.h"
|
||||
#include "../../src/pxa260/disasm.h"
|
||||
#include "../../src/pxa260/pxa260.h"
|
||||
#include "../../src/armv5te/disasm.h"
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ static QVector<QString> debugStrings;
|
||||
static uint64_t debugDeletedStrings;
|
||||
static QVector<uint64_t> debugDuplicateCallCount;
|
||||
static bool debugAbort = false;
|
||||
static QString debugAbortString = "";
|
||||
static QString debugAbortString = "";//"Called \"HALDbgBreak\"";//"Someone tried to set processor power mode (cp14 reg7) to 0x00000001";
|
||||
uint32_t frontendDebugStringSize;
|
||||
char* frontendDebugString;
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ SettingsManager::SettingsManager(QWidget* parent) :
|
||||
|
||||
//init GUI
|
||||
ui->setupUi(this);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
//set all GUI items to current config values
|
||||
ui->homeDirectory->setText(settings->value("resourceDirectory", "").toString());
|
||||
|
||||
@@ -21,6 +21,7 @@ StateManager::StateManager(QWidget* parent) :
|
||||
|
||||
//init GUI
|
||||
ui->setupUi(this);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
//this allows resizing the screenshot of the savestate
|
||||
this->installEventFilter(this);
|
||||
|
||||
26
readme.md
26
readme.md
@@ -1,29 +1,27 @@
|
||||
# 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).
|
||||
|
||||
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).
|
||||
|
||||
# The goal of this project
|
||||
|
||||
To emulate every part of the Palm m515 that is used by the OS perfectly.
|
||||
This means no hacks like POSE where API calls are intercepted and replaced with those that dont use the actual hardware.
|
||||
To emulate every part of the Palm m515 and Tungsten T3 that is used by the OS perfectly.
|
||||
This means no hacks like POSE where API calls are intercepted and replaced with those that don't use the actual hardware.
|
||||
It is also written in C for RetroArch so it will run on everything at the start, not just Win95 like POSE and Copilot and is being developed with modern emulator features in mind, save/load state and overclocking are available from the start.
|
||||
|
||||
## Why the m515?
|
||||
|
||||
It is the best Palm brand OS 4 device that has no special hardware builtin(cellphone, barcode scanner, keyboard attachment), it has a color screen, and available ROM dumps.
|
||||
It is the best Palm brand OS 4 device that has no special hardware built-in(cellphone, barcode scanner, keyboard attachment), it has a color screen, and available ROM dumps.
|
||||
|
||||
## Why the Tungsten T3
|
||||
|
||||
It has the best resolution and a well documented CPU.
|
||||
|
||||
## What about accessories?
|
||||
|
||||
The Palm keyboard attachment will likely be emulated later on so the PC keyboard can be used on text fields.
|
||||
|
||||
## What about OS 5?
|
||||
|
||||
I am planning on adding Tungsten T3 support as soon as possible.
|
||||
|
||||
## Credits
|
||||
[Firebird Emu](https://github.com/nspire-emus/firebird) (ARMv5TE Core)
|
||||
[uARM](https://dmitry.gr/?r=05.Projects&proj=07.%20Linux%20on%208bit) (PXA260 CPU Peripherals and reference CPU core)
|
||||
[uARM](https://dmitry.gr/?r=05.Projects&proj=07.%20Linux%20on%208bit) (Most of the PXA260 CPU)
|
||||
[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)
|
||||
@@ -55,12 +53,6 @@ Install prc-tools from the below link(self compiled or prepackaged VM)
|
||||
cd ./tools/palm/hwTestSuite
|
||||
./make.sh
|
||||
|
||||
#### MuExpDriver for Palm OS
|
||||
Install prc-tools from the below link(self compiled or prepackaged VM)
|
||||
|
||||
cd ./tools/palm/muExpansionDriver
|
||||
./make.sh
|
||||
|
||||
## Running
|
||||
#### Files
|
||||
palmos40-en-m500.rom: f50e4d5e4d98dc831f2c34a9107651eb (MD5)
|
||||
|
||||
@@ -1,541 +0,0 @@
|
||||
#include <assert.h>
|
||||
|
||||
#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);//TODO: this signed int shift is undefined behavior by the C standard
|
||||
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;//TODO: this signed int shift is undefined behavior by the C standard
|
||||
arm.reg[15] += 4;
|
||||
if(arm.reg[15] >= 0x2009B130 && arm.reg[15] <= 0x200C7EEB && i.branch.l)
|
||||
gui_debug_printf("ARM DAL function call, jump from 0x%08X to 0x%08X\n", arm.reg[14] - 4, arm.reg[15]);
|
||||
}
|
||||
else if((insn & 0xF000F10) == 0xE000F10)
|
||||
do_cp15_instruction(i);
|
||||
else if((insn & 0xF000F10) == 0xE000E10)
|
||||
do_cp14_instruction(i);
|
||||
else if((insn & 0xF000000) == 0xF000000)
|
||||
cpu_exception(EX_SWI);
|
||||
else
|
||||
undefined_instruction();
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#ifndef H_ARMSNIPPETS
|
||||
#define H_ARMSNIPPETS
|
||||
|
||||
#define armloader_cb()
|
||||
|
||||
#endif
|
||||
@@ -1,167 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/* 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
|
||||
@@ -1,400 +0,0 @@
|
||||
.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, #80*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
|
||||
@@ -1,334 +0,0 @@
|
||||
.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, #80*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, #80*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
|
||||
@@ -1,496 +0,0 @@
|
||||
// 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 (80*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:
|
||||
@@ -1,335 +0,0 @@
|
||||
// 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 (80*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
|
||||
@@ -1,81 +0,0 @@
|
||||
#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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
//Mac OS RetroArch port is lacking <type_traits>
|
||||
template<bool B, class T, class F>
|
||||
struct typeConditional { typedef T type; };
|
||||
|
||||
template<class T, class F>
|
||||
struct typeConditional<false, T, F> { typedef F type; };
|
||||
|
||||
template <size_t LastBit>
|
||||
struct MinimumTypeHelper {
|
||||
typedef
|
||||
typename typeConditional<LastBit == 0 , void,
|
||||
typename typeConditional<LastBit <= 8 , uint8_t,
|
||||
typename typeConditional<LastBit <= 16, uint16_t,
|
||||
typename typeConditional<LastBit <= 32, uint32_t,
|
||||
typename typeConditional<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
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
#include "cpu.h"
|
||||
#include "cpudefs.h"
|
||||
#include "mmu.h"
|
||||
|
||||
extern "C" {
|
||||
#include "../pxa260/pxa260.h"
|
||||
#include "../pxa260/pxa260_PwrClk.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 PXA260 "01101001000001010010000100000000"
|
||||
value = 0x69052D05;//Intel PXA261 "01101001000001010010110100000101"
|
||||
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;
|
||||
case 0x0F0001: /* MRC p15, 0, <Rd>, c15, c1, 0: Unknown */
|
||||
//TODO: Unknown(implmentation defined cp15 register)
|
||||
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;
|
||||
//TODO: actually implement this register fully
|
||||
/*
|
||||
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();
|
||||
emuprintf("Turned MMU %s\n", value & 1 ? "on" : "off");
|
||||
}
|
||||
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 */
|
||||
emuprintf("Wait for interrupt, does not work with uARM core!\n");
|
||||
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;
|
||||
case 0x0F0001: /* MCR p15, 0, <Rd>, c15, c1, 0: Unknown */
|
||||
//TODO: Unknown(implmentation defined cp15 register)
|
||||
break;
|
||||
default:
|
||||
warn("Unknown coprocessor instruction MCR %08X\n", 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);
|
||||
}
|
||||
|
||||
void do_cp14_instruction(Instruction i)
|
||||
{
|
||||
uint32_t instr = i.raw;
|
||||
bool specialInstr = i.cond == 0xF;
|
||||
bool success;
|
||||
|
||||
success = pxa260pwrClkPrvCoprocRegXferFunc(&pxa260PwrClk, specialInstr, (instr & 0x00100000) != 0, (instr >> 21) & 0x07, (instr >> 12) & 0x0F, (instr >> 16) & 0x0F, instr & 0x0F, (instr >> 5) & 0x07);
|
||||
|
||||
if(!success)
|
||||
warn("Unknown coprocessor instruction MCR %08X\n", instr);
|
||||
}
|
||||
|
||||
@@ -1,428 +0,0 @@
|
||||
#include <algorithm>
|
||||
//#include <mutex>
|
||||
#include <assert.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
// Uncomment the following line to measure the time until the OS is loaded
|
||||
// #define BENCHMARK
|
||||
#ifdef BENCHMARK
|
||||
#include <time.h>
|
||||
#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
|
||||
|
||||
/*
|
||||
//TODO: remove this, causes slowdown
|
||||
if(arm.reg[15] == 0x200AC088)
|
||||
emuprintf("HAL state set:%d\n", arm.reg[0]);
|
||||
*/
|
||||
|
||||
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");
|
||||
longjmp(restart_after_exception, 1);
|
||||
}
|
||||
|
||||
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);
|
||||
longjmp(restart_after_exception, 1);
|
||||
}
|
||||
|
||||
void undefined_instruction()
|
||||
{
|
||||
fix_pc_for_fault();
|
||||
if(current_instr_size == 4)
|
||||
warn("Undefined instruction 0x%08X at 0x%08X\n", read_word(arm.reg[15]), arm.reg[15]);
|
||||
else
|
||||
warn("Undefined instruction 0x%04X at 0x%08X\n", read_half(arm.reg[15]), arm.reg[15]);
|
||||
arm.reg[15] += current_instr_size;
|
||||
cpu_exception(EX_UNDEFINED);
|
||||
longjmp(restart_after_exception, 1);
|
||||
}
|
||||
|
||||
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 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 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;
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
/* 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 get_cpsr() __asm__("get_cpsr");
|
||||
void set_cpsr_full(uint32_t cpsr);
|
||||
void FASTCALL set_cpsr(uint32_t cpsr, uint32_t mask);
|
||||
uint32_t 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
|
||||
@@ -1,157 +0,0 @@
|
||||
#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);
|
||||
void do_cp14_instruction(Instruction i);
|
||||
|
||||
#endif // CPUDEFS_H
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
/* 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 "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
extern FILE *debugger_input;
|
||||
#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 debugger(x, y)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,56 +0,0 @@
|
||||
#ifndef H_EMU
|
||||
#define H_EMU
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "mem.h"
|
||||
|
||||
#include "../emulator.h"
|
||||
#include "../portability.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#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, ...);
|
||||
#define logprintf(type, ...) debugLog(__VA_ARGS__)
|
||||
#define emuprintf(...) debugLog(__VA_ARGS__)
|
||||
|
||||
//void warn(const char *fmt, ...);
|
||||
//__attribute__((noreturn)) void error(const char *fmt, ...);
|
||||
#define warn(...) debugLog(__VA_ARGS__)
|
||||
#define error(...) abort()
|
||||
|
||||
extern jmp_buf restart_after_exception;
|
||||
|
||||
// GUI callbacks
|
||||
#define gui_debug_printf(...) debugLog(__VA_ARGS__)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,20 +0,0 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <setjmp.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;
|
||||
|
||||
jmp_buf restart_after_exception;
|
||||
|
||||
uint32_t cpu_events;
|
||||
@@ -1,37 +0,0 @@
|
||||
#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
|
||||
@@ -1,143 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/* Declarations for memory.c */
|
||||
|
||||
#ifndef H_MEM
|
||||
#define H_MEM
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "cpu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MEM_MAXSIZE (80*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
|
||||
@@ -1,289 +0,0 @@
|
||||
#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"
|
||||
|
||||
#if !defined(EMU_NO_SAFETY)
|
||||
#include "uArm/CPU_2.h"
|
||||
#include "uArm/icache.h"
|
||||
#include "../pxa260/pxa260.h"
|
||||
#endif
|
||||
|
||||
/* 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(void) {
|
||||
#if !defined(EMU_NO_SAFETY)
|
||||
icacheInval(&pxa260CpuState.ic);//icache needs to be flushed when MMU state changes
|
||||
#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();
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
#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);
|
||||
void mmu_dump_tables(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,136 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
#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
|
||||
@@ -1,310 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/* 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
|
||||
@@ -1,907 +0,0 @@
|
||||
/* 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 <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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 wd, [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((char*)jump_table_start[0], (char*)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));
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,179 +0,0 @@
|
||||
#ifndef _CPU_H_
|
||||
#define _CPU_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//#define ARM_V6 //define to allow v6 instructions
|
||||
//#define THUMB_2 //define to allow Thumb2
|
||||
|
||||
#include "../../pxa260/pxa260_types.h"
|
||||
|
||||
struct ArmCpu;
|
||||
|
||||
#define ARM_SR_N 0x80000000UL
|
||||
#define ARM_SR_Z 0x40000000UL
|
||||
#define ARM_SR_C 0x20000000UL
|
||||
#define ARM_SR_V 0x10000000UL
|
||||
#define ARM_SR_Q 0x08000000UL
|
||||
#ifdef ARM_V6 //V6KT2, but without T2 to be exact (we implement things like MLS, but not Thumb2 or ThumbEE)
|
||||
#define ARM_SR_J 0x01000000UL
|
||||
#define ARM_SR_E 0x00000200UL
|
||||
#define ARM_SR_A 0x00000100UL
|
||||
#define ARM_SR_GE_0 0x00010000UL
|
||||
#define ARM_SR_GE_1 0x00020000UL
|
||||
#define ARM_SR_GE_2 0x00040000UL
|
||||
#define ARM_SR_GE_3 0x00080000UL
|
||||
#define ARM_SR_GE_MASK 0x000F0000UL
|
||||
#define ARM_SR_GE_SHIFT 16
|
||||
#endif
|
||||
#define ARM_SR_I 0x00000080UL
|
||||
#define ARM_SR_F 0x00000040UL
|
||||
#define ARM_SR_T 0x00000020UL
|
||||
#define ARM_SR_M 0x0000001FUL
|
||||
|
||||
#define ARM_SR_MODE_USR 0x00000010UL
|
||||
#define ARM_SR_MODE_FIQ 0x00000011UL
|
||||
#define ARM_SR_MODE_IRQ 0x00000012UL
|
||||
#define ARM_SR_MODE_SVC 0x00000013UL
|
||||
#define ARM_SR_MODE_ABT 0x00000017UL
|
||||
#define ARM_SR_MODE_UND 0x0000001BUL
|
||||
#define ARM_SR_MODE_SYS 0x0000001FUL
|
||||
|
||||
#define ARV_VECTOR_OFFT_RST 0x00000000UL
|
||||
#define ARM_VECTOR_OFFT_UND 0x00000004UL
|
||||
#define ARM_VECTOR_OFFT_SWI 0x00000008UL
|
||||
#define ARM_VECTOR_OFFT_P_ABT 0x0000000CUL
|
||||
#define ARM_VECTOR_OFFT_D_ABT 0x00000010UL
|
||||
#define ARM_VECTOR_OFFT_UNUSED 0x00000014UL
|
||||
#define ARM_VECTOR_OFFT_IRQ 0x00000018UL
|
||||
#define ARM_VECTOR_OFFT_FIQ 0x0000001CUL
|
||||
|
||||
#define HYPERCALL_ARM 0xF7BBBBBBUL
|
||||
#define HYPERCALL_THUMB 0xBBBBUL
|
||||
|
||||
//the following are for cpuGetRegExternal() and are generally used for debugging purposes
|
||||
#define ARM_REG_NUM_CPSR 16
|
||||
#define ARM_REG_NUM_SPSR 17
|
||||
|
||||
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);
|
||||
|
||||
#include "icache.h"
|
||||
|
||||
|
||||
/*
|
||||
|
||||
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
|
||||
|
||||
#ifdef ARM_V6
|
||||
|
||||
Boolean EEE; //endianness one exception entry
|
||||
Boolean impreciseAbtWaiting;
|
||||
#endif
|
||||
|
||||
ArmCpuMemF memF;
|
||||
ArmCpuEmulErr emulErrF;
|
||||
ArmCpuHypercall hypercallF;
|
||||
ArmSetFaultAdrF setFaultAdrF;
|
||||
|
||||
icache ic;
|
||||
|
||||
void* userData; //shared by all callbacks
|
||||
}ArmCpu;
|
||||
|
||||
|
||||
Err cpuInit(ArmCpu* cpu, UInt32 pc, ArmCpuMemF memF, ArmCpuEmulErr emulErrF, ArmCpuHypercall hypercallF, ArmSetFaultAdrF setFaultAdrF);
|
||||
Err cpuDeinit(ArmCpu* cp);
|
||||
void cpuCycle(ArmCpu* cpu);
|
||||
void cpuIrq(ArmCpu* cpu, Boolean fiq, Boolean raise); //unraise when acknowledged
|
||||
|
||||
#ifdef ARM_V6
|
||||
|
||||
void cpuSignalImpreciseAbt(ArmCpu* cpu, Boolean raise);
|
||||
|
||||
#endif
|
||||
|
||||
UInt32 cpuGetRegExternal(ArmCpu* cpu, UInt8 reg);
|
||||
void cpuSetReg(ArmCpu* cpu, UInt8 reg, UInt32 val);
|
||||
|
||||
void cpuCoprocessorRegister(ArmCpu* cpu, UInt8 cpNum, ArmCoprocessor* coproc);
|
||||
void cpuCoprocessorUnregister(ArmCpu* cpu, UInt8 cpNum);
|
||||
|
||||
void cpuSetVectorAddr(ArmCpu* cpu, UInt32 adr);
|
||||
|
||||
UInt16 cpuGetCPAR(ArmCpu* cpu);
|
||||
void cpuSetCPAR(ArmCpu* cpu, UInt16 cpar);
|
||||
|
||||
void cpuIcacheInval(ArmCpu* cpu);
|
||||
void cpuIcacheInvalAddr(ArmCpu* cpu, UInt32 addr);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,164 +0,0 @@
|
||||
#include "uArmGlue.h"
|
||||
#include "CPU_2.h"
|
||||
|
||||
#include "../../emulator.h"
|
||||
#include "../../armv5te/cpu.h"
|
||||
#include "../../armv5te/asmcode.h"
|
||||
#include "../../armv5te/cpudefs.h"
|
||||
#include "../../pxa260/pxa260.h"
|
||||
|
||||
|
||||
static ArmCoprocessor uArmCp14;
|
||||
static ArmCoprocessor uArmCp15;
|
||||
|
||||
|
||||
Boolean uArmCp14RegXferF (struct ArmCpu* cpu, void* userData, Boolean two/* MCR2/MRC2 ? */, Boolean MRC, UInt8 op1, UInt8 Rx, UInt8 CRn, UInt8 CRm, UInt8 op2){
|
||||
//if(!cpu->coproc[vb8].regXfer(cpu, cpu->coproc[vb8].userData, specialInstr, (instr & 0x00100000UL) != 0, (instr >> 21) & 0x07, (instr >> 12) & 0x0F, (instr >> 16) & 0x0F, instr & 0x0F, (instr >> 5) & 0x07)) goto invalid_instr;
|
||||
Instruction inst;
|
||||
|
||||
inst.raw = 0xE000E10 | op1 << 21 | Rx << 12 | CRn << 16 | CRm | op2 << 5;
|
||||
|
||||
if(MRC)
|
||||
inst.raw |= 0x00100000;
|
||||
|
||||
do_cp14_instruction(inst);
|
||||
return true;
|
||||
}
|
||||
|
||||
Boolean uArmCp14DatProcF (struct ArmCpu* cpu, void* userData, Boolean two/* CDP2 ? */, UInt8 op1, UInt8 CRd, UInt8 CRn, UInt8 CRm, UInt8 op2){
|
||||
debugLog("uARM CP14 dat proc unimplemented\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
Boolean uArmCp14MemAccsF (struct ArmCpu* cpu, void* userData, Boolean two /* LDC2/STC2 ? */, Boolean N, Boolean store, UInt8 CRd, UInt32 addr, UInt8* option /* NULL if none */){
|
||||
debugLog("uARM CP14 mem access unimplemented\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
Boolean uArmCp14TwoRegF (struct ArmCpu* cpu, void* userData, Boolean MRRC, UInt8 op, UInt8 Rd, UInt8 Rn, UInt8 CRm){
|
||||
debugLog("uARM CP14 2 reg access unimplemented\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
Boolean uArmCp15RegXferF (struct ArmCpu* cpu, void* userData, Boolean two/* MCR2/MRC2 ? */, Boolean MRC, UInt8 op1, UInt8 Rx, UInt8 CRn, UInt8 CRm, UInt8 op2){
|
||||
//if(!cpu->coproc[vb8].regXfer(cpu, cpu->coproc[vb8].userData, specialInstr, (instr & 0x00100000UL) != 0, (instr >> 21) & 0x07, (instr >> 12) & 0x0F, (instr >> 16) & 0x0F, instr & 0x0F, (instr >> 5) & 0x07)) goto invalid_instr;
|
||||
Instruction inst;
|
||||
|
||||
if(two)
|
||||
debugLog("uARM unimplemented 2 register CP15 access\n");
|
||||
|
||||
if(Rx == 15)
|
||||
set_cpsr_full(pxa260CpuState.CPSR);
|
||||
else
|
||||
arm.reg[Rx] = pxa260CpuState.regs[Rx];
|
||||
|
||||
inst.raw = 0xE000F10 | op1 << 21 | Rx << 12 | CRn << 16 | CRm | op2 << 5;
|
||||
|
||||
if(MRC)
|
||||
inst.raw |= 0x00100000;
|
||||
|
||||
do_cp15_instruction(inst);
|
||||
//addr_cache_flush handles icache flushing too
|
||||
|
||||
if(Rx == 15)
|
||||
pxa260CpuState.CPSR = get_cpsr();//this could be catastrophic in any other circumstance but only the CPSR data flags can be changed by CP15
|
||||
else
|
||||
pxa260CpuState.regs[Rx] = arm.reg[Rx];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Boolean uArmCp15DatProcF (struct ArmCpu* cpu, void* userData, Boolean two/* CDP2 ? */, UInt8 op1, UInt8 CRd, UInt8 CRn, UInt8 CRm, UInt8 op2){
|
||||
debugLog("uARM CP15 dat proc unimplemented\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
Boolean uArmCp15MemAccsF (struct ArmCpu* cpu, void* userData, Boolean two /* LDC2/STC2 ? */, Boolean N, Boolean store, UInt8 CRd, UInt32 addr, UInt8* option /* NULL if none */){
|
||||
debugLog("uARM CP15 mem access unimplemented\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
Boolean uArmCp15TwoRegF (struct ArmCpu* cpu, void* userData, Boolean MRRC, UInt8 op, UInt8 Rd, UInt8 Rn, UInt8 CRm){
|
||||
debugLog("uARM CP15 2 reg access unimplemented\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
Boolean uArmMemAccess(struct ArmCpu* cpu, void* buf, UInt32 vaddr, UInt8 size, Boolean write, Boolean priviledged, UInt8* fsr){
|
||||
if(write){
|
||||
switch(size){
|
||||
case 1:
|
||||
write_byte(vaddr, *(uint8_t*)buf);
|
||||
return true;
|
||||
|
||||
case 2:
|
||||
write_half(vaddr, *(uint16_t*)buf);
|
||||
return true;
|
||||
|
||||
case 4:
|
||||
write_word(vaddr, *(uint32_t*)buf);
|
||||
return true;
|
||||
|
||||
default:
|
||||
debugLog("uARM wrote memory with invalid byte count:%d\n", size);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else{
|
||||
switch(size){
|
||||
case 1:
|
||||
*(uint8_t*)buf = read_byte(vaddr);
|
||||
return true;
|
||||
|
||||
case 2:
|
||||
*(uint16_t*)buf = read_half(vaddr);
|
||||
return true;
|
||||
|
||||
case 4:
|
||||
*(uint32_t*)buf = read_word(vaddr);
|
||||
return true;
|
||||
|
||||
case 32:
|
||||
((uint32_t*)buf)[0] = read_word(vaddr);
|
||||
((uint32_t*)buf)[1] = read_word(vaddr + 4);
|
||||
((uint32_t*)buf)[2] = read_word(vaddr + 8);
|
||||
((uint32_t*)buf)[3] = read_word(vaddr + 12);
|
||||
((uint32_t*)buf)[4] = read_word(vaddr + 16);
|
||||
((uint32_t*)buf)[5] = read_word(vaddr + 20);
|
||||
((uint32_t*)buf)[6] = read_word(vaddr + 24);
|
||||
((uint32_t*)buf)[7] = read_word(vaddr + 28);
|
||||
return true;
|
||||
|
||||
default:
|
||||
debugLog("uARM read memory with invalid byte count:%d\n", size);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Boolean uArmHypercall(struct ArmCpu* cpu){
|
||||
//no hypercalls
|
||||
return true;
|
||||
}
|
||||
|
||||
void uArmEmulErr (struct ArmCpu* cpu, const char* err_str){
|
||||
debugLog("uARM error:%s\n", err_str);
|
||||
}
|
||||
|
||||
void uArmSetFaultAddr(struct ArmCpu* cpu, UInt32 adr, UInt8 faultStatus){
|
||||
debugLog("uARM set fault addr:0x%08X, status:0x%02X\n", adr, faultStatus);
|
||||
}
|
||||
|
||||
void uArmInitCpXX(ArmCpu* cpu){
|
||||
uArmCp14.regXfer = uArmCp14RegXferF;
|
||||
uArmCp14.dataProcessing = uArmCp14DatProcF;
|
||||
uArmCp14.memAccess = uArmCp14MemAccsF;
|
||||
uArmCp14.twoRegF = uArmCp14TwoRegF;
|
||||
|
||||
uArmCp15.regXfer = uArmCp15RegXferF;
|
||||
uArmCp15.dataProcessing = uArmCp15DatProcF;
|
||||
uArmCp15.memAccess = uArmCp15MemAccsF;
|
||||
uArmCp15.twoRegF = uArmCp15TwoRegF;
|
||||
|
||||
cpuCoprocessorRegister(cpu, 14, &uArmCp14);
|
||||
cpuCoprocessorRegister(cpu, 15, &uArmCp15);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
#ifndef UARM_GLUE_H
|
||||
#define UARM_GLUE_H
|
||||
|
||||
#include "CPU_2.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
Boolean uArmMemAccess(struct ArmCpu* cpu, void* buf, UInt32 vaddr, UInt8 size, Boolean write, Boolean priviledged, UInt8* fsr); //read/write
|
||||
Boolean uArmHypercall(struct ArmCpu* cpu);//return true if handled
|
||||
void uArmEmulErr (struct ArmCpu* cpu, const char* err_str);
|
||||
void uArmSetFaultAddr(struct ArmCpu* cpu, UInt32 adr, UInt8 faultStatus);
|
||||
|
||||
void uArmInitCpXX(ArmCpu* cpu);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -77,29 +77,6 @@ void (*palmSerialDataFlush)(void);//called by the emulator to delete all da
|
||||
void (*palmGetRtcFromHost)(uint8_t* writeBack);//[0] = hours, [1] = minutes, [2] = seconds
|
||||
|
||||
|
||||
static void patchOsRom(uint32_t address, char* patch){
|
||||
uint32_t offset;
|
||||
uint32_t patchBytes = strlen(patch) / 2;//1 char per nibble
|
||||
uint32_t swapBegin = address & 0xFFFFFFFE;
|
||||
uint32_t swapSize = patchBytes / sizeof(uint16_t) + 1;
|
||||
char conv[5] = "0xXX";
|
||||
|
||||
#if defined(EMU_SUPPORT_PALM_OS5)
|
||||
if(!palmEmulatingTungstenT3)
|
||||
#endif
|
||||
swap16BufferIfLittle(&palmRom[swapBegin], swapSize);
|
||||
for(offset = 0; offset < patchBytes; offset++){
|
||||
conv[2] = patch[offset * 2];
|
||||
conv[3] = patch[offset * 2 + 1];
|
||||
palmRom[address + offset] = strtol(conv, NULL, 0);
|
||||
}
|
||||
#if defined(EMU_SUPPORT_PALM_OS5)
|
||||
if(!palmEmulatingTungstenT3)
|
||||
#endif
|
||||
swap16BufferIfLittle(&palmRom[swapBegin], swapSize);
|
||||
}
|
||||
|
||||
|
||||
uint32_t emulatorInit(uint8_t emulatedDevice, uint8_t* palmRomData, uint32_t palmRomSize, uint8_t* palmBootloaderData, uint32_t palmBootloaderSize, bool syncRtc, bool allowInvalidBehavior){
|
||||
if(emulatorInitialized)
|
||||
return EMU_ERROR_RESOURCE_LOCKED;
|
||||
@@ -127,22 +104,23 @@ uint32_t emulatorInit(uint8_t emulatedDevice, uint8_t* palmRomData, uint32_t pal
|
||||
|
||||
if(palmEmulatingTungstenT3){
|
||||
//emulating Tungsten T3
|
||||
bool dynarecInited = false;
|
||||
|
||||
dynarecInited = pxa260Init(&palmRom, &palmRam);
|
||||
palmRom = malloc(TUNGSTEN_T3_ROM_SIZE);
|
||||
palmRam = malloc(TUNGSTEN_T3_RAM_SIZE);
|
||||
palmFramebuffer = malloc(320 * 480 * sizeof(uint16_t));
|
||||
palmAudio = malloc(AUDIO_SAMPLES_PER_FRAME * 2 * sizeof(int16_t));
|
||||
palmAudioResampler = blip_new(AUDIO_SAMPLE_RATE);//have 1 second of samples
|
||||
if(!palmFramebuffer || !palmAudio || !palmAudioResampler || !dynarecInited){
|
||||
if(!palmRom || !palmRam || !palmFramebuffer || !palmAudio || !palmAudioResampler){
|
||||
free(palmRom);
|
||||
free(palmRam);
|
||||
free(palmFramebuffer);
|
||||
free(palmAudio);
|
||||
blip_delete(palmAudioResampler);
|
||||
pxa260Deinit();
|
||||
return EMU_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
memcpy(palmRom, palmRomData, FAST_MIN(palmRomSize, TUNGSTEN_T3_ROM_SIZE));
|
||||
if(palmRomSize < TUNGSTEN_T3_ROM_SIZE)
|
||||
memset(palmRom + palmRomSize, 0x00, TUNGSTEN_T3_ROM_SIZE - palmRomSize);
|
||||
swap32BufferIfBig(palmRom, TUNGSTEN_T3_ROM_SIZE / sizeof(uint32_t));
|
||||
memset(palmRam, 0x00, TUNGSTEN_T3_RAM_SIZE);
|
||||
memset(palmFramebuffer, 0x00, 320 * 480 * sizeof(uint16_t));//TODO:PXA260 code doesnt always output a picture like my SED1376 code, so clear the buffer to prevent garbage from being displayed before the first render
|
||||
memset(palmAudio, 0x00, AUDIO_SAMPLES_PER_FRAME * 2/*channels*/ * sizeof(int16_t));
|
||||
@@ -159,9 +137,6 @@ uint32_t emulatorInit(uint8_t emulatedDevice, uint8_t* palmRomData, uint32_t pal
|
||||
pxa260Framebuffer = palmFramebuffer;
|
||||
blip_set_rates(palmAudioResampler, DBVZ_AUDIO_MAX_CLOCK_RATE, AUDIO_SAMPLE_RATE);
|
||||
|
||||
patchOsRom(0x333EC6, "0000");//blocks out the slot driver
|
||||
patchOsRom(0x205C, "0000A0E1");//blocks idle loop jump with NOP
|
||||
|
||||
//reset everything
|
||||
emulatorSoftReset();
|
||||
pxa260SetRtc(0, 0, 0, 0);
|
||||
@@ -231,21 +206,11 @@ uint32_t emulatorInit(uint8_t emulatedDevice, uint8_t* palmRomData, uint32_t pal
|
||||
|
||||
void emulatorDeinit(void){
|
||||
if(emulatorInitialized){
|
||||
#if defined(EMU_SUPPORT_PALM_OS5)
|
||||
if(!palmEmulatingTungstenT3){
|
||||
#endif
|
||||
free(palmRom);
|
||||
free(palmRam);
|
||||
#if defined(EMU_SUPPORT_PALM_OS5)
|
||||
}
|
||||
#endif
|
||||
free(palmRom);
|
||||
free(palmRam);
|
||||
free(palmFramebuffer);
|
||||
free(palmAudio);
|
||||
blip_delete(palmAudioResampler);
|
||||
#if defined(EMU_SUPPORT_PALM_OS5)
|
||||
if(palmEmulatingTungstenT3)
|
||||
pxa260Deinit();
|
||||
#endif
|
||||
free(palmSdCard.flashChipData);
|
||||
emulatorInitialized = false;
|
||||
}
|
||||
@@ -385,6 +350,7 @@ bool emulatorSaveState(uint8_t* data, uint32_t size){
|
||||
|
||||
//memory
|
||||
memcpy(data + offset, palmRam, TUNGSTEN_T3_RAM_SIZE);
|
||||
swap32BufferIfBig(data + offset, TUNGSTEN_T3_ROM_SIZE / sizeof(uint32_t));
|
||||
offset += TUNGSTEN_T3_RAM_SIZE;
|
||||
}
|
||||
else{
|
||||
@@ -509,6 +475,7 @@ bool emulatorLoadState(uint8_t* data, uint32_t size){
|
||||
|
||||
//memory
|
||||
memcpy(palmRam, data + offset, TUNGSTEN_T3_RAM_SIZE);
|
||||
swap32BufferIfBig(palmRam, TUNGSTEN_T3_ROM_SIZE / sizeof(uint32_t));
|
||||
offset += TUNGSTEN_T3_RAM_SIZE;
|
||||
}
|
||||
else{
|
||||
@@ -618,6 +585,7 @@ bool emulatorSaveRam(uint8_t* data, uint32_t size){
|
||||
return false;
|
||||
|
||||
memcpy(data, palmRam, TUNGSTEN_T3_RAM_SIZE);
|
||||
swap32BufferIfBig(data, TUNGSTEN_T3_ROM_SIZE / sizeof(uint32_t));
|
||||
}
|
||||
else{
|
||||
#endif
|
||||
@@ -640,6 +608,7 @@ bool emulatorLoadRam(uint8_t* data, uint32_t size){
|
||||
return false;
|
||||
|
||||
memcpy(palmRam, data, TUNGSTEN_T3_RAM_SIZE);
|
||||
swap32BufferIfBig(palmRam, TUNGSTEN_T3_ROM_SIZE / sizeof(uint32_t));
|
||||
}
|
||||
else{
|
||||
#endif
|
||||
|
||||
@@ -26,72 +26,27 @@ ifeq ($(EMU_HAVE_FILE_LAUNCHER), 1)
|
||||
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)/pxa260/pxa260_DMA.c \
|
||||
$(EMU_PATH)/pxa260/pxa260_DSP.c \
|
||||
$(EMU_PATH)/pxa260/pxa260_GPIO.c \
|
||||
$(EMU_PATH)/pxa260/pxa260_IC.c \
|
||||
$(EMU_PATH)/pxa260/pxa260_LCD.c \
|
||||
$(EMU_PATH)/pxa260/pxa260_PwrClk.c \
|
||||
$(EMU_PATH)/pxa260/pxa260_RTC.c \
|
||||
$(EMU_PATH)/pxa260/pxa260_TIMR.c \
|
||||
$(EMU_PATH)/pxa260/pxa260_UART.c \
|
||||
$(EMU_PATH)/pxa260/pxa260I2c.c \
|
||||
$(EMU_PATH)/pxa260/pxa260Memctrl.c \
|
||||
$(EMU_PATH)/pxa260/pxa260Timing.c \
|
||||
$(EMU_PATH)/pxa260/pxa260Ssp.c \
|
||||
$(EMU_PATH)/pxa260/pxa260Udc.c \
|
||||
$(EMU_PATH)/pxa260/pxa260.c \
|
||||
$(EMU_PATH)/armv5te/emuVarPool.c \
|
||||
$(EMU_PATH)/armv5te/mem.c \
|
||||
$(EMU_PATH)/armv5te/mmu.c \
|
||||
$(EMU_PATH)/tps65010.c \
|
||||
$(EMU_PATH)/tsc2101.c \
|
||||
$(EMU_PATH)/w86l488.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
|
||||
|
||||
ifneq ($(EMU_NO_SAFETY), 1)
|
||||
EMU_SOURCES_C += $(EMU_PATH)/armv5te/uArm/CPU_2.c \
|
||||
$(EMU_PATH)/armv5te/uArm/icache.c
|
||||
|
||||
EMU_SOURCES_CXX += $(EMU_PATH)/armv5te/uArm/uArmGlue.cpp
|
||||
endif
|
||||
|
||||
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 ifneq (,$(filter armv5 armv6 armv7,$(EMU_ARCH)))
|
||||
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
|
||||
else
|
||||
# No dynarec available
|
||||
EMU_DEFINES += -DNO_TRANSLATION
|
||||
EMU_SOURCES_C += $(EMU_PATH)/armv5te/asmcode.c
|
||||
endif
|
||||
endif
|
||||
|
||||
EMU_SOURCES_C += $(EMU_PATH)/src/pxa260/pxa260.c \
|
||||
$(EMU_PATH)/src/pxa260/pxa260I2c.c \
|
||||
$(EMU_PATH)/src/pxa260/pxa260Memctrl.c \
|
||||
$(EMU_PATH)/src/pxa260/pxa260Ssp.c \
|
||||
$(EMU_PATH)/src/pxa260/pxa260Timing.c \
|
||||
$(EMU_PATH)/src/pxa260/pxa260Udc.c \
|
||||
$(EMU_PATH)/src/pxa260/pxa260_CPU.c \
|
||||
$(EMU_PATH)/src/pxa260/pxa260_DMA.c \
|
||||
$(EMU_PATH)/src/pxa260/pxa260_DSP.c \
|
||||
$(EMU_PATH)/src/pxa260/pxa260_GPIO.c \
|
||||
$(EMU_PATH)/src/pxa260/pxa260_IC.c \
|
||||
$(EMU_PATH)/src/pxa260/pxa260_LCD.c \
|
||||
$(EMU_PATH)/src/pxa260/pxa260_MMU.c \
|
||||
$(EMU_PATH)/src/pxa260/pxa260_PwrClk.c \
|
||||
$(EMU_PATH)/src/pxa260/pxa260_RTC.c \
|
||||
$(EMU_PATH)/src/pxa260/pxa260_TIMR.c \
|
||||
$(EMU_PATH)/src/pxa260/pxa260_UART.c \
|
||||
$(EMU_PATH)/src/pxa260/pxa260_cp15.c \
|
||||
$(EMU_PATH)/src/pxa260/pxa260_icache.c \
|
||||
$(EMU_PATH)/src/tps65010.c \
|
||||
$(EMU_PATH)/src/tsc2101.c \
|
||||
$(EMU_PATH)/src/w86l488.c
|
||||
endif
|
||||
|
||||
@@ -42,17 +42,35 @@ static inline void swap16(uint8_t* buffer, uint32_t count){
|
||||
}
|
||||
}
|
||||
|
||||
static inline void swap16BufferIfLittle(uint8_t* buffer, uint32_t count){
|
||||
#if !defined(EMU_BIG_ENDIAN)
|
||||
swap16(buffer, count);
|
||||
#endif
|
||||
static inline void swap32(uint8_t* buffer, uint32_t count){
|
||||
uint32_t index;
|
||||
|
||||
//count specifys the number of uint16_t's that need to be swapped, the uint8_t* is because of alignment restrictions that crash on some platforms
|
||||
count *= sizeof(uint32_t);
|
||||
MULTITHREAD_LOOP(index) for(index = 0; index < count; index += 4){
|
||||
uint8_t temp = buffer[index];
|
||||
buffer[index] = buffer[index + 3];
|
||||
buffer[index + 3] = temp;
|
||||
temp = buffer[index + 1];
|
||||
buffer[index + 1] = buffer[index + 2];
|
||||
buffer[index + 2] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void swap16BufferIfBig(uint8_t* buffer, uint32_t count){
|
||||
#if defined(EMU_BIG_ENDIAN)
|
||||
swap16(buffer, count);
|
||||
#if !defined(EMU_BIG_ENDIAN)
|
||||
#define swap16BufferIfLittle(buffer, count) swap16((buffer), (count))
|
||||
#define swap32BufferIfLittle(buffer, count) swap32((buffer), (count))
|
||||
|
||||
#define swap16BufferIfBig(buffer, count)
|
||||
#define swap32BufferIfBig(buffer, count)
|
||||
#else
|
||||
#define swap16BufferIfLittle(buffer, count)
|
||||
#define swap32BufferIfLittle(buffer, count)
|
||||
|
||||
#define swap16BufferIfBig(buffer, count) swap16((buffer), (count))
|
||||
#define swap32BufferIfBig(buffer, count) swap32((buffer), (count))
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//custom operators
|
||||
#define SIZEOF_BITS(value) (sizeof(value) * 8)
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "disasm.h"
|
||||
#include "emu.h"
|
||||
#include "pxa260.h"
|
||||
|
||||
|
||||
char disasmReturnBuf[80];
|
||||
|
||||
static uint32_t returnData;
|
||||
|
||||
|
||||
static char *strcpy2(char *dest, const char *src) {
|
||||
while ((*dest = *src)) { dest++; src++; }
|
||||
@@ -14,9 +15,21 @@ static char *strcpy2(char *dest, const char *src) {
|
||||
}
|
||||
|
||||
static inline void *virt_mem_ptr(uint32_t addr, uint32_t size) {
|
||||
//this is needed by the disasembler
|
||||
// Note: this is not guaranteed to be correct when range crosses page boundary
|
||||
return (void *)(intptr_t)phys_mem_ptr(mmu_translate(addr, false, NULL, NULL), size);
|
||||
switch(size){
|
||||
case 1:
|
||||
*((uint8_t*)&returnData) = pxa260ReadArbitraryMemory(addr, size * 8);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
*((uint16_t*)&returnData) = pxa260ReadArbitraryMemory(addr, size * 8);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
*((uint32_t*)&returnData) = pxa260ReadArbitraryMemory(addr, size * 8);
|
||||
break;
|
||||
}
|
||||
|
||||
return &returnData;
|
||||
}
|
||||
|
||||
const char reg_name[16][4] = {
|
||||
@@ -540,7 +553,6 @@ invalid:
|
||||
if (!(insn & 0x1000)) target &= ~3;
|
||||
sprintf(out - 5, "%04X\t%s\t%08X", insn,
|
||||
(insn & 0x1000) ? "bl" : "blx", target);
|
||||
gui_debug_printf("%s\n", disasmReturnBuf);
|
||||
return 4;
|
||||
}
|
||||
sprintf(out, "(add\tlr,pc,%08X)", target);
|
||||
@@ -3,17 +3,9 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern char disasmReturnBuf[80];
|
||||
|
||||
uint32_t disasm_arm_insn(uint32_t pc);
|
||||
uint32_t disasm_thumb_insn(uint32_t pc);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -2,6 +2,8 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "pxa260.h"
|
||||
#include "pxa260_MMU.h"
|
||||
#include "pxa260_cp15.h"
|
||||
#include "pxa260_DMA.h"
|
||||
#include "pxa260_DSP.h"
|
||||
#include "pxa260_GPIO.h"
|
||||
@@ -16,181 +18,41 @@
|
||||
#include "pxa260Ssp.h"
|
||||
#include "pxa260Udc.h"
|
||||
#include "pxa260Timing.h"
|
||||
#if !defined(EMU_NO_SAFETY)
|
||||
#include "../armv5te/uArm/CPU_2.h"
|
||||
#include "../armv5te/uArm/uArmGlue.h"
|
||||
#endif
|
||||
#include "../armv5te/cpu.h"
|
||||
#include "../armv5te/emu.h"
|
||||
#include "../armv5te/mem.h"
|
||||
#include "../armv5te/mmu.h"
|
||||
#include "../armv5te/os/os.h"
|
||||
#include "../armv5te/translate.h"
|
||||
#include "../tungstenT3Bus.h"
|
||||
#include "../tsc2101.h"
|
||||
#include "../tps65010.h"
|
||||
#include "../emulator.h"
|
||||
#include "../portability.h"
|
||||
|
||||
|
||||
#define PXA260_IO_BASE 0x40000000
|
||||
#define PXA260_MEMCTRL_BASE 0x48000000
|
||||
|
||||
#define PXA260_TIMER_TICKS_PER_FRAME (TUNGSTEN_T3_CPU_CRYSTAL_FREQUENCY / EMU_FPS)
|
||||
|
||||
#if !defined(EMU_NO_SAFETY)
|
||||
|
||||
ArmCpu pxa260CpuState;
|
||||
#endif
|
||||
uint16_t* pxa260Framebuffer;
|
||||
Pxa260pwrClk pxa260PwrClk;
|
||||
Pxa260ic pxa260Ic;
|
||||
Pxa260rtc pxa260Rtc;
|
||||
Pxa260gpio pxa260Gpio;
|
||||
Pxa260timr pxa260Timer;
|
||||
|
||||
static Pxa260lcd pxa260Lcd;
|
||||
static ArmMmu uArmMmu;
|
||||
static ArmCoprocessor uArmCp14;
|
||||
static ArmCP15 uArmCp15;
|
||||
|
||||
|
||||
#include "pxa260Accessors.c.h"
|
||||
|
||||
bool pxa260Init(uint8_t** returnRom, uint8_t** returnRam){
|
||||
uint32_t mem_offset = 0;
|
||||
uint8_t i;
|
||||
|
||||
//set timing callback pointers
|
||||
pxa260TimingInit();
|
||||
|
||||
//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 = PXA260_ROM_START_ADDRESS;
|
||||
mem_areas[0].size = TUNGSTEN_T3_ROM_SIZE;
|
||||
mem_areas[0].ptr = mem_and_flags + mem_offset;
|
||||
mem_offset += TUNGSTEN_T3_ROM_SIZE;
|
||||
|
||||
//RAM
|
||||
mem_areas[1].base = PXA260_RAM_START_ADDRESS;
|
||||
mem_areas[1].size = TUNGSTEN_T3_RAM_SIZE;
|
||||
mem_areas[1].ptr = mem_and_flags + mem_offset;
|
||||
mem_offset += TUNGSTEN_T3_RAM_SIZE;
|
||||
|
||||
//memory regions that are not directly mapped to a buffer are not added to mem_areas
|
||||
//adding them causes SIGSEGVs
|
||||
|
||||
//accessors
|
||||
//default
|
||||
for(i = 0; i < PXA260_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 = PXA260_START_BANK(PXA260_PCMCIA0_START_ADDRESS); i <= PXA260_END_BANK(PXA260_PCMCIA0_START_ADDRESS, PXA260_PCMCIA0_SIZE); i++){
|
||||
read_byte_map[i] = pxa260_pcmcia0_read_byte;
|
||||
read_half_map[i] = pxa260_pcmcia0_read_half;
|
||||
read_word_map[i] = pxa260_pcmcia0_read_word;
|
||||
write_byte_map[i] = pxa260_pcmcia0_write_byte;
|
||||
write_half_map[i] = pxa260_pcmcia0_write_half;
|
||||
write_word_map[i] = pxa260_pcmcia0_write_word;
|
||||
}
|
||||
|
||||
//PCMCIA1
|
||||
for(i = PXA260_START_BANK(PXA260_PCMCIA1_START_ADDRESS); i <= PXA260_END_BANK(PXA260_PCMCIA1_START_ADDRESS, PXA260_PCMCIA1_SIZE); i++){
|
||||
read_byte_map[i] = pxa260_pcmcia1_read_byte;
|
||||
read_half_map[i] = pxa260_pcmcia1_read_half;
|
||||
read_word_map[i] = pxa260_pcmcia1_read_word;
|
||||
write_byte_map[i] = pxa260_pcmcia1_write_byte;
|
||||
write_half_map[i] = pxa260_pcmcia1_write_half;
|
||||
write_word_map[i] = pxa260_pcmcia1_write_word;
|
||||
}
|
||||
|
||||
//IO
|
||||
read_byte_map[PXA260_START_BANK(PXA260_IO_BASE)] = pxa260_io_read_byte;
|
||||
read_half_map[PXA260_START_BANK(PXA260_IO_BASE)] = pxa260_io_read_half;
|
||||
read_word_map[PXA260_START_BANK(PXA260_IO_BASE)] = pxa260_io_read_word;
|
||||
write_byte_map[PXA260_START_BANK(PXA260_IO_BASE)] = pxa260_io_write_byte;
|
||||
write_half_map[PXA260_START_BANK(PXA260_IO_BASE)] = pxa260_io_write_half;
|
||||
write_word_map[PXA260_START_BANK(PXA260_IO_BASE)] = pxa260_io_write_word;
|
||||
|
||||
//LCD
|
||||
read_byte_map[PXA260_START_BANK(PXA260_LCD_BASE)] = bad_read_byte;
|
||||
read_half_map[PXA260_START_BANK(PXA260_LCD_BASE)] = bad_read_half;
|
||||
read_word_map[PXA260_START_BANK(PXA260_LCD_BASE)] = pxa260_lcd_read_word;
|
||||
write_byte_map[PXA260_START_BANK(PXA260_LCD_BASE)] = bad_write_byte;
|
||||
write_half_map[PXA260_START_BANK(PXA260_LCD_BASE)] = bad_write_half;
|
||||
write_word_map[PXA260_START_BANK(PXA260_LCD_BASE)] = pxa260_lcd_write_word;
|
||||
|
||||
//MEMCTRL
|
||||
read_byte_map[PXA260_START_BANK(PXA260_MEMCTRL_BASE)] = bad_read_byte;
|
||||
read_half_map[PXA260_START_BANK(PXA260_MEMCTRL_BASE)] = bad_read_half;
|
||||
read_word_map[PXA260_START_BANK(PXA260_MEMCTRL_BASE)] = pxa260MemctrlReadWord;
|
||||
write_byte_map[PXA260_START_BANK(PXA260_MEMCTRL_BASE)] = bad_write_byte;
|
||||
write_half_map[PXA260_START_BANK(PXA260_MEMCTRL_BASE)] = bad_write_half;
|
||||
write_word_map[PXA260_START_BANK(PXA260_MEMCTRL_BASE)] = pxa260MemctrlWriteWord;
|
||||
|
||||
//W86L488
|
||||
read_byte_map[PXA260_START_BANK(TUNGSTEN_T3_W86L488_START_ADDRESS)] = bad_read_byte;
|
||||
read_half_map[PXA260_START_BANK(TUNGSTEN_T3_W86L488_START_ADDRESS)] = pxa260_static_chip_select_2_read_half;
|
||||
read_word_map[PXA260_START_BANK(TUNGSTEN_T3_W86L488_START_ADDRESS)] = bad_read_word;
|
||||
write_byte_map[PXA260_START_BANK(TUNGSTEN_T3_W86L488_START_ADDRESS)] = bad_write_byte;
|
||||
write_half_map[PXA260_START_BANK(TUNGSTEN_T3_W86L488_START_ADDRESS)] = pxa260_static_chip_select_2_write_half;
|
||||
write_word_map[PXA260_START_BANK(TUNGSTEN_T3_W86L488_START_ADDRESS)] = bad_write_word;
|
||||
|
||||
*returnRom = mem_areas[0].ptr;
|
||||
*returnRam = mem_areas[1].ptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void pxa260Deinit(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 pxa260Reset(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();
|
||||
}
|
||||
*/
|
||||
|
||||
//set up extra CPU hardware
|
||||
pxa260icInit(&pxa260Ic);
|
||||
pxa260pwrClkInit(&pxa260PwrClk);
|
||||
pxa260lcdInit(&pxa260Lcd, &pxa260Ic);
|
||||
pxa260timrInit(&pxa260Timer, &pxa260Ic);
|
||||
pxa260rtcInit(&pxa260Rtc, &pxa260Ic);
|
||||
pxa260gpioInit(&pxa260Gpio, &pxa260Ic);
|
||||
pxa260TimingInit();
|
||||
pxa260I2cReset();
|
||||
pxa260MemctrlReset();
|
||||
pxa260SspReset();
|
||||
@@ -200,22 +62,20 @@ void pxa260Reset(void){
|
||||
//set first timer event
|
||||
pxa260TimingTriggerEvent(PXA260_TIMING_CALLBACK_TICK_CPU_TIMER, TUNGSTEN_T3_CPU_PLL_FREQUENCY / TUNGSTEN_T3_CPU_CRYSTAL_FREQUENCY);
|
||||
|
||||
#if !defined(EMU_NO_SAFETY)
|
||||
debugLog("Using uARM CPU core!\n");
|
||||
|
||||
cpuInit(&pxa260CpuState, 0x00000000, uArmMemAccess, uArmEmulErr, uArmHypercall, uArmSetFaultAddr);
|
||||
uArmInitCpXX(&pxa260CpuState);
|
||||
#else
|
||||
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;
|
||||
#endif
|
||||
|
||||
addr_cache_flush();//SIGSEGVs on reset without this because the MMU needs to be turned off
|
||||
|
||||
//PC starts at 0x00000000, the first opcode for Palm OS 5 is a jump
|
||||
uArmCp14.regXfer = pxa260pwrClkPrvCoprocRegXferFunc;
|
||||
uArmCp14.dataProcessing = NULL;
|
||||
uArmCp14.memAccess = NULL;
|
||||
uArmCp14.twoRegF = NULL;
|
||||
uArmCp14.userData = &pxa260PwrClk;
|
||||
|
||||
//pwrclk already inited in pxa260Reset
|
||||
cpuCoprocessorRegister(&pxa260CpuState, 14, &uArmCp14);
|
||||
|
||||
mmuInit(&uArmMmu, mmuReadF, NULL);
|
||||
cp15Init(&uArmCp15, &pxa260CpuState, &uArmMmu);
|
||||
}
|
||||
|
||||
void pxa260SetRtc(uint16_t days, uint8_t hours, uint8_t minutes, uint8_t seconds){
|
||||
@@ -242,61 +102,50 @@ void pxa260Execute(bool wantVideo){
|
||||
tsc2101UpdateInterrupt();
|
||||
tps65010UpdateInterrupt();
|
||||
pxa260gpioUpdateKeyMatrix(&pxa260Gpio);
|
||||
pxa260rtcUpdate(&pxa260Rtc);
|
||||
|
||||
//start render
|
||||
if(likely(wantVideo))
|
||||
pxa260lcdFrame(&pxa260Lcd);
|
||||
|
||||
pxa260TimingRun(TUNGSTEN_T3_CPU_PLL_FREQUENCY / EMU_FPS);
|
||||
|
||||
//render
|
||||
//end render
|
||||
if(likely(wantVideo))
|
||||
pxa260lcdFrame(&pxa260Lcd);
|
||||
}
|
||||
|
||||
uint32_t pxa260GetRegister(uint8_t reg){
|
||||
#if !defined(EMU_NO_SAFETY)
|
||||
return cpuGetRegExternal(&pxa260CpuState, reg);
|
||||
#else
|
||||
return reg_pc_mem(reg);
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t pxa260GetCpsr(void){
|
||||
#if !defined(EMU_NO_SAFETY)
|
||||
return cpuGetRegExternal(&pxa260CpuState, ARM_REG_NUM_CPSR);
|
||||
#else
|
||||
return get_cpsr();
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t pxa260GetSpsr(void){
|
||||
#if !defined(EMU_NO_SAFETY)
|
||||
return cpuGetRegExternal(&pxa260CpuState, ARM_REG_NUM_SPSR);
|
||||
#else
|
||||
return get_spsr();
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t pxa260ReadArbitraryMemory(uint32_t address, uint8_t size){
|
||||
uint64_t data = UINT64_MAX;//invalid access
|
||||
uint32_t value;
|
||||
uint8_t unused;
|
||||
|
||||
address = mmu_translate(address, false, NULL, NULL);
|
||||
if(uArmMemAccess(&pxa260CpuState, &value, address, size / 8, false, true, &unused)){
|
||||
switch(size){
|
||||
case 8:
|
||||
data = *(uint8_t*)&value;
|
||||
break;
|
||||
|
||||
switch(size){
|
||||
case 8:
|
||||
if(read_byte_map[address >> 26] != bad_read_byte){
|
||||
data = read_byte_map[address >> 26](address);
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
data = *(uint16_t*)&value;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
if(read_half_map[address >> 26] != bad_read_half){
|
||||
data = read_half_map[address >> 26](address);
|
||||
}
|
||||
break;
|
||||
|
||||
case 32:
|
||||
if(read_word_map[address >> 26] != bad_read_word){
|
||||
data = read_word_map[address >> 26](address);
|
||||
}
|
||||
break;
|
||||
case 32:
|
||||
data = *(uint32_t*)&value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
@@ -10,22 +10,26 @@
|
||||
#include "pxa260_IC.h"
|
||||
#include "pxa260_PwrClk.h"
|
||||
#include "pxa260_GPIO.h"
|
||||
#include "pxa260_RTC.h"
|
||||
#include "pxa260_TIMR.h"
|
||||
#if !defined(EMU_NO_SAFETY)
|
||||
#include "../armv5te/uArm/CPU_2.h"
|
||||
#endif
|
||||
#include "pxa260_CPU.h"
|
||||
|
||||
#if !defined(EMU_NO_SAFETY)
|
||||
extern ArmCpu pxa260CpuState;
|
||||
#endif
|
||||
extern uint16_t* pxa260Framebuffer;
|
||||
extern Pxa260pwrClk pxa260PwrClk;
|
||||
extern Pxa260ic pxa260Ic;
|
||||
extern Pxa260rtc pxa260rtc;
|
||||
extern Pxa260gpio pxa260Gpio;
|
||||
extern Pxa260timr pxa260Timer;
|
||||
|
||||
bool pxa260Init(uint8_t** returnRom, uint8_t** returnRam);
|
||||
void pxa260Deinit(void);
|
||||
uint8_t read_byte(uint32_t address);
|
||||
uint16_t read_half(uint32_t address);
|
||||
uint32_t read_word(uint32_t address);
|
||||
|
||||
void write_byte(uint32_t address, uint8_t byte);
|
||||
void write_half(uint32_t address, uint16_t half);
|
||||
void write_word(uint32_t address, uint32_t word);
|
||||
|
||||
void pxa260Reset(void);
|
||||
void pxa260SetRtc(uint16_t days, uint8_t hours, uint8_t minutes, uint8_t seconds);
|
||||
uint32_t pxa260StateSize(void);
|
||||
|
||||
@@ -36,9 +36,11 @@ static uint32_t pxa260_io_read_word(uint32_t addr){
|
||||
case PXA260_IC_BASE >> 16:
|
||||
pxa260icPrvMemAccessF(&pxa260Ic, addr, 4, false, &out);
|
||||
break;
|
||||
case PXA260_RTC_BASE >> 16:
|
||||
pxa260rtcPrvMemAccessF(&pxa260Rtc, addr, 4, false, &out);
|
||||
break;
|
||||
|
||||
case PXA260_DMA_BASE >> 16:
|
||||
case PXA260_RTC_BASE >> 16:
|
||||
case PXA260_FFUART_BASE >> 16:
|
||||
case PXA260_BTUART_BASE >> 16:
|
||||
case PXA260_STUART_BASE >> 16:
|
||||
@@ -90,9 +92,11 @@ static void pxa260_io_write_word(uint32_t addr, uint32_t value){
|
||||
case PXA260_IC_BASE >> 16:
|
||||
pxa260icPrvMemAccessF(&pxa260Ic, addr, 4, true, &value);
|
||||
break;
|
||||
case PXA260_RTC_BASE >> 16:
|
||||
pxa260rtcPrvMemAccessF(&pxa260Rtc, addr, 4, true, &value);
|
||||
break;
|
||||
|
||||
case PXA260_DMA_BASE >> 16:
|
||||
case PXA260_RTC_BASE >> 16:
|
||||
case PXA260_FFUART_BASE >> 16:
|
||||
case PXA260_BTUART_BASE >> 16:
|
||||
case PXA260_STUART_BASE >> 16:
|
||||
@@ -181,3 +185,150 @@ static void pxa260_static_chip_select_2_write_half(uint32_t addr, uint16_t value
|
||||
w86l488Write16(addr & 0x0E, value);
|
||||
}
|
||||
|
||||
uint8_t read_byte(uint32_t address){
|
||||
if(address >= PXA260_ROM_START_ADDRESS && address < PXA260_ROM_START_ADDRESS + TUNGSTEN_T3_ROM_SIZE)
|
||||
return palmRom[PXA260_ADDR_FIX_ENDIAN_8(address) - PXA260_ROM_START_ADDRESS];
|
||||
else if(address >= PXA260_RAM_START_ADDRESS && address < PXA260_RAM_START_ADDRESS + TUNGSTEN_T3_RAM_SIZE)
|
||||
return palmRam[PXA260_ADDR_FIX_ENDIAN_8(address) - PXA260_RAM_START_ADDRESS];
|
||||
else if(address >= PXA260_IO_BASE && address < PXA260_IO_BASE + PXA260_BANK_SIZE)
|
||||
return pxa260_io_read_byte(address);
|
||||
|
||||
debugLog("Invalid byte read at address: 0x%08X\n", address);
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
uint16_t read_half(uint32_t address){
|
||||
if(address >= PXA260_ROM_START_ADDRESS && address < PXA260_ROM_START_ADDRESS + TUNGSTEN_T3_ROM_SIZE)
|
||||
return *(uint16_t*)(&palmRom[PXA260_ADDR_FIX_ENDIAN_16(address) - PXA260_ROM_START_ADDRESS]);
|
||||
else if(address >= PXA260_RAM_START_ADDRESS && address < PXA260_RAM_START_ADDRESS + TUNGSTEN_T3_RAM_SIZE)
|
||||
return *(uint16_t*)(&palmRam[PXA260_ADDR_FIX_ENDIAN_16(address) - PXA260_RAM_START_ADDRESS]);
|
||||
else if(address >= TUNGSTEN_T3_W86L488_START_ADDRESS && address < TUNGSTEN_T3_W86L488_START_ADDRESS + TUNGSTEN_T3_W86L488_SIZE)
|
||||
return w86l488Read16(address);
|
||||
else if(address >= PXA260_IO_BASE && address < PXA260_IO_BASE + PXA260_BANK_SIZE)
|
||||
return pxa260_io_read_half(address);
|
||||
|
||||
debugLog("Invalid half read at address: 0x%08X\n", address);
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
uint32_t read_word(uint32_t address){
|
||||
if(address >= PXA260_ROM_START_ADDRESS && address < PXA260_ROM_START_ADDRESS + TUNGSTEN_T3_ROM_SIZE)
|
||||
return *(uint32_t*)(&palmRom[PXA260_ADDR_FIX_ENDIAN_32(address) - PXA260_ROM_START_ADDRESS]);
|
||||
else if(address >= PXA260_RAM_START_ADDRESS && address < PXA260_RAM_START_ADDRESS + TUNGSTEN_T3_RAM_SIZE)
|
||||
return *(uint32_t*)(&palmRam[PXA260_ADDR_FIX_ENDIAN_32(address) - PXA260_RAM_START_ADDRESS]);
|
||||
else if(address >= PXA260_MEMCTRL_BASE && address < PXA260_MEMCTRL_BASE + PXA260_BANK_SIZE)
|
||||
return pxa260MemctrlReadWord(address);
|
||||
else if(address >= PXA260_LCD_BASE && address < PXA260_LCD_BASE + PXA260_BANK_SIZE)
|
||||
return pxa260_lcd_read_word(address);
|
||||
else if(address >= PXA260_IO_BASE && address < PXA260_IO_BASE + PXA260_BANK_SIZE)
|
||||
return pxa260_io_read_word(address);
|
||||
|
||||
debugLog("Invalid word read at address: 0x%08X\n", address);
|
||||
return 0x00000000;
|
||||
}
|
||||
|
||||
void write_byte(uint32_t address, uint8_t byte){
|
||||
if(address >= PXA260_RAM_START_ADDRESS && address < PXA260_RAM_START_ADDRESS + TUNGSTEN_T3_RAM_SIZE)
|
||||
palmRam[PXA260_ADDR_FIX_ENDIAN_8(address) - PXA260_RAM_START_ADDRESS] = byte;
|
||||
else if(address >= PXA260_IO_BASE && address < PXA260_IO_BASE + PXA260_BANK_SIZE)
|
||||
pxa260_io_write_byte(address, byte);
|
||||
else
|
||||
debugLog("Invalid byte write at address: 0x%08X, value:0x%02X\n", address, byte);
|
||||
}
|
||||
|
||||
void write_half(uint32_t address, uint16_t half){
|
||||
if(address >= PXA260_RAM_START_ADDRESS && address < PXA260_RAM_START_ADDRESS + TUNGSTEN_T3_RAM_SIZE)
|
||||
*(uint16_t*)(&palmRam[PXA260_ADDR_FIX_ENDIAN_16(address) - PXA260_RAM_START_ADDRESS]) = half;
|
||||
else if(address >= TUNGSTEN_T3_W86L488_START_ADDRESS && address < TUNGSTEN_T3_W86L488_START_ADDRESS + TUNGSTEN_T3_W86L488_SIZE)
|
||||
w86l488Write16(address, half);
|
||||
else if(address >= PXA260_IO_BASE && address < PXA260_IO_BASE + PXA260_BANK_SIZE)
|
||||
pxa260_io_write_half(address, half);
|
||||
else
|
||||
debugLog("Invalid half write at address: 0x%08X, value:0x%04X\n", address, half);
|
||||
}
|
||||
|
||||
void write_word(uint32_t address, uint32_t word){
|
||||
if(address >= PXA260_RAM_START_ADDRESS && address < PXA260_RAM_START_ADDRESS + TUNGSTEN_T3_RAM_SIZE)
|
||||
*(uint32_t*)(&palmRam[PXA260_ADDR_FIX_ENDIAN_32(address) - PXA260_RAM_START_ADDRESS]) = word;
|
||||
else if(address >= PXA260_MEMCTRL_BASE && address < PXA260_MEMCTRL_BASE + PXA260_BANK_SIZE)
|
||||
pxa260MemctrlWriteWord(address, word);
|
||||
else if(address >= PXA260_LCD_BASE && address < PXA260_LCD_BASE + PXA260_BANK_SIZE)
|
||||
pxa260_lcd_write_word(address, word);
|
||||
else if(address >= PXA260_IO_BASE && address < PXA260_IO_BASE + PXA260_BANK_SIZE)
|
||||
pxa260_io_write_word(address, word);
|
||||
else
|
||||
debugLog("Invalid word write at address: 0x%08X, value:0x%08X\n", address, word);
|
||||
}
|
||||
|
||||
static Err mmuReadF(void* userData, UInt32* buf, UInt32 pa){
|
||||
*(uint32_t*)buf = read_word(pa);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static Boolean uArmMemAccess(struct ArmCpu* cpu, void* buf, UInt32 vaddr, UInt8 size, Boolean write, Boolean priviledged, UInt8* fsr){
|
||||
if(!mmuTranslate(&uArmMmu, vaddr, priviledged, write, &vaddr, fsr))
|
||||
return false;
|
||||
|
||||
if(write){
|
||||
switch(size){
|
||||
case 1:
|
||||
write_byte(vaddr, *(uint8_t*)buf);
|
||||
return true;
|
||||
|
||||
case 2:
|
||||
write_half(vaddr, *(uint16_t*)buf);
|
||||
return true;
|
||||
|
||||
case 4:
|
||||
write_word(vaddr, *(uint32_t*)buf);
|
||||
return true;
|
||||
|
||||
default:
|
||||
debugLog("uARM wrote memory with invalid byte count:%d\n", size);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else{
|
||||
switch(size){
|
||||
case 1:
|
||||
*(uint8_t*)buf = read_byte(vaddr);
|
||||
return true;
|
||||
|
||||
case 2:
|
||||
*(uint16_t*)buf = read_half(vaddr);
|
||||
return true;
|
||||
|
||||
case 4:
|
||||
*(uint32_t*)buf = read_word(vaddr);
|
||||
return true;
|
||||
|
||||
case 32:
|
||||
((uint32_t*)buf)[0] = read_word(vaddr);
|
||||
((uint32_t*)buf)[1] = read_word(vaddr + 4);
|
||||
((uint32_t*)buf)[2] = read_word(vaddr + 8);
|
||||
((uint32_t*)buf)[3] = read_word(vaddr + 12);
|
||||
((uint32_t*)buf)[4] = read_word(vaddr + 16);
|
||||
((uint32_t*)buf)[5] = read_word(vaddr + 20);
|
||||
((uint32_t*)buf)[6] = read_word(vaddr + 24);
|
||||
((uint32_t*)buf)[7] = read_word(vaddr + 28);
|
||||
return true;
|
||||
|
||||
default:
|
||||
debugLog("uARM read memory with invalid byte count:%d\n", size);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Boolean uArmHypercall(struct ArmCpu* cpu){
|
||||
//no hypercalls
|
||||
return true;
|
||||
}
|
||||
|
||||
static void uArmEmulErr (struct ArmCpu* cpu, const char* err_str){
|
||||
debugLog("uARM error:%s\n", err_str);
|
||||
}
|
||||
|
||||
static void uArmSetFaultAddr(struct ArmCpu* cpu, UInt32 adr, UInt8 faultStatus){
|
||||
debugLog("uARM set fault addr:0x%08X, status:0x%02X\n", adr, faultStatus);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
#include "pxa260.h"
|
||||
#include "pxa260_TIMR.h"
|
||||
@@ -8,13 +7,8 @@
|
||||
#include "pxa260Ssp.h"
|
||||
#include "pxa260Udc.h"
|
||||
#include "pxa260Timing.h"
|
||||
#include "pxa260_CPU.h"
|
||||
#include "../tsc2101.h"
|
||||
#if !defined(EMU_NO_SAFETY)
|
||||
#include "../armv5te/uArm/CPU_2.h"
|
||||
#endif
|
||||
#include "../armv5te/os/os.h"
|
||||
#include "../armv5te/emu.h"
|
||||
#include "../armv5te/cpu.h"
|
||||
#include "../emulator.h"
|
||||
|
||||
|
||||
@@ -22,6 +16,7 @@
|
||||
|
||||
|
||||
static int32_t pxa260TimingLeftoverCycles;//doesnt need to go in save states
|
||||
static int32_t pxa260CycleCountDelta;//doesnt need to go in save states
|
||||
|
||||
void (*pxa260TimingCallbacks[PXA260_TIMING_TOTAL_CALLBACKS])(void);
|
||||
int32_t pxa260TimingQueuedEvents[PXA260_TIMING_TOTAL_CALLBACKS];
|
||||
@@ -56,9 +51,9 @@ void pxa260TimingReset(void){
|
||||
void pxa260TimingTriggerEvent(uint8_t callbackId, int32_t wait){
|
||||
pxa260TimingQueuedEvents[callbackId] = wait;
|
||||
//dont need to check if in handler since cycle_count_delta is 0 or positive when in handlers are called
|
||||
if(wait < -cycle_count_delta){
|
||||
pxa260TimingLeftoverCycles = -cycle_count_delta - wait;
|
||||
cycle_count_delta = -wait;
|
||||
if(wait < -pxa260CycleCountDelta){
|
||||
pxa260TimingLeftoverCycles = -pxa260CycleCountDelta - wait;
|
||||
pxa260CycleCountDelta = -wait;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,53 +62,22 @@ void pxa260TimingCancelEvent(uint8_t callbackId){
|
||||
}
|
||||
|
||||
void pxa260TimingRun(int32_t cycles){
|
||||
#if OS_HAS_PAGEFAULT_HANDLER
|
||||
os_exception_frame_t seh_frame = {NULL, NULL};
|
||||
#endif
|
||||
uint8_t index;
|
||||
int32_t addCycles;
|
||||
|
||||
#if OS_HAS_PAGEFAULT_HANDLER
|
||||
os_faulthandler_arm(&seh_frame);
|
||||
#endif
|
||||
|
||||
while(setjmp(restart_after_exception)){};
|
||||
exiting = false;
|
||||
pxa260TimingLeftoverCycles = 0;//used when an event is added while the CPU is running
|
||||
|
||||
keepRunning:
|
||||
addCycles = pxa260TimingGetDurationUntilNextEvent(cycles);
|
||||
cycle_count_delta = -addCycles * palmClockMultiplier;
|
||||
pxa260CycleCountDelta = -addCycles * palmClockMultiplier;
|
||||
|
||||
while (!exiting && cycle_count_delta < 0) {
|
||||
#if !defined(EMU_NO_SAFETY)
|
||||
while(pxa260CycleCountDelta < 0){
|
||||
cpuCycle(&pxa260CpuState);
|
||||
cycle_count_delta += 1;
|
||||
#else
|
||||
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;//the wait opcode will be executed again if still waiting, that will clear the remaining cycle count and exit the function again
|
||||
|
||||
if (arm.cpsr_low28 & 0x20)
|
||||
cpu_thumb_loop();
|
||||
else
|
||||
cpu_arm_loop();
|
||||
#endif
|
||||
pxa260CycleCountDelta += 1;
|
||||
}
|
||||
|
||||
//if more then the requested cycles are executed count those too
|
||||
addCycles += cycle_count_delta / palmClockMultiplier;
|
||||
addCycles += pxa260CycleCountDelta / palmClockMultiplier;
|
||||
|
||||
//remove the unused cycles
|
||||
addCycles -= pxa260TimingLeftoverCycles;
|
||||
@@ -133,10 +97,6 @@ void pxa260TimingRun(int32_t cycles){
|
||||
cycles -= addCycles;
|
||||
if(cycles > 0)
|
||||
goto keepRunning;
|
||||
|
||||
#if OS_HAS_PAGEFAULT_HANDLER
|
||||
os_faulthandler_unarm(&seh_frame);
|
||||
#endif
|
||||
}
|
||||
|
||||
void pxa260TimingTickCpuTimer(void){
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
*/
|
||||
|
||||
|
||||
#include "CPU_2.h"
|
||||
#include "../../pxa260/pxa260_math64.h"
|
||||
#include "pxa260_CPU.h"
|
||||
#include "pxa260_math64.h"
|
||||
|
||||
|
||||
|
||||
@@ -89,6 +89,234 @@
|
||||
|
||||
|
||||
|
||||
#if 1
|
||||
//ARM debugging sandbox, no separate file this time
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../emulator.h"
|
||||
|
||||
|
||||
#define DAL_START_IN_ROM 0x0009B130
|
||||
#define DAL_END_IN_ROM 0x000C7EEB
|
||||
|
||||
#define DAL_ADDR_FROM_PC(pc) ((pc) - 0x20000000 - DAL_START_IN_ROM)
|
||||
#define PC_FROM_DAL_ADDR(dalAddr) ((dalAddr) + 0x20000000 + DAL_START_IN_ROM)
|
||||
|
||||
|
||||
static char* getArmString(ArmCpu* cpu, uint32_t address, uint32_t maxSize){
|
||||
char* str = malloc(maxSize);
|
||||
uint8_t fsr;
|
||||
uint32_t index = 0;
|
||||
|
||||
if(!str)
|
||||
abort();
|
||||
|
||||
for(index = 0; index < maxSize; index++){
|
||||
cpu->memF(cpu, str + index, address + index, 1, false, true, &fsr);
|
||||
if(str[index] == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static void cpuOnPcUpdate(ArmCpu* cpu, uint32_t newPc){
|
||||
//debug tool for extracting data from ARM function calls
|
||||
bool logSource = true;
|
||||
static uint32_t callCount;
|
||||
static int32_t captureReturn = -1;
|
||||
static uint32_t captureReturnAddress;
|
||||
static int64_t divisor;
|
||||
static int64_t value;
|
||||
|
||||
callCount++;
|
||||
|
||||
//reset when the system resets
|
||||
if(newPc == 0x00000000)
|
||||
callCount = 0;
|
||||
|
||||
//jumps to reach EnterIdleMode - amount before to log
|
||||
/*
|
||||
if(callCount > 71313793 - 500){
|
||||
switch(newPc){
|
||||
case 0x200BA16A:
|
||||
case 0x200BA170:
|
||||
case 0x200BA174:
|
||||
case 0x200ACAF4://HALDelay loop
|
||||
//repetitive calls to ignore
|
||||
break;
|
||||
|
||||
default:
|
||||
debugLog("Jumped to:0x%08X\n", newPc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
switch(newPc){
|
||||
case PC_FROM_DAL_ADDR(0x00016C94):
|
||||
//ReadGPIOPin
|
||||
debugLog("Called \"ReadGPIOPin\", pin:%d\n", cpuGetRegExternal(cpu, 0));
|
||||
break;
|
||||
|
||||
/*
|
||||
case PC_FROM_DAL_ADDR(0x00016D34):
|
||||
//WriteGPIOPinInvertedValue
|
||||
debugLog("Called \"WriteGPIOPinInvertedValue\", pin:%d, value:%d\n", cpuGetRegExternal(cpu, 0), cpuGetRegExternal(cpu, 1));
|
||||
break;
|
||||
*/
|
||||
|
||||
case PC_FROM_DAL_ADDR(0x00024644):{
|
||||
//PrintLineError
|
||||
char* strings[2];
|
||||
|
||||
strings[0] = getArmString(cpu, cpuGetRegExternal(cpu, 0), 200);
|
||||
strings[1] = getArmString(cpu, cpuGetRegExternal(cpu, 2), 200);
|
||||
|
||||
debugLog("Called \"PrintLineError\", file:%s, line:%d, error:%s\n", strings[0], cpuGetRegExternal(cpu, 1), strings[1]);
|
||||
|
||||
free(strings[0]);
|
||||
free(strings[1]);
|
||||
}
|
||||
break;
|
||||
|
||||
case PC_FROM_DAL_ADDR(0x0000B6B4):{
|
||||
//HALDbgMessage
|
||||
char* string = getArmString(cpu, cpuGetRegExternal(cpu, 0), 200);
|
||||
|
||||
debugLog("Called \"HALDbgMessage\", msg:%s\n", string);
|
||||
|
||||
free(string);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x200A0580:{
|
||||
//HALErrDisplay
|
||||
char* string = getArmString(cpu, cpuGetRegExternal(cpu, 0), 200);
|
||||
|
||||
debugLog("Called \"HALErrDisplay\", arg0:%s, arg1:%d, arg2:%d\n", string, cpuGetRegExternal(cpu, 1), cpuGetRegExternal(cpu, 2));
|
||||
|
||||
free(string);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x200A6754:
|
||||
//HALDbgBreak
|
||||
debugLog("Called \"HALDbgBreak\", callCount:%d\n", callCount);
|
||||
break;
|
||||
|
||||
case PC_FROM_DAL_ADDR(0x00010F58):
|
||||
//HALSetInitStage
|
||||
debugLog("Called \"HALSetInitStage\", stage:%d\n", cpuGetRegExternal(cpu, 0));
|
||||
break;
|
||||
|
||||
/*
|
||||
case 0x2009F638:
|
||||
//CallsEnterIdleModeLayer0
|
||||
debugLog("Called \"CallsEnterIdleModeLayer0\", R9:0x%08X\n", cpuGetRegExternal(cpu, 9));
|
||||
break;
|
||||
|
||||
case PC_FROM_DAL_ADDR(0x000276F0):
|
||||
//EnterIdleMode
|
||||
debugLog("Called \"EnterIdleMode\", took %d jumps to reach this function\n", callCount);
|
||||
break;
|
||||
*/
|
||||
|
||||
case 0x200B1C2C:
|
||||
//HALDisplayDrawBootScreen
|
||||
debugLog("Called \"HALDisplayDrawBootScreen\"\n");
|
||||
break;
|
||||
|
||||
case PC_FROM_DAL_ADDR(0x00000000):
|
||||
//BootBigRom
|
||||
debugLog("Called \"BootBigRom\"\n");
|
||||
break;
|
||||
|
||||
case 0x200BDB58:
|
||||
//sdivmod start
|
||||
debugLog("sdivmod start R0:%d, R1:%d\n", cpuGetRegExternal(cpu, 0), cpuGetRegExternal(cpu, 1));
|
||||
divisor = (int32_t)cpuGetRegExternal(cpu, 0);
|
||||
value = (int32_t)cpuGetRegExternal(cpu, 1);
|
||||
captureReturn = 0;
|
||||
captureReturnAddress = cpuGetRegExternal(cpu, 14) & ~1;//prevent thumb change bit from blocking return readback
|
||||
break;
|
||||
|
||||
case 0x200BDC30:
|
||||
//udivmod start
|
||||
debugLog("udivmod start R0:%d, R1:%d\n", cpuGetRegExternal(cpu, 0), cpuGetRegExternal(cpu, 1));
|
||||
divisor = cpuGetRegExternal(cpu, 0);
|
||||
value = cpuGetRegExternal(cpu, 1);
|
||||
captureReturn = 1;
|
||||
captureReturnAddress = cpuGetRegExternal(cpu, 14) & ~1;//prevent thumb change bit from blocking return readback
|
||||
break;
|
||||
|
||||
case 0x00000000:
|
||||
//reset vector
|
||||
debugLog("Jumped to reset vector\n");
|
||||
break;
|
||||
|
||||
case 0x00000004:
|
||||
//undefined instruction vector
|
||||
debugLog("Jumped to undefined instruction vector\n");
|
||||
break;
|
||||
|
||||
/*
|
||||
case 0x00000008:
|
||||
//SWI vector
|
||||
debugLog("Jumped to SWI vector\n");
|
||||
break;
|
||||
*/
|
||||
|
||||
case 0x0000000C:
|
||||
//prefetch abort vector
|
||||
debugLog("Jumped to prefetch abort vector\n");
|
||||
break;
|
||||
|
||||
case 0x00000010:
|
||||
//data abort vector
|
||||
debugLog("Jumped to data abort vector\n");
|
||||
break;
|
||||
|
||||
case 0x0000001C:
|
||||
//FIQ vector
|
||||
debugLog("Jumped to FIQ vector\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
logSource = false;
|
||||
if(captureReturn != -1 && newPc == captureReturnAddress){
|
||||
switch(captureReturn){
|
||||
case 0:
|
||||
debugLog("sdivmod end R0:%d, R1:%d\n", cpuGetRegExternal(cpu, 0), cpuGetRegExternal(cpu, 1));
|
||||
if((int32_t)cpuGetRegExternal(cpu, 0) != value / divisor || (int32_t)cpuGetRegExternal(cpu, 1) != value % divisor){
|
||||
bool breakpoint = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
debugLog("udivmod end R0:%d, R1:%d\n", cpuGetRegExternal(cpu, 0), cpuGetRegExternal(cpu, 1));
|
||||
if(cpuGetRegExternal(cpu, 0) != value / divisor || cpuGetRegExternal(cpu, 1) != value % divisor){
|
||||
bool breakpoint = true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
captureReturn = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(logSource)
|
||||
debugLog("Called from address:0x%08X\n", cpuGetRegExternal(cpu, 15) - 4);
|
||||
}
|
||||
#else
|
||||
#define cpuOnPcUpdate(x, y)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
static _INLINE_ UInt32 cpuPrvROR(UInt32 val, UInt8 ror){
|
||||
|
||||
if(ror) val = (val >> (UInt32)ror) | (val << (UInt32)(32 - ror));
|
||||
@@ -97,6 +325,7 @@ static _INLINE_ UInt32 cpuPrvROR(UInt32 val, UInt8 ror){
|
||||
}
|
||||
|
||||
static _INLINE_ void cpuPrvSetPC(ArmCpu* cpu, UInt32 pc){
|
||||
cpuOnPcUpdate(cpu, pc &~ 1UL);
|
||||
cpu->regs[15] = pc &~ 1UL;
|
||||
cpu->CPSR &=~ ARM_SR_T;
|
||||
if(pc & 1) cpu->CPSR |= ARM_SR_T;
|
||||
@@ -210,13 +439,13 @@ static void cpuPrvSwitchToMode(ArmCpu* cpu, UInt8 newMode){
|
||||
}
|
||||
|
||||
static void cpuPrvException(ArmCpu* cpu, UInt32 vector_pc, UInt32 lr, UInt32 newCPSR){
|
||||
|
||||
UInt32 cpsr = cpu->CPSR;
|
||||
|
||||
cpuPrvSwitchToMode(cpu, newCPSR & ARM_SR_M);
|
||||
cpu->CPSR = newCPSR;
|
||||
cpu->SPSR = cpsr;
|
||||
cpu->regs[14] = lr;
|
||||
cpuOnPcUpdate(cpu, vector_pc);
|
||||
cpu->regs[15] = vector_pc;
|
||||
}
|
||||
|
||||
@@ -1948,12 +2177,12 @@ data_processing: //data processing
|
||||
if(S){ //update flags or restore CPSR
|
||||
|
||||
if(!usesUsrRegs && vb8 == 15 && store){
|
||||
|
||||
UInt32 sr;
|
||||
|
||||
|
||||
sr = cpu->SPSR;
|
||||
cpuPrvSwitchToMode(cpu, sr & ARM_SR_M);
|
||||
cpu->CPSR = sr;
|
||||
cpuOnPcUpdate(cpu, tmp);
|
||||
cpu->regs[15] = tmp; //do it right here - if we let it use cpuPrvSetReg, it will check lower bit...
|
||||
store = false;
|
||||
}
|
||||
@@ -2581,6 +2810,7 @@ static Err cpuPrvCycleThumb(ArmCpu* cpu){
|
||||
|
||||
case 1: //BLX(1)_suffix
|
||||
instr = cpu->regs[15];
|
||||
cpuOnPcUpdate(cpu, (cpu->regs[14] + 2 + (((UInt32)v16) << 1)) &~ 3UL);
|
||||
cpu->regs[15] = (cpu->regs[14] + 2 + (((UInt32)v16) << 1)) &~ 3UL;
|
||||
cpu->regs[14] = instr | 1UL;
|
||||
cpu->CPSR &=~ ARM_SR_T;
|
||||
@@ -2594,6 +2824,7 @@ static Err cpuPrvCycleThumb(ArmCpu* cpu){
|
||||
|
||||
case 3: //BL_suffix
|
||||
instr = cpu->regs[15];
|
||||
cpuOnPcUpdate(cpu, cpu->regs[14] + 2 + (((UInt32)v16) << 1));
|
||||
cpu->regs[15] = cpu->regs[14] + 2 + (((UInt32)v16) << 1);
|
||||
cpu->regs[14] = instr | 1UL;
|
||||
goto instr_done;
|
||||
@@ -1,19 +1,170 @@
|
||||
#ifndef PXA260_CPU_H
|
||||
#define PXA260_CPU_H
|
||||
#ifndef _CPU_H_
|
||||
#define _CPU_H_
|
||||
|
||||
//#define ARM_V6 //define to allow v6 instructions
|
||||
//#define THUMB_2 //define to allow Thumb2
|
||||
|
||||
#include "pxa260_types.h"
|
||||
|
||||
#if defined(EMU_NO_SAFETY)
|
||||
#include "../armv5te/emu.h"
|
||||
#include "../armv5te/cpu.h"
|
||||
struct ArmCpu;
|
||||
|
||||
#define cpuGetRegExternal(x, regNum) reg_pc(regNum)
|
||||
#define cpuSetReg(x, regNum, value) set_reg(regNum, value)
|
||||
#define ARM_SR_N 0x80000000UL
|
||||
#define ARM_SR_Z 0x40000000UL
|
||||
#define ARM_SR_C 0x20000000UL
|
||||
#define ARM_SR_V 0x10000000UL
|
||||
#define ARM_SR_Q 0x08000000UL
|
||||
#ifdef ARM_V6 //V6KT2, but without T2 to be exact (we implement things like MLS, but not Thumb2 or ThumbEE)
|
||||
#define ARM_SR_J 0x01000000UL
|
||||
#define ARM_SR_E 0x00000200UL
|
||||
#define ARM_SR_A 0x00000100UL
|
||||
#define ARM_SR_GE_0 0x00010000UL
|
||||
#define ARM_SR_GE_1 0x00020000UL
|
||||
#define ARM_SR_GE_2 0x00040000UL
|
||||
#define ARM_SR_GE_3 0x00080000UL
|
||||
#define ARM_SR_GE_MASK 0x000F0000UL
|
||||
#define ARM_SR_GE_SHIFT 16
|
||||
#endif
|
||||
#define ARM_SR_I 0x00000080UL
|
||||
#define ARM_SR_F 0x00000040UL
|
||||
#define ARM_SR_T 0x00000020UL
|
||||
#define ARM_SR_M 0x0000001FUL
|
||||
|
||||
#define cpuIrq(x, fiq, raise) (raise ? (cpu_events |= (fiq ? EVENT_FIQ : EVENT_IRQ)) : (cpu_events &= (fiq ? ~EVENT_FIQ : ~EVENT_IRQ)))
|
||||
#else
|
||||
#include "../armv5te/uArm/CPU_2.h"
|
||||
#define ARM_SR_MODE_USR 0x00000010UL
|
||||
#define ARM_SR_MODE_FIQ 0x00000011UL
|
||||
#define ARM_SR_MODE_IRQ 0x00000012UL
|
||||
#define ARM_SR_MODE_SVC 0x00000013UL
|
||||
#define ARM_SR_MODE_ABT 0x00000017UL
|
||||
#define ARM_SR_MODE_UND 0x0000001BUL
|
||||
#define ARM_SR_MODE_SYS 0x0000001FUL
|
||||
|
||||
#define ARV_VECTOR_OFFT_RST 0x00000000UL
|
||||
#define ARM_VECTOR_OFFT_UND 0x00000004UL
|
||||
#define ARM_VECTOR_OFFT_SWI 0x00000008UL
|
||||
#define ARM_VECTOR_OFFT_P_ABT 0x0000000CUL
|
||||
#define ARM_VECTOR_OFFT_D_ABT 0x00000010UL
|
||||
#define ARM_VECTOR_OFFT_UNUSED 0x00000014UL
|
||||
#define ARM_VECTOR_OFFT_IRQ 0x00000018UL
|
||||
#define ARM_VECTOR_OFFT_FIQ 0x0000001CUL
|
||||
|
||||
#define HYPERCALL_ARM 0xF7BBBBBBUL
|
||||
#define HYPERCALL_THUMB 0xBBBBUL
|
||||
|
||||
//the following are for cpuGetRegExternal() and are generally used for debugging purposes
|
||||
#define ARM_REG_NUM_CPSR 16
|
||||
#define ARM_REG_NUM_SPSR 17
|
||||
|
||||
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);
|
||||
|
||||
#include "pxa260_icache.h"
|
||||
|
||||
|
||||
/*
|
||||
|
||||
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
|
||||
|
||||
#ifdef ARM_V6
|
||||
|
||||
Boolean EEE; //endianness one exception entry
|
||||
Boolean impreciseAbtWaiting;
|
||||
#endif
|
||||
|
||||
ArmCpuMemF memF;
|
||||
ArmCpuEmulErr emulErrF;
|
||||
ArmCpuHypercall hypercallF;
|
||||
ArmSetFaultAdrF setFaultAdrF;
|
||||
|
||||
icache ic;
|
||||
|
||||
void* userData; //shared by all callbacks
|
||||
}ArmCpu;
|
||||
|
||||
|
||||
Err cpuInit(ArmCpu* cpu, UInt32 pc, ArmCpuMemF memF, ArmCpuEmulErr emulErrF, ArmCpuHypercall hypercallF, ArmSetFaultAdrF setFaultAdrF);
|
||||
Err cpuDeinit(ArmCpu* cp);
|
||||
void cpuCycle(ArmCpu* cpu);
|
||||
void cpuIrq(ArmCpu* cpu, Boolean fiq, Boolean raise); //unraise when acknowledged
|
||||
|
||||
#ifdef ARM_V6
|
||||
|
||||
void cpuSignalImpreciseAbt(ArmCpu* cpu, Boolean raise);
|
||||
|
||||
#endif
|
||||
|
||||
UInt32 cpuGetRegExternal(ArmCpu* cpu, UInt8 reg);
|
||||
void cpuSetReg(ArmCpu* cpu, UInt8 reg, UInt32 val);
|
||||
|
||||
void cpuCoprocessorRegister(ArmCpu* cpu, UInt8 cpNum, ArmCoprocessor* coproc);
|
||||
void cpuCoprocessorUnregister(ArmCpu* cpu, UInt8 cpNum);
|
||||
|
||||
void cpuSetVectorAddr(ArmCpu* cpu, UInt32 adr);
|
||||
|
||||
UInt16 cpuGetCPAR(ArmCpu* cpu);
|
||||
void cpuSetCPAR(ArmCpu* cpu, UInt16 cpar);
|
||||
|
||||
void cpuIcacheInval(ArmCpu* cpu);
|
||||
void cpuIcacheInvalAddr(ArmCpu* cpu, UInt32 addr);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -167,7 +167,7 @@ trigger_intrs:
|
||||
case 1:
|
||||
case 2:
|
||||
val = gpio->levels[pa - 0];
|
||||
debugLog("PXA260 GPIO register read:%d, PC:0x%08X\n", pa, pxa260GetPc());
|
||||
//debugLog("PXA260 GPIO register read:%d, PC:0x%08X\n", pa, pxa260GetPc());
|
||||
break;
|
||||
|
||||
case 3:
|
||||
@@ -290,6 +290,7 @@ void pxa260gpioUpdateKeyMatrix(Pxa260gpio* gpio){
|
||||
pxa260gpioSetState(gpio, 11, inRail2);
|
||||
|
||||
//TODO: move these to the init routine
|
||||
pxa260gpioSetState(gpio, 1, true);//set the reset button to not be pressed
|
||||
pxa260gpioSetState(gpio, 3, true);//set the slider postion
|
||||
pxa260gpioSetState(gpio, 12, false);//prevent HotSync button from being pressed
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "pxa260_LCD.h"
|
||||
#include "pxa260.h"
|
||||
#include "../armv5te/mem.h"
|
||||
|
||||
#define UNMASKABLE_INTS 0x7C8E
|
||||
|
||||
@@ -194,7 +193,7 @@ Boolean pxa260lcdPrvMemAccessF(void* userData, UInt32 pa, UInt8 size, Boolean wr
|
||||
return true;
|
||||
}
|
||||
|
||||
#define pxa260PrvGetWord(x, addr) mmio_read_word(addr)
|
||||
#define pxa260PrvGetWord(x, addr) read_word(addr)
|
||||
|
||||
static void pxa260LcdPrvDma(Pxa260lcd* lcd, void* dest, UInt32 addr, UInt32 len){
|
||||
|
||||
@@ -205,7 +204,7 @@ static void pxa260LcdPrvDma(Pxa260lcd* lcd, void* dest, UInt32 addr, UInt32 len)
|
||||
|
||||
while(len){
|
||||
|
||||
t = pxa260PrvGetWord(lcd, addr);
|
||||
t = pxa260PrvGetWord(lcd, addr);
|
||||
if(len--) *d++ = t;
|
||||
if(len--) *d++ = t >> 8;
|
||||
if(len--) *d++ = t >> 16;
|
||||
@@ -217,7 +216,7 @@ static void pxa260LcdPrvDma(Pxa260lcd* lcd, void* dest, UInt32 addr, UInt32 len)
|
||||
static _INLINE_ void pxa260LcdScreenDataPixel(Pxa260lcd* lcd, UInt8* buf){
|
||||
static UInt32 pos = 0;
|
||||
|
||||
pxa260Framebuffer[pos] = buf[0] || (buf[1] << 8);
|
||||
pxa260Framebuffer[pos] = buf[0] | (buf[1] << 8);
|
||||
pos++;
|
||||
if(pos == 320 * 480)
|
||||
pos = 0;
|
||||
@@ -311,9 +310,9 @@ void pxa260lcdFrame(Pxa260lcd* lcd){
|
||||
if(lcd->fbr0 & 2) lcd->lcsr |= 0x0200;
|
||||
descrAddr = lcd->fbr0 &~ 0xFUL;
|
||||
} else descrAddr = lcd->fdadr0;
|
||||
lcd->fdadr0 = pxa260PrvGetWord(lcd, descrAddr + 0);
|
||||
lcd->fsadr0 = pxa260PrvGetWord(lcd, descrAddr + 4);
|
||||
lcd->fidr0 = pxa260PrvGetWord(lcd, descrAddr + 8);
|
||||
lcd->fdadr0 = pxa260PrvGetWord(lcd, descrAddr + 0);
|
||||
lcd->fsadr0 = pxa260PrvGetWord(lcd, descrAddr + 4);
|
||||
lcd->fidr0 = pxa260PrvGetWord(lcd, descrAddr + 8);
|
||||
lcd->ldcmd0 = pxa260PrvGetWord(lcd, descrAddr + 12);
|
||||
|
||||
lcd->state = LCD_STATE_DMA_0_START;
|
||||
@@ -332,9 +331,7 @@ void pxa260lcdFrame(Pxa260lcd* lcd){
|
||||
pxa260LcdPrvDma(lcd, lcd->palette, lcd->fsadr0, len);
|
||||
}
|
||||
else{
|
||||
|
||||
lcd->frameNum++;
|
||||
if(!(lcd->frameNum & 63)) pxa260LcdScreenDataDma(lcd, lcd->fsadr0, len);
|
||||
pxa260LcdScreenDataDma(lcd, lcd->fsadr0, len);
|
||||
}
|
||||
|
||||
lcd->state = LCD_STATE_DMA_0_END;
|
||||
|
||||
@@ -41,9 +41,6 @@ typedef struct{
|
||||
UInt8 enbChanged : 1;
|
||||
|
||||
UInt8 palette[512];
|
||||
|
||||
UInt32 frameNum;
|
||||
|
||||
}Pxa260lcd;
|
||||
|
||||
Boolean pxa260lcdPrvMemAccessF(void* userData, UInt32 pa, UInt8 size, Boolean write, void* buf);
|
||||
|
||||
454
src/pxa260/pxa260_MMU.c
Normal file
454
src/pxa260/pxa260_MMU.c
Normal file
@@ -0,0 +1,454 @@
|
||||
#include "pxa260_MMU.h"
|
||||
|
||||
|
||||
|
||||
void mmuTlbFlush(ArmMmu* mmu){
|
||||
|
||||
UInt8 i, j;
|
||||
|
||||
for(i = 0; i < MMU_TLB_BUCKET_NUM; i++){
|
||||
for(j = 0; j < MMU_TLB_BUCKET_SIZE; j++) mmu->tlb[i][j].sz = 0;
|
||||
mmu->replPos[i] = 0;
|
||||
mmu->readPos[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void mmuInit(ArmMmu* mmu, ArmMmuReadF readF, void* userData){
|
||||
|
||||
__mem_zero(mmu, sizeof(ArmMmu));
|
||||
mmu->readF = readF;
|
||||
mmu->userData = userData;
|
||||
mmu->transTablPA = MMU_DISABLED_TTP;
|
||||
mmu->domainCfg = 0;
|
||||
mmuTlbFlush(mmu);
|
||||
}
|
||||
|
||||
void muDeinit(_UNUSED_ ArmMmu* mmu){
|
||||
|
||||
//nothing here
|
||||
}
|
||||
|
||||
static _INLINE_ UInt8 mmuPrvHashAddr(UInt32 addr){ //addresses are granular on 1K
|
||||
|
||||
addr >>= 10;
|
||||
|
||||
addr = addr ^ (addr >> 5) ^ (addr >> 10);
|
||||
|
||||
return addr % MMU_TLB_BUCKET_NUM;
|
||||
}
|
||||
|
||||
Boolean mmuTranslate(ArmMmu* mmu, UInt32 adr, Boolean priviledged, Boolean write, UInt32* paP, UInt8* fsrP){
|
||||
|
||||
UInt32 va, pa = 0, sz, t;
|
||||
UInt8 i, j, dom, ap = 0;
|
||||
Boolean section = false, coarse = true, pxa_tex_page = false;
|
||||
UInt8 bucket;
|
||||
|
||||
//handle the 'MMU off' case
|
||||
|
||||
if(mmu->transTablPA == MMU_DISABLED_TTP){
|
||||
va = pa = 0;
|
||||
goto calc;
|
||||
}
|
||||
|
||||
//check the TLB
|
||||
if(MMU_TLB_BUCKET_SIZE && MMU_TLB_BUCKET_NUM){
|
||||
|
||||
bucket = mmuPrvHashAddr(adr);
|
||||
|
||||
for(j = 0, i = mmu->readPos[bucket]; j < MMU_TLB_BUCKET_SIZE; j++, i--){
|
||||
|
||||
if(i == 0xFF) i = MMU_TLB_BUCKET_SIZE - 1;
|
||||
|
||||
va = mmu->tlb[bucket][i].va;
|
||||
sz = mmu->tlb[bucket][i].sz;
|
||||
|
||||
if(va <= adr && va + sz > adr){
|
||||
|
||||
pa = mmu->tlb[bucket][i].pa;
|
||||
ap = mmu->tlb[bucket][i].ap;
|
||||
dom = mmu->tlb[bucket][i].domain;
|
||||
mmu->readPos[bucket] = i;
|
||||
|
||||
goto check;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//read first level table
|
||||
|
||||
if(mmu->transTablPA & 3){
|
||||
*fsrP = 0x01; //alignment fault
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!mmu->readF(mmu->userData, &t, mmu->transTablPA + ((adr & 0xFFF00000) >> 18))){
|
||||
|
||||
*fsrP = 0x0C; //translation external abort first level
|
||||
return false;
|
||||
}
|
||||
|
||||
dom = (t >> 5) & 0x0F;
|
||||
switch(t & 3){
|
||||
|
||||
case 0: //fault
|
||||
|
||||
*fsrP = 0x5; //section translation fault
|
||||
return false;
|
||||
|
||||
case 1: //coarse pagetable
|
||||
|
||||
t &= 0xFFFFFC00UL;
|
||||
t += (adr & 0x000FF000UL) >> 10;
|
||||
break;
|
||||
|
||||
case 2: //1MB section
|
||||
|
||||
pa = t & 0xFFF00000UL;
|
||||
va = adr & 0xFFF00000UL;
|
||||
sz = 1UL << 20;
|
||||
ap = (t >> 10) & 3;
|
||||
section = true;
|
||||
goto translated;
|
||||
|
||||
case 3: //fine page table
|
||||
|
||||
coarse = false;
|
||||
t &= 0xFFFFF000UL;
|
||||
t += (adr & 0x000FFC00UL) >> 8;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
//read second level table
|
||||
|
||||
if(!mmu->readF(mmu->userData, &t, t)){
|
||||
*fsrP = 0x0E | (dom << 4); //translation external abort second level
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(t & 3){
|
||||
|
||||
case 0: //fault
|
||||
|
||||
*fsrP = 0x07 | (dom << 4); //page translation fault
|
||||
return false;
|
||||
|
||||
case 1: //64K mapping
|
||||
|
||||
pa = t & 0xFFFF0000UL;
|
||||
va = adr & 0xFFFF0000UL;
|
||||
sz = 65536UL;
|
||||
ap = (adr >> 14) & 3; //in "ap" store which AP we need [of the 4]
|
||||
break;
|
||||
|
||||
case 2: //4K mapping (1K effective thenks to having 4 AP fields)
|
||||
|
||||
page_size_4k:
|
||||
pa = t & 0xFFFFF000UL;
|
||||
va = adr & 0xFFFFF000UL;
|
||||
sz = 4096;
|
||||
ap = (adr >> 10) & 3; //in "ap" store which AP we need [of the 4]
|
||||
break;
|
||||
|
||||
case 3: //1K mapping
|
||||
|
||||
if(coarse){
|
||||
|
||||
pxa_tex_page = true;
|
||||
goto page_size_4k;
|
||||
}
|
||||
|
||||
pa = t & 0xFFFFFC00UL;
|
||||
va = adr & 0xFFFFFC00UL;
|
||||
ap = (t >> 4) & 3; //in "ap" store the actual AP [and skip quarter-page resolution later using the goto]
|
||||
sz = 1024;
|
||||
goto translated;
|
||||
}
|
||||
|
||||
|
||||
//handle 4 AP sections
|
||||
|
||||
i = (t >> 4) & 0xFF;
|
||||
if(pxa_tex_page || ((i & 0x0F) == (i >> 4) && (i & 0x03) == ((i >> 2) & 0x03))){ //if all domains are the same, add the whole thing
|
||||
|
||||
ap = (t >> 4) & 3;
|
||||
}
|
||||
else{ //take the quarter that is the one we need
|
||||
|
||||
err_str("quarter page found!\r\n");
|
||||
ap = (t >> (4 + 2 * ap)) & 3;
|
||||
sz /= 4;
|
||||
pa += ((UInt32)ap) * sz;
|
||||
va += ((UInt32)ap) * sz;
|
||||
}
|
||||
|
||||
|
||||
translated:
|
||||
|
||||
//insert tlb entry
|
||||
if(MMU_TLB_BUCKET_NUM && MMU_TLB_BUCKET_SIZE){
|
||||
|
||||
mmu->tlb[bucket][mmu->replPos[bucket]].pa = pa;
|
||||
mmu->tlb[bucket][mmu->replPos[bucket]].sz = sz;
|
||||
mmu->tlb[bucket][mmu->replPos[bucket]].va = va;
|
||||
mmu->tlb[bucket][mmu->replPos[bucket]].ap = ap;
|
||||
mmu->tlb[bucket][mmu->replPos[bucket]].domain = dom;
|
||||
mmu->readPos[bucket] = mmu->replPos[bucket];
|
||||
if(++mmu->replPos[bucket] == MMU_TLB_BUCKET_SIZE) mmu->replPos[bucket] = 0;
|
||||
}
|
||||
|
||||
check:
|
||||
|
||||
//check domain permissions
|
||||
|
||||
switch((mmu->domainCfg >> (dom * 2)) & 3){
|
||||
|
||||
case 0: //NO ACCESS:
|
||||
case 2: //RESERVED: unpredictable (treat as no access)
|
||||
|
||||
*fsrP = (section ? 0x08 : 0xB) | (dom << 4); //section or page domain fault
|
||||
return false;
|
||||
|
||||
|
||||
case 1: //CLIENT: check permissions
|
||||
|
||||
break;
|
||||
|
||||
|
||||
case 3: //MANAGER: allow all access
|
||||
|
||||
goto calc;
|
||||
|
||||
}
|
||||
|
||||
//check permissions
|
||||
|
||||
switch(ap){
|
||||
|
||||
case 0:
|
||||
|
||||
if(write || (!mmu->R && (!priviledged || !mmu->S))) break;
|
||||
goto calc;
|
||||
|
||||
case 1:
|
||||
|
||||
if(!priviledged) break;
|
||||
goto calc;
|
||||
|
||||
case 2:
|
||||
|
||||
if(!priviledged && write) break;
|
||||
goto calc;
|
||||
|
||||
case 3:
|
||||
|
||||
//all is good, allow access!
|
||||
goto calc;
|
||||
}
|
||||
|
||||
//perm_err:
|
||||
|
||||
*fsrP = (section ? 0x0D : 0x0F) | (dom << 4); //section or subpage permission fault
|
||||
return false;
|
||||
|
||||
calc:
|
||||
|
||||
*paP = adr - va + pa;
|
||||
return true;
|
||||
}
|
||||
|
||||
UInt32 mmuGetTTP(ArmMmu* mmu){
|
||||
|
||||
return mmu->transTablPA;
|
||||
}
|
||||
|
||||
void mmuSetTTP(ArmMmu* mmu, UInt32 ttp){
|
||||
|
||||
UInt8 i;
|
||||
|
||||
mmuTlbFlush(mmu);
|
||||
for(i = 0; i < MMU_TLB_BUCKET_NUM; i++){
|
||||
|
||||
mmu->replPos[i] = 0;
|
||||
mmu->readPos[i] = 0;
|
||||
}
|
||||
mmu->transTablPA = ttp;
|
||||
}
|
||||
|
||||
void mmuSetS(ArmMmu* mmu, Boolean on){
|
||||
|
||||
mmu->S = on;
|
||||
}
|
||||
|
||||
void mmuSetR(ArmMmu* mmu, Boolean on){
|
||||
|
||||
mmu->R = on;
|
||||
}
|
||||
|
||||
Boolean mmuGetS(ArmMmu* mmu){
|
||||
|
||||
return mmu->S;
|
||||
}
|
||||
|
||||
Boolean mmuGetR(ArmMmu* mmu){
|
||||
|
||||
return mmu->R;
|
||||
}
|
||||
|
||||
UInt32 mmuGetDomainCfg(ArmMmu* mmu){
|
||||
|
||||
return mmu->domainCfg;
|
||||
}
|
||||
|
||||
void mmuSetDomainCfg(ArmMmu* mmu, UInt32 val){
|
||||
|
||||
mmu->domainCfg = val;
|
||||
}
|
||||
|
||||
/////////////////////////// debugging helpers ///////////////////////////
|
||||
|
||||
|
||||
|
||||
UInt32 mmuDR(ArmMmu* mmu, UInt32 addr){
|
||||
|
||||
UInt32 t = 0;
|
||||
|
||||
if(!mmu->readF(mmu->userData, &t, addr)) t = 0xFFFFFFF0UL;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static void mmuDumpUpdate(UInt32 va, UInt32 pa, UInt32 len, UInt8 dom, UInt8 ap, Boolean c, Boolean b, Boolean valid){
|
||||
|
||||
UInt32 va_end;;
|
||||
|
||||
static Boolean wasValid = false;
|
||||
static UInt8 wasDom = 0;
|
||||
static UInt8 wasAp = 0;
|
||||
static Boolean wasB = 0;
|
||||
static Boolean wasC = 0;
|
||||
static UInt32 startVa = 0;
|
||||
static UInt32 startPa = 0;
|
||||
static UInt32 expectPa = 0;
|
||||
|
||||
|
||||
va_end = (va || len) ? va - 1 : 0xFFFFFFFFUL;
|
||||
|
||||
if(!wasValid && !valid) return; //no need to bother...
|
||||
|
||||
if(valid != wasValid || dom != wasDom || ap != wasAp || c != wasC || b != wasB || expectPa != pa){ //not a continuation of what we've been at...
|
||||
|
||||
if(wasValid){
|
||||
|
||||
|
||||
err_str("0x");
|
||||
err_hex(startVa);
|
||||
err_str("-0x");
|
||||
err_hex(va_end);
|
||||
err_str(" -> 0x");
|
||||
err_hex(startPa);
|
||||
err_str("-0x");
|
||||
err_hex(startPa + (va_end - startVa));
|
||||
err_str(" dom");
|
||||
err_dec(wasDom);
|
||||
err_str(" ap");
|
||||
err_dec(wasAp);
|
||||
err_str(" ");
|
||||
err_str(wasC ? "c" : " ");
|
||||
err_str(wasB ? "b" : " ");
|
||||
err_str("\r\n");
|
||||
}
|
||||
|
||||
wasValid = valid;
|
||||
if(valid){ //start of a new range
|
||||
|
||||
wasDom = dom;
|
||||
wasAp = ap;
|
||||
wasC = c;
|
||||
wasB = b;
|
||||
startVa = va;
|
||||
startPa = pa;
|
||||
expectPa = pa + len;
|
||||
}
|
||||
}
|
||||
else{ //continuation of what we've been up to...
|
||||
|
||||
expectPa += len;
|
||||
}
|
||||
}
|
||||
|
||||
static void mmuDump(ArmMmu* mmu){
|
||||
|
||||
UInt32 i, j, t, sla, va, psz;
|
||||
UInt8 dom;
|
||||
Boolean coarse = false;
|
||||
|
||||
for(i = 0; i < 0x1000; i++){
|
||||
|
||||
t = mmuDR(mmu, mmu->transTablPA + (i << 2));
|
||||
va = i << 20;
|
||||
dom = (t >> 5) & 0x0F;
|
||||
switch(t & 3){
|
||||
|
||||
case 0: //done
|
||||
mmuDumpUpdate(va, 0, 1UL << 20, 0, 0, false, false, false);
|
||||
continue;
|
||||
|
||||
case 1: //coarse page table
|
||||
coarse = true;
|
||||
t &= 0xFFFFFC00UL;
|
||||
break;
|
||||
|
||||
case 2: //section
|
||||
mmuDumpUpdate(va, t & 0xFFF00000UL, 1UL << 20, dom, (t >> 10) & 3, !!(t & 8), !!(t & 4), true);
|
||||
continue;
|
||||
|
||||
case 3: //fine page table
|
||||
t &= 0xFFFFF000UL;
|
||||
break;
|
||||
}
|
||||
|
||||
sla = t;
|
||||
psz = coarse ? 4096 : 1024;
|
||||
for(j = 0; j < ((1UL << 20) / psz); j++){
|
||||
t = mmuDR(mmu, sla + (j << 2));
|
||||
va = (i << 20) + (j * psz);
|
||||
switch(t & 3){
|
||||
|
||||
case 0: //invalid
|
||||
mmuDumpUpdate(va, 0, psz, 0, 0, false, false, false);
|
||||
break;
|
||||
|
||||
case 1: //large 64k page
|
||||
mmuDumpUpdate(va + 0 * 16384UL, (t & 0xFFFF0000UL) + 0 * 16384UL, 16384, dom, (t >> 4) & 3, !!(t & 8), !!(t & 4), true);
|
||||
mmuDumpUpdate(va + 1 * 16384UL, (t & 0xFFFF0000UL) + 1 * 16384UL, 16384, dom, (t >> 6) & 3, !!(t & 8), !!(t & 4), true);
|
||||
mmuDumpUpdate(va + 2 * 16384UL, (t & 0xFFFF0000UL) + 2 * 16384UL, 16384, dom, (t >> 8) & 3, !!(t & 8), !!(t & 4), true);
|
||||
mmuDumpUpdate(va + 3 * 16384UL, (t & 0xFFFF0000UL) + 3 * 16384UL, 16384, dom, (t >> 10) & 3, !!(t & 8), !!(t & 4), true);
|
||||
j += coarse ? 15 : 63;
|
||||
break;
|
||||
|
||||
case 2: //small 4k page
|
||||
mmuDumpUpdate(va + 0 * 1024, (t & 0xFFFFF000UL) + 0 * 1024, 1024, dom, (t >> 4) & 3, !!(t & 8), !!(t & 4), true);
|
||||
mmuDumpUpdate(va + 1 * 1024, (t & 0xFFFFF000UL) + 1 * 1024, 1024, dom, (t >> 6) & 3, !!(t & 8), !!(t & 4), true);
|
||||
mmuDumpUpdate(va + 2 * 1024, (t & 0xFFFFF000UL) + 2 * 1024, 1024, dom, (t >> 8) & 3, !!(t & 8), !!(t & 4), true);
|
||||
mmuDumpUpdate(va + 3 * 1024, (t & 0xFFFFF000UL) + 3 * 1024, 1024, dom, (t >> 10) & 3, !!(t & 8), !!(t & 4), true);
|
||||
if(!coarse) j += 3;
|
||||
break;
|
||||
|
||||
case 3: //tiny 1k page or TEX page on pxa
|
||||
if(coarse){
|
||||
|
||||
mmuDumpUpdate(va, t & 0xFFFFF000UL, 4096, dom, (t >> 4) & 3, !!(t & 8), !!(t & 4), true);
|
||||
}
|
||||
else{
|
||||
|
||||
mmuDumpUpdate(va, t & 0xFFFFFC00UL, 1024, dom, (t >> 4) & 3, !!(t & 8), !!(t & 4), true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
mmuDumpUpdate(0, 0, 0, 0, 0, false, false, false); //finish things off
|
||||
}
|
||||
70
src/pxa260/pxa260_MMU.h
Normal file
70
src/pxa260/pxa260_MMU.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#ifndef _MMU_H_
|
||||
#define _MMU_H_
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "pxa260_types.h"
|
||||
|
||||
|
||||
#define MMU_TLB_BUCKET_SIZE 8
|
||||
#define MMU_TLB_BUCKET_NUM 32
|
||||
#define MMU_DISABLED_TTP 0xFFFFFFFFUL
|
||||
|
||||
|
||||
typedef Err (*ArmMmuReadF)(void* userData, UInt32* buf, UInt32 pa); //read a word
|
||||
|
||||
#define errMmuTranslation (errMmu + 1)
|
||||
#define errMmuDomain (errMmu + 2)
|
||||
#define errMmuPermission (errMmu + 3)
|
||||
|
||||
typedef struct {
|
||||
|
||||
UInt32 pa, va;
|
||||
UInt32 sz;
|
||||
UInt32 ap:2;
|
||||
UInt32 domain:4;
|
||||
|
||||
}ArmPrvTlb;
|
||||
|
||||
typedef struct ArmMmu{
|
||||
|
||||
UInt32 transTablPA;
|
||||
UInt8 S:1;
|
||||
UInt8 R:1;
|
||||
UInt8 readPos[MMU_TLB_BUCKET_NUM];
|
||||
UInt8 replPos[MMU_TLB_BUCKET_NUM];
|
||||
ArmPrvTlb tlb[MMU_TLB_BUCKET_NUM][MMU_TLB_BUCKET_SIZE];
|
||||
UInt32 domainCfg;
|
||||
ArmMmuReadF readF;
|
||||
void* userData;
|
||||
|
||||
}ArmMmu;
|
||||
|
||||
|
||||
void mmuInit(ArmMmu* mmu, ArmMmuReadF readF, void* userData);
|
||||
void muDeinit(ArmMmu* mmu);
|
||||
Boolean mmuTranslate(ArmMmu* mmu, UInt32 va, Boolean priviledged, Boolean write, UInt32* paP, UInt8* fsrP);
|
||||
|
||||
UInt32 mmuGetTTP(ArmMmu* mmu);
|
||||
void mmuSetTTP(ArmMmu* mmu, UInt32 ttp);
|
||||
|
||||
void mmuSetS(ArmMmu* mmu, Boolean on);
|
||||
void mmuSetR(ArmMmu* mmu, Boolean on);
|
||||
Boolean mmuGetS(ArmMmu* mmu);
|
||||
Boolean mmuGetR(ArmMmu* mmu);
|
||||
|
||||
UInt32 mmuGetDomainCfg(ArmMmu* mmu);
|
||||
void mmuSetDomainCfg(ArmMmu* mmu, UInt32 val);
|
||||
|
||||
void mmuTlbFlush(ArmMmu* mmu);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "pxa260.h"
|
||||
|
||||
|
||||
Boolean pxa260pwrClkPrvCoprocRegXferFunc(void* userData, Boolean two, Boolean read, UInt8 op1, UInt8 Rx, UInt8 CRn, UInt8 CRm, UInt8 op2){
|
||||
Boolean pxa260pwrClkPrvCoprocRegXferFunc(struct ArmCpu* unused, void* userData, Boolean two, Boolean read, UInt8 op1, UInt8 Rx, UInt8 CRn, UInt8 CRm, UInt8 op2){
|
||||
|
||||
Pxa260pwrClk* pc = userData;
|
||||
UInt32 val = 0;
|
||||
|
||||
@@ -16,7 +16,7 @@ typedef struct{
|
||||
#define PXA260_POWER_MANAGER_BASE 0x40F00000UL
|
||||
#define PXA260_POWER_MANAGER_SIZE 0x00001000UL
|
||||
|
||||
Boolean pxa260pwrClkPrvCoprocRegXferFunc(void* userData, Boolean two, Boolean read, UInt8 op1, UInt8 Rx, UInt8 CRn, UInt8 CRm, UInt8 op2);
|
||||
Boolean pxa260pwrClkPrvCoprocRegXferFunc(struct ArmCpu* unused, void* userData, Boolean two, Boolean read, UInt8 op1, UInt8 Rx, UInt8 CRn, UInt8 CRm, UInt8 op2);
|
||||
Boolean pxa260pwrClkPrvClockMgrMemAccessF(void* userData, UInt32 pa, UInt8 size, Boolean write, void* buf);
|
||||
Boolean pxa260pwrClkPrvPowerMgrMemAccessF(void* userData, UInt32 pa, UInt8 size, Boolean write, void* buf);
|
||||
void pxa260pwrClkInit(Pxa260pwrClk* pc);
|
||||
|
||||
@@ -31,7 +31,7 @@ void pxa260rtcPrvUpdate(Pxa260rtc* rtc){
|
||||
pxa260icInt(rtc->ic, PXA260_I_RTC_HZ, (rtc->RTSR & 2) != 0);
|
||||
}
|
||||
|
||||
static Boolean pxa260rtcPrvMemAccessF(void* userData, UInt32 pa, UInt8 size, Boolean write, void* buf){
|
||||
Boolean pxa260rtcPrvMemAccessF(void* userData, UInt32 pa, UInt8 size, Boolean write, void* buf){
|
||||
|
||||
Pxa260rtc* rtc = userData;
|
||||
UInt32 val = 0;
|
||||
|
||||
@@ -28,9 +28,9 @@ typedef struct{
|
||||
|
||||
}Pxa260rtc;
|
||||
|
||||
Boolean pxa260rtcPrvMemAccessF(void* userData, UInt32 pa, UInt8 size, Boolean write, void* buf);
|
||||
void pxa260rtcInit(Pxa260rtc* rtc, Pxa260ic* ic);
|
||||
void pxa260rtcUpdate(Pxa260rtc* rtc);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
186
src/pxa260/pxa260_cp15.c
Normal file
186
src/pxa260/pxa260_cp15.c
Normal file
@@ -0,0 +1,186 @@
|
||||
#include "pxa260_cp15.h"
|
||||
|
||||
|
||||
#define CPUID_PXA255 0x69052D06UL //spepping A0
|
||||
#define CPUID_PXA270 0x69054114UL //stepping C0
|
||||
|
||||
static Boolean cp15prvCoprocRegXferFunc(struct ArmCpu* cpu, void* userData, Boolean two, Boolean read, UInt8 op1, UInt8 Rx, UInt8 CRn, UInt8 CRm, UInt8 op2){
|
||||
|
||||
ArmCP15* cp15 = userData;
|
||||
UInt32 val = 0, tmp;
|
||||
|
||||
|
||||
if(!read) val = cpuGetRegExternal(cpu, Rx);
|
||||
|
||||
if(op1 != 0 || two) goto fail; //CP15 only accessed with MCR/MRC with op1 == 0
|
||||
|
||||
switch(CRn){
|
||||
|
||||
case 0: //ID codes
|
||||
|
||||
if(!read) goto fail; //cannot write to ID codes register
|
||||
if(CRm != 0) goto fail; //CRm must be zero for this read
|
||||
if(op2 == 0){ //main ID register
|
||||
|
||||
val = CPUID_PXA255;
|
||||
goto success;
|
||||
}
|
||||
else if(op2 == 1){ //cahce type register - we lie here
|
||||
|
||||
val = 0x0B16A16AUL;
|
||||
goto success;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: //control register
|
||||
|
||||
if(op2 == 0){
|
||||
if(read){
|
||||
val = cp15->control;
|
||||
}
|
||||
else{
|
||||
tmp = val ^ cp15->control; //see what changed and mask off then chack for what we support changing of
|
||||
if(tmp & 0x84F0UL){
|
||||
err_str("cp15: unknown bits changed 0x");
|
||||
err_hex(cp15->control);
|
||||
err_str("->0x");
|
||||
err_hex(val);
|
||||
err_str(", halting\r\n");
|
||||
while(true);
|
||||
}
|
||||
|
||||
if(tmp & 0x00002000UL){ // V bit
|
||||
|
||||
cpuSetVectorAddr(cp15->cpu, (val & 0x00002000UL) ? 0xFFFF0000UL : 0x00000000UL);
|
||||
cp15->control ^= 0x00002000UL;
|
||||
}
|
||||
if(tmp & 0x00000200UL){ // R bit
|
||||
|
||||
mmuSetR(cp15->mmu, (val & 0x00000200UL) != 0);
|
||||
cp15->control ^= 0x00000200UL;
|
||||
}
|
||||
if(tmp & 0x00000100UL){ // S bit
|
||||
|
||||
mmuSetS(cp15->mmu, (val & 0x00000100UL) != 0);
|
||||
cp15->control ^= 0x00000100UL;
|
||||
}
|
||||
if(tmp & 0x00000001UL){ // M bit
|
||||
|
||||
mmuSetTTP(cp15->mmu, (val & 0x00000001UL) ? cp15->ttb : MMU_DISABLED_TTP);
|
||||
mmuTlbFlush(cp15->mmu);
|
||||
cp15->control ^= 0x00000001UL;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else if(op2 == 1){ //PXA-specific thing
|
||||
if(read) val = cp15->ACP;
|
||||
else cp15->ACP = val;
|
||||
}
|
||||
else break;
|
||||
goto success;
|
||||
|
||||
case 2: //translation tabler base
|
||||
if(read) val = cp15->ttb;
|
||||
else{
|
||||
if(cp15->control & 0x00000001UL){ //mmu is on
|
||||
|
||||
mmuSetTTP(cp15->mmu, val);
|
||||
mmuTlbFlush(cp15->mmu);
|
||||
}
|
||||
cp15->ttb = val;
|
||||
}
|
||||
goto success;
|
||||
|
||||
case 3: //domain access control
|
||||
if(read) val = mmuGetDomainCfg(cp15->mmu);
|
||||
else mmuSetDomainCfg(cp15->mmu, val);
|
||||
goto success;
|
||||
|
||||
case 5: //FSR
|
||||
if(read) val = cp15->FSR;
|
||||
else cp15->FSR = val;
|
||||
goto success;
|
||||
|
||||
case 6: //FAR
|
||||
if(read) val = cp15->FAR;
|
||||
else cp15->FAR = val;
|
||||
goto success;
|
||||
|
||||
case 7: //cache ops
|
||||
if((CRm == 5 || CRm == 7)&& op2 == 0) cpuIcacheInval(cp15->cpu); //invalidate entire {icache(5) or both i and dcache(7)}
|
||||
if((CRm == 5 || CRm == 7) && op2 == 1) cpuIcacheInvalAddr(cp15->cpu, val); //invalidate {icache(5) or both i and dcache(7)} line, given VA
|
||||
if((CRm == 5 || CRm == 7) && op2 == 2) cpuIcacheInval(cp15->cpu); //invalidate {icache(5) or both i and dcache(7)} line, given set/index. i dont know how to do this, so flush thee whole thing
|
||||
goto success;
|
||||
|
||||
case 8: //TLB ops
|
||||
mmuTlbFlush(cp15->mmu);
|
||||
goto success;
|
||||
|
||||
case 9: //cache lockdown
|
||||
if(CRm == 1 && op2 == 0){
|
||||
err_str("Attempt to lock 0x");
|
||||
err_hex(val);
|
||||
err_str("+32 in icache\r\n");
|
||||
}
|
||||
else if(CRm == 2 && op2 == 0){
|
||||
err_str("Dcache now ");
|
||||
err_str(val ? "in" : "out of");
|
||||
err_str(" lock mode\r\n");
|
||||
}
|
||||
goto success;
|
||||
|
||||
case 10: //TLB lockdown
|
||||
goto success;
|
||||
|
||||
case 13: //FCSE
|
||||
err_str("FCSE not supported\n");
|
||||
break;
|
||||
|
||||
case 15:
|
||||
if(op2 == 0 && CRm == 1){ //CPAR
|
||||
if(read) val = cpuGetCPAR(cp15->cpu);
|
||||
else cpuSetCPAR(cp15->cpu, val & 0x3FFF);
|
||||
goto success;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
fail:
|
||||
//TODO: cause invalid instruction trap in cpu
|
||||
return false;
|
||||
|
||||
success:
|
||||
|
||||
if(read) cpuSetReg(cpu, Rx, val);
|
||||
return true;
|
||||
}
|
||||
|
||||
void cp15Init(ArmCP15* cp15, ArmCpu* cpu, ArmMmu* mmu){
|
||||
|
||||
ArmCoprocessor cp;
|
||||
|
||||
cp.regXfer = cp15prvCoprocRegXferFunc;
|
||||
cp.dataProcessing = NULL;
|
||||
cp.memAccess = NULL;
|
||||
cp.twoRegF = NULL;
|
||||
cp.userData = cp15;
|
||||
|
||||
__mem_zero(cp15, sizeof(ArmCP15));
|
||||
cp15->cpu = cpu;
|
||||
cp15->mmu = mmu;
|
||||
cp15->control = 0x00004072UL;
|
||||
|
||||
cpuCoprocessorRegister(cpu, 15, &cp);
|
||||
}
|
||||
|
||||
void cp15Deinit(ArmCP15* cp15){
|
||||
|
||||
cpuCoprocessorUnregister(cp15->cpu, 15);
|
||||
}
|
||||
|
||||
void cp15SetFaultStatus(ArmCP15* cp15, UInt32 addr, UInt8 faultStatus){
|
||||
|
||||
cp15->FAR = addr;
|
||||
cp15->FSR = faultStatus;
|
||||
}
|
||||
34
src/pxa260/pxa260_cp15.h
Normal file
34
src/pxa260/pxa260_cp15.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef _CP15_H_
|
||||
#define _CP15_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "pxa260_types.h"
|
||||
#include "pxa260_CPU.h"
|
||||
#include "pxa260_MMU.h"
|
||||
|
||||
typedef struct{
|
||||
|
||||
ArmCpu* cpu;
|
||||
ArmMmu* mmu;
|
||||
|
||||
UInt32 control;
|
||||
UInt32 ttb;
|
||||
UInt32 FSR; //fault sttaus register
|
||||
UInt32 FAR; //fault address register
|
||||
UInt32 CPAR; //coprocessor access register
|
||||
UInt32 ACP; //auxilary control reg for xscale
|
||||
}ArmCP15;
|
||||
|
||||
void cp15Init(ArmCP15* cp15, ArmCpu* cpu, ArmMmu* mmu);
|
||||
void cp15Deinit(ArmCP15* cp15);
|
||||
void cp15SetFaultStatus(ArmCP15* cp15, UInt32 addr, UInt8 faultStatus);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "../../pxa260/pxa260_types.h"
|
||||
#include "CPU_2.h"
|
||||
#include "icache.h"
|
||||
#include "pxa260_types.h"
|
||||
#include "pxa260_CPU.h"
|
||||
#include "pxa260_icache.h"
|
||||
|
||||
//#define ICACHE_DEBUGGING
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
#define ICACHE_H
|
||||
|
||||
|
||||
#include "../../pxa260/pxa260_types.h"
|
||||
#include "CPU_2.h"
|
||||
#include "pxa260_types.h"
|
||||
#include "pxa260_CPU.h"
|
||||
|
||||
|
||||
#define ICACHE_L 5UL //line size is 2^L bytes
|
||||
@@ -288,6 +288,7 @@ static void tsc2101RegisterWrite(uint8_t page, uint8_t address, uint16_t value){
|
||||
case TOUCH_CONTROL_CONFIGURATION:
|
||||
//TODO: TOUCH_CONTROL_CONFIGURATION SWPDTD bit
|
||||
debugLog("TSC2101 config register writes not fully implemented\n");
|
||||
debugLog("TSC2101 SWPDTD bit:%d\n", !!(value & 0x0040));
|
||||
tsc2101Registers[TOUCH_CONTROL_CONFIGURATION] = value & 0x007F;
|
||||
tsc2101UpdateInterrupt();
|
||||
return;
|
||||
@@ -440,7 +441,7 @@ bool tsc2101ExchangeBit(bool bit){
|
||||
}
|
||||
|
||||
void tsc2101UpdateInterrupt(void){
|
||||
debugLog("TSC2101 PINTDAV not fully implemented\n");
|
||||
//debugLog("TSC2101 PINTDAV not fully implemented\n");
|
||||
|
||||
//check if PINTDAV is data or pen and data interrupt
|
||||
if(tsc2101Registers[TOUCH_CONTROL_STATUS] >> 14 & 0x0003){
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
#ifndef TUNGSTEN_T3_BUS_H
|
||||
#define TUNGSTEN_T3_BUS_H
|
||||
|
||||
#define PXA260_BANK_SCOOT 26
|
||||
#define PXA260_NUM_BANKS(areaSize) (((areaSize) >> PXA260_BANK_SCOOT) + ((areaSize) & ((1 << PXA260_BANK_SCOOT) - 1) ? 1 : 0))
|
||||
#define PXA260_START_BANK(address) ((address) >> PXA260_BANK_SCOOT)
|
||||
#define PXA260_END_BANK(address, size) (PXA260_START_BANK(address) + PXA260_NUM_BANKS(size) - 1)
|
||||
#define PXA260_BANK_IN_RANGE(bank, address, size) ((bank) >= PXA260_START_BANK(address) && (bank) <= PXA260_END_BANK(address, size))
|
||||
#define PXA260_BANK_ADDRESS(bank) ((bank) << PXA260_BANK_SCOOT)
|
||||
#define PXA260_TOTAL_MEMORY_BANKS (1 << (32 - PXA260_BANK_SCOOT))//64 banks for *_BANK_SCOOT = 26
|
||||
|
||||
#define PXA260_BANK_SIZE 0x04000000
|
||||
#define PXA260_ROM_START_ADDRESS 0x00000000
|
||||
#define PXA260_RAM_START_ADDRESS 0xA0000000
|
||||
#define TUNGSTEN_T3_W86L488_START_ADDRESS 0x08000000
|
||||
@@ -16,8 +9,21 @@
|
||||
#define PXA260_PCMCIA1_START_ADDRESS 0x30000000
|
||||
#define TUNGSTEN_T3_ROM_SIZE (16 * 0x100000)//16mb ROM
|
||||
#define TUNGSTEN_T3_RAM_SIZE (64 * 0x100000)//64mb RAM
|
||||
#define TUNGSTEN_T3_W86L488_SIZE 0x04000000
|
||||
#define TUNGSTEN_T3_W86L488_SIZE PXA260_BANK_SIZE
|
||||
#define PXA260_PCMCIA0_SIZE 0x10000000
|
||||
#define PXA260_PCMCIA1_SIZE 0x10000000
|
||||
#define PXA260_IO_BASE 0x40000000
|
||||
#define PXA260_MEMCTRL_BASE 0x48000000
|
||||
|
||||
#if defined(EMU_BIG_ENDIAN)
|
||||
//can take advantage of forced 32 bit alignment to allow almost free endian swaps
|
||||
#define PXA260_ADDR_FIX_ENDIAN_8(address) (address ^ 3)
|
||||
#define PXA260_ADDR_FIX_ENDIAN_16(address) (address ^ 1)
|
||||
#define PXA260_ADDR_FIX_ENDIAN_32(address) (address)
|
||||
#else
|
||||
#define PXA260_ADDR_FIX_ENDIAN_8(address) (address)
|
||||
#define PXA260_ADDR_FIX_ENDIAN_16(address) (address)
|
||||
#define PXA260_ADDR_FIX_ENDIAN_32(address) (address)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
15
tools/desktop/pcFromDalAddr.py
Normal file
15
tools/desktop/pcFromDalAddr.py
Normal file
@@ -0,0 +1,15 @@
|
||||
# get PC address for DAL function
|
||||
|
||||
import ida_kernwin
|
||||
|
||||
|
||||
DAL_START_IN_ROM = 0x0009B130
|
||||
DAL_END_IN_ROM = 0x000C7EEB
|
||||
|
||||
|
||||
def pcFromDalAddr():
|
||||
dalAddr = ida_kernwin.ask_addr(0x00000000, "DAL ADDR:")
|
||||
ida_kernwin.info("PC:" + "0x{0:0{1}X}".format(0x20000000 + DAL_START_IN_ROM + dalAddr, 8))
|
||||
|
||||
|
||||
pcFromDalAddr()
|
||||
Reference in New Issue
Block a user