68000 Assembly Programming for the NeoGeo

The SNK NeoGeo always had "mythical" status during my childhood... everyone knew what one was, but it was so out of the price-range of what we could hope to get it was never really on our radar.

The NeoGeo was a simple concept, using the exact same hardware as an arcade, with just some technical tweaks to make it more suited for home users... (and unfortunately the same cost!)... the Neo Geo gave the wealthy the ability to have all the power of high-end arcade hardware at home..

Now that the NeoGeo is easily emulatable, we can all run - and develop for - this hardware at home!
Cpu 12mhz 68000
Ram 64k
Cart ROM 2MB
Graphics ROM 16MB  sprites + 128K Fix Tiles (Font)
Vram 64K + 4K
Resolution 320x224 (288x208 visible) - 4096 colors onscreen
Sprites 380 sprites of size 16x16-16x512 - (96 per line)
Bitmap planes NONE!
Colors 16 per 8x8 sprite - 256 x 16 color palettes from 65536
Max Sprites 128 sprites 16 color  (16x16 px 32 per line)
Sound chip Z80 with YM1610 + ADPCM

The Fix Layer
The Neogeo screen has a resolution of 320x224, and each tile is 8x8 - giving an effective screen size of 40x28...

The actual tilemap is 40x32... the top and bottom two lines are not show (Shown in red on the chart to the right)

However, because of the CRT layout - it is likely that the left and rightmost 1 column will not be visible (Shown in orange to the right)... this gives a visible screen of 38x28

The Fix Layer is positioned in VRAM at &7000 (each position contains 1 word / 2 bytes)- each Tile is defined by 16 bits in the following format:

F E D C B A 7 8   7 6 5 4 3 2 1 0

Where P is the Palette number, and T is the Tile number

Tiles in memory are ordered in COLUMNS... so (X,Y) co-ordinate (0,1) comes after (0,0)... and (1,0) comes 32 words  ($7020) after (0,0)

For example - to set tile (0,2) to tile 256 in palette 1 (note this is in the "May be offscreen" area, but should appear on an emulator)

    Move.W  #$7002,d1        ;Address - Tile 2
    Move.W  #$1100,d0        ;PTTT    - Palette and tile
    Move.w d1,$3C0000       ;set address in vdp
    Move.w d0,$3C0002       ;set tile data in vdp
Fix Layer tiles are in an odd format!

Each tile is 8x8 at 4bpp ... so 32 bytes per tile...
The two nibbles of each byte represent 2 pixels , but they are BACKWARDS... so the 1st (High) nibble is the 2nd pixel color, and the 2nd nibble (Low) is the 1st pixel color

The bytes are stored in Columns, then rows... and the columns are out of order too! .. the first 8 bytes are from columns 5,6.. next are 7,8... then 1,2... finally 3,4

The 8 bytes of each column are in normal top->bottom format... so at least that:s something!
My AkuSprite editor (used in these tutorials) has support to export images in the correct "Fix" format for the NeoGeo

Vram Map

Start End Words Zone Description
$0000 $6FFF 28672 Lower SCB1
$7000 $74FF 4096
Fix map
$7500 $7FFF

$8000 $81FF 512 Upper SCB2
$8200 $83FF 512
$8400 $85FF 512
$8600 $867F 128
Sprite list for even scanlines
$8680 $86FF 128
Sprite list for odd scanlines
$8700 $87FF 256
Unused (free)

Memory Map
Function Port Details

$000000 Vector Table

$000100 Cart Header and User Vectors

$000400 1st bank of Rom

$100000 Ram (64k)
BIOS_SYSTEM_MODE $10FD80 (byte) VBL for $00=system,$80=game
BIOS_SYSRET_STATUS $10FD81 (byte) Stores function code for SYSTEM_RETURN
BIOS_MVS_FLAG $10FD82 (byte) 0=Home/AES,1=MVS
BIOS_COUNTRY_CODE $10FD83 (byte) 0=Japan,1=USA,2=Europe/Export (xxx:Korea?)
BIOS_GAME_DIP $10FD84 $10FD84-$10FD93 (all bytes)
BIOS_GAMEDIP_TIME1 $10FD84 (word) timed option 1 (minutes and seconds in BCD)
BIOS_GAMEDIP_TIME2 $10FD86 (word) timed option 2 (minutes and seconds in BCD)
BIOS_GAMEDIP_COUNT1 $10FD88 (byte) counter option 1 (1-99; value set here is default)
BIOS_GAMEDIP_COUNT2 $10FD89 (byte) counter option 2 (WITHOUT, INFINITE, 1-99 TIMES)
BIOS_GAMEDIP_01 $10FD8A (byte) Game soft dip 01
BIOS_GAMEDIP_02 $10FD8B (byte) Game soft dip 02
BIOS_GAMEDIP_03 $10FD8C (byte) Game soft dip 03
BIOS_GAMEDIP_04 $10FD8D (byte) Game soft dip 04
BIOS_GAMEDIP_05 $10FD8E (byte) Game soft dip 05
BIOS_GAMEDIP_06 $10FD8F (byte) Game soft dip 06
BIOS_GAMEDIP_07 $10FD90 (byte) Game soft dip 07
BIOS_GAMEDIP_08 $10FD91 (byte) Game soft dip 08
BIOS_GAMEDIP_09 $10FD92 (byte) Game soft dip 09
BIOS_GAMEDIP_10 $10FD93 (byte) Game soft dip 10
BIOS_P1STATUS $10FD94 (byte) Controller 1 status
BIOS_P1PREVIOUS $10FD95 (byte) Inputs from last frame
BIOS_P1CURRENT $10FD96 (byte) Inputs from current frame
BIOS_P1CHANGE $10FD97 (byte) Active-edge input
BIOS_P1REPEAT $10FD98 (byte) Auto-repeat flag
BIOS_P1TIMER $10FD99 (byte) Input repeat timer
BIOS_P2STATUS $10FD9A (byte) Controller 2 status
BIOS_P2PREVIOUS $10FD9B (byte) Inputs from last frame
BIOS_P2CURRENT $10FD9C (byte) Inputs from current frame
BIOS_P2CHANGE $10FD9D (byte) Active-edge input
BIOS_P2REPEAT $10FD9E (byte) Auto-repeat flag
BIOS_P2TIMER $10FD9F (byte) Input repeat timer
BIOS_P3STATUS $10FDA0 (byte) Controller 3 status
BIOS_P3PREVIOUS $10FDA1 (byte) Inputs from last frame
BIOS_P3CURRENT $10FDA2 (byte) Inputs from current frame
BIOS_P3CHANGE $10FDA3 (byte) Active-edge input
BIOS_P3REPEAT $10FDA4 (byte) Auto-repeat flag
BIOS_P3TIMER $10FDA5 (byte) Input repeat timer
BIOS_P4STATUS $10FDA6 (byte) Controller 4 status
BIOS_P4PREVIOUS $10FDA7 (byte) Inputs from last frame
BIOS_P4CURRENT $10FDA8 (byte) Inputs from current frame
BIOS_P4CHANGE $10FDA9 (byte) Active-edge input
BIOS_P4REPEAT $10FDAA (byte) Auto-repeat flag
BIOS_P4TIMER $10FDAB (byte) Input repeat timer
BIOS_STATCURNT $10FDAC (byte) Start and Select from current frame (Select bits = 0 on MVS)
BIOS_STATCHANGE $10FDAD (byte) Start and Select active-edge input (Select bits = 0 on MVS)
BIOS_USER_REQUEST $10FDAE (byte) Command for USER ($122)
BIOS_USER_MODE $10FDAF (byte) Current game status (0:init/boot, 1:title/demo, 2:game)
BIOS_CREDIT1_DEC $10FDB0 (byte) Credit decrement Player 1 (BCD)
BIOS_CREDIT2_DEC $10FDB1 (byte) Credit decrement Player 2 (BCD)
BIOS_CREDIT3_DEC $10FDB2 (byte) Credit decrement Player 3 (BCD)
BIOS_CREDIT4_DEC $10FDB3 (byte) Credit decrement Player 4 (BCD)
BIOS_START_FLAG $10FDB4 (byte) Player(s) starting the game on PLAYER_START call
BIOS_PLAYER1_MODE $10FDB6 (byte) Player 1 Status (a.k.a. "BIOS_PLAYER_MOD1")
BIOS_PLAYER2_MODE $10FDB7 (byte) Player 2 Status
BIOS_PLAYER3_MODE $10FDB8 (byte) Player 3 Status
BIOS_PLAYER4_MODE $10FDB9 (byte) Player 4 Status
;??? $10FDBA (long)
BIOS_MESS_POINT $10FDBE (long) pointer to MESS_OUT buffer
BIOS_MESS_BUSY $10FDC2 (word) 0=run MESS_OUT, 1=skip MESS_OUT
BIOS_CARD_COMMAND $10FDC4 (byte) Command to execute
BIOS_CARD_MODE $10FDC5 (byte) "command error job mode"
BIOS_CARD_ANSWER $10FDC6 (byte) answer code for command; set by BIOS
;BIOS_?????????? $10FDC7 (byte) ?
BIOS_CARD_START $10FDC8 (long) Pointer to start of card data
BIOS_CARD_SIZE $10FDCC (word) Size of data
BIOS_CARD_FCB $10FDCE (word) Game NGH number
BIOS_CARD_SUB $10FDD0 (byte/word) Game sub number (0-15)
BIOS_YEAR $10FDD2 (byte) Current Year (starts at ??)
BIOS_MONTH $10FDD3 (byte) Current Month
BIOS_DAY $10FDD4 (byte) Current Day
BIOS_WEEKDAY $10FDD5 (byte) Current Weekday (0:Sunday, 1:Monday, ... 6:Saturday)
BIOS_HOUR $10FDD6 (byte) Current Hour (24 hour format)
BIOS_MINUTE $10FDD7 (byte) Current Minute
BIOS_SECOND $10FDD8 (byte) Current Second
;BIOS_?????????? $10FDD9 (byte) ?
BIOS_SELECT_TIMER $10FDDA (word) (a.k.a. "compulsion timer")
BIOS_START_TEST $10FDDC (word) ?? (might be used differently on CD systems)
BIOS_CREDIT1 $10FE00 (byte)
BIOS_CREDIT2 $10FE01 (byte)
BIOS_CREDIT3 $10FE02 (byte)
BIOS_CREDIT4 $10FE03 (byte)
BIOS_COIN1_NOR_CRED $10FE10 (word)
BIOS_COIN2_NOR_CRED $10FE12 (word)
;??? $10FE19 (byte)
BIOS_NON_LIMIT $10FE1A (byte/word?)
BIOS_SR_STORE $10FE20 (word)
BIOS_GD_STORE $10FE26 (byte) 32 bytes
BIOS_DEVMODE $10FE80 (byte) nonzero value = enabled
BIOS_FRAMECOUNT $10FE88 (long) (a.k.a. "SYS_INT1_TIMER")
BIOS_SYS_STOPPER $10FE8C (byte) "system stopper", actually BIOS VBlank flag
BIOS_Z80_BANK $10FE8D (byte)
BIOS_CARD_TIMER $10FE90 (word)
BIOS_CARD_START2 $10FE92 (long)
BIOS_CARD_SIZE2 $10FE96 (word)
BIOS_CARD_FCB2 $10FE98 (word)
BIOS_CARD_SUB2 $10FE9A (word)
BIOS_DIR_BUFFER $10FE9C (byte) 20 bytes
BIOS_DIR_NUMBER $10FEB0 (word) 5 words
BIOS_INST_RAD $10FEC0 (long)
BIOS_INST_RCO $10FEC4 (byte)
BIOS_TITLE_MODE $10FEC5 (byte) When set to 1, stops BIOS from calling command 3 twice after Game Over if credits are in the system. -paraphrasing Razoola, NeoGeo Development Wiki
BIOS_MESS_STACK $10FEC6 (long) 5 longs; first is the pointer to the data.
BIOS_STATCURNT_RAW $10FEDC (byte) raw version of BIOS_STATCURNT (includes Select on MVS) (a.k.a. "INPUT_SS")
BIOS_STATCHANGE_RAW $10FEDD (byte) raw version of BIOS_STATCHANGE (includes Select on MVS)
BIOS_KYOUSEI_MODE $10FEE0 (byte) a.k.a. "KYOUSEI_MODE" (Game start compulsion?)
BIOS_CS_REMAIN $10FEE2 (byte) (a.k.a. "CS_REMAIN")
BIOS_INT_OFF $10FEE3 (byte) (a.k.a. "BIOS_INT1_SKIP", "INT_OFF")
BIOS_P5STATUS $10FEE8 (byte) Input 5 status
BIOS_P5PREVIOUS $10FEE9 (byte) Inputs from last frame
BIOS_P5CURRENT $10FEEA (byte) Inputs from current frame
BIOS_P5CHANGE $10FEEB (byte) Active-edge input
BIOS_P5REPEAT $10FEEC (byte) Auto-repeat flag
BIOS_P5TIMER $10FEED (byte) Input repeat timer
BIOS_P6STATUS $10FEEE (byte) Input 6 status
BIOS_P6PREVIOUS $10FEEF (byte) Inputs from last frame
BIOS_P6CURRENT $10FEF0 (byte) Inputs from current frame
BIOS_P6CHANGE $10FEF1 (byte) Active-edge input
BIOS_P6REPEAT $10FEF2 (byte) Auto-repeat flag
BIOS_P6TIMER $10FEF3 (byte) Input repeat timer
BIOS_MESS_BUFFER $10FF00 100 byte buffer
BIOS_4P_REQUESTED $10FEF8 (bios) local copy of hard dip 2 (0=off; 2=on)
BIOS_4P_MODE $10FEFA (bios) Main 4P flag (0=regular; $FF=4P ok)
BIOS_4P_PLUGGED $10FEFB (bios) Is NEO-FTC1B board present? (0=not found; $FF=plugged in)
BIOS_CD_UPZONE $10FEDA (byte) zone (0=PRG/TXT, 1=FIX, 2=SPR, 3=Z80, 4=PCM, 5=PAT, 6=???, 7=OBJ, 8=A**)
BIOS_CD_UPBANK $10FEDB (byte) bank
BIOS_CD_UPDEST $10FEF4 (long) destination address
BIOS_CD_UPSRC $10FEF8 (long) source address
BIOS_CD_UPSIZE $10FEFC (long) size

$110000 Ram Mirror

$200000 2nd bank of Rom
REG_P1CNT $300000 Player 1 Controls (DCBArldu)/Trackball data [active low]
REG_DIPSW $300001 Read Hardware DIPs [active low], Kick watchdog (a.k.a. "WATCH_DOG")
REG_SOUND $320000 (Byte) Send command to Z80 (Z80 port &00), Read Z80 reply (Z80 port &0C)
REG_STATUS_A $320001 uPD4990 bits, Coin switches [switches active low]
REG_P2CNT $340000 Player 2 Controls (DCBArldu) [active low]
REG_STATUS_B $380000 Aux inputs (Start/Select, Memory Card...) [active low]
REG_POUTPUT $380001 Joypad port outputs
CARD_BANK $380011 Memory Card bank select
REG_SLOT $380021 Slot Number (MVS)/REG_POUTPUT mirror (home)
REG_LEDLATCHES $380031 LED Latches (latched on 1->0/falling edge transition)
REG_LEDDATA $380041 LED Output Data
REG_RTCCTRL $380051 Strobe/Clock/DIN for uPD4990
REG_COIN1COUNT_HI $380061 Chute 1 coin counter -> High
REG_COIN2COUNT_HI $380063 Chute 2 coin counter -> High
REG_COIN1LOCK_HI $380065 Chute 1 coin lockout -> High
REG_COIN2LOCK_HI $380067 Chute 2 coin lockout -> High
REG_RTCWRITE $3800D1 Write to RTC
REG_COIN1COUNT_LO $3800E1 Chute 1 coin counter -> Low
REG_COIN2COUNT_LO $3800E3 Chute 2 coin counter -> Low
REG_COIN1LOCK_LO $3800E5 Chute 1 coin lockout -> Low
REG_COIN2LOCK_LO $3800E7 Chute 2 coin lockout -> Low
SYS_NOSHADOW $3A0001 Normal output (a.k.a. "SHADOW_OFF","REG_NOSHADOW")
SYS_BIOSVEC $3A0003 Use BIOS vectors (a.k.a. "SYSTEM_ROM","REG_SWPBIOS")
CARD_ENABLE_1 $3A0005 Enable card writes (w/CARD_ENABLE_2). (a.k.a. "REG_CRDUNLOCK1","IC_WRITE_EI1")
CARD_DISABLE_1 $3A0007 Disable card writes (w/CARD_DISABLE_2). (a.k.a. "REG_CRDLOCK1","IC_WRITE_DI1")
CARD_SELECT $3A0009 Enable card register select
SYS_BIOSFIX $3A000B Use BIOS Fix tiles
SYS_MVSBRAM_LOCK $3A000D Write-protect MVS-only Backup RAM
PALETTE_BANK1 $3A000F (byte) Palette bank 1 register
SYS_SHADOW $3A0011 Darkened output (a.k.a. "SHADOW_ON","REG_SHADOW")
SYS_CARTVEC $3A0013 Use Cart vectors (a.k.a. "USER_ROM","REG_SWPROM")
CARD_DISABLE_2 $3A0015 Disable card writes (w/CARD_DISABLE_1). (a.k.a. "REG_CRDLOCK2","IC_WRITE_DI2")
CARD_ENABLE_2 $3A0017 Enable card writes (w/CARD_ENABLE_1). (a.k.a. "REG_CRDUNLOCK2","IC_WRITE_EI2")
CARD_NORMAL $3A0019 Disable card register select
SYS_CARTFIX $3A001B Use Cart Fix tiles
SYS_MVSBRAM_UNLOCK $3A001D Unprotect MVS-only Backup-RAM
PALETTE_BANK0 $3A001F (byte) Palette bank 0 register
LSPC_ADDR $3C0000 VRAM Address
LSPC_INCR $3C0004 VRAM Increment
LSPC_IRQ_ACK $3C000C Interrupt acknowlege
LSPC_TIMER_STOP $3C000E Stop timer for 32 lines in PAL mode? (LSPC2 only, apparently)
PALETTES $400000 $400000-$401FFF (2 bytes per color - 16 colors - 256 palettes)
PALETTE_REFERENCE $400000 Reference Color (must be $8000)
MEMCARD_DATA $800000 Memory Card Data (a.k.a. "IC_MEMORY")
BIOSROM $C00000 BIOS ROM begins at $C00000
SYSTEM_INT1 $C00438 System VBlank
SYSTEM_INT2 $C0043E System Int2 (Cart systems only)
SYSTEM_RETURN $C00444 Returns from the game to the BIOS
SYSTEM_IO $C0044A Reads player inputs (and cabinet if MVS)
CREDIT_CHECK $C00450 Check a player's number of credits
CREDIT_DOWN $C00456 Deduct credits
READ_CALENDAR $C0045C Read values from uPD4990 chip (MVS-only)
SETUP_CALENDAR $C00462 Sets up uPD4990 chip (MVS-only)
SYS_CARD $C00468 Memory Card commands
SYS_CARD_ERROR $C0046E Handle card errors and display message
SYSTEM_HOW_TO_PLAY $C00474 display old-style how to play. uncomment if you want to use it.
CALC_CHECKSUM $C0047A calc a checksum and loop forever
FIX_CLEAR $C004C2 Clear Fix layer
LSP_1st $C004C8 Clear SCB2-4, first SCB1 tilemap
MESS_OUT $C004CE Fix layer message output
CONTROLLER_SETUP $C004D4 Initializes controllers
BIOSF_HBLANK $C004DA HBlank handler (replaces SYSTEM_INT2)
BIOSF_UPLOAD $C00546 Upload data to DRAM

$C0054C ? (CDZ)
BIOSF_LOADFILE $C00552 Load file from CD, with animation

$C00558 ? (CDZ)
BIOSF_CDPLAYER $C0055E Reset to CD Player
BIOSF_LOADFILEX $C00564 Load file from CD, no animation
BIOSF_CDDACMD $C0056A Issue CDDA command
BIOSF_VIDEOEN $C00570 Enable/Disable video layers
UPLOAD $E00000 CD Upload Zone ($E00000-$EFFFFF)

$F00000 Registers

Color definitions

Palette data is stored between $400000-$401FFF
Each color is 2 bytes in size, there are 16 colors in each palette, and 256 palettes.

The first color in the first palette is special ($400000), it must be $8000... this is called the "Reference color"
The last color in the last palette is special ($401FFE), it is the "Background" color that shows through, when all other layers are transparent

the 16 bits of Color data are in the following format:

F E D C B A 9 8 7 6 5 4 3 2 1 0
D R0 G0 B0 R4 R3 R2 R1 G4 G3 G2 G1 B4 B3 B2 B1

The Color format is quite odd!
each color is defined by 5 bits.. .but the lowest bits for each channel is separate from the rest... this means each color can be quickly defined by a single nibble - or all 5 can be used together...
There is also a "Dark" bit... which is effectively the least significant bit for all 3 color channels.

FM Sound with the YM2610

Get the YM2610 PDF for all the info!

The NeoGeo sound chip is backwards compatible with the AY... and cannot be accessed by the 68000, so we have to get the Z80 CPU to do the sound work...

The Z80 has its own program code contained in a ROM and separate memory - and we have to pass data betrween the Z80 and 68000 with a single byte RW port!
Writes to Z80 port &0C can be read from 68000 port $320000 , and writes to 68000 port $32000 can be read from port &00

Z80 Sound Commands
The Z80 can recieve single byte commands from the 68000 to the Z80, it can also return a single byte
Command Meaning
$00 nop/do nothing
$01 Slot switch (needed by bios)
$02 Play eyecatch music (needed by bios)
$03 Soft Reset (needed by bios)
$04 Disable All (Music & Sounds)
$05 Disable Music
$06 Disable Sounds
$07 Enable All (Music & Sounds)
$08 Enable Music
$09 Enable Sounds
$0A Silence SSG channels
$0B Silence FM channels
$0C Stop all ADPCM-A samples
$0D Stop current ADPCM-B sample
$0E Tempo Change (1 argument; new tempo)
$0F unused
$10 Fade Out (1 argument; fade speed)
$11 Stop Fade In/Out
$12 Fade In (1 argument; fade speed)
$13-$1F Unused
$20-FF Free for user use

NeoGeo Links!
NeoGeo Programmers Guide PDF

Learn Assembly for the Greatest Classic Processors:  Z80 - 6502 - 68000
Visit www.ChibiAkumas.com to get my games and their source code! | Support me on patreon