# Ghidra processor module for the NXP e200z4 (MPC5746R)
A Ghidra language for the e200z4 PowerPC core of NXP MPC57xx microcontrollers such as
the MPC5746R: VLE-only code, 32-bit GPRs, EFPU2 single-precision floating point, the
Lightweight Signal Processing APU (LSP) and the core MPU. Its analyzer turns a raw flash
image into typed, decompilable code: entry points, functions, RAM and peripheral
registers, and the types of globals, parameters and return values.
| Language ID | Description |
|--------------------------|-------------------------------------------------------------------|
| `PowerPC:BE:32:VLE-e200` | NXP e200z4 (MPC57xx, e.g. MPC5746R) 32-bit big endian, VLE only, EFPU2, LSP |
## Contents
The language includes Ghidra's own PowerPC SLEIGH sources unmodified (see
`data/languages/UPSTREAM` for the revision) and adds everything e200z4-specific in
separate files:
- `e200_efpu2.sinc`: EFPU2 scalar single precision (`efs*`) for 32-bit GPRs, including
the fused multiply-adds, `efssqrt`, `efsmax`/`efsmin` and the half-precision conversions.
Ghidra's SPE versions assume 64-bit GPRs.
- `e200_lsp.sinc`: all 678 LSP instructions with their full semantics: saturation,
rounding, SPEFSCR overflow bits, register pairs, and circular and bit-reversed
addressing. Generated by `tools/gen_lsp.py`.
- `e200_isa.sinc`:
- MPU (`mpure`, `mpuwe`, `mpusync`), decorated storage including the cache bypass forms,
`wait`, `e_ldmvmcsrrw`/`e_stmvmcsrrw`, `nego`, `lwarx` with EH=1, `dcbt`/`dcbtst` with a
cache target, the loads that bypass the data cache (`lbcbx`, `lhcbx`, `lwcbx`)
- the saturated math of the e200z425 (`e_addwus`, `e_addhus`, ..., `e_addwss`)
- corrections of Ghidra's VLE instructions:
- `e_ori.` no longer decodes as `e_nop`; `e_cmp16i` and related instructions are no
longer shown as record forms
- the conditional branch and link (`e_beql`, `e_bdnzl`, ...) sets LR whether or not it
is taken, and calls its target instead of the address stored at the target
- `e_rlwinm` and `e_rlwimi` with a mask that wraps around (MB > ME) keep bits MB and ME
- `se_srw` and `se_sraw` shift by 6 bits of rY; `se_sraw` and `se_srawi` set CA only
when 1 bits are shifted out of a negative value
- `se_bctr` and `se_bctrl` go to CTR as is, so calls through function pointers do not
decompile as `(*(code *)((uint)p & 0xfffffffe))()`
- `ppc_32_e200.pspec`: e200z4 SPR names (`MCSRR0`, `DSRR0`, `MAS0`-`MAS3`, `MPU0CSR0`,
`L1CSR0`, `HID0`, ...), VLE decoding everywhere, and which of Ghidra's analyzers run.
SPRs that the hardware changes between accesses (timebase, DEC, DBSR, TSR, MCSR,
MAS0-MAS3, and cache, branch buffer and MPU control) are volatile, so that the decompiler
keeps the loops that poll them. Generated by `tools/gen_pspec.py`.
- `ppc_32_e200.cspec`: the PowerPC EABI of e200 compilers: arguments in r3-r10, floats
included; results in r3 (r3:r4 for 8 bytes, and structures of up to 8 bytes); unsigned
`char`; r2 and r13 (small data area bases) preserved; no `__get_pc_thunk_lr` call fixup,
which models a classic PowerPC idiom that VLE code cannot execute. MSR and the SPRs other
than XER, LR, CTR and SPEFSCR are global state of the core, so the decompiler shows them as
globals (`tbl[PIR & 0xffff]`, `(MSR >> 0xf & 1) != 0`, `SPRG0 = x`) instead of `in_PIR`
inputs, and keeps writes to them.
- `data/patterns/e200_VLE_patterns.xml`: VLE function start patterns (the prologues
`se_mflr r0; se_stw r0,4(r1)`, `e_stwu r1,-X(r1)`, ... after returns, branches, padding
or erased flash), used instead of Ghidra's classic PowerPC patterns, which VLE code does
not contain.
- `data/svd/MPC5746R.svd.gz`: NXP's CMSIS-SVD description of the MPC5746R peripherals,
under the license in the file.
## Analyzer
One analyzer, **PowerPC e200** (`E200Analyzer.java`), does all the analysis below and runs
by default for this language; each step has its options under it in **Analysis Options**.
It runs on the code that analysis finds, where Ghidra's constant reference analyzers run.
At the start of every analysis, not only the first, it finds the entry points and works
out the small data area bases, and it propagates constants in the new code and again where
the bases changed. It schedules the peripherals, functions and data types steps later in
analysis, after Decompiler Parameter ID, for the functions with new or changed code and
those it has not seen. Each step leaves what is already marked up as it is, so running
analysis again, or the analyzer on a selection with **Analysis > One Shot**, redoes only
what changed.
### Entry points
`E200EntryPoints.java` finds the entry points of raw flash images at every analysis (option
**Find boot headers and interrupt vectors**):
- **Boot headers** of the MPC57xx boot assist flash (`0x005A....` at the start of a flash
block or a 16 KB boundary): typed `MPC57xx_boot_header`, with a function `reset` (or
`reset_0`, `reset_1`, ...) at each core's reset vector.
- **Interrupt vector tables**: at least 8 vectors of `e_b` to a handler, 16 bytes apart
from a 256-byte boundary, labeled `IVOR0_CriticalInput` to `IVOR34_EfpuRoundException`,
with the handlers made functions.
### Small data area bases and constant references
- **Small data area bases** (`E200SmallDataBases.java`). The analyzer assumes the EABI
bases r13 (`_SDA_BASE_`) and r2 (`_SDA2_BASE_`) in executable memory, so that
`e_lwz r3,-0x7ff0(r13)` resolves to a global. The values come from the linker symbols or
from the startup code that loads the registers, found even where analysis does not reach
it. When an image holds several programs, such as a bootloader and an application, each
value applies to the code its startup code reaches, up to the erased flash before the next
program; code copied to local memory gets the values of the program it was copied from.
They are worked out at every analysis: the analyzer records the values it sets in the
program and replaces them where the symbols, the startup code or the memory changed, or
where they were removed. Where a value changes, the references that the code using it got
from the earlier value are removed and constants are propagated there again. Values the
loader or the user set, or that the user changed, are left alone. Option **Assume small
data area bases**.
`E200ConstantPropagation.java` is derived from Ghidra's PowerPC Constant Reference
Analyzer, which the processor spec disables, as it does the Basic one.
- **VLE instructions.** No references from `e_lis`, which holds only the upper half of an
address, or from constants loaded with `e_li`, `se_li`, `se_bgeni` and `se_bmaski`.
- **Switch tables** behind `se_bctr`, bounded by VLE compares, also in flash images loaded
into a writable block. Option **Switch Table Recovery**.
- **Split array addresses.** An array address that code adds around the index, the lower
half with `e_add16i` or `e_addi` and the upper half with `e_add2is`, gets a reference.
### Peripherals
`E200Peripherals.java` adds the 101 MPC5746R peripherals from the SVD description (option
**Add MPC5746R peripherals**): a volatile, uninitialized block and a structure of registers
at each base address, at the end of analysis like the RAM blocks. Peripheral code then
decompiles as
```c
INTC.PSR[0x1b] = 0;
FCCU.CTRLK = 0x913756af;
MEMU.SYS_RAM_CERR[i].STS = 0xff;
INTC.CPR[core] = 0xe;
```
instead of `_DAT_fc040076 = 0` and so on.
- Arrays of registers become arrays (`SIUL2_SVD_GEN.MSCR[12]`), registers repeated for each
channel arrays of structures (`DMA_0.TCD[ch].SADDR`), and registers listed one by one as
`NAME0`, `NAME1`, ... at consecutive addresses arrays (`INTC.CPR[2]`).
- Peripherals with the same registers share a type named after their group (`DSPI_Type`).
- Register comments give the description and the field masks (`Fields: MDIS=0x2 FRZ=0x1`).
- Space the SVD does not describe is typed as words named by offset (`field_0xc90`).
- Tables of two or more peripheral base addresses, as drivers for several instances of a
peripheral use, become pointers to the peripherals' structures (`base->UARTSR = 0x3d04`).
- Option **Register bit fields** types registers as bit field structures instead of plain
integers: tests then name the field (`FCCU.CTRL.OPS != 3`), but whole-register writes get
casts.
### Functions
`E200Functions.java` completes the functions after Decompiler Parameter ID, in three steps,
each with an option:
- **Find functions in gaps** (`E200CodeGaps.java`): leaf functions that nothing calls
directly, such as getters and state machine handlers reached through tables, match no
start pattern. A gap after a return or unconditional branch becomes functions when all of
it decodes as complete subroutines that stay in the gap, use no `se_illegal` and none of
the instructions typical of data decoded as code that the program does not otherwise use.
An empty handler, a single `se_blr` between two functions, is too short for that rule;
it becomes a function when a word of flash next to a pointer to a function points to it,
as the entries of a table of handlers do. Each such function gets an analysis bookmark.
- **Calling conventions for kept registers** (`E200KeptRegisters.java`): e200 compilers
know which volatile registers (r0, r3-r12) the functions they compile change, and keep
values in the others across calls, which the decompiler showed as `extraout_` variables.
The analyzer works out the registers each function changes, through its callees, and
gives a function whose callers rely on it a calling convention that lists the registers
it keeps as unaffected, such as `__keeps_r3to6_r8to12`, added as a specification
extension. Decompiler Parameter ID then runs again for the callers whose view of a call
changed.
- **Signatures of interrupt handlers**: functions whose returns are all interrupt returns
(`se_rfi`, `se_rfci`, ...) get `void f(void)`. A handler restores r3 and r4 before
returning, which Decompiler Parameter ID took for parameters and a result.
Signatures set by the user or an import are left alone.
### Data types
`E200DataTypes.java` and `E200TypeFlow.java` type global data, parameters and return values
that are still `undefined1`, `undefined2`, `undefined4`, ... after Decompiler Parameter ID,
from what the decompiler infers in all functions (option **Infer data types**; this
decompiles every function and is the longest part of the analysis):
- **Globals from their uses**: a value used by `efs*` instructions is a `float`, one that is
sign extended is signed, one that is dereferenced is a pointer. A type is applied only
when the uses agree, except that a `float` wins over integers of its size and integers
that differ in signedness go to a clear majority. Single bytes become `byte` or `sbyte`.
- **Arrays and tables** (`E200Tables.java`): code reads an element of a global array at the
array's address plus the index times a stride, and a field of an array of structures at
the field's address in the first element plus the index times the structure size. The
accesses with the same stride whose addresses lie within one stride make a table: an
array of the field's type if one field fills the stride, otherwise an array of a structure
`S_
` with fields `f` (also those code accesses directly in the first
element), labeled `tbl_`. A field indexed again, as in a two-dimensional array,
is an array itself. The decompiler then shows `tbl_08f9e258[i].f4` instead of
`(&BYTE_08f9e25c)[i * 8]`. The length comes from comparisons of the index with a constant
(`i < 8`, not `i != 0xff`) and masks (`i & 7`); without one, a table reaches the next
element that code accesses or that a pointer in memory points to, within 512 bytes (an
array of structures keeps one element if there is none). A table never takes in an access
or data that does not fit its elements, code, or another table. Where code indexes one
address with several strides, as in `a[i]` and `a[2 * i]`, an array of the smallest
stride whose elements all the accesses are wins over a structure of the larger one.
Fields are typed from their uses like globals, in every round. A pointer field gets the
type code casts it to, `float *` for `*(float *)tbl[i].f34`, or a pointer to a structure
the analyzer made for a parameter it is passed as, and the data it points to gets the
pointed-to type: Ghidra's reference analysis would otherwise take an undefined target for
a pointer if its value is an address, as the float 2.0 (0x40000000, the start of SRAM)
is, or for code. Addresses used as pointers to a type get data of that type, as in
`FUN_08faf3e0(&SHORT_09294ba0, x)`, also where the reference analysis left undefined data
of another size at an address an instruction only forms, as long as nothing accesses that
data as it is.
- **Values passed between functions**: a function that only stores, passes on or returns a
value gives it no type, but the value has one where it comes from or goes to. Globals,
parameters and return values are linked where decompiled code copies a value from one to
another, and each group of linked places gets the type all their uses agree on. A
parameter or return value is typed only when all callers of its function were decompiled.
- **Structures that pointers point to** (`E200Structures.java`): code that reads or writes
through a pointer at constant offsets, as in `*(int *)(param_1 + 8)`, uses a structure
the decompiler does not know. What code does with each pointer value (the fields it
accesses at each offset with their sizes and types, what a pointer field points to, a
stride if it is indexed) is merged over the linked places above. A group with fields at
other offsets than 0 gets a structure `P_` with fields `f`, named after a
global, a parameter (`P__`) or a return value, and its places that hold
an integer or a pointer to a single value get a pointer to it, so that the decompiler
shows `param_1->f8->f0`. A run of bytes from offset 0, as code accesses a buffer, stays a
`byte *`; a pointer indexed with several strides, as the data of a lookup table whose
element type a field selects, is a `void *`; and a group with a place that gets a
constant that is no address (an error code passed where other callers pass a pointer)
gets no structure, and no pointer type. A pointer field whose target is such a run of
bytes is a `byte *`, and one that code only passes to a function that knows what it points
to gets that type. A structure built in an early round, from the functions decompiled
then, gets the field types that later rounds show. The places that are passed the address of the same
global, as the parameters of the functions that initialize, run and read one module's
instance, point to data of one layout: their groups are one group, with one structure,
where their shapes fit together (fields that overlap are the same field, or bytes of it).
The instances that code passes by address bound the structure's length, since none holds
another: a lookup function reads the scale and offset of a map descriptor whether or not
the descriptor has them, and the curve descriptors 12 bytes apart give a structure of 12
bytes, so that `lookup(&P_50800274_0928b4b4, x)` needs no cast at the hundreds of calls
and the two fields past the end show in the one function that reads them. Where the data at
the instances is in smaller pieces than a field, as the `byte` that code reads where a
function writes a `ushort` of the value and its complement, the structure has the pieces as
fields and the function writes it as a whole. Data that code passes as a pointer to such a
structure, as a map descriptor in flash passed to a lookup function, gets the structure
where only single values at its fields are there (an integer over whole integer fields, or
a byte of one, counts), and the data its pointer fields point to gets the pointed-to type
(a byte for a `void *`; all of flash is executable in an image, and the reference analysis
would disassemble an undefined target): `lookup(&P_50800274_092923ec, x)` instead of
`lookup((P_50800274 *)&USHORT_092923ec, x)`. A constant that is an address in RAM or flash,
passed where a pointer to a type is expected, gets data of that type there, so that the
decompiler shows `&USHORT_4000d178` instead of `(ushort *)0x4000d178`.
- **Pointers to functions** (`E200FunctionPointers.java`): a pointer in flash, an element of
an array of pointers or a pointer field of a structure whose values are all entry points of
functions (or null) gets a pointer to a function definition: the functions' signature where
they all have the same, otherwise one with the number of parameters most of them have
(the handlers in a table are called alike), as long as it is plausible for C (a
hand-written routine that reads all the argument registers gets eight from Decompiler
Parameter ID). Handlers called through tables then decompile as
`(*tbl_08fab6f4[i].f4)(x)` instead of `(*(code *)tbl_08fab6f4[i].f4)(x)`. A parameter,
global, return value or structure field that code calls through, or that gets the entry
point of a function (`PTR_40008000 = FUN_08fa1234`, `f(handler)`), gets a pointer to a
function definition too: the functions' where they are known and agree, else one with the
arguments of the calls, named after its prototype (`F_void_byte_uint`). Its parameters
have the sizes of the arguments: the decompiler dies on a call through a definition whose
parameter is wider than the argument.
- **Signedness and width of integers** (`E200Integers.java`): the decompiler infers integer
types before it removes dead code, and `se_cmp`, `se_cmpi` and `e_cmpi`, which compilers
also use to test for equality, set the less and greater bits with signed comparisons, so
values that code only compares for equality came out signed. The signedness is taken
instead from the operations that remain on a value: sign extension, signed comparisons,
arithmetic shifts and signed division make it signed, their counterparts unsigned, and a
value without either is unsigned. Integers that analysis typed get the signedness of the
uses of their group. A parameter that the callee masks or truncates to one or two bytes
before every use (`param_1 & 0xff`) is a `byte` or `ushort` (Decompiler Parameter ID gives
it the register's four bytes), and so is a return value that every caller uses so, or
that the function itself extends from a byte or halfword on every return (`return
(uint)BYTE_4000e5d8;` returns a `byte`, whatever the callers do with the register). A
value that a function only passes on to another function or returns has the width of the
place it goes to, and a parameter whose uses say nothing, as one the function ignores, the
width of the values its callers pass, when all of them are small constants or values
extended from a byte or halfword.
- New types change the decompiled code of the functions that use them, so the analyzer
decompiles again those that still use undefined places or table fields, or use a place
now typed as a pointer (which shows the data it points to), three rounds at most, and
after the default types below those that use pointer fields still untyped.
- **Unsigned types for untyped values** (option): places whose group shows no type at all,
values that are only copied, compared for equality or accessed in bytes, get the unsigned
integer of their size, and pointers to undefined data of 1, 2 or 4 bytes a pointer to it,
unless a linked value is a pointer, a `float` or of another size. Functions that return
nothing get the return type `void`, which Decompiler Parameter ID leaves undefined, and so
do functions whose undefined result no decompiled caller reads, such as handlers called
through a table that leave a callee's result in r3.
- **Results callers use** (`E200PassedResults.java`): e200 compilers do not turn
`return g(x);` into a branch to `g`. The function calls `g` and returns with the result of
`g` still in r3, which Decompiler Parameter ID misses when `g` had no return type yet. A
function whose callers read r3 after calling it, or return it from a function whose result
is used, is not given `void`. It gets the return type of the functions whose results it
passes on. If it does not only pass results on and an instruction of a caller reads the
result, it gets `undefined4`, which the steps above type where they can: Parameter ID also
misses results that some paths compute and others get from an indirect call. Without this,
callers show such results as `extraout_r3`.
A flash image has no RAM, so RAM variables have no data and the decompiler shows them as
`_DAT_40001234`. The analyzer therefore first adds uninitialized blocks for the MPC5746R
RAM where nothing is mapped (`SRAM` at 0x40000000, `IMEM_0`/`IMEM_1` at 0x50000000 and
0x51000000, `DMEM_0`/`DMEM_1` at 0x50800000 and 0x51800000; around a dump of part of a RAM,
blocks such as `SRAM_40000000`) and creates data of the accessed size where code
references them. It does this at the end of analysis: with RAM mapped from the start,
Ghidra's Create Address Tables analyzer takes flash values such as the float 2.0
(0x40000000) for pointers into RAM. Option **Add MPC5746R RAM blocks**.
### Memory dumps
`ghidra_scripts/AddMemoryDumps.java` (category **PowerPC e200**) adds the dumps in a folder
to the current program, each at the range its file name ends with, as in
`sram_40000000-4003FFFF.bin`: RAM as read/write, local instruction memory also executable,
the data flash at 0x00800000 as read/write, anything else as read/write/execute. A dump
fills an empty RAM block that analysis added; one over initialized memory is skipped. Run
analysis afterwards: it finds the code in the dumps and assumes r13 and r2 there.
## Results
On 11 flash images of the test firmware (several releases of an MPC5746R application with
its bootloader, 4 MB each), analyzed with the default options and every function
decompiled, per image:
| | Mean | Range |
|---|---|---|
| Functions | 14,487 | 13,953-14,977 |
| Instructions | 446,151 | 419,350-465,310 |
| Decompiler failures | 0 | 0 |
| Functions with decompiler warnings | 92 | 89-95 |
| Functions with `undefined` in their signature | 31 | 31 |
| Functions with `extraout_` variables | 31 | 30-31 |
| `undefinedN` in the C | 1,330 | 1,324-1,335 |
| `DAT_` references in the C | 451 | 442-461 |
| Pointer casts in the C | 4,594 | 4,394-4,732 |
| Calls through `(code *)` casts | 29 | 29 |
| Analysis time (2 CPUs) | 301 s | 279 s-318 s |
Most decompiler warnings are about infinite loops (halt loops) and unreachable blocks the
decompiler removes; the `extraout_` and `undefined` signatures are in hand-written
startup and core self-test code. The data types step takes most of the analysis time,
Decompiler Parameter ID about a fifth.
## Installation
Build the extension with Gradle and install the zip from `dist/` with
**File > Install Extensions** in Ghidra:
```
gradle -PGHIDRA_INSTALL_DIR=
```
Cloning this repository into `/Ghidra/Extensions/` provides the language but not
the analyzer, whose Java code the Gradle build compiles. Ghidra compiles the SLEIGH
specification the first time the language is used. `version` in `extension.properties` is
the Ghidra release whose PowerPC files are included.
## Usage
- Raw flash images: import with the **Raw Binary** loader, choose `PowerPC:BE:32:VLE-e200`
and set the base address where the image sits in the device's memory map. An image with
a bootloader and an application can be imported as one file.
- RAM and peripherals: if you map memory yourself, do it after the first analysis, for the
reason above, or use `AddMemoryDumps.java`. Avoid a block that reaches 0xFFFFFFFF: erased
flash reads 0xFFFFFFFF, and Create Address Tables turns erased words into pointers.
- ELF files: the importer lists the language for 32-bit big-endian PowerPC ELF files.
## Verification
- **Decoding:** every 16-bit encoding and every 32-bit encoding of the VLE primary opcodes,
for two register field patterns (2.1 million instruction words), compared with GNU
objdump 2.42 (`-M e200z4,lsp`) and NXP's binutils 2.28 (`-M e200z4`). All e200z4
instructions decode; the differences are below.
- **LSP semantics:** 156,000 randomized single-instruction tests in Ghidra's p-code
emulator against an independent Python model of the manual's pseudo-RTL; all pass. See
`tools/lsp_test`.
- **VLE semantics:** 130,000 randomized single-instruction tests of 283 integer, CR,
branch, load/store and EFPU2 instructions, all those the test images use except the
opaque ones, against an independent Python model of the Power ISA; all pass (5,688 failed
with Ghidra's semantics). See `tools/vle_test`.
- **Firmware:** the disassembly of four MPC5746R images (1.3 million instructions) matches
NXP's binutils 2.28 apart from mnemonic aliases, the e200z425 saturated math and
`e_ldmvmcsrrw`/`e_stmvmcsrrw`, which binutils 2.28 lacks, and the instructions below.
### Differences from GNU objdump
- `lbcbx`, `lhcbx` and `lwcbx` (X-form, XO 516, 548 and 580) decode; binutils shows them as
data. AUTOSAR OS code for the e200z4 uses XO 548 and 580; XO 516 follows the pattern of
the decorated forms.
- Encodings with nonzero reserved fields are not decoded, for example EFPU2 compares with
bits 9-10 set, or `efsabs` with a nonzero rB.
- LSP encodings the manual defines as illegal are not decoded: an odd rD or rS for
register pair instructions, and rA=0 in the update and modify forms.
- binutils decodes `zvcmpgths` and `zvcmplths` as `zvcmpgthu` and `zvcmplthu`.
- Instructions of older e200 and e500 cores that the MPC57xx core lacks are not decoded:
`ev*`, `efd*`, branch buffer and external PID cache locking, `mfdcrx`/`mtdcrx`, TLB.
- Ghidra's unmodified PowerPC files also decode some instructions the e200z4 lacks in VLE
mode (string, 4xx cache and TLB access), and show some mnemonics differently.
- `mtdcr` and `mfdcr` decode in VLE mode; binutils 2.28 shows them as data with
`-M e200z4`. Firmware for the e200z4 uses them, for example for DCRs 0x1F1, 0x1F2 and 0x1FE.
### Modelling notes
- EFPU2: conversions to integer do not saturate out-of-range and NaN inputs, and round a
value halfway between two integers up instead of to even; SPEFSCR is not updated;
compares set the GT bit of the CR field; the fused multiply-adds round twice.
- LSP (see the header of `e200_lsp.sinc`): signed-by-unsigned multiplies treat rA as
signed; circular buffer indexes wrap at the end of the buffer; exceptions are not
modelled.
- Decorated storage, cache bypass and MPU instructions are opaque p-code operations.
- `se_bctr` and `se_bctrl` do not clear bit 0 of CTR.
## Maintenance
| Tool | Purpose |
|------|---------|
| `tools/sync_upstream.py [--check]` | Copy Ghidra's PowerPC files from a source tree or installation and set the extension version |
| `tools/gen_pspec.py [--check]` | Generate `ppc_32_e200.pspec` |
| `tools/gen_lsp.py [--check]` | Generate `e200_lsp.sinc` from `tools/lsp_opcodes.txt` |
| `tools/lsp_test/` | Emulator tests of the LSP semantics |
| `tools/vle_test/` | Emulator tests of the VLE integer, branch, load/store and EFPU2 semantics |
The tools are not part of the extension zip. `E200ConstantPropagation.java` is derived from
Ghidra's `PowerPCAddressAnalyzer.java`; when syncing, check that file for changes worth
carrying over.
## Sources
- NXP, *Lightweight Signal Processing APU Reference Manual*, Rev. 3
- NXP AN12177, *e200z4 and e200z7 Core Memory Protection Unit*
- NXP AN4802, *MPC57xx e200zx Core Differences*
- NXP, CMSIS-SVD description of the MPC5746R, version 1.6
- NXP, *EREF 2.0: A Programmer's Reference Manual for Freescale Power Architecture Processors*
- GNU binutils 2.42 and NXP's binutils 2.28 for e200 (opcode tables)
- Ghidra pull requests [#8828](https://github.com/NationalSecurityAgency/ghidra/pull/8828)
(`efsmax`/`efsmin`) and [#8829](https://github.com/NationalSecurityAgency/ghidra/pull/8829)
(saturated math)