IREM M72

System Information:

Memory Addresses:
    $00000 ROMSIZE-1 Rom
    WORKRAM WORKRAM+$3fff RaM
    $B0000 $B0001 IRQ Line
    $BC000 $BC001 Sprite Control
    $C0000 $C03FF Ram (Read)
    $C0000 $C03FF Sprite Ram (Write)
    $C8000 $C8BFF Palette 1
    $CC000 $CCBFF Palette 2
    $D0000 $D3FFF Video Ram 1
    $D8000 $DBFFF Video Ram 2
    $E0000 $EFFFF Sound Ram

Sizes:
            ROMSIZE WORKRAM
R Type      $40000  $40000
M72         $80000  $a0000
XMultipl    $80000  $80000
DBreed      $80000  $90000

IO Ports: (Write)
    $00 $01 Sound Command
    $02 $03 Coin counters, reset sound cpu, other stuff?
    $04 $05 Sprite Control
    $06 $07 IRQ Line
    $80 $81 Scroll Y1
    $82 $83 Scroll X1
    $84 $85 Scroll Y2
    $86 $87 Scroll X2
    $c0 $c0 Trigger sample, filled by init_ function */

IO Ports: (Read)
    $00     ?
    $01     ?
    $02     ?
    $03     ?
    $04     ?
    $05     ?

Sound Memory:
    $0000 $ffff Ram

Sound Ports (Read):
    $01     YM2151 Status Port
    $02     Sound Latch
    $84     Sample

Sound Ports (Write):
    $00     YM2151 Register Port
    $01     YM2151 Data Port
    $06     Sound IRQ Ack
    $82     Sample

Sample playback:
In the later games, the sound CPU can program the start offset of the PCM
samples, but it seems the earlier games have them hardcoded somewhere (maybe
a PROM?). So, here I provided some tables with the start offset precomputed.
They could be built automatically for the most part (00 marks the end of a
sample), but some games have holes in the numbering so we would have to
do some alterations anyway.

Protection simulation:
Most of the games running on this board have an 8751 protection mcu.
It is not known how it works in detail, however it's pretty clear that it
shares RAM at b0000-b0fff.
On startup, the game writes a pattern to the whole RAM, then reads it back
expecting it to be INVERTED. If it isn't, it reports a RAM error.
If the RAM passes the test, the program increments every byte up to b0ffb,
then calls a subroutine at b0000, which has to be provided by the mcu.
It seems that this routine is not supposed to RET, but instead it should
jump directly to the game entry point. The routine should also write some
bytes here and there in RAM (different in every game); those bytes are
checked at various points during the game, causing a crash if they aren't
right.
Note that the program keeps incrementing b0ffe while the game is running,
maybe this is done to keep the 8751 alive. We don't bother with that.

Finally, to do the ROM test the program asks the mcu to provide the correct
values. This is done only in service, so doesn't seem to be much of a
protection. Here we have provided the correct crcs for the available dumps,
of course there is no guarantee that they are actually good.

Sound:
  The sound CPU runs in interrup mode 0. IRQ is shared by two sources: the
  YM2151 (bit 4 of the vector), and the main CPU (bit 5).
  Since the vector can be changed from different contexts (the YM2151 timer
  callback, the main CPU context, and the sound CPU context), it's important
  to accurately arbitrate the changes to avoid out-of-order execution. We do
  that by handling all vector changes in a single timer callback.

Input:
    $01 Right
    $02 Left
    $04 Down
    $08 Up
    $10 Button 4
    $20 Button 3
    $40 Button 2
    $80 Button 1

Graphics:
    8 * 8 characters
    RGN_FRAC(1,4)  NUM characters */ 0x8(1-4bits)(4-4bits)[23 bits]
    4 bits per pixel
    RGN_FRAC(3,4) RGN_FRAC(2,4) RGN_FRAC(1,4) RGN_FRAC(0,4)
    0   1   2   3   4   5   6   7
    0*8 1*8 2*8 3*8 4*8 5*8 6*8 7*8
    Every char takes 8 consecutive bytes

    16 * 16 sprites
    RGN_FRAC(1,4)   NUM characters See above
    4 bits per pixel
    RGN_FRAC(3,4) RGN_FRAC(2,4) RGN_FRAC(1,4) RGN_FRAC(0,4)
    0      1      2      3      4      5      6      7
    16*8+0 16*8+1 16*8+2 16*8+3 16*8+4 16*8+5 16*8+6 16*8+7
    0*8    1*8     2*8    3*8    4*8    5*8    6*8    7*8
    8*8    9*8    10*8   11*8   12*8   13*8   14*8   15*8
    Every sprite takes 32 consecutive bytes

Foreground Tilemap:
    Attribute   = Video Ram1[Tile + 1]
    Tile        = Video Ram1[Tile] + ((Attribute & $3f) << 8)
    Palette     = Video Ram1[Tile + 2] & $0f
    FlipXY      = Attribute & $c0
    Priority    = Video Ram1[Tile + 2] & $80

Background Tilemap:
    Attribute   = Video Ram2[Tile + 1]
    Tile        = Video Ram2[Tile] + ((Attribute & $3f) << 8)
    Palette     = Video Ram2[Tile + 2] & $0f
    FlipXY      = Attribute & $c0

Tilemap can be 256x64, but seems to be used at 128x64 (scroll wraparound)

Palette:
    Palette Stored in planar format. ($400 per color)

SpriteRam:
    $BC001
        $40 Clears Sprite Ram

    Tile    = Sprite Ram[offs+2] | (Sprite Ram[offs+3] << 8)
    Color   = Sprite Ram[offs+4] & $0f
    x       = -256 + Sprite Ram[offs+6] | ((Sprite Ram[offs+7] & $03) << 8))
    y       = 512 - (Sprite Ram[offs+0] | ((Sprite Ram[offs+1] & $01) << 8))
    Flipx   = Sprite Ram[offs+5] & $08;
    Flipy   = Sprite Ram[offs+5] & $04;
    Width   = 1 << ((Sprite Ram[offs+5] & $c0) >> 6);
    Height  = 1 << ((Sprite Ram[offs+5] & $30) >> 4);
    y       -= 16 * Height;

IRQs:



Hosted by the Vintage Gaming Network