Contents

Memory Map
I/O Maps
Emotion Engine (EE)
Graphics Interface (GIF)
DMA Controller (DMAC)
Graphics Synthesizer (GS)
Vector Unit (VU)
EE Interrupt Controller (INTC)
IOP Hardware and Peripherals
Subsystem Interface (SIF)
BIOS


  Memory Map

EE Virtual/Physical Memory Map
  KUSEG: 00000000h-7FFFFFFFh User segment
  KSEG0: 80000000h-9FFFFFFFh Kernel segment 0
  KSEG1: A0000000h-BFFFFFFFh Kernel segment 1
  
  Virtual    Physical
  00000000h  00000000h  32 MB    Main RAM (first 1 MB reserved for kernel)
  20000000h  00000000h  32 MB    Main RAM, uncached
  30100000h  00100000h  31 MB    Main RAM, uncached and accelerated
  10000000h  10000000h  64 KB    I/O registers
  11000000h  11000000h  4 KB     VU0 code memory
  11004000h  11004000h  4 KB     VU0 data memory
  11008000h  11008000h  16 KB    VU1 code memory
  1100C000h  1100C000h  16 KB    VU1 data memory
  12000000h  12000000h  8 KB     GS privileged registers
  1C000000h  1C000000h  2 MB     IOP RAM
  1FC00000h  1FC00000h  4 MB     BIOS, uncached (rom0)
  9FC00000h  1FC00000h  4 MB     BIOS, cached (rom09)
  BFC00000h  1FC00000h  4 MB     BIOS, uncached (rom0b)
  70000000h  ---------  16 KB    Scratchpad RAM (only accessible via virtual addressing)
EE RAM is reportedly expandable up to 256 MB. However, the maximum seen in practice is 128 MB, for special TOOL consoles.

IOP Physical Memory Map
  KUSEG: 00000000h-7FFFFFFFh User segment
  KSEG0: 80000000h-9FFFFFFFh Kernel segment 0
  KSEG1: A0000000h-BFFFFFFFh Kernel segment 1
  
  Physical
  00000000h  2 MB     Main RAM (same as on PSX)
  1D000000h           SIF registers
  1F800000h  64 KB    Various I/O registers
  1F900000h  1 KB     SPU2 registers
  1FC00000h  4 MB     BIOS (rom0) - Same as EE BIOS
  
  FFFE0000h (KSEG2)   Cache control

Additional Memory
  4 MB   GS VRAM (used for framebuffer, textures, zbuffer, etc)
  2 MB   SPU2 work RAM - quadrupled from PSX's SPU
  8 MB   Memory card

  I/O Maps

EE Map
EE Timers
  100000xxh        Timer 0
  100008xxh        Timer 1
  100010xxh        Timer 2
  100018xxh        Timer 3
Image Processing Unit (IPU)
  10002000h 8h     IPU Command
  10002010h 4h     IPU Control
  10002020h 4h     IPU bit pointer control
  10002030h 8h     Top of bitstream
  10007000h 10h    Out FIFO (read)
  10007010h 10h    In FIFO (write)
Graphics Interface (GIF)
  10003000h 4h     GIF_CTRL - Control register
  10003010h 4h     GIF_MODE - Mode setting
  10003020h 4h     GIF_STAT - Status
  10003040h 4h     GIF_TAG0 - Bits 0-31 of tag before
  10003050h 4h     GIF_TAG1 - Bits 32-63 of tag before
  10003060h 4h     GIF_TAG2 - Bits 64-95 of tag before
  10003070h 4h     GIF_TAG3 - Bits 96-127 of tag before
  10003080h 4h     GIF_CNT - Transfer status counter
  10003090h 4h     GIF_P3CNT - PATH3 transfer status counter
  100030A0h 4h     GIF_P3TAG - Bits 0-31 of PATH3 tag when interrupted
  10006000h 10h    GIF FIFO
DMA Controller (DMAC)
  100080xxh        VIF0 - channel 0
  100090xxh        VIF1 - channel 1
  1000A0xxh        GIF - channel 2
  1000B0xxh        IPU_FROM - channel 3
  1000B4xxh        IPU_TO - channel 4
  1000C0xxh        SIF0 - channel 5
  1000C4xxh        SIF1 - channel 6
  1000C8xxh        SIF2 - channel 7
  1000D0xxh        SPR_FROM - channel 8
  1000D4xxh        SPR_TO - channel 9
  1000E000h 4h     D_CTRL - DMAC control
  1000E010h 4h     D_STAT - DMAC interrupt status
  1000E020h 4h     D_PCR - DMAC priority control
  1000E030h 4h     D_SQWC - DMAC skip quadword
  1000E040h 4h     D_RBSR - DMAC ringbuffer size
  1000E050h 4h     D_RBOR - DMAC ringbuffer offset
  1000E060h 4h     D_STADR - DMAC stall address
  1000F520h 4h     D_ENABLER - DMAC disabled status
  1000F590h 4h     D_ENABLEW - DMAC disable
Interrupt Controller (INTC)
  1000F000h 4h     INTC_STAT - Interrupt status
  1000F010h 4h     INTC_MASK - Interrupt mask
Subsystem Interface (SIF)
  1000F200h 4h     MSCOM - EE->IOP communication
  1000F210h 4h     SMCOM - IOP->EE communication
  1000F220h 4h     MSFLAG - EE->IOP flags
  1000F230h 4h     SMFLAG - IOP->EE flags
  1000F240h 4h     Control register
Privileged GS registers
  12000000h 8h     PMODE - various PCRTC controls
  12000010h 8h     SMODE1
  12000020h 8h     SMODE2
  12000030h 8h     SRFSH
  12000040h 8h     SYNCH1
  12000050h 8h     SYNCH2
  12000060h 8h     SYNCV
  12000070h 8h     DISPFB1 - display buffer for output circuit 1
  12000080h 8h     DISPLAY1 - output circuit 1 control
  12000090h 8h     DISPFB2 - display buffer for output circuit 2
  120000A0h 8h     DISPLAY2 - output circuit 2 control
  120000B0h 8h     EXTBUF
  120000C0h 8h     EXTDATA
  120000D0h 8h     EXTWRITE
  120000E0h 8h     BGCOLOR - background color
  12001000h 8h     GS_CSR - control register
  12001010h 8h     GS_IMR - GS interrupt control
  12001040h 8h     BUSDIR - transfer direction
  12001080h 8h     SIGLBLID - signal

IOP Map
Subsystem Interface (SIF)
  1D000000h 4h     MSCOM - EE->IOP communication
  1D000010h 4h     SMCOM - IOP->EE communication
  1D000020h 4h     MSFLAG - EE->IOP flags
  1D000030h 4h     SMFLAG - IOP->EE flags
  1D000040h 4h     Control register
CDVD Drive
  1F402004h 1h     Current N command
  1F402005h 1h     N command status (R)
  1F402005h 1h     N command params (W)
  1F402006h 1h     Error
  1F402007h 1h     Send BREAK command
  1F402008h 1h     CDVD I_STAT - interrupt register
  1F40200Ah 1h     Drive status
  1F40200Fh 1h     Disk type
  1F402016h 1h     Current S command
  1F402017h 1h     S command status
  1F402018h 1h     S command params
Interrupt Control
  1F801070h 4h     I_STAT - Interrupt status
  1F801074h 4h     I_MASK - Interrupt mask
  1F801078h 1h     I_CTRL - Global interrupt disable
DMA registers
  1F80108xh        MDECin - channel 0
  1F80109xh        MDECout - channel 1
  1F8010Axh        SIF2 (GPU) - channel 2
  1F8010Bxh        CDVD - channel 3
  1F8010Cxh        SPU2 Core0 - channel 4
  1F8010Dxh        PIO - channel 5
  1F8010Exh        OTC - channel 6
  1F80150xh        SPU2 Core1 - channel 8
  1F80151xh        ??? - channel 9
  1F80152xh        SIF0 - channel 10
  1F80153xh        SIF1 - channel 11
  1F80154xh        SIO2in - channel 12
  1F80155xh        SIO2out - channel 13
  
  1F8010F0h 4h     DPCR - DMA priority control
  1F8010F4h 4h     DICR - DMA interrupt control
  1F801570h 4h     DPCR2 - DMA priority control 2
  1F801574h 4h     DICR2 - DMA priority control 2
IOP Timers
  1F80110xh        Timer 0
  1F80111xh        Timer 1
  1F80112xh        Timer 2
  1F80148xh        Timer 3
  1F80149xh        Timer 4
  1F8014Axh        Timer 5
Serial Interface (SIO2)
  1F808200h 40h    SEND3 buffer
  1F808240h 20h    SEND1/2 buffers
  1F808260h 1h     In FIFO
  1F808264h 1h     Out FIFO
  1F808268h 4h     SIO2 control
  1F80826Ch 4h     RECV1
  1F808270h 4h     RECV2
  1F808274h 4h     RECV3
Sound Processing Unit (SPU2)
  1F900000h 180h   Core0 Voice 0-23 registers
  1F900190h 4h     Key ON 0/1
  1F900194h 4h     Key OFF 0/1
  1F90019Ah 2h     Core attributes
  1F90019Ch 4h     Interrupt address H/L
  1F9001A8h 4h     DMA transfer address H/L
  1F9001ACh 2h     Internal transfer FIFO
  1F9001B0h 2h     AutoDMA status
  1F9001C0h 120h   Core0 Voice 0-23 start/loop/next addresses
  1F900340h 4h     ENDX 0/1
  1F900344h 2h     Status register
  
  ... above addresses repeat for Core1 starting at 1F900400h ...
  
  1F900760h 2h     Master Volume Left
  1F900762h 2h     Master Volume Right
  1F900764h 2h     Effect Volume Left
  1F900766h 2h     Effect Volume Right
  1F900768h 2h     Core1 External Input Volume Left
  1F90076Ah 2h     Core1 External Input Volume Right



  Emotion Engine (EE)

The EE is the main CPU of the PS2. Also known as the R5900, it is a custom MIPS core designed by Sony and Toshiba. It runs at 294.912 MHz and uses a mix of MIPS III, MIPS IV, and custom instructions. It is a superscalar in-order CPU able to execute up to two instructions per cycle, containing a branch predictor, MMU, 16 KB instruction cache, 8 KB data cache, a single-precision FPU coprocessor, and a Vector Unit coprocessor.

EE Reference
EE Registers


  EE Registers

General-Purpose Registers (GPRs)
As per MIPS tradition, the EE has 32 GPRs. Notably, they are all 128-bit, though the full 128 bits are only used in certain instructions.
  Name       Convention
  zero       Hardwired to 0, writes are ignored
  at         Temporary register used for pseudo-instructions
  v0-v1      Return register, holds values returned by functions
  a0-a3      Argument registers, holds first four parameters passed to a function
  t0-t7      Temporary registers. t0-t3 may also be used as additional argument registers
  s0-s7      Saved registers. Functions must save and restore these before using them
  t8-t9      Temporary registers
  k0-k1      Reserved for use by kernels
  gp         Global pointer
  sp         Stack pointer
  fp         Frame pointer
  ra         Return address. Used by JAL and (usually) JALR to store the address to return to after a function
Aside from zero, all GPRs may be freely accessed if convention rules are respected.

Special Registers
  Name       Purpose
  pc         Program counter, address of currently-executing instruction (32-bit)
  hi/lo      Stores multiplication and division results (64-bit)
  hi1/lo1    Used by MULT1/DIV1 type instructions, same as above (64-bit)
  sa         Shift amount used by QFSRV instruction


  Graphics Interface (GIF)

The GIF sends textures and geometry to the GS. It takes data from three different PATHs:
- PATH1: VU1 via XGKICK instruction. Highest priority.
- PATH2: VIF1 via DIRECT/DIRECTHL. Medium priority.
- PATH3: GIF DMAC channel (channel 2). Lowest priority.
Only one PATH may run at a time. Under normal conditions, when one PATH stops, the highest priority queued PATH will begin transfer.

GIF Reference
GIF I/O
GIFtags
GIF Data Formats
GIF PATH3 Masking


  GIF I/O

10003000h GIF_CTRL - Control register (W)
  0     Reset GIF
  1-2   Unused
  3     Temporary stop (1=stop transfers, 0=restart transfers)
  4-31  Unused

10003010h GIF_MODE - Mode of operation (W)
  0     Mask PATH3 (1=Mask)
  1     Unused
  2     Intermittent mode
When PATH3 is masked by this register and GIF DMA is ongoing, the mask applies once GIF DMA ends its transfer.

10003020h GIF_STAT - Status register (R)
  0     PATH3 masked by GIF_MODE
  1     PATH3 masked by VIF1 MASKP3 register
  2     Intermittent mode activated
  3     Temporary stop 
  4     Unused
  5     PATH3 interrupted (by intermittent mode?)
  6     PATH3 queued
  7     PATH2 queued
  8     PATH1 queued
  9     Output path (1=transfer ongoing)
  10-11 Active path
        0=Idle
        1=PATH1
        2=PATH2
        3=PATH3
  12    Transfer direction (0=EE->GS, 1=GS->EE)
  13-23 Unused
  24-28 Data in GIF FIFO (in quadwords, max 16)
  29-31 Unused

10003040h GIF_TAG0 (R)
  0-31  Bits 0-31 of most recently read GIFtag
10003050h GIF_TAG1 (R)
  0-31  Bits 32-63 of most recently read GIFtag
10003060h GIF_TAG2 (R)
  0-31  Bits 64-95 of most recently read GIFtag
10003070h GIF_TAG3 (R)
  0-31  Bits 96-127 of most recently read GIFtag
These registers are only readable when the GIF has been paused by GIF_CTRL.
For details on the GIFtag format, see
GIFtags

10003080h GIF_CNT (R)
  0-14  Backwards loop counter from NLOOP
        Decrements to zero
  15    Unused
  16-19 Register descriptor in progress
        0=highest
        1=lowest
        2=2nd lowest
        ...
        15=15th lowest
  20-29 VU data address being transferred
  30-31 Unused
Only accessible when GIF is paused by GIF_CTRL.

10003090h GIF_P3CNT (R)
  0-14  Backwards loop counter from PATH3 NLOOP when PATH3 is interrupted
  15-31 Unused
Only accessible when GIF is paused by GIF_CTRL.

100030A0h GIF_P3TAG (R)
  0-31  Bits 0-31 of PATH3 GIFtag when PATH3 is interrupted
Only accessible when GIF is paused by GIF_CTRL.


  GIFtags

The basic unit of GIF transfer data is the GIF packet. Each packet is split into one or more primitives. Every primitive must be preceded by a GIFtag.

GIFtag Format
  0-14    NLOOP - Data per register to transfer
  15      EOP - End of packet
  16-45   Unused
  46      Enable PRIM field
  47-57   Data to be sent to GS PRIM register if GIFtag.46 == 1
  58-59   Data format
          0=PACKED
          1=REGLIST
          2=IMAGE
          3=IMAGE
  60-63   NREGS - Number of registers
          0=16 registers
  64-127  Register field, 4 bits each

When NLOOP == 0, all fields are ignored except EOP and no further processing is done.
When GIFtag.46 == 0, an idle cycle is inserted before processing begins.
Registers are handled in little-endian order; i.e. bits 64-67 are processed first, then 68-71, and so on.

NOTE: The GS Q register is initialized to 1.0f when reading a GIFtag.


  GIF Data Formats

The data format after a GIFtag varies depending on the value of GIFtag.58-59. The GIF can transfer to the GS in PACKED, REGLIST, or IMAGE formats.

PACKED Format
Data is transferred in units of quadwords (16 bytes). Total amount of data in the GIF primitive = NLOOP * NREGS.
All registers not specified below output the lower 64-bits of the quadword to their GS register directly. The upper 64-bits are discarded.

Current reg=0h PRIM
  0-10    Data to write to GS PRIM
  11-127  Unused

Current reg=1h RGBA
  Writes to RGBAQ register (Q is unchanged)
  0-7     R
  8-31    Unused
  32-39   G
  40-63   Unused
  64-71   B
  72-95   Unused
  96-103  A
  104-127 Unused
R, G, B, and A are 8-bit values. Q is set by the STQ command.

Current reg=2h STQ
  Writes to ST register and Q component of RGBAQ.
  0-31    S
  32-63   T
  64-95   Q
  96-127  Unused
S, T, and Q are single-precision (32-bit) floats in IEEE 754 format.

Current reg=3h UV
  0-13    U
  14-31   Unused
  32-45   V
  46-127  Unused
U and V are 14-bit unsigned fixed-point integers with a 4-bit fractional component.

Current reg=4h XYZ2F/XYZ3F
  0-15    X
  16-31   Unused
  32-47   Y
  48-67   Unused
  68-91   Z
  92-99   Unused
  100-107 F
  108-110 Unused
  111     Disable drawing (1=write to XYZ3F, 0=write to XYZ2F)
  112-127 Unused
X and Y are signed 16-bit fixed-point integers with a 4-bit fractional component.
Z is a 24-bit integer, and F is an 8-bit integer.
The "disable drawing" bit controls whether the data is written to XYZ2F or XYZ3F.

Current reg=5h XYZ2/XYZ3
  0-15    X
  16-31   Unused
  32-47   Y
  48-63   Unused
  64-95   Z
  96-110  Unused
  111     Disable drawing (1=write to XYZ3F, 0=write to XYZ2F)
  112-127 Unused
Similar to XYZ2F/XYZ3F above, except there is no F register and Z is 32-bit.

Current reg=Ah FOG
  0-99    Unused
  100-107 F
  108-127 Unused

Current reg=Eh A+D (output data to specified address)
  0-63    Data
  64-71   Register address
  72-127  Unused
Outputs data to the given register address.

WARNING: Do not use this register descriptor on PATH3 when PATH1 is also running, or the GS may lock up!

Current reg=Fh NOP
  Data not output to GS.

REGLIST Format
Total data in GIF primitive = NREGS * NLOOP, in units of doublewords. (64-bits)
In REGLIST, a doubleword is sent directly to the register descriptor. Every quadword contains data for two registers. When the data is prepared properly, this can multiply data density by up to a factor of 2.
When NREGS * NLOOP is odd, the last doubleword in a primitive is discarded.

IMAGE Format
Total data in GIF primitive = NLOOP only, in units of quadwords.
IMAGE is a shortcut for writing to the GS HWREG register, which transfers textures and other data to VRAM. Each quadword has enough data for two writes to HWREG.


  GIF PATH3 Masking

PATH1 and PATH2 transfer to the GS without any internal buffering. However, PATH3 has a 16-quadword GIF FIFO.
When PATH3 masking is enabled, data sent by PATH3 will reside in the FIFO until the mask is lifted. This is useful for queueing texture transfers while other paths are sending geometry. GTA: San Andreas, Lemmings, and some other games rely on this.

Furthermore, Wallace and Gromit at Project Zoo will enable the mask and start a GIF DMA transfer, expecting it to finish. Emulating PATH3 masking without emulating the GIF FIFO will cause it to hang on a black screen, as the DMA channel is unable to write any data.


  DMA Controller (DMAC)

The DMAC gives an interface for the EE to access the rest of the system. It can transfer one quadword (16 bytes) at bus speed (half the rate of the EE's clock).

DMAC Reference
DMAC I/O
DMAC Chain Mode
DMAC Interrupts


  DMAC I/O

Channels
  100080xxh    VIF0
  100090xxh    VIF1 (can act as PATH2 for GIF)
  1000A0xxh    GIF (PATH3)
  1000B0xxh    IPU_FROM
  1000B4xxh    IPU_TO
  1000C0xxh    SIF0 (from IOP)
  1000C4xxh    SIF1 (to IOP)
  1000C8xxh    SIF2 (bidirectional, used for PSX mode and debugging)
  1000D0xxh    SPR_FROM
  1000D4xxh    SPR_TO

1000xx00h Dn_CHCR - Channel control (R/W)
  0     DIR - Direction (0=to memory, 1=from memory)
        Only effective for VIF1 and SIF2
  1     Unused
  2-3   MOD - Mode (0=normal, 1=chain, 2=interleave)
  4-5   ASP - Address stack pointer
  6     TTE - Transfer DMAtag (only in source chain mode)
  7     TIE - Enable IRQ bit in DMAtag
  8     STR - Start/busy
  9-15  Unused
  16-31 TAG - Bits 16-31 of most recently read DMAtag

1000xx10h Dn_MADR - Channel address (R/W)
1000xx30h Dn_TADR - Channel tag address (R/W)
  0-30  Address (lower 4 bits must be zero)
  31    Memory selection (0=RAM, 1=scratchpad)
The channel will read from/write to MADR directly during a transfer. TADR is only used for chain mode.
MADR and TADR update while a transfer is ongoing, i.e., the DMAC has no internal channels for memory addresses.
Certain games expect MADR and TADR to increment during a transfer.

1000xx20h Dn_QWC - Quadword count (R/W)
  0-15  Quadwords
  16-31 Unused
In normal and interleaved mode, the transfer ends when QWC reaches zero. Chain mode behaves differently; see
DMAC Chain Mode

1000xx40h Dn_ASR0 - Channel saved tag address (R/W)
1000xx50h Dn_ASR1 - Channel saved tag address (R/W)
  0-30  Tag address (lower 4 bits must be zero)
  31    Memory selection (0=RAM, 1=scratchpad)
ASR0/ASR1 can only be used by the VIF0, VIF1, and GIF channels.

1000xx80h Dn_SADR - Channel scratchpad address (R/W)
  0-13  Address (lower 4 bits must be zero)
  14-31 Unused
SADR is only used by SPR_FROM and SPR_TO.

1000E000h D_CTRL - DMAC control (R/W)
  0    DMA enable
  1    Cycle stealing on
  2-3  MFIFO drain channel
       0=None
       1=Reserved
       2=VIF1
       3=GIF
  4-5  Stall control channel
       0=None
       1=SIF0
       2=SPR_FROM
       3=IPU_FROM
  6-7  Stall control drain channel
       0=None
       1=VIF1
       2=GIF
       3=SIF1
  8-10 Release cycle period
       0=8
       1=16
       2=32
       3=64
       4=128
       5=256
When cycle stealing is on, the release cycle period controls how long the EE has control of the bus.

1000E010h D_STAT - DMAC interrupt status (R/W)
  0-9    Channel interrupt status (1=IRQ, write 1 to clear)
  10-12  Unused
  13     DMA stall interrupt status
  14     MFIFO empty interrupt status
  15     BUSERR interrupt status
  16-25  Channel interrupt mask (1=enabled, write 1 to reverse)
  26-28  Unused
  29     Stall interrupt mask
  30     MFIFO empty mask
INT1 is asserted when (status & mask) != 0.

1000E020h D_PCR - DMAC priority control (R/W)
  0-9    COP0 condition control
  10-15  Unused
  16-25  Channel priority (0=Channel disabled, only if D_PCR.31 is true)
  26-30  Unused
  31     Priority enable
COP0 control determines whether or not CPCOND0 is activated. If (~control | D_STAT.0-9) == 0x3FF, CPCOND0 is set. This is useful for knowing when multiple DMA transfers have finished without using interrupts.

1000E030h D_SQWC - DMAC skip quadword (R/W)
  0-7   Quadwords to skip
  8-15  Unused
  16-23 Quadwords to transfer
  24-31 Unused
In interleave mode, every time D_SQWC.16-23 quadwords have been transferred, D_SQWC.0-7 quadwords are skipped.

1000E040h D_RBSR - DMAC ringbuffer size (R/W)

1000E050h D_RBOR - DMAC ringbuffer offset (R/W)

1000F520h D_ENABLER - DMAC disabled status (R)
1000F590h D_ENABLEW - DMAC disable (W)
  16    DMAC disabled
  All other bits appear to be garbage, but writable
  SCPH-39001 (and other BIOSes?) seems to expect D_ENABLER to be set to 1201h upon boot


  DMAC Chain Mode

The DMAC can intelligently chain DMA transfers together. When a transfer starts in chain mode, the channel will transfer QWC quadwords and then read a DMAtag.
In source chain mode, the DMAtag comes from TADR. In destination chain mode, the DMAtag comes from the peripheral the channel is reading from.

DMAtag
  0-15    QWC to transfer
  16-25   Unused
  26-27   Priority control
          0=No effect
          1=Reserved
          2=Priority control disabled (D_PCR.31 = 0)
          3=Priority control enabled (D_PCR.31 = 1)
  28-30   Tag ID
  31      IRQ
  32-62   ADDR field (lower 4 bits must be zero)
  63      Memory selection for ADDR (0=RAM, 1=scratchpad)
  64-127  Data to transfer (only if Dn_CHCR.TTE==1)
When both IRQ and Dn_CHCR.TIE are set, the transfer ends after QWC has been transferred.
When Dn_CHCR.TTE is on, bits 64-127 are transferred BEFORE QWC.
The effects of the tag ID vary depending on if the channel is in source chain or dest chain mode.

Source Chain Tag ID
  0    refe    MADR=DMAtag.ADDR
               TADR+=16
               tag_end=true
  
  1    cnt     MADR=TADR+16 (next to DMAtag)
               TADR=MADR (next to transfer data)
  
  2    next    MADR=TADR+16
               TADR=DMAtag.ADDR
  
  3    ref     MADR=DMAtag.ADDR
               TADR+=16
  
  4    refs    MADR=DMAtag.ADDR
               TADR+=16
  
  5    call    MADR=TADR+16
               if (CHCR.ASP == 0)
                 ASR0=MADR+(QWC*16)
               else if (CHCR.ASP == 1)
                 ASR1=MADR+(QWC*16)
               TADR=DMAtag.ADDR
               CHCR.ASP++
  
  6    ret     MADR=TADR+16
               if (CHCR.ASP == 2)
                 TADR=ASR1
                 CHCR.ASP--
               else if (CHCR.ASP == 1)
                 TADR=ASR0
                 CHCR.ASP--
               else
                 tag_end=true
  
  7    end     MADR=TADR+16
                 tag_end=true
               
When tag_end=true, the transfer ends after QWC has been transferred.

Dest Chain Tag ID
  0    cnt     MADR=DMAtag.ADDR
  
  1    cnts    MADR=DMAtag.ADDR
  
  7    end     MADR=DMAtag.ADDR
               tag_end=true

DMA Resuming
If a transfer starts in source chain mode and QWC > 0, the DMAC assumes that the TAG field of CHCR was the last read DMAtag. This means that if the tag ID is REFE or END, the channel will stop after QWC has been transferred.
Sony's movie-playing library requires this behavior, as it uses MADR to determine the current movie buffer position.


  DMAC Interrupts

Upon any transfer completion, the DMAC will raise the channel's stat flag in D_STAT. When (stat & mask) != 0, the DMAC asserts an INT1 signal to the EE.
INT1 may also be asserted when MFIFO is empty, a stall occurs, or a bus error occurs. The bus error is not maskable via D_STAT.
NOTE: INT0 (INTC) has priority over INT1. If INT0 and INT1 are asserted at the same time, the INT0 interrupt will be processed first.


  Graphics Synthesizer (GS)

GS Reference
GS Register List
GS Primitives
GS Vertex Attributes
GS Frame and Z Buffers
GS Transfers
GS Textures
GS Fog
GS Alpha Blending
GS Tests and Pixel Control
GS Special Effects and Optimizations


  GS Register List

Internal registers (accessible via GIF)
  00h     PRIM
  01h     RGBAQ
  02h     ST
  03h     UV
  04h     XYZF2
  05h     XYZ2
  06h/07h TEX0_1/2
  08h/09h CLAMP_1/2
  0Ah     FOG
  0Ch     XYZF3
  0Dh     XYZ3
  14h/15h TEX1_1/2
  16h/17h TEX2_1/2
  18h/19h XYOFFSET_1/2
  1Ah     PRMODECONT
  1Bh     PRMODE
  1Ch     TEXCLUT
  22h     SCANMSK
  34h/35h MIPTBP1_1/2
  36h/37h MIPTBP2_1/2
  3Bh     TEXA
  3Dh     FOGCOL
  3Fh     TEXFLUSH
  40h/41h SCISSOR_1/2
  42h/43h ALPHA_1/2
  44h     DIMX
  45h     DTHE
  46h     COLCLAMP
  47h/48h TEST_1/2
  49h     PABE
  4Ah/4Bh FBA_1/2
  4Ch/4Dh FRAME_1/2
  4Eh/4Fh ZBUF_1/2
  50h     BITBLTBUF
  51h     TRXPOS
  52h     TRXREG
  53h     TRXDIR
  54h     HWREG
  60h     SIGNAL
  61h     FINISH
  62h     LABEL

Privileged registers (accessible via EE)
  12000000h    PMODE
  12000020h    SMODE2
  12000070h    DISPFB1
  12000080h    DISPLAY1
  12000090h    DISPFB2
  120000A0h    DISPLAY2
  120000B0h    EXTBUF
  120000C0h    EXTDATA
  120000D0h    EXTWRITE
  120000E0h    BGCOLOR
  12001000h    CSR
  12001010h    IMR
  12001040h    BUSDIR
  12001080h    SIGLBLID


  GS Primitives

The GS supports point, line, triangle, and sprite (2D rectangle with two points) primitives.

00h PRIM
  0-2   Primitive type
        0=Point
        1=Line
        2=LineStrip
        3=Triangle
        4=TriangleStrip
        5=TriangleFan
        6=Sprite
        7=Reserved
  3     Gourand shading
  4     Texture mapping
  5     Fog
  6     Alpha blending
  7     Antialiasing
  8     Use UV for texture coordinates (0=Use STQ)
  9     Use Context 2's registers for drawing (0=Use Context 1)
  10    Fix fragment value (related to antialiasing?)

1Ah PRMODECONT
When this register is set to 1, bits 3-10 in PRIM are used when rendering primitives. Otherwise, bits 3-10 in PRMODE are used.

1Bh PRMODE
Same as PRIM, except bits 0-2 are unused.
PRMODE can be useful when you want to draw multiple primitives with different attributes but the same type.


  GS Vertex Attributes

01h RGBAQ
  0-7    Red
  8-15   Green
  16-23  Blue
  24-31  Alpha
  32-63  Q
These attributes are applied to newly created vertices.

04h/0Ch XYZF2/XYZF3
  0-15   X
  16-31  Y
  32-55  Z
  56-63  Fog coefficient (placed in FOG register)
This defines a new vertex to be placed in the vertex queue, a process known as the vertex kick. X and Y are 12-bit fixed-point integers with a 4-bit fractional component, ranging from 0 to 4095.9375.
When 1, 2, or 3 vertices have been defined as per the primitive type, a "drawing kick" occurs, where the GS begins drawing the primitive.
NOTE: Drawing kicks can only occur on XYZF2! XYZF3 will clear the queue as needed but will not draw any primitives. XYZF3 exists to allow the programmer to easily cull polygons, as the GS has no native support for culling.

See GS Fog for details on fog.

05h/0Dh XYZ2/XYZ3
  0-15   X
  16-31  Y
  32-63  Z
Same as XYZF2/XYZF3, except Z is a 32-bit unsigned integer rather than a 24-bit unsigned integer.
XYZ2 can also result in a drawing kick, whereas XYZ3 cannot.

18h/19h XYOFFSET_1/2
  0-15   X
  32-47  Y
When a vertex kick occurs, XYOFFSET is subtracted from the vertex's X and Y coordinates.


  GS Frame and Z Buffers

4Ch/4Dh FRAME_1/2
  0-8    Base pointer in words/2048
  16-21  Buffer width in pixels/64
  24-29  Format
         00h=PSMCT32
         01h=PSMCT24
         02h=PSMCT16
         0Ah=PSMCT16S
         30h=PSMZ32
         31h=PSMZ24
         32h=PSMZ16
         3Ah=PSMZ16S
  32-63  Framebuffer mask
Bits 32-63 prevent the specified bits in the framebuffer from being updated by the following formula:
final_color = (final_color & ~mask) | (frame_color & mask)
This mask is applied before format conversions, e.g., bit 63 of FRAME will affect bit 15 (alpha bit) of a 16-bit color.

4Eh/4Fh ZBUF_1/2
  0-8    Base pointer in words/2048
  24-27  Format
         00h=PSMZ32
         01h=PSMZ24
         02h=PSMZ16
         0Ah=PSMZ16S
  32     Buffer mask (1=do not update zbuffer)
The zbuffer's width is the same as the framebuffer's, specified in FRAME.


  GS Transfers

The GS supports GIF->VRAM, VRAM->VRAM, and VRAM->GIF data transfers.

50h BITBLTBUF
  0-13    Source base pointer in words/64
  16-21   Source buffer width in pixels/64
  24-29   Source format
          00h=PSMCT32
          01h=PSMCT24
          02h=PSMCT16
          0Ah=PSMCT16S
          13h=PSMCT8
          14h=PSMCT4
          1Bh=PSMCT8H
          24h=PSMCT4HL
          2Ch=PSMCT4HH
          30h=PSMZ32
          31h=PSMZ24
          32h=PSMZ16
          3Ah=PSMZ16S
  32-45   Destination base pointer in words/64
  48-53   Destination buffer width in pixels/64
  56-61   Destination format (same as source format)
In VRAM->VRAM transfers, the source and destination formats must have the same bits per pixel.

51h TRXPOS
  0-10    X for source rectangle
  16-26   Y for source rectangle
  32-42   X for destination rectangle
  48-58   Y for destination rectangle
  59-60   Transmission order for VRAM->VRAM transfers
          0=Upper-left->lower-right
          1=Lower-left->upper-right
          2=Upper-right->lower-left
          3=Lower-right->upper-left
X and Y are in units of pixels and define the upper-left corner of their respective rectangle.
NOTE: During transfer, X and Y wrap around if they exceed 2048, e.g., by the following formula:
X = (TRXPOS.X + TRXREG.width) % 2048

52h TRXREG
  0-11    Width in pixels of transmission area
  32-43   Height in pixels of transmission area

53h TRXDIR
  0-1     Transmission direction
          0=GIF->VRAM
          1=VRAM->GIF
          2=VRAM->VRAM
          3=Deactivated
Note that the privileged register BUSDIR must be set appropriately for GIF->VRAM and VRAM->GIF.

54h HWREG
  0-63    Data to be transferred for GIF->VRAM
The "IMAGE" GIFtag format is a shortcut for writing to this register. Data is packed according to the format. For example, PSMCT4 will have 16 4-bit pixels per doubleword.


  GS Textures

06h/07h TEX0_1/2
  0-13    Base pointer in words/64
  14-19   Buffer width in pixels/64
  20-25   Texture format
          00h=PSMCT32
          01h=PSMCT24
          02h=PSMCT16
          0Ah=PSMCT16S
          13h=PSMCT8
          14h=PSMCT4
          1Bh=PSMCT8H
          24h=PSMCT4HL
          2Ch=PSMCT4HH
          30h=PSMZ32
          31h=PSMZ24
          32h=PSMZ16
          3Ah=PSMZ16S
  26-29   Texture width (width = min(2^value, 1024))
  30-33   Texture height (height = min(2^value, 1024))
  34      Alpha control (0=texture is RGB, 1=texture is RGBA)
  35-36   Color function
          0=Modulate
          1=Decal
          2=Highlight
          3=Highlight2
  37-50   CLUT base pointer in words/64
  51-54   CLUT format
          00h=PSMCT32
          02h=PSMCT16
          0Ah=PSMCT16S
  55      CLUT uses CSM2 (0=CSM1)
  56-60   CLUT entry offset/16 (In CSM2, this value must be 0)
  61-63   CLUT cache control
          0=Do not reload cache
          1=Reload cache
          2=Reload cache and copy CLUT base pointer to CBP0
          3=Reload cache and copy CLUT base pointer to CBP1
          4=IF CLUT base pointer != CBP0, reload cache and copy pointer to CBP0
          5=IF CLUT base pointer != CBP1, reload cache and copy pointer to CBP1

14h/15h TEX1_1/2
  0       LOD (level of detail) calculation method
          0=LOD=(log2(1/abs(Q))<= 0 (reduced texture)
          0=nearest
          1=bilinear
          2=nearest_mipmap_nearest
          3=nearest_mipmap_bilinear
          4=bilinear_mipmap_nearest
          5=bilinear_mipmap_bilinear
  9       Automatic calculation of mipmap levels 1-3 (0=use MIPTBP1)
  19-20   L parameter
  32-43   K parameter (signed fixed-point, 7 bits whole, 4 bits fractional)

16h/17h TEX2_1/2
  20-25   Texture format (same as TEX0)
  37-50   CLUT base pointer in words/64
  51-54   CLUT format
  55      CLUT uses CSM2 (0=CSM1)
  56-60   CLUT entry offset/16
  61-63   CLUT cache control (same as TEX0)
TEX2 is a subset of TEX0. This is useful for modifying texture format and CLUT information when the texture base pointer, width, etc. must stay the same.

3Fh TEXFLUSH
Writing any value to this register will invalidate the texture cache. Do this in the following situations:
- Using newly transferred texture data
- Using newly transferred CLUT data or reloading the CLUT cache
- Using framebuffer or zbuffer data as textures

Texture coordinates
The GS supports STQ texture coordinates and UV texel coordinates. The relationship between the two is as follows.
U=(S/Q)*TEX0.texwidth
V=(T/Q)*TEX0.texheight

Q is defined by RGBAQ and used for perspective correction. It is not possible to use perspective correction when directly using UV coordinates.

02h ST
  0-31    S (lower 8 bits are rounded down to zero)
  32-63   T (lower 8 bits are rounded down to zero)
S and T are (mostly) IEEE 754-compliant single-precision floating-point values. For both S and T, the range [0.0, 1.0] refers to the whole texture.

03h UV
  0-13    U
  16-29   V
U and V are unsigned 10-bit fixed-point integers with a 4-bit fractional component. The ranges [0, TEXWIDTH] and [0, TEXHEIGHT] for U and V respectively refer to the whole texture.

Color function TEX0 defines four possible color functions that can be used to blend texture and vertex colors:
               RGB                            RGBA
  Modulate     Rv = (Rv * Rt) >> 7            <- Same
               Gv = (Gv * Gt) >> 7            <- Same
               Bv = (Bv * Bt) >> 7            <- Same
               Av = Av                        Av = (Av * At) >> 7
  Decal        Rv = Rt                        <- Same
               Gv = Gt                        <- Same
               Bv = Bt                        <- Same
               Av = Av                        Av = At
  Highlight    Rv = ((Rv * Rt) >> 7) + Av     <- Same
               Gv = ((Gv * Gt) >> 7) + Av     <- Same
               Bv = ((Bv * Bt) >> 7) + Av     <- Same
               Av = Av                        Av = At + Av
  Highlight2   Same as Highlight              Same as Highlight, but Av = At


  GS Fog

3Dh FOGCOL
  0-7   R
  8-15  G
  16-23 B
FOGCOL represents the color of a "distant" object, or one enshrounded in fog. See below for details.

0Ah FOG
  0-7  Fog effect
FOG gives a fog effect F to the current vertex. XYZ2F/XYZ3F also modify this register.
During rasterization, if fog is enabled in PRIM/PRMODE, F is linearly interpolated for all vertices. The fog equation is then applied to an outputted texture color after the color function has been applied:
Output = ((F * input) >> 8) + (((255 - F) * FOGCOL) >> 8)
So a value of 0xFF results in no change and a value of 0 completely converts the color to FOGCOL.
You can see that fog equation is just another alpha blending equation.


  GS Alpha Blending

42h/43h ALPHA_1/2
  0-1   Spec A
  2-3   Spec B
  4-5   Spec C
  6-7   Spec D
  8-15  Alpha FIX
The GS's alpha blending formula is fixed but it contains four variables that can be reconfigured:
Output = (((A - B) * C) >> 7) + D
A, B, and D are colors and C is an alpha value. Their specific values come from the ALPHA register:
      A                B                C                   D
  0   Source RGB       Source RGB       Source alpha        Source RGB
  1   Framebuffer RGB  Framebuffer RGB  Framebuffer alpha   Framebuffer RGB
  2   0                0                FIX                 0
  3   Reserved         Reserved         Reserved            Reserved
Internally, alpha-blending treats each color component as 9-bit. The output is then clamped accordingly by COLCLAMP.

46h COLCLAMP
  0    8-bit signed clamp (0=8-bit AND)
When COLCLAMP is 1, RGB components will be 0 if negative after alpha-blending or 0xFF if 0x100 or above. Otherwise, each color component will be ANDed with 0xFF.


  GS Tests and Pixel Control

40h/41h SCISSOR_1/2
  0-10   X0
  16-26  X1
  32-42  Y0
  48-58  Y1
SCISSOR defines a rectangle with 11-bit unsigned integer coordinates, which range from 0 to 2047. During drawing, pixels that fall outside the boundaries of this rectangle fail automatically and are not processed.

47h/48h TEST_1/2
  0      Alpha test enabled
  1-3    Alpha test method
         0=NEVER (all pixels fail)
         1=ALWAYS (all pixels pass)
         2=LESS (pixel alpha < AREF passes)
         3=LEQUAL (pixel alpha <= AREF passes)
         4=EQUAL (pixel alpha == AREF passes)
         5=GEQUAL (pixel alpha >= AREF passes)
         6=GREATER (pixel alpha > AREF passes)
         7=NEQUAL (pixel alpha != AREF passes)
  4-11   AREF
  12-13  Alpha test failure processing
         0=Neither framebuffer nor zbuffer are updated.
         1=Only framebuffer is updated.
         2=Only zbuffer is updated.
         3=Only RGB in framebuffer is updated.
  14     Destination alpha test enabled
  15     Destination alpha test method
         0=destination alpha bit == 0 passes
         1=destination alpha bit == 1 passes
  16     Depth test enabled (0 is prohibited?)
  17-18  Depth test method
         0=NEVER (all pixels fail)
         1=ALWAYS (all pixels pass)
         2=GEQUAL (pixel Z >= zbuffer Z passes)
         3=GREATER (pixel Z > zbuffer Z passes)
Note on destination alpha test:
The alpha bit tested depends on the framebuffer format. If the format is PSMCT32, bit 7 of alpha is tested. If the format is PSMCT16, the sole alpha bit is tested. If the format is PSMCT24, all pixels pass due to the lack of alpha.


  GS Special Effects and Optimizations

The GS has an absurdly high fillrate of 1.2 gigapixels/second when drawing textured polygons. This advantage is further compounded by free (!) alpha blending and depth buffering. However, the GS only supports single-pass texturing and is entirely fixed-unit. The combination of these factors led developers to find creative ways to make their games look good...
Note: many of these effects don't have an official name, so they are made up.

Fast Screen Draw
Drawing a single sprite that covers the entire screen is relatively slow. First, this causes many DRAM page breaks per scanline drawn. Second, for textured sprites, the texture cache also has to be reloaded many times.
Instead, one can split a screen draw into many 64x32 sprites. This greatly improves cache locality, based upon the GS's nonlinear mmemory.
The fast screen draw is often used as a building block for other advanced techniques.

Double Half Clear
Because the framebuffer and depth buffer both occupy the same memory space, framebuffer clears can be optimized by placing the start of the depth buffer halfway down the screen and halving the height of the clear draw. As depth buffering is free, this will result in the clear being around twice as fast.

Interleaved Clear
Similar to the double half clear in principle, but takes advantage of color and depth formats being swizzled differently. The depth buffer base is set to the framebuffer base (ZBP=FBP), and instead of the clear being split into 64x32 sprites, it is split into 32x32 sprites instead. This results in the clear being interleaved, as the color clear touches the left side of a 64x32 block and the depth clear touches the right side. Because the framebuffer and depth buffer share the same cache, this should be faster than a double half clear.

Powerdrome uses a crazy variant of this, where the framebuffer is given a Z format. Due to quirks in the GS rendering process, this causes the depth clear to draw as if it had a color format. This doesn't really speed anything up, as all that changes is the order of the interleave, but it does make emudevs cry a lot.

VIS Clear
Absolutely insane clear used in the VIS Games engine. The framebuffer width (FBW) is set to 1 (64 pixels for a 32-bit texture), then a very thin and tall sprite is drawn. In normal cases, the sprite is 64x2048. The clear is essentially if one took all the pages from the framebuffer, stacked them vertically, then drew a sprite large enough to clear the stack. This should avoid the horizontal page breaks that other methods cause, but it is unknown how fast this clear is.

Superman Returns, another VIS game, uses a somehow wilder variant, where the clear sprite is 32x4096. The sprite is so large, it wraps around from the bottom of VRAM to the top.

Recursive Drawing
Draws where the texture buffer and framebuffer overlap, usually done with TBP = FBP. Because of the texture cache, texels do not get overwritten in VRAM until after they are read. Recursive drawing can be used to apply special effects without having to create a temporary buffer in memory.

Channel Shuffle
A sophisticated technique made to overcome the GS's lack of shader units. At a high level, the most basic channel shuffle effect uses a texture's 32-bit RGBA channels as 8-bit palette indices to apply an effect on the texture, such as gamma correction or a brightness effect.

To perform the channel shuffle, draw a series of 8x2 sprites that read the 32-bit texture as 8-bit - normally this is done with recursive drawing, but temporary buffers can also be used. The size of the sprites is important, as 32-bit and 8-bit textures are swizzled differently. The sprites will read from a palette which applies the effect, then FBMASK is used to restrict the draw to a single channel. This must be repeated for every channel the effect is to be applied to, with a total of four shuffles for all four channels.

This effect is extremely difficult to emulate with a modern GPU. A 640x224 framebuffer requires 8960 8x2 sprites for a single shuffle. The 32-bit texture must be deswizzled, converted to 8-bit, and reconverted back to 32-bit for each sprite. Since this is usually done with recursive drawing, texture memory and framebuffer memory on the host GPU also needs to be synchronized. Finally, modern GPUs mask on the byte level, but games can mask on the bit level, which requires a slow fragment shader to process the effect correctly if the game doesn't fully mask on the byte level. All this results in a channel shuffle being more expensive than even every other draw call in the game. Other variants of channel shuffle also exist that involve reading the depth buffer, and some shuffle effects are not well-understood.


  Vector Unit (VU)

The EE contains two Vector Units (VU0 and VU1), custom SIMD processors designed for fast floating-point manipulation.
Both VUs can run concurrently with the EE Core in micro mode. VU0 is also available in macro mode as COP2.
The architecture is similar to MIPS, including branch delays and the use of register zero as a constant.
VU Registers
VU Instruction Format
VU FMAC Pipeline


  VU Registers

General-purpose registers
Each VU contains 32 vector floating-point registers (vf00-vf31) and 16 16-bit integer registers (vi00-vi15).
A vector register has 4 32-bit single-precision floating-point elements: {x, y, z, w}
vi00 is hardwired to 0. vf00 is hardwired to the vector {0.0, 0.0, 0.0, 1.0}. That is to say, vf00.w = 1.0.

Accumulator (ACC)
The accumulator is a special register with the same format as the 32 general-purpose vector registers.
It is intended to be used as an intermediate result by certain instructions.

Q and P
Q and P are 32-bit floating point registers. Operations that use the FDIV or EFU units store their result in the Q and P registers respectively.
For example, a DIV stores its result in Q, and ESQRT stores its result in P.

MAC Flags
Each FMAC unit (corresponding to a vector field) has four MAC flags, which can be read by various instructions.
The MAC flags are overflow, underflow, sign, and zero. The full 16-bit MAC flag register has this format:
15                                              0
-------------------------------------------------
|Ox|Oy|Oz|Ow|Ux|Uy|Uz|Uw|Sx|Sy|Sz|Sw|Zx|Zy|Zz|Zw|
-------------------------------------------------

Zero is set when the result is 0.0f or -0.0f (which also clears overflow and underflow), and sign is set when the result is negative (bit 31 is set).
Overflow and underflow are set when the exponent field is 0xFF or 0x0 respectively.
IMPORTANT: MAC flags are modified in the "writeback" stage in the FMAC pipeline. However, an instruction like FMAND will read the flags in the register read stage, and this does not constitute a hazard.
This means every modification to the MAC flags has a delay of four cycles (not necessarily four instructions).

Clip Flags
The clipping flags register is 24-bit and contains the result of up to four CLIP instructions. Every CLIP instruction produces six flags: -x, +x, -y, +y, -z, +z. They are stored in the following format:
5                 0
-------------------
|-z|+z|-y|+y|-x|+x|
-------------------

Like the MAC flags, the clip flags are also pipelined (and thus delayed).

Status Flags
todo


  EE Interrupt Controller (INTC)

The EE has two separate interrupt signals: INT0 (raised by INTC) and INT1 (raised by DMAC).

1000F000h INTC_STAT - Interrupt status register (R=Status, W=Acknowledge)
1000F010h INTC_MASK - Interrupt mask register (R/W)
Status: Read INTC_STAT (1=IRQ raised)
Acknowledge: Write INTC_STAT (0=No effect 1=Clear bit)
Mask: Write INTC_MASK (0=No effect, 1=Reverse)
  0     IRQ0   GS interrupt
  1     IRQ1   SBUS
  2     IRQ2   VBLANK start
  3     IRQ3   VBLANK end
  4     IRQ4   VIF0
  5     IRQ5   VIF1
  6     IRQ6   VU0
  7     IRQ7   VU1
  8     IRQ8   IPU
  9     IRQ9   Timer 0
  10    IRQ10  Timer 1
  11    IRQ11  Timer 2
  12    IRQ12  Timer 3
  13    IRQ13  SFIFO
  14    IRQ14  VU0 Watchdog

When (INTC_STAT & INTC_MASK), INT0 is asserted on COP0.Cause:8. When COP0.Status:8 is true, an interrupt occurs, and the EE jumps to 80000200h.


  IOP Hardware and Peripherals

CDVD Drive
PS2 Serial Port (SIO2)
IOP Interrupts
IOP DMA
IOP Timers


  CDVD Drive

Using the CDVD drive, the PS2 has the ability to read CDROMs, single-layer DVDs, and dual-layer DVDs. It also sports backwards compatibility with the PSX's CDROM drive in PSX mode.

CDVD commands are either asynchronous (N commands) or synchronous (S commands). Seeks and reads fall into the former category, and miscellaneous commands, such as RTC access, fall into the latter.

CDVD Reference
CDVD I/O Ports
CDVD N Commands
CDVD Reads and Seeks
CDVD S Commands


  CDVD I/O Ports

1F402004h Current N command (R/W)
Write to this register to send an N command. For a list of N commands, see
CDVD N Commands

1F402005h N command status (R)
  0     Unused
  1-3   Ready?
  4-5   Unused
  6     Ready
  7     Unused
This shows if the CDVD drive is ready to receive an N command. When bit 6 (0x40) is on, an N command can be sent. Oddly, bits 1-3, when set, also seem to indicate ready status?

1F402005h N command param (W)
Send parameters for an N command here. This must be done BEFORE the N command has been sent via 1F402004h.

1F402006h CDVD error (R)
Any non-zero value indicates an error? Unknown what kind of errors are possible and what their values are.

1F402007h BREAK
Writing any value to this register sends a BREAK command to the CDVD drive, stopping execution of the current N command.

1F402008h CDVD I_STAT (R=Status, W=Acknowledge)
Status = Read I_STAT (1=Reason for IRQ)
Acknowledge = Write I_STAT (1=Clear bit)
  0     Data ready?
  1     (N?) Command complete
  2     Power off pressed
  3     Disk ejected
  4     BS_Power DET?
  5-7   Unused
When a CDVD IRQ is raised on I_STAT, this register shows the reason for the interrupt. Bit 0 seems to be raised when a read command completes, but I'm not certain about this...
Unknown if bit 1 only applies to N commands, although this appears to be the case.

1F40200Ah CDVD drive status (R)
  00h  Stopped
  02h  Spinning
  06h  Reading
  0Ah  Paused

1F40200Fh CDVD disk type (R)
  00h  None?
  12h  CDROM
  14h  DVD
  Unknown what other types are possible.
1F402016h Current S command (R/W)
Write to this register to send an S command. For a list of S commands, see
CDVD S Commands

1F402017h S command status (R)
40h indicates that the CDVD drive can receive an S command. 00h means that it is busy.

1F402018h S command result (R)
When an S command has finished executing, read the result here. Some S commands may require multiple reads.

1F402018h S command params (W)
Parameters must be sent BEFORE the S command is sent.


  CDVD N Commands

Parameters are in units of bytes and are little-endian. All N commands raise IRQ2 (bit 1 of CDVD I_STAT).

00h NOP
01h NOPsync
Params: None.
These commands do nothing? They do raise an IRQ upon their "completion".

02h Standby
Params: None.
Returns the read position to sector 0 and sets the drive status to PAUSED.
Possibly also spins the drive if it's not spinning already?

03h Stop
Params: None.
Returns the read position to sector 0, sets the drive status to STOPPED, and stops the drive from spinning.
Seems to have a 166 ms delay?

04h Pause
Params: None.
Unknown what effect this has, from the perspective of emulation.

05h Seek
Params:
  0-3   Sector position
Moves the read position to the indicated parameter.

06h ReadCd
Params:
  0-3   Sector position
  4-7   Sectors to read
  10    Block size (1=2328 bytes, 2=2340 bytes, all others=2048 bytes)
Performs a CD-style read. Seems to raise bit 0 of CDVD I_STAT upon completion?

08h ReadDvd
Params:
  0-3   Sector position
  4-7   Sectors to read
Performs a DVD-style read, with a block size of 2064 bytes. The format of the data is as follows:
  0    1    Volume number + 0x20
  1    3    Sector number - volume start + 0x30000, in big-endian.
  4    8    ? (all zeroes)
  12   2048 Raw sector data
  2060 4    ? (all zeroes)

09h GetToc
Params: None? Fetches the ToC from the disk, with a block size of 2064 bytes.


  CDVD Reads and Seeks

Seeking
If a read command is called, a seek must be performed. An IRQ is NOT raised when the seek finishes during a read command.
When a seek begins, if the drive is currently not spinning, it takes 333 ms for the drive to spin and finish the seek.
Otherwise, one of three seek modes is possible:
- Contiguous read: When the seek delta is very small or zero, seek time = block_timing * delta
- Fast seek: When seek delta < 14764 for DVD reads and < 4371 for CD reads, seek time = ~30 ms
- Full seek: Seek time = ~100 ms
block_timing (in IOP cycles) can be found by the following formula:
  block_timing = (IOP_CLOCK * block_size) / read_speed
Where IOP_CLOCK is ~36,864,000 Hz. read_speed for CD reads is 24 * 153600. For DVD reads read_speed = 4 * 1382400.
Unknown what delta is needed for a contiguous read (8/16 is used for CD/DVD respectively in PCSX2).

Reads
The time needed to read a single sector is the block_timing formula above. Once one sector has been read, the CDVD DMA channel can store the data in memory and allow the CDVD drive to continue.
When all sectors have been read, a CDVD IRQ is raised. Successful reads seem to raise both bits 1 AND 0 of CDVD I_STAT?


  CDVD S Commands

The results of an S command can be read one at a time from 1F402017. All units are in bytes.

08h ReadRTC
Params: None. Result:
  0     Zero
  1     Second
  2     Minute
  3     Hour
  4     Zero
  5     Day
  6     Month
  7     Year
Returns the current time stored on the RTC, in BCD format.
The RTC is mostly just used by the BIOS. Notably, Metal Gear Solid 3 requires the RTC to boot. This is likely an anti-piracy feature.

09h WriteRTC
Params:
  0     Ignored
  1     Second
  2     Minute
  3     Hour
  4     Ignored
  5     Day
  6     Month
  7     Year
Overwrites the RTC's time in BCD format.

  PS2 Serial Port (SIO2)

SIO2 provides access to controllers and memory cards in PS2 mode, as well as other peripherals. While following a similar principle as the PSX's serial port (called SIO0 here), SIO2 is completely different in design.

SIO2 Reference
SIO2 Registers
SIO2 PS2 Memcards


  SIO2 Registers

A good portion of SIO2 is a mystery. Having no documentation whatsoever, its behavior has to be inferred from how programs use it.

1F808200-1F808240 SIO2_SEND3 - Command Parameters
  0-1   Port
  8-16  Fake command length, not used by SIO2
  17-24 Real command length (counting peripheral byte)
  Other Unknown
SEND3 is an array of up to 16 different SIO2 commands. A command consists of a peripheral byte that selects to what the command is being sent, a command sent to the peripheral, and an arbitrary amount of data.

Known peripheral bytes:
1F808240-1F808260 SIO2_SEND1/SEND2 - Port1/2 Control?
Unknown purpose.
When bit 2 of the address is set, SEND2 is accessed. Otherwise, SEND1 is accessed.

1F808260 SIO2_FIFOIN - Data Write
A one-byte register used to upload commands to SIO2. The SIO2in DMA channel also writes to this register.

1F808264 SIO2_FIFOOUT - Data Read
Used to read replies and data from SIO2 peripherals after a command is sent. SIO2out reads from this register.

1F808268 SIO2_CTRL - Control Register
Bit 0 seems to start the command transfer. An SIO2 interrupt is raised after touching this bit, presumably when the command has completed.
Bits 2 and 3 reset SIO2, preparing it for another transfer.
Rest of the bits are unknown. They probably control bandwidths and interrupt masking, at the very least.

The SIO2MAN module in the BIOS sets this register to 3BCh on a reset.

1F80826C SIO2_RECV1 - Response Status 1 (R)
Set after a transfer, indicating if the peripheral is connected.
If (RECV1 & F000h) == 1000h after a memory card command, the memory card is connected. Else, disconnected.
If (RECV1 & 2000h) == 0 after a pad command, the pad is connected.

Known value for a disconnected peripheral is 1D100h. Known value for a connected peripheral is 1100h.

1F808270 SIO2_RECV2 - Response Status 2 (R)
Read by PADMAN. Always equal to 0xF?

1F808274 SIO2_RECV3 - Response Status 3 (R)
Unknown.

SIO2MAN Program Flow For Transfer
  Write CTRL | 0Ch to CTRL.
  Write data to SEND1 and SEND2, then write data to SEND3.
  Write data to DATAIN if applicable, then start SIO2in and SIO2out DMA transfers if applicable.
  Write CTRL | 01h to CTRL, then wait for an SIO2 interrupt.
  After interrupt, read RECV1, RECV2, and RECV3, then read DATAOUT if applicable.


  SIO2 PS2 Memcards

Commands
11h - Probe
  Command: 11 + 1 XX byte
  Reply: 2B + Terminator
  Length: 2 bytes
First command sent when trying to detect a card.
The terminator defaults to 55h on reset. Newer versions of MCMAN check for this.

12h - Unk12
  Command: 12 + 1 XX byte
  Reply: 2B + Terminator
  Length: 2 bytes
Unknown purpose. Sent on write/erase commands. Maybe some sort of flush?

21h - Start Erase
22h - Start Write
23h - Start Read
  Command: 21 + 4-byte sector address + 2 XX bytes
  Reply: 5 XX bytes + 2Bh + Terminator
  Length: 7 bytes
These commands prepare read/write/erase sector operations by providing a starting address in sectors.

26h - Get Specs
  Command: 26 + 10 XX bytes
  Reply: 2B + 2-byte sector size in bytes + 2-byte erase block size + 4-byte sector count + 1-byte checksum + Terminator
  Length: 11 bytes
Retrieves the card's capabilities. The total size of the memory card is (sector size + 16) * sectors (the 16 is space for error correction).
The checksum is an XOR between the individual bytes of sector size, erase blocks, and sectors.
A standard Sony 8 MB memory card will report the following.
  2B 00 02 10 00 00 40 00 00 52
That is to say, a sector size of 512 bytes, an erase block page count of 16 sectors, and a sector count of 16,384.

27h - Set Terminator
  Command: 27 + 1-byte new terminator + 1 XX byte
  Reply: XX + 2B + Old terminator
  Length: 3 bytes

28h - Get Terminator
  Command: 28 + 2 XX bytes
  Reply: 2B + Terminator + 55
  Length: 3 bytes

42h - Write Data
  Command: 42 + 1-byte write size + 128 data bytes + 2 XX bytes
  Reply: 2B + Terminator + 129 XX bytes + Terminator
  Length: 132 bytes
Writes up to 128 bytes at the memory card address given by command 22h. 128 data bytes must be sent, though anything more than the given size is ignored.

43h - Read Data
  Command: 42 + 1-byte read size + 130 XX bytes
  Reply: 2B + Terminator + 128 data bytes + 1-byte XOR checksum + Terminator
  Length: 132 bytes
Reads up to 128 bytes at the address given by command 23h. The checksum is applied to the amount of bytes read.

81h - Read/Write End
  Command: 81 + 1 XX byte
  Reply: 2B + Terminator
  Length: 2 bytes
Sent at the end of a read or write. Unknown purpose.

82h - Erase Block
  Command: 82 + 1 XX byte
  Reply: 2B + Terminator
  Length: 2 bytes
Erases data at the address given by command 21h by setting it all to FFh. On a standard 8 MB card, this would be (512 + 16) * 16 bytes of data.

BFh - UnkBF
  Command: 82 + 2 XX bytes
  Reply: 1 XX byte + 2B + Terminator
  Length: 3 bytes
Used during card detection.

F0h - AuthXorF0
  Command: F0 + 1-byte param + variable
  Reply: 1 XX byte + 2B + Variable + Terminator
  Length: 12 bytes
This strange command is used by SECRMAN while detecting the card and does some XOR checksumming.
(todo: better explanation)

F3h - AuthF3
  Command: F3 + 2 XX bytes
  Reply: 1 XX byte + 2B + Terminator
  Length: 3 bytes
Sent by SECRMAN. Unknown purpose.

F7h - AuthF7
  Command: F7 + 2 XX bytes
  Reply: 1 XX byte + 2B + Terminator
  Length: 3 bytes
Sent by SECRMAN. Unknown purpose.


  IOP Interrupts

Interrupt handling for the IOP is similar to its PSX counterpart. The main differences are an additional register (I_CTRL) and more interrupt lines.

1F801070h I_STAT - Interrupt status register (R=Status, W=Acknowledge)
1F801074h I_MASK - Interrupt mask register (R/W)
Status: Read I_STAT (1=IRQ raised)
Acknowledge: Write I_STAT (0=Clear bit 1=No effect)
Mask: Read/Write I_MASK (0=Disabled 1=Enabled)
  0     IRQ0   VBLANK start
  1     IRQ1   GPU (used in PSX mode)
  2     IRQ2   CDVD Drive
  3     IRQ3   DMA
  4     IRQ4   Timer 0
  5     IRQ5   Timer 1
  6     IRQ6   Timer 2
  7     IRQ7   SIO0
  8     IRQ8   SIO1
  9     IRQ9   SPU2
  10    IRQ10  PIO
  11    IRQ11  VBLANK end
  12    IRQ12  DVD? (unknown purpose)
  13    IRQ13  PCMCIA (related to DEV9 expansion slot)
  14    IRQ14  Timer 3
  15    IRQ15  Timer 4
  16    IRQ16  Timer 5
  17    IRQ17  SIO2
  18    IRQ18  HTR0? (unknown purpose)
  19    IRQ19  HTR1?
  20    IRQ20  HTR2?
  21    IRQ21  HTR3?
  22    IRQ22  USB
  23    IRQ23  EXTR? (unknown purpose)
  24    IRQ24  FWRE (related to FireWire)
  25    IRQ25  FDMA? (FireWire DMA?)
  26-31 Unused/garbage

1F801078h I_CTRL - Global interrupt control (R=Status and Disable, W)
  0     Disable all interrupts
  1-31  Unused/garbage
When bit 0=1, all IOP interrupts are disabled. Reading this register returns bit 0 AND clears it, re-enabling interrupts. Writing can set or reset bit 0.
NOTE: There seems to be a 4 cycle delay when re-enabling interrupts via this register.

Raising Interrupts
If !I_CTRL && (I_STAT & I_MASK), then COP0.Cause:8 is set. When COP0.Status:8 is also set when this occurs, COP0.Cause.Excode is set to 00h and the IOP jumps to 80000080h, where the interrupt will be processed.


  IOP DMA

The IOP re-uses the same DMA channels as found in the PSX, and it contains additional channels for new peripherals.

Channels
  Old channels
  1F80108xh    MDECin
  1F80109xh    MDECout
  1F8010Axh    SIF2 (EE<->IOP, GPU in PSX mode)
  1F8010Bxh    CDVD (CDROM in PSX mode)
  1F8010Cxh    SPU1
  1F8010Dxh    PIO
  1F8010Exh    OTC
  
  New channels
  1F80150xh    SPU2
  1F80151xh    DEV9 (expansion port)
  1F80152xh    SIF0 (IOP->EE, uses TADR)
  1F80153xh    SIF1 (EE->IOP)
  1F80154xh    SIO2in
  1F80155xh    SIO2out


  IOP Timers

1F801100h+N*10h Timer 0..2 count (R/W)
  0-15  Current value
  16-31 Unused/garbage
1F801480h+(N-3)*10h Timer 3..5 count (R/W)
  0-31  Current value
Timers 0..5 increment automatically. 0..2 are 16-bit, and 3..5 are 32-bit. Writes set the counter to the value written.

1F801104h+N*10h Timer 0..2 mode (R/W)
1F801484h+(N-3)*10h Timer 3..5 mode (R/W)
  0     Gate enable
  1-2   Gate mode
  3     Zero return - reset counter on interrupt
  4     Compare interrupt enabled
  5     Overflow interrupt enabled
  6     Repeat interrupt - if unset, bit 10 is set to 0 after interrupt occurs.
  7     LEVL - toggle bit 10 on IRQs if bit 6 is set.
  8     Use external signal
        If set:
          Timer 0: pixel clock (13.5 MHz regardless of screen mode)
          Timer 1/3: HBLANK
          Others: sysclock (no effect)
  9     Timer 2 prescaler
  10    Interrupts enabled (R)
  11    Compare interrupt raised (R)
  12    Overflow interrupt raised (R)
  13-14 Timer 4/5 prescalar
  15-31 Unused/garbage
Writes to mode reset the count to zero and set bit 10 to 1. Reads from mode clear the two raised interrupt flags.
Prescalers adjust clockrate as follows:
  0     normal
  1     1/8 speed
  2     1/16 speed
  3     1/256 speed

1F801108h+N*10h Timer 0..2 target (R/W)
  0-15  Value
  16-31 Unused/garbage
1F801488h+(N-3)*10h Timer 3..5 target (R/W)
  0-31  Value
When count == target, a compare interrupt is raised. This raises an IRQ in I_STAT if both mode.4 and mode.10 are enabled.
If mode.7 (LEVL) is not set, writes to target set mode.10 to 1.


  Subsystem Interface (SIF)

The SIF is how the EE and IOP communicate with each other. SIF has some hardware registers that the CPUs can use to pass values to each other, which happens during SIF initialization. However, once both sides have booted, they use the SIF0 (IOP->EE) and SIF1 (EE->IOP) DMA channels to communicate.

SIF Registers
SIF RPC Basics
SIF RPC Structs and Definitions
SIF RPC Commands
SIF RPC System Servers


  SIF Registers

Note: EE base is at 1000F200h, IOP base is at 1D000000h.

1000F200h/1D000000h SIF_MSCOM (Only writable by EE)
1000F210h/1D000010h SIF_SMCOM (Only writable by IOP)
  0-31  Value
These registers could be used for generic communication, but in the PS2 they are used to tell the other side the address of its DMA receive buffer. For example, the EE places its SIF0 receive address in MSCOM so that the IOP knows where to transfer data to, and the opposite is true for SMCOM.

1000F220h/1D000020h SIF_MSFLG (IOP writes mask)
1000F230h/1D000030h SIF_SMFLG (EE writes mask)
  0-31  Value
These registers are used like semaphores. The following values are used:
  10000h: SIF DMA/hardware initialized
  20000h: SIFCMD initialized
  40000h: IOP has finished booting (sent by EESYNC)
If the EE sends 20000h to MSFLG, the IOP can read this and clear MSFLG by writing 20000h to it. The opposite is true for SMFLG.

1000F240h/1D000040h SIF_CTRL
  1     Always 1?
  8     Always 1 for EE, 0 for IOP?
  28-31 Always 0xF?
  Other Unknown
Very little is known about this register.

1000F260h/1D000060h SIF_BD6
  0-31  Unknown
Nothing is known about this register, other than that it does get accessed during initialization.


  SIF RPC Basics

Broadly, the SIF protocol can be split into three different layers of abstraction, from lowest to highest:
Calling RPC functions
Assume that a game on the EE wants to open a file.
In more succinct terms, the EE first uses SifBindRpc to bind the server to the client, then it uses SifCallRpc to call an IOP function.

Registering an RPC server
Servers can be registered on both the EE and the IOP. In the vast majority of cases however, only IOP modules register servers.
The above creates a dedicated server thread, which can only be used for SIF RPC. If non-blocking execution is desired for whatever reason, one can use SifGetNextRequest and SifExecRequest.


  SIF RPC Structs and Definitions

Functions
  typedef void (*SifCmdHandler)(void *data, void *harg);
  typedef void* (*SifRpcFunc)(int fno, void *buff, int length);
  typedef void (*SifRpcEndFunc)(void *end_param);

Packet Headers
  struct SifCmdHeader
  {
    uint psize:8; //Size of the command packet
    uint dsize:24; //Size of the payload, if any
    void *dest; //Destination of the payload, if any
    int	cid; //Command ID
    uint opt;
  }
  struct SifRpcPktHeader
  {
    struct SifCmdHeader	sifcmd;
    int	rec_id;
    void *pkt_addr;
    int	rpc_id;
  }

Client
  struct SifRpcClientData
  {
    struct SifRpcHeader	hdr;
    u32	command;
    void *buff, *cbuff;
    SifRpcEndFunc end_function;
    void *end_param;
    struct SifRpcServerData *server;
  }

Server
  struct SifRpcServerData
  {
    int	sid;

    SifRpcFunc func;
    void *buff;
    int	size;

    SifRpcFunc cfunc;
    void *cbuff;
    int	size2;

    struct SifRpcClientData *client;
    void *pkt_addr;
    int	rpc_number;

    void *receive;
    int	rsize;
    int	rmode;
    int	rid;

    struct SifRpcServerData *link;
    struct SifRpcServerData *next;
    struct SifRpcDataQueue *base;
  }

Server Data Queue
  struct SifRpcDataQueue
  {
    int	thread_id, active;
    struct SifRpcServerData *link, *start, *end;
    struct SifRpcDataQueue *next;
  }

DMA Packet
  struct SifDmaTransfer
  {
    void *src, *dest;
    int size;
    int attr;
  }


  SIF RPC Commands

80000000h Change SADDR
  struct SifSaddrPkt
  {
    struct SifCmdHeader header;
    void* buff;
  }
This just changes the EE's receive buffer on the IOP side. Used when SifInitRpc is called more than once.

80000001h Set SREG
  struct SifCmdSRegData
  {
    SifCmdHeader header;
    int	index;
    uint value;
  }
Sets a software SIF register - that is, a variable in memory rather than an I/O register.
Used by the IOP during SIF RPC initialization to tell the EE what the IOP has stored for the EE's receive buffer.

80000002h SIFCMD Init
  struct SifInitPkt
  {
    struct SifCmdHeader header; //NOTE: The "opt" field is used by the IOP.
    void* buff;
  }
This command is sent twice by the EE during SIFRPC initialization, the first with opt=0 and the second with opt=1.
When opt=0, buff is used as the EE's SIF0 receive address, and the IOP sets SMFLG to 20000h.
When opt=1, the IOP finishes SIFRPC initialization.

80000003h Reboot IOP
  struct SifIopResetPkt
  {
    struct SifCmdHeader header;
    int	arglen; //Length of the command
    int	mode; //Bit 31 enables debug logging, unknown what other values do
    char arg[80]; //The command to be passed to MODLOAD
  }
See BIOS IOP REBOOT for details on the reboot procedure.

80000008h Request End
struct SifRpcRendPkt
{
   struct SifCmdHeader sifcmd;
   int rec_id;
   void	*pkt_addr;
   int rpc_id;
   struct SifRpcClientData *client;
   uint cid; //ID of the command sent by the other side (e.g. the command that triggered a REND)
   struct SifRpcServerData *server;
   void	*buff, *cbuff;
}

80000009h Bind
  struct SifRpcBindPkt
  {
     struct SifCmdHeader sifcmd;
     int rec_id;
     void *pkt_addr;
     int rpc_id;
     struct SifRpcClientData *client;
     int sid; //ID of the server
  }

8000000Ah Call
  struct SifRpcCallPkt
  {
     struct SifCmdHeader sifcmd;
     int rec_id;	
     void *pkt_addr;
     int rpc_id;
     struct SifRpcClientData *client;
     int rpc_number; //ID of the function to call on the server
     int send_size; //Size of data to send to the server
     void *receive; //Buffer to hold reply data from the server
     int recv_size; //Size of reply buffer
     int rmode;
     struct SifRpcServerData *server;
  }

8000000Ch Get other data
  struct SifRpcOtherDataPkt
  {
     struct SifCmdHeader sifcmd;
     int rec_id;
     void *pkt_addr;
     int rpc_id;

     struct SifRpcReceiveData *receive;
     void *src;
     void *dest;
     int size;
  }
Used by a server to request more data from a client, which replies with an END packet.


  SIF RPC System Servers

System server IDs have bit 31 set (e.g. 80000000h). Known system IDs are listed here. Note that games can install custom servers with any possible ID, as long as bit 31 is not set, so those are excluded from the list.

System Server IDs
80000001h - File I/O (FILEIO)
80000003h - IOP Heap Allocation (FILEIO)
80000006h - Module/ELF Loader (LOADFILE)
80000100h - Pad (PADMAN)
80000101h - Pad extension? (PADMAN)
80000400h - Memory cards (MCSERV)
80000592h - CDVD Init (CDVDFSV)
80000593h - CDVD S commands (CDVDFSV)
80000595h - CDVD N commands (CDVDFSV)
80000597h - CDVD SearchFile (CDVDFSV)
8000059Ah - CDVD Disk Ready (CDVDFSV)
80000701h - LIBSD Remote (SDRDRV, not in BIOS)
80000901h - MTAP Port Open (MTAPMAN, not in BIOS)
80000902h - MTAP Port Close (MTAPMAN)
80000903h - MTAP Get Connection (MTAPMAN)
80000904h - MTAP Unknown (MTAPMAN)
80000905h - MTAP Unknown (MTAPMAN)
80001400h - EyeToy (EYETOY, not in BIOS)


  BIOS

The PS2 BIOS resides in a 4 MB ROM. Its purpose is to initialize hardware into a usable state, provide EE services and IOP modules, and launch PS2/PSX games.

BIOS Reference
BIOS File Structure
BIOS Boot Process
BIOS EE Threading
BIOS EE Syscalls
BIOS PlayStation Compatibility

BIOS IOP Modules
Unlike the EE, the IOP does not have a monolithic kernel. Instead, the kernel and device drivers are split into many modules.
These modules are represented as IRX files, which are custom relocatable ELFs.

BIOS IOP Module Linking
BIOS List of IOP Modules


  BIOS File Structure

The BIOS consists of dozens of separate files. These files are indexed and accessed through ROMDIR.
Each ROMDIR entry has the following format.
  struct romdir_entry
  {
    char name[10]; //File name, must be null terminated
    ushort ext_info_size; //Size of the file's extended info in EXTINFO
    uint file_size; //Size of the file itself
  }
First four entries in an SCPH-39001 ROMDIR:
  0x00002740: 52455345 54000000 00000C00 40270000      RESET
  0x00002750: 524F4D44 49520000 00005400 D0050000      ROMDIR
  0x00002760: 45585449 4E464F00 00000000 80070000      EXTINFO
  0x00002770: 524F4D56 45520000 00000000 10000000      ROMVER
  ...
To find the start of ROMDIR, look for the first occurrence of "RESET" in the BIOS.
To find a specific file in the BIOS, find its entry in ROMDIR and then add the file sizes of all previous files to get the starting address.


  BIOS Boot Process

Upon reset, both the EE and IOP begin executing at BFC00000h. The BIOS checks which CPU is executing by checking COP0.PRid (register 15). If PRid >= 59h, the EE boot code is executed. Else, the IOP boot code is executed.

EE boot process
- The EE clock speed is measured. (SCPH-10000 uses COP0.Count; SCPH-39001 counts HBLANKs using one of the timers. Unknown what other BIOSes use.)
- The memory controller is initialized, giving access to the 32 MB of RDRAM.
- The EE kernel is copied into RAM starting at virtual address 80000000h. The BIOS jumps to the entry point at 80001000h.
- The TLB and PGIF handler are initialized and various hardware components are reset.
- EENULL, an idle loop thread, is loaded into 00081FC0h.
- SIF DMA is initialized. The EE writes to an SIF register and waits for a reply from the IOP.
- The EELOAD module is loaded, which in turn loads OSDSYS.

OSDSYS, once loaded, is responsible for bringing up the "Sony Computer Entertainment" screen and the browser.

IOP boot process
- Various hardware registers are initialized, and IOPBOOT is loaded.
- IOPBOOT finds SYSMEM and LOADCORE, loads them into memory, and executes their entry points.
- LOADCORE boots all of the IOP modules in the BIOS, starting with EXCEPMAN.
- At some point, SIF is initialized. The IOP waits for the EE to send a message and replies over another SIF register.
- Once all modules have been loaded, the IOP enters an infinite loop, waiting for the EE to send commands.


  BIOS EE Threading

The EE kernel uses a cooperative priority-based thread scheduler. It can support up to 256 threads and 256 semaphores. 128 priority levels are available.

Structs
  struct ThreadParam //Used as argument for CreateThread, ReferThreadStatus
  {
    int status;
    void *func; //function to execute when thread begins
    void *stack;
    int stack_size;
    void *gp_reg;
    int initial_priority;
    int current_priority;
    u32 attr;
    u32 option;
  }
  struct SemaParam //Used as argument for CreateSema
  {
    int count, //used by WaitSema and SignalSema
	      max_count,
	      init_count, //initial value for count
	      wait_threads; //number of threads associated with this semaphore
	  u32 attr, //not used by kernel
	      option; //not used by kernel
  }
  //Thread statuses
  #define THS_RUN 0x01
  #define THS_READY 0x02
  #define THS_WAIT 0x04
  #define THS_SUSPEND 0x08
  #define THS_WAITSUSPEND 0x0C //THS_WAIT | THS_SUSPEND
  #define THS_DORMANT 0x10
  struct TCB //Internal thread structure
  {
    struct TCB *prev;
    struct TCB *next;
    int status;
    void *func;
    void *current_stack;
    void *gp_reg;
    short current_priority;
    short init_priority;
    int wait_type; //0=not waiting, 1=sleeping, 2=waiting on semaphore
    int sema_id;
    int wakeup_count;
    int attr;
    int option;
    void *_func; //???
    int argc;
    char **argv;
    void *initial_stack;
    int stack_size;
    int *root; //function to return to when exiting thread?
    void *heap_base;
  }
  struct sema //Internal semaphore structure
  {
    struct sema *free; //pointer to empty slot for a new semaphore
    int count;
    int max_count;
    int attr;
    int option;
    int wait_threads;
    struct TCB *wait_next, *wait_prev;
  }

Thread Scheduler
The kernel uses an array of 128 doubly-linked lists for managing thread priorities. The general algorithm for the scheduler is as follows:
  void reschedule(uint32 EPC, uint32 stack)
    Set current thread entry function to EPC (instruction after exception)
    Set current thread stack base to stack
    Set current thread status to READY
    Loop through active thread priority list, starting from 0 (highest priority)
      If an active thread is found, set current thread to it and break
      If no active thread is found, print an error and call Exit(1)
    Set found thread's status to RUN and return its entry function and stack pointer
Scheduling is only invoked by syscalls and relies on absolute priority. Threads with lower priority will never run as long as there is an active thread with higher priority.

Semaphores
Threads that call WaitSema on a semaphore with a "count" of zero are placed in a WAIT state and removed from the active thread list.
Conversely, calling SignalSema re-adds a thread waiting on a semaphore to the active list.
Semaphores are useful for blocking execution of a thread until a task completes. Sony's official SIF protocol, for instance, use a semaphore to place the caller thread to sleep while the IOP processes a request. The caller thread is reactivated in the SIF interrupt handler.


  BIOS EE Syscalls

When a SYSCALL instruction is executed, the EE jumps to 80000180h and sets COP0.Cause to 08h. The kernel syscall handler retrieves the syscall number from the v1 register.
Negative syscalls use the current thread's stack for internal operations and are meant to be used in interrupt handlers. Positive syscalls use the kernel stack, and some of them can trigger thread reschedules.

List of EE Syscalls
01h void ResetEE(int reset_flag)
Resets EE components depending on which bits are set in reset_flag:
  0 - DMAC
  1 - VU1
  2 - VIF1
  3 - GIF
  4 - VU0
  5 - VIF0
  6 - IPU
NOTE: INTC is reset regardless of the flag's settings.

02h void SetGsCrt(bool interlaced, int display_mode, bool frame)
Initializes the PCRTC.

04h void Exit(int status)
Returns to the OSDSYS browser. Internally calls LoadExecPS2("rom0:OSDSYS", 1, "BootBrowser").

05h void _ExceptionEpilogue/RFU005()
Internal syscall used by kernel exception handlers. Returns to the user program.

06h void LoadExecPS2(const char* filename, int argc, char** argv)
Loads an ELF with the specified arguments and executes it. This function also clears all of the internal kernel state, such as threads and semaphores, before execution.
This function does not (and must not) return.

07h void ExecPS2(void* entry, void* gp, int argc, char** argv)
Clears all of the internal kernel state and creates a thread with priority 0 (main thread). The thread begins executing from the entry point.
This function does not (and must not) return.

10h int AddIntcHandler(int int_cause, int (*handler)(int), int next, void* arg, int flag)
Adds an interrupt handler with the specified cause to a queue. This function returns the id of the handler or -1 if the operation fails. int_cause corresponds to a bit in INTC_STAT/INTC_MASK. For example, int_cause=2 will register a VBLANK handler.
next is a previously registered handler id which this handler will be placed before. next==0 will place the handler in front of the queue, and next==-1 will place it at the back of the queue.
arg and flag are stored in the handler struct but don't seem to be used otherwise?

11h int RemoveIntcHandler(int int_cause, int handler_id)
Removes the handler associated with the interrupt cause. Returns -1 if the operation fails.

12h int AddDmacHandler(int dma_cause, int (*handler)(int), int next, void* arg, int flag)
Adds an interrupt handler for INT1 (DMAC) interrupts. Otherwise works the same as AddIntcHandler.

13h int RemoveDmacHandler(int dma_cause, int handler_id)
Works same as RemoveIntcHandler.

14h bool _EnableIntc(int cause_bit)
Enables the indicated bit in INTC_MASK. Returns true if this bit was set to 0 and false if it was already set to 1.

15h bool _DisableIntc(int cause_bit)
Disables the indicated bit in INTC_MASK. Returns true if this bit was set to 1 and false if it was already set to 0.

16h bool _EnableDmac(int cause_bit)
Same as _EnableIntc, but for D_STAT's mask.

17h bool _DisableDmac(int cause_bit)
Same as _DisableIntc, but for D_STAT's mask.

20h int CreateThread(ThreadParam* t)
Creates a thread with DORMANT status. The thread is not placed in the priority linked-list.
Only t->func, t->initial_priority, t->stack, t->stack_size, and t->gp_reg are used. The new thread re-uses the heap of its parent thread.
Returns the id of the newly created thread, or -1 if the function fails.

21h void DeleteThread(int thread_id)
Deletes a thread. The function only succeeds if the thread has DORMANT status and is not the current thread.

22h void StartThread(int thread_id, void* arg)
Adds a DORMANT thread to the active thread list and gives it READY status, forcing a thread reschedule. arg is intended to be used by the callee thread if it becomes the current thread.
The function fails if the thread is the current thread or is not DORMANT.

23h void ExitThread()
Removes the current thread from the active thread list, resets it, and gives it DORMANT status, forcing a thread reschedule.

24h void ExitDeleteThread()
Deletes the current thread, forcing a thread reschedule.

25h void TerminateThread(int thread_id)
Has different behavior depending on the thread's status:
  No status (0), RUN, DORMANT - No effect.
  READY - Removes thread from active thread list, resets it, and gives it DORMANT status, forcing a thread reschedule.
  WAIT, WAITSUSPEND - Same as READY but also decrements the thread's semaphore's "wait_threads" by one.
  All other statuses - Resets the thread and gives the thread DORMANT status, forcing a thread reschedule.

26h void iTerminateThread(int thread_id)
Same as TerminateThread, but does not force a thread reschedule. Used internally by TerminateThread.
Returns -1 if unsuccessful.

29h void ChangeThreadPriority(int thread_id, int priority)
Changes the thread's priority, forcing a thread reschedule. If thread_id==0, the current thread's id is used.
This function fails if the thread is DORMANT.

2Ah int iChangeThreadPriority(int thread_id, int priority)
Same as ChangeThreadPriority, but does not force a thread reschedule. Used internally by ChangeThreadPriority.
Returns -1 upon a failure.

2Bh void RotateThreadReadyQueue(int priority)

2Ch int _iRotateThreadReadyQueue(int priority)

2Dh void ReleaseWaitThread(int thread_id)
If the thread has WAIT status, it is given READY status and added to the active thread list. If it has WAITSUSPEND status, it is given SUSPEND status.
Both (and only) these cases force a thread reschedule. The reference to the thread's semaphore is also removed.

2Eh int iReleaseWaitThread(int thread_id)
Same as ReleaseWaitThread, but does not force a thread reschedule. Internally used by ReleaseWaitThread.
Returns -1 if unsuccessful.

2Fh int GetThreadId()
Returns the current thread's id.

30h int ReferThreadStatus(int thread_id, ThreadParam* status)
31h int iReferThreadStatus(int thread_id, ThreadParam* status)
Fills out the ThreadParam struct with the given thread's information. Returns -1 if unsuccessful.
The "i" variant performs exactly the same function.

32h void SleepThread()
If the current thread's "wakeup_count" is greater than zero, it is decremented. Else, if the thread status is RUN or READY, the current thread is removed from the active thread list and set to WAIT, forcing a thread reschedule.

33h void WakeupThread(int thread_id)
Has different behavior depending on the thread's status:
  WAIT (sleeping) - Set to READY and re-added to active thread list, forcing a thread reschedule.
  WAITSUSPEND (sleeping) - Placed in SUSPEND status, forcing a thread reschedule.
  READY, SUSPEND, WAIT/WAITSUSPEND (semaphore) - Increments "wakeup_count", forcing a thread reschedule.
  Other statuses - No effect.

34h int iWakeupThread(int thread_id)
Same as WakeupThread, but does not force a thread reschedule. Internally used by WakeupThread.
Returns -1 if unsuccessful.

35h int CancelWakeupThread(int thread_id)
36h int iCancelWakeupThread(int thread_id)
Resets the thread's "wakeup_count" to zero. Returns -1 if unsuccessful, or the old wakeup_count otherwise.

37h int SuspendThread(int thread_id)
38h int iSuspendThread(int thread_id)
Has different behavior depending on the thread's status:
  READY, RUN - Removed from active thread list and placed in SUSPEND status.
  WAIT - Placed in WAITSUSPEND status.
  All other statuses - No effect.
Returns -1 on failure or thread_id otherwise.
BUG: This function does NOT force a thread reschedule! If the current thread is suspended, it will continue to run!

39h void ResumeThread(int thread_id)
Has different behavior depending on the thread's status:
  SUSPEND - Placed in READY or RUN status and added to active thread list, forcing a thread reschedule.
  WAITSUSPEND - Placed in WAIT status, forcing a thread reschedule.
  All other statuses - No effect

3Ah int iResumeThread(int thread_id)
Same as ResumeThread, but does not force a thread reschedule. Internally used by ResumeThread.
Returns -1 if unsuccessful or thread_id otherwise.

3Bh void JoinThread()

3Ch void* InitMainThread/RFU060(uint32 gp, void* stack, int stack_size, char* args, int root)
Initializes the current thread. Returns the stack pointer of the thread.
If stack == -1, the stack pointer equals the end of RDRAM - stack_size. Else, it equals stack + stack_size.
This function should only be called before the program's main function.

3Dh void* InitHeap/RFU061(void* heap, int heap_size)
Initializes the current thread's heap. If heap == -1, the end of the heap resides at the thread's stack pointer. Else, the end of the heap is heap + heap_size.
Returns the end of the thread's heap.

3Eh void* EndOfHeap()
Returns the current thread's heap base.

40h int CreateSema(SemaParam* s)
Creates a semaphore. Returns the semaphore's id if successful and -1 if not.
Only s->init_count and s->max_count need to be specified. s->attr and s->option may also be specified.

41h int DeleteSema(int sema_id)
Deletes the semaphore, forcing a thread reschedule. Threads waiting on the semaphore will either be released or suspended, depending on their status.

42h int SignalSema(int sema_id)
Signals a semaphore. If a thread has called WaitSema on this semaphore, this forces a thread rescheduling. Otherwise, the semaphore's count is incremented by one.
Returns -1 if unsuccessful.

43h int iSignalSema(int sema_id)
Similar to SignalSema, except this does not reschedule threads. The semaphore's threads are simply re-added to the active thread list.
This function is called internally by SignalSema. Returns -1 if unsuccessful and -2 if the thread is released from its wait state.

44h void WaitSema(int sema_id)
If the semaphore's "count" variable > 0, count is decremented. Else, a thread rescheduling occurs, changing the active thread. The caller thread's status is set to WAIT.

45h int PollSema(int sema_id)
46h int iPollSema(int sema_id)
Decrements the semaphore's count variable. Returns -1 if unsuccessful.

64h void FlushCache(int mode)
  Modes of operation
  mode=0: Flush data cache (invalidate+writeback dirty contents to memory)
  mode=1: Invalidate data cache
  mode=2: Invalidate instruction cache
  All other modes invalidate both caches.

70h uint64_t GsGetIMR()
Returns the value of the privileged register GS_IMR.

71h void GsPutIMR(uint64_t value)
Sets GS_IMR to the value.

74h void SetSyscall/RFU116(int index, int address)
Replaces an entry on the syscall table with the specified address.
This function does not perform any bounds checking.

76h int SifDmaStat(unsigned int dma_id)
Returns a positive value if the SIF transfer is queued, 0 if the transfer is in progress, and a negative value if the transfer has completed.

77h unsigned int SifSetDma(SifDmaTransfer* trans, int len)
Low-level syscall for starting SIF1 transfers. The format of SifDmaTransfer is as follows.
  struct SifDmaTransfer //Internal semaphore structure
  {
    void* src; //EE source
    void* dest; //IOP destination
    int size; //Size in bytes
    int attr;
  }
The len parameter is how large the SifDmaTransfer array is. Multiple SIF1 transfers are queued by the kernel.
This function returns the ID of the current transfer. It does not wait for the transfer to complete.

78h void SifSetDChain()
Initializes the SIF0 channel by resetting QWC and setting CHCR to 184h (channel busy, chain mode, TTE bit in tag enabled).

7Bh void ExecOSD(int argc, char** argv)
Shorthand for LoadExecPS2("rom0:OSDSYS", argc, argv).

7Dh void PSMode()

7Eh int MachineType()

7Fh int GetMemorySize()
Returns the amount of RDRAM on the console in bytes.


  BIOS PlayStation Compatibility

EE Side
In PSX mode, the EE and GS work together to emulate the PSX GPU. PS1DRV, a standard ELF, handles this emulation.
The main emulation loop of PS1DRV acts as a translation layer between PSX and GS commands. It polls PGIF registers to know when a command has arrived.

After OSDSYS detects a PSX disc, it loads PS1DRV and jumps to it. The setup for PSX mode is as follows.
IOP Side
TBIN is the PSX BIOS driver stored in the PS2 BIOS. It lacks the shell and jumps directly to the PS logo when loading a game. Unknown what other differences exist compared to real PSX models.
RESET will jump to TBIN if COP0.PRid is less than 0Fh or if I/O register 1F801570.3 is set. Unknown which one is true after the PS2->PSX transition, or if both are true after that.
Unknown what TBIN means. A string in it says "PS compatible mode by M.T.", which most likely refers to a person. If that is the case, TBIN could mean T's Binary, whoever T is.

The most significant change when transitioning to PSX mode is that the IOP is underclocked to 33.8688 MHz, the original speed of the PSX. SPU2 also outputs samples at 44.1 KHz, rather than 48 KHz.

Hacks
The overhead of translating GPU commands, combined with the GS being far, far faster than the PSX GPU, means that the emulator will have different timings from a real PSX. Some PSX games are exceptionally sensitive to timings, so the emulator is able to apply various hacks. All hacks are listed below. Note that the hacks aren't actually given names, so they are made up here.

  BIOS IOP Module Linking

IOP modules are dynamically linked to each other, but Sony devised an ingenious way to handle this without exposing symbols.
Linking is accomplished through export tables and import tables. A module can have multiple export tables, which define the functions other modules may access. Import tables give a module access to another module's export table.

The format of an export table is as follows.
  struct export_table
  {
    uint magic; //Must equal 0x41C00000!
    export_table* next; //Internal data for LOADCORE
    ushort version; //0x101 would be version 1.01
    ushort mode; //Unknown what this does
    char name[8]; //Name of the module. Must include a NULL terminator.
    void* export[0]; //An arbitrarily sized array of function pointers. 
  }
The format of a import table is similar.
  struct import_table
  {
    uint magic; //Must equal 0x41E00000!
    import_table* next; //Internal data for LOADCORE
    ushort version;
    ushort mode;
    char name[8];
    void* import[0]; //An arbitrarily sized array of jr ra; addiu zero, zero, X instruction pairs. More on that below.
  }
LOADCORE is responsible for linking modules to each other. When a new module is loaded, LOADCORE parses its import tables. When an import table matches an export table, it looks up functions by taking X from addiu zero, zero, X, which is used as an index in the export's function pointer array. Each jr ra is then replaced with j $function. In other words, the return statement is replaced with a direct jump to an imported module's function.

Since functions are only linked by their IDs, rather than their names, in theory this keeps others from learning the names of the functions. In practice, game developers often created custom IOP modules which they forgot to strip, which is how the symbols in Sony's IOP modules are known.


  BIOS List of IOP Modules

The below IOP modules are loaded on reset.
BIOS IOP IOPBOOT - Kernel Bootstrap
BIOS IOP SYSMEM - Memory Management
BIOS IOP LOADCORE - Kernel Loader and Linker
BIOS IOP EXCEPMAN - Exception Manager
BIOS IOP INTRMAN - Interrupt Manager
BIOS IOP SSBUSC - Subsystem Bus Controller
BIOS IOP DMACMAN - DMAC Manager
BIOS IOP TIMRMAN - Timer Manager
BIOS IOP SYSCLIB - Standard C Library
BIOS IOP HEAPLIB - Heap Allocation Library
BIOS IOP EECONF - EE Configuration
BIOS IOP THREADMAN - Thread Manager
BIOS IOP VBLANK - VBLANK Interrupt Manager
BIOS IOP IOMAN - File Input/Output Manager
BIOS IOP MODLOAD - Module Loader
BIOS IOP ROMDRV - ROM File Driver
BIOS IOP STDIO - C Standard Input/Output
BIOS IOP SIFMAN - SIF Low-level Manager
BIOS IOP IGREETING - Boot Info Display
BIOS IOP SIFCMD - SIF RPC Manager
BIOS IOP REBOOT - SIF Reboot Server
BIOS IOP LOADFILE - Module Loader RPC Server
BIOS IOP CDVDMAN - CDVD Manager
BIOS IOP CDVDFSV - CDVD RPC Server
BIOS IOP SIFINIT - SIF Initialization
BIOS IOP FILEIO - File Input/Output RPC Server
BIOS IOP SECRMAN - Security Manager
BIOS IOP EESYNC - Boot Finish Messager

Below modules are not loaded at startup, though they are in the BIOS. Instead, the EE must manually load them.
BIOS IOP LIBSD - Low-level Sound Library
BIOS IOP SIO2MAN - SIO2 Manager
BIOS IOP MCMAN - Memory Card Manager
BIOS IOP MCSERV - Memory Card RPC Server
BIOS IOP PADMAN - Pad Input Manager


  BIOS IOP IOPBOOT - Kernel Bootstrap

IOPBOOT is technically not an IOP module, but rather, a raw binary file. It is responsible for parsing IOPBTCONF, loading SYSMEM and LOADCORE into memory, and passing execution to LOADCORE.

_entry(int ram_size, int boot_info, char *udnl_cmd, int unk)
The entry point is located at the start of IOPBOOT, BFC4A000h on SCPH-39001. ram_size is the total size of IOP RAM in megabytes. boot_info determines how IOPBOOT will find IOPBTCONF, more on that below. udnl_cmd is the full command string used to load UDNL and an IOPRP image. unk is passed but not used anywhere.

IOPBTCONF Format and Parsing
IOPBTCONF is the full list of modules that are loaded in the boot process, including SYSMEM and LOADCORE.
When IOPBOOT runs, it will first try to parse IOPBTCONx (where x is boot_info above), and if it can't find this, then it tries IOPBTCONF. On retail BIOSes, the only other file is IOPBTCON2, which is used to load modules after an IOP reboot. The regular IOPBTCONF is used for resets and the final phase of IOP reboot.

Each line in IOPBTCONF takes one of the following formats.
  1. @(START ADDR)
  2. The starting address of the kernel. IOPBOOT will place the first module at this address, and all other modules will be placed afterwards on 256-byte boundaries.
  3. !addr (FUNCTION ADDR)
  4. The function address here is executed while the modules are being loaded.
  5. !include (NAME)
  6. Acts like header file declarations, allowing another IOPBTCONF file to be parsed inside of the current one.
    NOTE: The parsing for this is buggy. Files can only be included at the start or end of the list, and only one file can ever be included.
  7. #(COMMENT)
  8. Lines starting with # are treated as comments and ignored.
  9. (MODULE NAME)
  10. An IOP module to be loaded during boot.

Example IOPBTCONF
This excerpt was pulled from an SCPH-39001 BIOS.
  @800
  SYSMEM
  LOADCORE
  EXCEPMAN
  INTRMANP
  INTRMANI
  SSBUSC
  DMACMAN
  TIMEMANP
  TIMEMANI
  ...


  BIOS IOP SYSMEM - Memory Management

Exports
04h void* AllocSysMemory(int mode, int size, void* ptr)

05h int FreeSysMemory(void* ptr)

06h uint QueryMemSize()

07h uint QueryMaxFreeMemSize()

08h uint QueryTotalFreeMemSize()

09h void* QueryBlockTopAddress(void* addr)

0Ah int QueryBlockSize(void* addr)

0Eh int Kprintf(const char* format,...)

0Fh void KprintfSet(KprintfHandler* handler, void* context)


  BIOS IOP LOADCORE - Kernel Loader and Linker

Exports
04h void FlushIcache()

05h void FlushDcache()

06h int RegisterLibraryEntries(export_table* t)

07h int ReleaseLibraryEntries(export_table* t)

0Ah int RegisterNonAutoLinkEntries(export_table* t)

0Bh void* QueryLibraryEntryTable(iop_library* library)

0Ch int* QueryBootMode(int mode)

0Dh void RegisterBootMode(boot_mode* b)

14h int RegisterPostBootCallback(BootupCallback func, int priority, int *stat)


  BIOS IOP EXCEPMAN - Exception Manager

EXCEPMAN installs exception vectors at RAM addresses 0x40 and 0x80, but they do not do anything useful by themselves. Other modules will use EXCEPMAN to install their own handlers.

Exports
04h int RegisterExceptionHandler(int exception, ExceptionHandler handler)
Alias for RegisterPriorityExceptionHandler(exception, 2, handler).

05h int RegisterPriorityExceptionHandler(int exception, int priority, ExceptionHandler handler)

06h int RegisterDefaultExceptionHandler(ExceptionHandler handler)

07h int ReleaseExceptionHandler(int exception, ExceptionHandler handler)

08h int ReleaseDefaultExceptionHandler(ExceptionHandler handler)


  BIOS IOP INTRMAN - Interrupt Manager

INTRMAN installs the default interrupt and syscall vectors, though like EXCEPMAN, it is up to other modules to install useful handlers.
Note: the actual module name is INTRMANI - "intrman" is the export table name.
There is also an INTRMANP variant, possibly for PSX compatibility?

Exports
04h int RegisterIntrHandler(int irq, int mode, int (*handler)(void *), void *arg)

05h int ReleaseIntrHandler(int irq)

06h int EnableIntr(int irq)

07h int DisableIntr(int irq)

08h int CpuDisableIntr()

09h int CpuEnableIntr()

0Eh int CpuInvokeInKmode(void *function, ...)

0Fh void DisableDispatchIntr(int irq)

10h void EnableDispatchIntr(int irq)

11h int CpuSuspendIntr(int *state)

12h int CpuResumeIntr(int state)

17h int QueryIntrContext()

18h int QueryIntrStack(void *sp)

19h int iCatchMultiIntr(void)


  BIOS IOP SSBUSC - Subsystem Bus Controller

Not entirely sure what SSBUSC does. It seems to involve memory mapping and access times for the IOP?

Exports
04h int SetDelay(int device, uint value)

05h int GetDelay(int device)

06h int SetBaseAddress(int device, uint value)

07h int GetBaseAddress(int device)

08h int SetRecoveryTime(uint value)

09h int GetRecoveryTime()

0Ah int SetHoldTime(uint value)

0Bh int GetHoldTime()

0Ch int SetFloatTime(uint value)

0Dh int GetFloatTime()

0Eh int SetStrobeTime(uint value)

0Fh int GetStrobeTime()

10h int SetCommonDelay(uint value)

11h int GetCommonDelay()


  BIOS IOP DMACMAN - DMA Manager

Note: The names below are made up, because no known modules expose symbols for DMACMAN.

Exports
04h void DmaSetMadr(int chan, uint value)

05h uint DmaGetMadr(int chan)

06h void DmaSetBcr(int chan, uint value)

07h uint DmaGetBcr(int chan)

08h void DmaSetChcr(int chan, uint value)

09h uint DmaGetChcr(int chan)

0Ah void DmaSetTadr(int chan, uint value)

0Bh uint DmaGetTadr(int chan)

0Eh void DmaSetDpcr(uint value)

0Fh uint DmaGetDpcr()

10h void DmaSetDpcr2(uint value)

11h uint DmaGetDpcr2()

12h void DmaSetDpcr3(uint value)

13h uint DmaGetDpcr3()

14h void DmaSetDicr(uint value)

15h uint DmaGetDicr()

16h void DmaSetDicr2(uint value)

17h uint DmaGetDicr2()

1Ch int DmaRequestTransfer(uint chan, void* addr, uint size, uint count, int dir)

20h void DmaStartTransfer(uint chan)

21h void DmaSetChanPrio(uint chan, uint val)

22h void DmaEnableChan(uint chan)

23h void DmaDisableChan(uint chan)


  BIOS IOP TIMRMAN - Timer Manager

Note: Like for INTRMAN, the actual module name is TIMEMANI - "timrman" is the export table name.
There is also a TIMEMANP variant, possibly for PSX mode?

Exports
04h int AllocHardTimer(int source, int size, int prescale)

05h int ReferHardTimer(int source, int size, int mode, int modemask)

06h int FreeHardTimer(int timid)

07h void SetTimerMode(int timid, int mode)

08h uint GetTimerStatus(int timid)

09h void SetTimerCounter(int timid, uint count)

0Ah uint GetTimerCounter(int timid)

0Bh void SetTimerCompare(int timid, uint compare)

0Ch uint GetTimerCompare(int timid)

0Dh void SetHoldMode(int holdnum, int mode)

0Eh int GetHoldMode(int holdnum)

0Fh uint GetHoldReg(int holdnum)

10h int GetHardTimerIntrCode(int timid)

Below are functions only found in newer TIMRMAN modules. (i.e., not in the BIOS)
14h int SetTimerHandler(int timid, ulong compare, uint (*handler)(void*), void *common)

15h int SetOverflowHandler(int timid, uint (*handler)(void*), void *common)

16h int SetupHardTimer(int timid, int source, int mode, int prescale)

17h int StartHardTimer(int timid)

18h int StopHardTimer(int timid)


  BIOS IOP SYSCLIB - Standard C Library

Exports
04h int setjmp(jmp_buf env)

05h void longjmp(jmp_buf env)

06h char _toupper(char c)
Note: non-standard, returns char instead of int.

07h char _tolower(char c)
Note: non-standard, returns char instead of int.

08h uchar look_ctype_table(char character)

09h void* get_ctype_table()

0Ah void* memchr(const void *s, int c, size_t n)

0Bh int memcmp(const void *p, const void *q, size_t size)

0Ch void* memcpy(void *dest, const void *src, size_t size)

0Dh void* memmove(void *dest, const void *src, size_t size)

0Eh void* memset(void *ptr, int c, size_t size)

0Fh int bcmp(const void *s1, const void *s2, size_t size)

10h void bcopy(const void *src, void *dest, size_t size)

11h void bzero(void *ptr, size_t size)

12h int prnt(PrintCallback func, void *context, const char *format, va_list ap)

13h int sprintf(char *str, const char *format, ...)

14h char* strcat(char *dest, const char *src)

15h char* strchr(const char *s, int c)

16h int strcmp(const char *p, const char *q)

17h char* strcpy(char *dest, const char *src)

18h size_t strcspn(const char *s, const char *reject)

19h char* index(const char *s, int c)

1Ah char* rindex(const char *s, int c)

1Bh size_t strlen(const char *s)

1Ch char* strncat(char *dest, const char *src, size_t size)

1Dh int strncmp(const char *p, const char *q, size_t size)

1Eh char* strncpy(char *dest, const char *src, size_t size)

1Fh char* strpbrk(const char *s, const char *accept)

20h char* strrchr(const char *s, int c)

21h size_t strspn(const char *s, const char *accept)

22h char* strstr(const char *haystack, const char *needle)

23h char* strtok(char *s, const char *delim)

24h long strtol(const char *s, char **endptr, int base)

25h char* atob(char *s, int *i)

26h ulong strtoul(const char *s, char **endptr, int base)

28h void* wmemcopy(uint *dest, const uint *src, size_t size)

29h void* wmemset(uint *dest, uint c, size_t size)

2Ah int vsprintf(char *str, const char *format, va_list ap)

2Bh char* strtok_r(char *s, const char *delim, char **lasts)


  BIOS IOP HEAPLIB - Heap Allocation Library

Exports
04h void* CreateHeap(int size, int flag)

05h void DeleteHeap(void* heap)

06h void* AllocHeapMemory(void *heap, size_t nbytes)

07h int FreeHeapMemory(void *heap, void *ptr)

08h int HeapTotalFreeSize(void *heap)


  BIOS IOP EECONF - EE Configuration

Not sure what this does. It seems to clear out NVRAM for configuration settings and initialize some things after a reboot? Needs more research. No exports.


  BIOS IOP THREADMAN - Thread Manager

This is a relatively large module that handles everything related to threading and scheduling.
The scheduler algorithm is currently unknown, though it is likely similar to the EE kernel's scheduler - that is to say, cooperatively threaded. The IOP kernel has more functionality though, such as event flags and message boxes.
THREADMAN contains multiple export tables. To make things more organized, they are split here into separate sections.

BIOS IOP THBASE - Basic Threading
BIOS IOP THEVENT - Event Flags
BIOS IOP THSEMAP - Semaphores
BIOS IOP THMSGBX - Message Boxes
BIOS IOP THFPOOL - Fixed-length Memory Pools
BIOS IOP THVPOOL - Variable-length Memory Pools


  BIOS IOP THBASE - Basic Threading

Exports
04h int CreateThread(ThreadParam *t)

05h int DeleteThread(int tid)

06h int StartThread(int tid, void *arg)

07h int StartThreadArgs(int tid, int args, void *argp)

08h int ExitThread()

09h int ExitDeleteThread()

0Ah int TerminateThread(int tid)

0Bh int iTerminateThread(int tid)

0Eh int ChangeThreadPriority(int tid, int priority)

0Fh int iChangeThreadPriority(int tid, int priority)

10h int RotateThreadReadyQueue(int priority)

11h int iRotateThreadReadyQueue(int priority)

12h int ReleaseWaitThread(int tid)

13h int iReleaseWaitThread(int tid)

14h int GetThreadId()

15h int CheckThreadStack()

16h int ReferThreadStatus(int tid, ThreadInfo *info)

17h int iReferThreadStatus(int tid, ThreadInfo *info)

18h int SleepThread()

19h int WakeupThread(int tid)

1Ah int iWakeupThread(int tid)

1Bh int CancelWakeupThread(int tid)

1Ch int iCancelWakeupThread(int tid)

21h int DelayThread(int usec)

22h int GetSystemTime(SysClock* clock)

23h int SetAlarm(SysClock* clock, uint (*alarm_cb)(void *), void* arg)

24h int iSetAlarm(SysClock* clock, uint (*alarm_cb)(void *), void* arg)

25h int CancelAlarm(uint (*alarm_cb)(void *), void* arg)

26h int iCancelAlarm(uint (*alarm_cb)(void *), void* arg)

27h void USec2SysClock(uint usec, SysClock* clock)

28h void SysClock2USec(SysClock* clock, uint* sec, uint* usec)

29h int GetSystemStatusFlag()


  BIOS IOP THEVENT - Event Flags

Exports
04h int CreateEventFlag(EventParam* e)

05h int DeleteEventFlag(int ef)

06h int SetEventFlag(int ef, uint bits)

07h int iSetEventFlag(int ef, uint bits)

08h int ClearEventFlag(int ef, uint bits)

09h int iClearEventFlag(int ef, uint bits)

0Ah int WaitEventFlag(int ef, uint bits, int mode, uint *resbits)

0Bh int PollEventFlag(int ef, uint bits, int mode, uint *resbits)

0Dh int ReferEventFlagStatus(int ef, EventInfo *info)

0Eh int iReferEventFlagStatus(int ef, EventInfo *info)


  BIOS IOP THSEMAP - Semaphores

Exports
04h int CreateSema(SemaParam *s)

05h int DeleteSema(int sid)

06h int SignalSema(int sid)

07h int iSignalSema(int sid)

08h int WaitSema(int sid)

09h int PollSema(int sid)

0Bh int ReferSemaStatus(int sid, SemaInfo *info)

0Ch int iReferSemaStatus(int sid, SemaInfo *info)


  BIOS IOP THMSGBX - Message Boxes

Exports
04h int CreateMbx(MbxParam* m)

05h int DeleteMbx(int mid)

06h int SendMbx(int mid, void* msg)

07h int iSendMbx(int mid, void* msg)

08h int ReceiveMbx(void** msg, int mid)

09h int PollMbx(void** msg, int mid)

0Bh int ReferMbxStatus(int mid, MbxInfo* info)

0Ch int iReferMbxStatus(int mid, MbxInfo* info)


  BIOS IOP THFPOOL - Fixed-length Memory Pools

Exports
04h int CreateFpl(FPoolParam* fp)

05h int DeleteFpl(int fpid)

06h void* AllocateFpl(int fpid)

07h void* pAllocateFpl(int fpid)

08h void* ipAllocateFpl(int fpid)

09h int FreeFpl(int fpid, void *mem)

0Bh int ReferFplStatus(int fpid, FPoolInfo *info)

0Ch int iReferFplStatus(int fpid, FPoolInfo *info)


  BIOS IOP THVPOOL - Variable-length Memory Pools

Exports
04h int CreateVpl(VPoolParam* vp)

05h int DeleteVpl(int vpid)

06h void* AllocateVpl(int vpid, int size)

07h void* pAllocateVpl(int vpid, int size)

08h void* ipAllocateVpl(int vpid, int size)

09h int FreeVpl(int vpid, void *mem)

0Bh int ReferVplStatus(int vpid, VPoolInfo *info)

0Ch int iReferVplStatus(int vpid, VPoolInfo *info)


  BIOS IOP VBLANK - VBLANK Interrupt Manager

Exports
04h void WaitVblankStart()

05h void WaitVblankEnd()

06h void WaitVblank()

07h void WaitNonVblank()

08h int RegisterVblankHandler(int end, int priority, int (*handler)(void *), void *arg)

09h int ReleaseVblankHandler(int end, int (*handler)(void *))


  BIOS IOP IOMAN - File Input/Output Manager

IOMAN provides a C-style interface for file operations - other modules must install their own drivers for IOMAN to be effective.
IOMAN does install a TTY driver intended for use with STDIO. On retail BIOSes, the TTY functions are all stubbed. Unknown if other IOMAN modules in other BIOSes install proper drivers, or if the drivers are re-installed by something else.

Exports
04h int open(const char *name, int mode)

05h int close(int fd)

06h int read(int fd, void *ptr, size_t size)

07h int write(int fd, void *ptr, size_t size)

08h int lseek(int fd, int pos, int whence)

09h int ioctl(int fd, int command, void *arg)

0Ah int remove(const char *name)

0Bh int mkdir(const char *path)

0Ch int rmdir(const char *path)

0Dh int dopen(const char *path, int mode)

0Eh int dclose(int fd)

0Fh int dread(int fd, void *buf)

10h int getstat(const char *name, IOStat *stat)

11h int chstat(const char *name, IOStat *stat, uint statmask)

12h int format(const char *dev)

14h int AddDrv(IODevice *device)

15h int DelDrv(const char *name)

Below are functions only found in newer IOMAN modules.
17h void StdioInit(int mode)

19h int rename(const char *old, const char *new)

1Ah int chdir(const char *name)

1Bh int sync(const char *dev, int flag)

1Ch int mount(const char *fsname, const char *dev, int flag, void *arg, int arglen)

1Dh int umount(const char *fsname)

1Eh long long lseek64(int fd, long long offset, int whence)

1Fh int devctl(const char *name, int cmd, void *arg, uint arglen, void *buf, uint buflen)

20h int symlink(const char *old, const char *new)

21h int readlink(const char *path, char *buf, unsigned int buflen)

22h int ioctl2(int fd, int cmd, void *arg, uint arglen, void *buf, uint buflen)


  BIOS IOP MODLOAD - Module Loader

Exports
04h int ReBootStart(const char *command, uint flags)

05h int LoadModuleAddress(const char *name, void* addr, int offs)

06h int LoadModule(const char *name)

07h int LoadStartModule(const char *name, int arglen, const char *args, int *result)

08h int StartModule(int id, const char *name, int arglen, const char *args, int *result)

09h int LoadModuleBufferAddress(void *buffer, void* addr, int offs)

0Ah int LoadModuleBuffer(void *buffer)

0Ch void SetSecrmanCallbacks(void *CardBootFunc, void *DiskBootFunc, void *LoadfileFunc)

0Dh void SetCheckKelfPathCallback(void *CheckKelfPathFunc)


  BIOS IOP ROMDRV - ROM File Driver

This module has no exports. It simply installs an IOMAN driver for rom0 and rom1, the boot ROM and DVD ROM respectively.


  BIOS IOP STDIO - C Standard Input/Output

Exports
04h int printf(const char *format, ...)

05h int getchar()

06h int putchar(int c)

07h int puts(const char *s)

08h char *gets(char *s)

09h int fdprintf(int fd, const char *format, ...)

0Ah int fdgetc(int fd)

0Bh int fdputc(int c, int fd)

0Ch int fdputs(const char *s, int fd)

0Dh char *fdgets(char *buf, int fd)

0Eh int vfdprintf(int fd, const char *format, va_list ap)


  BIOS IOP SIFMAN - SIF Low-level Manager

Exports
02h void sceSifExit()

04h void sceSifDma2Init()

05h void sceSifInit()

06h void sceSifSetDChain()

07h int sceSifSetDma(SifDmaTransfer *trans, int len)

08h int sceSifDmaStat(int trid)

09h void sceSifSetOneDma(SifDmaTransfer dmat)

0Ch void sceSifDma0Transfer(void *addr, int size, int mode)

0Dh void sceSifDma0Sync()

0Eh int sceSifDma0Sending()

0Fh void sceSifDma1Transfer(void *addr, int size, int mode)

10h void sceSifDma1Sync()

11h int sceSifDma1Sending()

12h void sceSifDma2Transfer(void *addr, int size, int mode)

13h void sceSifDma2Sync()

14h int sceSifDma2Sending()

15h uint sceSifGetMSFlag()

16h uint sceSifSetMSFlag(uint value)

17h uint sceSifGetSMFlag()

18h uint sceSifSetSMFlag(uint value)

19h uint sceSifGetMainAddr()

1Ah uint sceSifGetSubAddr()

1Bh uint sceSifSetSubAddr(uint value)

1Ch void sceSifIntrMain()

1Dh int sceSifCheckInit()

1Eh void sceSifSetDmaIntrHandler(void (*handler)(void *), void *arg)

1Fh void sceSifResetDmaIntrHandler()

20h uint sceSifSetDmaIntr(SifDmaTransfer *trans, int len, void (*func)(), void *data)


  BIOS IOP IGREETING - Boot Info Display

No exports. This module outputs console information to the TTY, as well as what kind of boot has occurred (hard reset, update reboot, etc).

Example output:
  PlayStation 2 ======== Hard reset boot
  ROMGEN=2002-0207, IOP info (CPUID=1f, CACH_CONFIG=0, 2MB, IOP mode)
  <20020207-164243,ROMconf,PS20160AC20020207.bin:11552>


  BIOS IOP SIFCMD - SIF RPC Manager

SIFCMD provides a server/client abstraction around SIFMAN, allowing modules to create their own RPC servers that listen to incoming requests from the EE.

Exports
02h int sceSifExitRpc()

04h int sceSifInitCmd()

05h void sceSifExitCmd()

06h uint sceSifGetSreg(int index)

07h void sceSifSetSreg(int index, uint value)

08h SifCmdHandlerData* sceSifSetCmdBuffer(SifCmdHandlerData *cmdBuffer, int size)

09h SifCmdHandlerData* sceSifSetSysCmdBuffer(SifCmdHandlerData *sysCmdBuffer, int size)

0Ah void sceSifAddCmdHandler(int cid, SifCmdHandler_t handler, void *harg)

0Bh void sceSifRemoveCmdHandler(int cid)

0Ch uint sceSifSendCmd(int cmd, void *packet, int packet_size, void *src_extra, void *dest_extra, int size_extra)

0Dh uint isceSifSendCmd(int cmd, void *packet, int packet_size, void *src_extra, void *dest_extra, int size_extra)

0Eh void sceSifInitRpc(int mode)

0Fh int sceSifBindRpc(SifRpcClientData *client, int rpc_number, int mode)

10h int sceSifCallRpc(SifRpcClientData *client, int rpc_number, int mode, void *send, int ssize, void *receive, int rsize, SifRpcEndFunc end_func, void *end_param)

11h void sceSifRegisterRpc(SifRpcServerData *sd, int sid, SifRpcFunc func, void *buf, SifRpcFunc cfunc, void *cbuf, SifRpcDataQueue *qd)

12h int sceSifCheckStatRpc(SifRpcClientData *cd)

13h SifRpcDataQueue* sceSifSetRpcQueue(SifRpcDataQueue *q, int thread_id)

14h SifRpcServerData* sceSifGetNextRequest(SifRpcDataQueue *qd)

15h void sceSifExecRequest(SifRpcServerData *srv)

16h void sceSifRpcLoop(SifRpcDataQueue *qd)

17h int sceSifGetOtherData(SifRpcReceiveData *rd, void *src, void *dest, int size, int mode)

18h SifRpcServerData* sceSifRemoveRpc(SifRpcServerData *sd, SifRpcDataQueue *qd)

19h SifRpcDataQueue* sceSifRemoveRpcQueue(SifRpcDataQueue_t *qd)

1Ah void sceSifSetSif1CB(void *func, int param)

1Bh void sceSifClearSif1CB()


  BIOS IOP REBOOT - SIF Reboot Server

REBOOT installs a custom SIF command handler for 80000003h and waits for this command to be sent.

IOP Reboot Procedure
Image File Format
Like the BIOS, an image file contains a ROMDIR filesystem. An image file usually contains a RESET file of zero bytes, ROMDIR, EXTINFO, and a list of newer IOP modules to load.


  BIOS IOP LOADFILE - Module Loader RPC Server

The first real SIF RPC server that is loaded. Mainly responsible for loading new IOP modules and EE ELFs.
NOTE: Newer LOADFILE modules seem to have a different interface. Needs more research.

LOADFILE RPC (0x80000006)
00h SifLoadModule

01h SifLoadElf

02h SifIopSetAddr

03h SifIopGetAddr

04h SifLoadModuleEncrypted

05h SifLoadElfEncrypted


  BIOS IOP CDVDMAN - CDVD Manager

For hardware details of the CDVD drive, see CDVD Drive.
CDVDMAN installs an IOMAN driver for "cdrom".

Exports
04h int sceCdInit(int mode)

05h int sceCdStandby()

06h int sceCdRead(uint lbn, uint sectors, void* buff, sceCdRMode *mode

07h int sceCdSeek(uint lbn)

08h int sceCdGetError()

09h int sceCdGetToc(void* toc)

0Ah int sceCdSearchFile(sceCdlFILE *file, const char *name)

0Bh int sceCdSync(int mode)

0Ch int sceCdGetDiskType()

0Dh int sceCdDiskReady(int mode)

0Eh int sceCdTrayReq(int param, uint *tray_status)

0Fh int sceCdStop()

10h uint sceCdPosToInt(sceCdlLOCCD *p)

11h sceCdlLOCCD* sceCdIntToPos(uint sector, sceCdlLOCCD *p)

13h int sceCdGetToc2(void* buff, char unk)

14h int sceCdReadDVDV(u32 lbn, u32 sectors, void *buff, sceCdRMode *mode)

15h int sceCdCheckCmd()

16h int sceCdRI(void *buff, uint *result)

17h int sceCdWI(void *buff, uint *result)

18h int sceCdReadClock(sceCdCLOCK *clock)

19h int sceCdWriteClock(const sceCdCLOCK *clock)

1Ch int sceCdStatus()

1Dh int sceCdApplySCmd(int cmd, void *input, ushort size, void *output)

1Eh int sceCdSetHDMode(uint mode)

1Fh int sceCdOpenConfig(int block, int mode, int block_count, uint *result)

20h int sceCdCloseConfig(uint *result)

21h int sceCdReadConfig(void *buff, uint *result)

22h int sceCdWriteConfig(void *buff, uint *result)

23h int sceCdReadKey(char unk1, char unk2, uint cmd, void *key)

24h int sceCdDecSet(char unk1, char unk2, char shift)

25h int sceCdCallback(void (*cb_func)(int reason))

26h int sceCdPause()

27h int sceCdBreak()

28h int sceCdReadCDDA(u32 lbn, u32 sectors, void *buff, sceCdRMode *mode)

29h int sceCdReadConsoleId(void *buff, uint *result)

2Ah int sceCdWriteConsoleId(void *buff, uint *result)

2Bh int sceCdMV(void *buff, uint *result)

2Ch int sceCdGetReadPos()

2Dh int sceCdCtrlADout(int unk, uint *result)

2Eh int sceCdNop

2Fh void* sceGetFsvRbuf()

30h int sceCdstm0Cb(void (*cb)(int))

31h int sceCdstm1Cb(void (*cb)(int))

32h int sceCdSC(int code, uint *error)

33h int sceCdRC(sceCdCLOCK *clock)

34h int sceCdForbidDVDP(uint *result)

35h int sceCdReadSUBQ(void *buff, uint *result)

36h int sceCdApplyNCmd(uint cmd, void *input, ushort size, void *output)

37h int sceCdAutoAdjustCtrl(int mode, uint *result)

38h int sceCdStInit(uint buff_size, uint banks, void *buff)

39h int sceCdStRead(uint sectors, uint *buff, uint mode, uint *result)

3Ah int sceCdStSeek(uint lbn)

3Bh int sceCdStStart(uint lbn, uint *result)

3Ch int sceCdStStat()

3Dh int sceCdStStop()

Below are exports only found in newer CDVDMAN modules.
3Eh int sceCdRead0(uint lbn, uint sectors, void *buff, sceCdRMode *mode, int csec, void *cb_func)

40h int sceCdRM(void *buff, uint *result)

41h int sceCdWM(void *buff, uint *result)

42h int sceCdReadChain(sceCdRChain *chain, sceCdRMode *mode)

43h int sceCdStPause()

44h int sceCdStResume()

45h int sceCdForbidRead(uint *result)

46h int sceCdBootCertify(const void *name)

47h int sceCdSpinCtrlIOP(uint speed)

48h int sceCdBlueLEDCtl(uchar value, uint *result)

49h int sceCdCancelPOffRdy(uint *result)

4Ah int sceCdPowerOff(uint *result)

4Bh int sceCdMmode(int media)

4Ch int sceCdReadFull(ulonglong lbn, ulonglong sectors, void *buff, sceCdRMode *mode)

4Dh int sceCdStSeekF(ulonglong lbn)

4Eh void* sceCdPOffCallback(void (*func)(void *),void *addr)

4Fh int sceCdReadDiskID(uint *id)

50h int sceCdReadGUID(ulonglong *guid)

51h int sceCdSetTimeout(int unk, int timeout)

52h int sceCdReadModelID(ulonglong *id)

53h int sceCdReadDvdDualInfo(int *is_dual, ulonglong *layer1_start)

54h int sceCdLayerSearchFile(sceCdlFILE *fp, const char *path, int layer)

5Ah int sceCdStatus2()

70h int sceCdApplySCmd2(uchar cmd, const void *input, ulonglong size, void *output)

72h int sceCdRE(ulonglong lbn, ulonglong sectors, void *buff, sceCdRMode *mode)

73h int sceCdRcBypassCtl(int mode, uint *result)


  BIOS IOP CDVDFSV - CDVD RPC Server

CDVDFSV creates several servers that allow the EE to interface with CDVDMAN. Some of these servers only perform a single function... not sure why Sony organized it like this.
Note: newer CDVDFSV modules seem to have a different interface. Needs research.

CdInit RPC (0x80000592)

CdSCmd RPC (0x80000593)

CdNCmd RPC (0x80000595)

CdSearchFile RPC (0x80000597)

CdDiskReady RPC (0x8000059A)


  BIOS IOP SIFINIT - SIF Initialization

A rather strange module... it only exists to call SIFMAN.sceSifInit. CDVDFSV already does this though, so the usefulness of SIFINIT is questionable. Perhaps it had another purpose at one point during development?


  BIOS IOP FILEIO - File Input/Output RPC Server

FILEIO creates two RPC servers: one for allocating memory on the IOP and one for interfacing with IOMAN.
Note: newer FILEIO modules have a different interface. Needs research.

IOPHEAP RPC (0x80000001)

FILEIO RPC (0x80000003)


  BIOS IOP SECRMAN - Security Manager

SECRMAN handles decrypting MagicGate-protected executables/data and authenticating PS2 memory cards.
The bulk of this work is handled by the CDVD Drive, which contains the actual MagicGate keys.

Exports
04h void SecrSetMcCommandHandler(int (*func)(int port, int slot, Sio2Transfer *sio2_trans))

05h void SecrSetMcDevIDHandler(int (*func)(int port, int slot))

06h int SecrAuthCard(int port, int slot, int cnum)

07h void SecrResetAuthCard(int port, int slot, int cnum)

08h int SecrCardBootHeader(int port, int slot, void *buff, SecrBitTable *BitTable, int *pSize)

09h int SecrCardBootBlock(void *src, void *dest, uint size)

0Ah void* SecrCardBootFile(int port, int slot, void *buff)

0Bh int SecrDiskBootHeader(void *buff, SecrBitTable *BitTable, int *pSize)

0Ch int SecrDiskBootBlock(void *src, void *dst, uint size)

0Dh void* SecrDiskBootFile(void *buff)


  BIOS IOP EESYNC - Boot Finish Messager

This module installs a post-boot callback in LOADCORE that calls SIFMAN.sceSifSetSMFlag(0x40000).
Not sure if anything on the EE side looks for this value, but the write does indicate that the IOP has finished booting.


  BIOS IOP LIBSD - Low-level Sound Library

Exports
02h int sceSdQuit()

04h int sceSdInit(int flag)

05h void sceSdSetParam(ushort entry, ushort value)

06h ushort sceSdGetParam(ushort entry)

07h void sceSdSetSwitch(ushort entry, uint value)

08h uint sceSdGetSwitch(ushort entry)

09h void sceSdSetAddr(ushort entry, uint value)

0Ah uint sceSdGetAddr(ushort entry)

0Bh void sceSdSetCoreAttr(ushort entry, ushort value)

0Ch ushort sceSdGetCoreAttr(ushort entry)

0Dh ushort sceSdNote2Pitch(ushort center_note, ushort center_fine, ushort note, short fine)

0Eh ushort sceSdPitch2Note(ushort center_note, ushort center_fine, ushort pitch)

0Fh int sceSdProcBatch(sceSdBatch *batch, uint *result, uint num)

10h int sceSdProcBatchEx(sceSdBatch *batch, uint *result, uint num, uint voice)

11h int sceSdVoiceTrans(short chan, ushort mode, void *addr, void *spuaddr, uint size)

12h int sceSdBlockTrans(short chan, ushort mode, void *addr, uint size, ...)

13h uint sceSdVoiceTransStatus(short chan, short flag)

14h uint sceSdBlockTransStatus(short chan, short flag)

15h SdIntrCallback sceSdSetTransCallback(int core, SdIntrCallback cb)

16h SdIntrCallback sceSdSetIRQCallback(SdIntrCallback cb)

17h int sceSdSetEffectAttr(int core, sceSdEffectAttr *attr)

18h void sceSdGetEffectAttr(int core, sceSdEffectAttr *attr)

19h int sceSdClearEffectWorkArea(int core, int channel, int effect_mode)

1Ah sceSdTransIntrHandler sceSdSetTransIntrHandler(int chan, sceSdTransIntrHandler func, void *arg)

1Bh sceSdSpu2IntrHandler sceSdSetSpu2IntrHandler(sceSdSpu2IntrHandler func, void *arg)

1Ch void* sceSdGetTransIntrHandlerArgument(int arg)

1Dh void* sceSdGetSpu2IntrHandlerArgument()

1Eh int sceSdStopTrans(int chan)

1Fh int sceSdCleanEffectWorkArea(int core, int chan, int effect_mode)

20h int sceSdSetEffectMode(int core, sceSdEffectAttr *param)

21h int sceSdSetEffectModeParams(int core, sceSdEffectAttr *attr)


  BIOS IOP SIO2MAN - SIO2 Manager

For details on SIO2 hardware, see SIO2.

Exports
02h void Sio2Exit()

04h void Sio2SetCtrl(uint value)

05h uint Sio2GetCtrl()

06h uint Sio2GetRecv1()

07h void Sio2SetPortCtrl1(int index, uint value)

08h uint Sio2GetPortCtrl1(int index)

09h void Sio2SetPortCtrl2(int index, uint value)

0Ah uint Sio2GetPortCtrl2(int index)

0Bh uint Sio2GetRecv2()

0Ch void Sio2SetReg(int index, uint value)

0Dh uint Sio2GetReg(int index)

0Eh uint Sio2GetRecv3()

0Fh void Sio2SetUnkReg78(uint value)

10h uint Sio2GetUnkReg78()

11h void Sio2SetUnkReg7C(uint value)

12h uint Sio2GetUnkReg7C()

13h void Sio2WriteDataFifo(uchar value)

14h uchar Sio2ReadDataFifo()

15h void Sio2SetIstat(uint value)

16h uint Sio2GetIstat()

17h void Sio2PadInitTransfer()

18h void Sio2McInitTransfer()

19h int Sio2StartTransfer(Sio2Transfer *trans)

The following exports are only available in newer SIO2MAN modules.
1Ah void Sio2TransferReset()

30h void Sio2MtapInitTransfer()

31h void Sio2RmInitTransfer()

32h void Sio2UnkInitTransfer()

33h int Sio2StartTransfer2(Sio2Transfer *trans)

34h void Sio2ResetTransfer2()

35h void Sio2MtapChangeSlotSet(void *func)

36h void Sio2MtapGetSlotMaxSet(void *func)

37h void Sio2MtapGetSlotMax2Set(void *func)

38h void Sio2MtapUpdateSlotsSet(void *func)

39h int Sio2MtapChangeSlot(int* arg)

3Ah int Sio2MtapGetSlotMax(int port)

3Bh int Sio2MtapGetSlotMax2(int port)

3Ch void Sio2MtapUpdateSlots()


  BIOS IOP MCMAN - Memory Card Manager

Exports
05h int McDetectCard(int port, int slot)

06h int McOpen(int port, int slot, char *path, int flags)

07h int McClose(int fd)

08h int McRead(int fd, void *buff, int size)

09h int McWrite(int fd, void *buff, int size)

0Ah int McSeek(int fd, int offs, int whence)

0Bh int McFormat(int port, int slot)

0Ch int McGetDir(int port, int slot, char *dir, int flags, int maxent, sceMcTblGetDir *info)

0Dh int McDelete(int port, int slot, char *path, int flags)

0Eh int McFlush(int fd)

0Fh int McChDir(int port, int slot, char *new_dir, char *cur_dir)

10h int McSetFileInfo(int port, int slot, char *path, sceMcTblGetDir *info, int flags)

11h int McEraseBlock(int port, int block, void **page, void *ecc) (only in older modules)

12h int McReadPage(int port, int slot, int page, void *buff)

13h int McWritePage(int port, int slot, int page, void *buff, void *ecc)

14h void McDataChecksum(void *buff, void *ecc)

1Dh int McReadPS1PDACard(int port, int slot, int page, void *buff)

1Eh int McWritePS1PDACard(int port, int slot, int page, void *buff)

24h int McUnformat(int port, int slot)

26h int McGetFreeClusters(int port, int slot)

27h int McGetMcType(int port, int slot)

28h void McSetPS1CardFlag(int flag)

Below are exports only found in newer MCMAN modules.
11h int McEraseBlock2(int port, int slot, int block, void **page, void *ecc) (replaces McEraseBlock!)

15h int McDetectCard2(int port, int slot)

16h int McGetFormat(int port, int slot)

17h int McGetEntSpace(int port, int slot, char *dir)

18h int McReplaceBadBlock()

19h int McCloseAll()

2Ah void* McGetModuleInfo()

2Bh int McGetCardSpec(int port, int slot, short *page_size, ushort *block_size, int *total_size, uchar *flags)

2Ch int McGetFATentry(int port, int slot, int fat_index, int *fat_entry)

2Dh int McCheckBlock(int port, int slot, int block)

2Eh int McSetFATentry(int port, int slot, int fat_index, int fat_entry)

2Fh int McReadDirEntry(int port, int slot, int cluster, int index, McFsEntry **pfse)

30h void Mc1stCacheEntSetWrFlagOff()

31h int McCreateDirentry(int port, int slot, int parent_cluster, int entries, int cluster, sceMcStDateTime *time)

32h int McReadCluster(int port, int slot, int cluster, McCacheEntry **pmce)

33h int McFlushCache(int port, int slot)

34h int McSetDirEntryState(int port, int slot, int cluster, int fsindex, int flags)


  BIOS IOP MCSERV - Memory Card RPC Server

(todo)


  BIOS IOP PADMAN - Pad Input Manager

(todo)