Memory Map
I/O Maps
Graphics Interface (GIF)
DMA Controller (DMAC)
Graphics Synthesizer (GS)
Vector Unit (VU)
EE Interrupt Controller (INTC)
CDVD Drive
IOP Interrupts
IOP Timers

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

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

  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 Data Formats
GIF PATH3 Masking


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

10003080h GIF_CNT (R)
  0-14  Backwards loop counter from NLOOP
        Decrements to zero
  15    Unused
  16-19 Register descriptor in progress
        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.


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

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.

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 Chain Mode
DMAC Interrupts


  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
  4-5  Stall control channel
  6-7  Stall control drain channel
  8-10 Release cycle period
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.

  0-15    QWC to transfer
  16-25   Unused
  26-27   Priority control
          0=No effect
          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
  1    cnt     MADR=TADR+16 (next to DMAtag)
               TADR=MADR (next to transfer data)
  2    next    MADR=TADR+16
  3    ref     MADR=DMAtag.ADDR
  4    refs    MADR=DMAtag.ADDR
  5    call    MADR=TADR+16
               if (CHCR.ASP == 0)
               else if (CHCR.ASP == 1)
  6    ret     MADR=TADR+16
               if (CHCR.ASP == 2)
               else if (CHCR.ASP == 1)
  7    end     MADR=TADR+16
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

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

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.

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

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

  0-13    Source base pointer in words/64
  16-21   Source buffer width in pixels/64
  24-29   Source format
  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.

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

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

  0-1     Transmission direction
Note that the privileged register BUSDIR must be set appropriately for GIF->VRAM and VRAM->GIF.

  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
  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
  37-50   CLUT base pointer in words/64
  51-54   CLUT format
  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)
  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.

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.

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

  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.

  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.

  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.

  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

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

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

Status Flags

  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.

  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
  0-3   Sector position
Moves the read position to the indicated parameter.

06h ReadCd
  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
  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

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

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
  0     Ignored
  1     Second
  2     Minute
  3     Hour
  4     Ignored
  5     Day
  6     Month
  7     Year
Overwrites the RTC's time in BCD format.

  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 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/no effect?
  7     LEVL - toggle bit 10 on IRQs
        If not set, bit 10 is set to 0 on IRQ
  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.


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 Boot Process
BIOS EE Threading
BIOS EE Syscalls

  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. However, only up to 128 threads may be active at a time.

  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
	      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_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_sema; //?
    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 a 128-element doubly-linked list 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.

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 can re-add all threads waiting on a semaphore to the 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 and activate an idle thread 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. Positive syscalls use the kernel stack. Unknown what the purpose of this is, but user-defined interrupt handlers use negative syscalls only (specifically negative "i" variants of syscalls).

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, the current thread is removed from the active thread list and given WAIT status, forcing a thread reschedule.

33h void WakeupThread(int thread_id)
Has different behavior depending on the thread's status:
  WAIT - Set to READY and re-added to active thread list, forcing a thread reschedule
  WAITSUSPEND - Placed in SUSPEND status, forcing a thread reschedule.
  READY, SUSPEND - 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)
Decrements the semaphore's count variable. Returns -1 if unsuccessful.

64h void FlushCache(int mode)
  Modes of operation
  mode=0: Flush primary instruction cache
  mode=1: Flush secondary instruction cache
  mode=2: Flush data cache
  All other modes flush BOTH secondary instruction cache and the data cache.

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.

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.