12 Commits

Author SHA1 Message Date
meepingsnesroms
2b39468a32 Add divide function tests 2020-01-20 15:55:24 -08:00
meepingsnesroms
c33be1988d Update readme.md 2019-12-28 14:19:08 -08:00
meepingsnesroms
82aa0c6fc0 Clean up main makefile 2019-12-28 13:06:14 -08:00
meepingsnesroms
018ed97874 OS 5 now outputting a framebuffer 2019-12-28 12:16:48 -08:00
meepingsnesroms
be43d3a39a Clean up dynarec mess
Will deal with speed later, just need it to boot for now.
2019-12-15 00:03:02 -08:00
meepingsnesroms
ee822d2800 Lock up bug seems fixed 2019-12-14 22:10:03 -08:00
meepingsnesroms
46918f8d0f Update CPU_2.c 2019-12-10 00:18:10 -08:00
meepingsnesroms
5601f0c3d4 Fixes, change logging 2019-12-10 00:06:59 -08:00
meepingsnesroms
349ff5d92f Update idleModeStackTrace.txt 2019-12-07 10:38:47 -08:00
meepingsnesroms
be02f9b313 Log all attempts at setting the PC, capture where the CPU idle error occurs 2019-12-05 23:56:00 -08:00
meepingsnesroms
87f5312954 Remove ? from window bar, debugging stuff 2019-12-05 13:38:01 -08:00
meepingsnesroms
34426a791b Add a way to capture function calls reliably 2019-11-29 16:25:59 -08:00
72 changed files with 2039 additions and 9973 deletions

View File

@@ -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)

View 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)

View 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)

View File

@@ -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

View File

@@ -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

View File

@@ -87,6 +87,7 @@ else ifneq ($(findstring MINGW,$(shell uname -a)),)
endif
#CC = gcc
#CXX = g++
TARGET_NAME := mu
LIBM = -lm

View File

@@ -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 \

View File

@@ -17,6 +17,7 @@ DebugViewer::DebugViewer(QWidget* parent) :
QDialog(parent),
ui(new Ui::DebugViewer){
ui->setupUi(this);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
bitsPerEntry = 8;
debugRadioButtonHandler();

View File

@@ -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;

View File

@@ -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());

View File

@@ -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);

View File

@@ -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)

View File

@@ -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();
}

View File

@@ -1,6 +0,0 @@
#ifndef H_ARMSNIPPETS
#define H_ARMSNIPPETS
#define armloader_cb()
#endif

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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 = &REG0;
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;
}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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);

View File

@@ -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

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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){

View File

@@ -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;

View File

@@ -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

View File

@@ -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
}

View File

@@ -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;

View File

@@ -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
View 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
View 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

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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
View 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
View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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){

View File

@@ -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

View 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()