- The SAS thread waited without checking the state first, so a mix
queued before it got to the wait was missed, and the next drain hung.
The state is now atomic too.
- SAS raw output mode writes four planes, twice the size it asked for.
- DMACplus colour conversion with a huge stride overflowed its size
math. The syscon serial data index wasn't bounded on reads, writes or
load.
- Replace_memcpy_swizzled copies whole 16x8 blocks, more than the
pitch * h it asked for. The God Eater depth hook tested the color
buffer twice instead of the depth buffer, and its size could wrap.
- GranularMixer used 1/0 before the first FPS sample, and read the
granule being written next when the queue was empty.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The audio hardware at 0xBE000000, as POPS's ME loop (popsman.prx +2F88)
drives it: after setting it up, it writes one stereo sample (left in the
low half) to 0x70 whenever status 0x28 bit 5 says the FIFO has room,
and sleeps in halt in between on the ME's interrupt source 12. Measured
from the ME on a PSP-1000 (pspautotest me/audiofifo): the FIFO holds 24
samples and plays them at 44.1kHz while 0x04 bit 1 is set; status 0x28
has bit 4 while playing, bit 5 while there's room, and bit 1 once it has
played out its last sample, a sample after the FIFO empties; and source
12 is a level, up while there's room, which halt wakes on with
interrupts disabled. What it plays is mixed into the output.
A halted ME used to sleep until the main CPU's next slice. It now wakes
within the slice when the full FIFO next has room, as POPS's loop sleeps
for every sample.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
popsman.prx starts the Media Engine and sets up audio through them. The
ME reset, bus clock and AVC reset are the calls the older NIDs already
have. The others set bits of registers lowio.prx 6.61 writes for them
(0x50 bit 15+n, 0x58 bit 16+n, 0x60 bit 18, 0x78 bits 7+n and 11), and
C36775AD (called with 0, 1 before audio is set up) isn't read yet. The
system register device now keeps every register it doesn't model.
Under --vsh these stand in for lowio.prx's own, which write the same
registers.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
MIST (and psp-media-engine-safe-task, and NJEMU through it) restart the
ME through me_wrapper's bootstrap at 0xBFC00040, after patching it to
point a0 at main RAM, which makes the firmware take its resume-from-
sleep path: restore eDRAM from a0 and go back to the main loop. They
then put a task's address in the firmware's command table with
DMACplus and send a command with the soft interrupt.
- The boot now leaves a bootstrap with 6.61's layout at 0xBFC00040,
and a stand-in for me_wrapper's ME stub (which isn't loaded under
HLE) that runs the image.
- The firmware never sets Status.IE: it gates interrupts with mtic
alone, so either one enables them now. Its main loop enables them for
only a few instructions, so mtic takes a pending one right away.
- Opcode 0x2C, which the resume path runs, is a no-op on the ME.
- Sysreg 0x00 reads 0 (the bootstrap's debug handler check), 0x70 is
kept, and the DDR flush register (0xBD000004) is always done.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Instead of only putting the image's code at 0x88300000, the ME now runs
it to where a PSP's ME is after boot: idle in its main loop's halt,
waiting for commands. As me_wrapper's boot (6.61, sceMeCore_driver
5dff5c50 and 0x1c30) and its ME stub leave things: the code at
0x88300000, the image's second stream still compressed after it, the
boot page's parameters (0x70C, 0x710 the CPU clock in MHz, 0x714), and
the ME at 0x88300000 with a0 at the second stream, which the code
decompresses into eDRAM itself. The stub's part (KL4E, caches) is done
here, since it runs main CPU kernel routines.
On a PSP the firmware has long since booted when a game starts, so it
runs to its idle halt the first time the ME runs, outside emulated time
(the JIT can't run earlier, at kernel init). About 80ms of startup in a
Debug build.
The compressed copy and the starting stack go below our idle threads'
stacks at the top of kernel memory, where a PSP has them higher up.
The firmware also needs:
- the ME to ignore the low two bits of jr/jalr targets: its command
table keeps flags there (interpreter, and the IR, for the ME only)
- source 5 of the ME's interrupt controller, the codec hardware it
starts and waits for in halt during init, which isn't emulated, so it
finishes at once
- sysreg 0x54 and 0x5C kept
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The ME's startup code (custom-core, libmecore) sets them, and Sony's ME
firmware lowers them around its halt. Only kept, like the other ME
devices, and only while the ME is emulated.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
What the firmware sets up for the ME at boot, which mcidclan's custom-core
libraries rely on, at kernel init when the ME is emulated:
- Sony's ME image at 0x88300000, as me_wrapper loads it: meimg.img on a
PSP-1000, me_t2img.img on later models, from the installed firmware's
flash0:/kd/resource, decrypted and KL4E-decompressed (only the first of
its two streams, the code). Custom-core calls functions in it, picking
per-model address tables by the word at 0x18. Without the firmware it's
logged and left out.
- A "SceMeRpc" sysevent handler record, which sceKernelReferSysEventHandler
(new, sceSysEventForKernel) returns, and custom-core takes over.
Also the VME's control registers (0xBCC00000), kept, with the status at
0x10 reading as done: custom-core's ME startup writes 1 there and waits.
mcidclan's custom-core poc, hw-mutex, gu-shared-list and exceptions
samples now run.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
As the firmware's DMACplus driver programs it (lowio.prx 6.61,
sceDmacplus_driver_d126494b for AVC and 9c492b9b for VME, the same in
1.50's dmacplus.prx): eight source buffers at 0x120-0x13C (two half
height sets), size in macroblocks and the mode at 0x140, two destinations
at 0x144/0x148, stride and output format (0 8888, 1 565, 2 5551, 3 4444)
at 0x14C, a Q3.7 matrix at 0x150-0x158 and the luma offset at 0x15C,
whose default (0x110, 16) is the firmware's. Writing 0x160 runs it, and
it finishes at once, with 0x04 bit 1 set.
AVC sources are in the decoder's tiled layout (UntileYCbCr): interleaved
into one destination they're the frame's even and odd rows, and going
to two, each set is a picture of its own with its own chroma, as
mcidclan's me-avc-csc-hd lays out its data. VME sources are planar.
Not checked on hardware: 0x140 bits 1 and 2 (taken as chroma line
replication and leaving out the second set, from mcidclan's samples)
and 0x15C bit 8 (taken as offsetting chroma by 128). mcidclan's
vme-hardware-csc, avc-csc-hd and vme-csc-hd-992x992 show their pictures.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A PL080-like controller: channels 4-7 at 0xBC800180, run at once when
enabled, following linked list items of five words (source, destination,
next, control, configuration). Addresses are physical, and on the ME's
bus (a master bit in the control word) the low ones are its eDRAM.
Transfer size is in units of the source width. A finished channel with
its interrupt bit set shows in 0x04, cleared through 0x08.
0xBC800100-0x10C set the LCD's framebuffer, which goes to sceDisplay.
Pixel format 0 is 8888 (mcidclan's samples); others are logged. The
colour space conversion registers (0x120-0x160) are only kept so far.
Like the other ME devices, this answers only while the ME is emulated.
mcidclan's dmacplus-me2sc and sc2me-then-me2sc samples show their
transferred eDRAM.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sysreg (0xBC100000) gets the ME's reset bit (0x4C bit 2), the soft
interrupt (0x44, which interrupts the other CPU: the ME's controller from
the main CPU, PSP_MECODEC_INTR from the ME), the hardware mutex (0x48,
each CPU sets its own bit only while the other's is clear) and the clock
and config registers it writes. The ME's interrupt controller (0xBC300000,
write-1-to-clear, mask at 8) answers the ME only. All of it is the ME's
state and stays unhandled without an ME.
On the ME: interrupts to EBase with EPC and EXL, halt sleeping until an
unmasked interrupt, and Count, which doesn't run: it stays 0 across 10ms
with ERL set after reset and with Status cleared, and reads back what was
written. Reset keeps the GPRs and sets Status ERL/BEV/SR, which homebrew
relies on: mcidclan's samples run C on the stack Sony's ME code left. The
values are from a kernel probe on a PSP-1000 (sp 0x801FFFE0, COP0 22 = 1,
Status 0x20500404, mfic 0, which doesn't gate interrupts).
The main CPU runs base interrupt handlers the way interruptman calls them
(a0 intno, a1 arg, a2 interrupted pc; -2 disables), and interrupt 31 has
a stand-in for me_wrapper's handler, which homebrew releases first.
While the ME is emulated, any main CPU code may access hardware registers:
homebrew calls its own code in kernel mode through a kcall module. This
is a stopgap (g_kernelModeAnywhere, marked HACK): kernel mode should be
part of the CPU's state, switched on while a kernel module's export runs.
The memcpy/memmove replacements copy through MMIO when an end is a
register address, for code copied to the boot page.
mcidclan's me-soft-interrupt sample now runs, counting each interrupt in
both directions.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>