Difference between revisions of "Sega MegaDrive"
(Added more correct information and polished introductory explanations a bit. Will add a few chapters and keep refining info later today :)) |
|||
| Line 1: | Line 1: | ||
== SEGA MegaDrive / Genesis == | == SEGA MegaDrive / Genesis == | ||
| + | === Please note === | ||
| + | This page is a work in progress. There are chapters missing and some information has to be revised. | ||
| + | |||
| + | |||
The SEGA MegaDrive / Genesis is a 16-bit videogame console released by SEGA in 1989. | The SEGA MegaDrive / Genesis is a 16-bit videogame console released by SEGA in 1989. | ||
| + | |||
It features: | It features: | ||
| − | * | + | * 320x224 screen resolution by default, read "Screen Resolutions" for more information. |
| − | * 4x 16 color palettes | + | * 4x 16 color palettes. |
| − | * Tile based display chip with 2 scrolling tilemaps | + | * Tile based display chip with 2 scrolling tilemaps. |
| − | * | + | * Full plane or 16 pixel column independent vertical scroll. |
| + | * Full plane, 8 pixel row or scanline independent horizontal scroll. | ||
| + | * 80 Sprites (up to 4x4tiles in size each) in H40 mode, 64 sprites in H32 mode. Read "Sprites" for more information. | ||
* 64K of dedicated Video Memory (VRAM) | * 64K of dedicated Video Memory (VRAM) | ||
| − | * YM2612 Sound Chip | + | * YM2612 FM Sound Chip + SN76489 PSG Sound Chip (Integrated in the VDP) |
=== Setting up === | === Setting up === | ||
| Line 14: | Line 21: | ||
* Emulator(s): BlastEm ( https://www.retrodev.com/blastem/ ) | * Emulator(s): BlastEm ( https://www.retrodev.com/blastem/ ) | ||
* Tool(s): MDTools ( https://github.com/sikthehedgehog/mdtools ) | * Tool(s): MDTools ( https://github.com/sikthehedgehog/mdtools ) | ||
| − | * Hardware: Sega | + | * Hardware: Sega Mega Drive / Genesis console with an everdrive. |
| − | + | Cartridges have a ROM header that can normally be ditched entirely, since it's only used by emulators to display information or choose correct emulation settings / peripherals. This is not true for all console revisions, though; one part of the header is mandatory for systems equiped with the TradeMark Security System. Read "ROM Header" and "TMSS" for more information. | |
=== CPU Vector table === | === CPU Vector table === | ||
| − | This must be the very first thing in the ROM | + | This must be the very first thing in the ROM. On power up or reset the CPU initializes the stack pointer held at address $000000 and jumps to the reset vector held at address $000004. The rest of the vectors can be discarded competely, since they are only used for exception handling and video interrupts (more information about interrupts can be found in the "VDP" section). |
| − | The | + | If a cartridge doesn't want to process a particular interrupt, or wants to ignore it, standard procedure is to set that vector to a dummy handler that simply returns from the exception (RTE). For our purposes, if only the stack pointer and reset vector will be used, we can use the remaining address space used by the vector table as executable code space. |
| − | + | ||
| − | + | Below are examples that illustrate how the vector table is conventionally set up and how we might want to do it. | |
<syntaxhighlight lang=""> | <syntaxhighlight lang=""> | ||
| − | + | ; Example 1: Standard procedure, where all vectors are initialized to something. | |
| + | ; If a particular vector is not to be used, a "null" or "dummy" handler is put. | ||
| + | dc.l 0x00FFE000 ; Initial stack pointer value | ||
dc.l CPU_EntryPoint ; Start of program | dc.l CPU_EntryPoint ; Start of program | ||
| − | dc.l CPU_Exception | + | dc.l CPU_Exception ; Bus error |
| − | dc.l CPU_Exception | + | dc.l CPU_Exception ; Address error |
| − | dc.l CPU_Exception | + | dc.l CPU_Exception ; Illegal instruction |
| − | dc.l CPU_Exception | + | dc.l CPU_Exception ; Division by zero |
| − | dc.l CPU_Exception | + | dc.l CPU_Exception ; CHK CPU_Exception |
| − | dc.l CPU_Exception | + | dc.l CPU_Exception ; TRAPV CPU_Exception |
| − | dc.l CPU_Exception | + | dc.l CPU_Exception ; Privilege violation |
dc.l INT_Null ; TRACE exception | dc.l INT_Null ; TRACE exception | ||
dc.l INT_Null ; Line-A emulator | dc.l INT_Null ; Line-A emulator | ||
| Line 89: | Line 98: | ||
dc.l INT_Null ; Unused (reserved) | dc.l INT_Null ; Unused (reserved) | ||
dc.l INT_Null ; Unused (reserved) | dc.l INT_Null ; Unused (reserved) | ||
| + | |||
| + | ; Example 2: For sizecoding purposes, we only specify the initial stack pointer | ||
| + | ; and the reset vector, the rest of the space we can use as executable code space. | ||
| + | ; In theory this should work, in non TMSS systems. In practice TMSS compatibility should | ||
| + | ; be taken into account (read "TMSS" for more information) and some emulators might not like this. | ||
| + | dc.l 0x00FFE000 ; Initial stack pointer value | ||
| + | dc.l CPU_EntryPoint ; Start of program | ||
| + | CPU_EntryPoint: | ||
| + | <our demo> | ||
| + | bra.s CPU_EntryPoint | ||
| + | |||
</syntaxhighlight> | </syntaxhighlight> | ||
This vector table is then followed by the ROM header at 0x100 | This vector table is then followed by the ROM header at 0x100 | ||
Revision as of 08:40, 19 December 2025
Contents
SEGA MegaDrive / Genesis
Please note
This page is a work in progress. There are chapters missing and some information has to be revised.
The SEGA MegaDrive / Genesis is a 16-bit videogame console released by SEGA in 1989.
It features:
- 320x224 screen resolution by default, read "Screen Resolutions" for more information.
- 4x 16 color palettes.
- Tile based display chip with 2 scrolling tilemaps.
- Full plane or 16 pixel column independent vertical scroll.
- Full plane, 8 pixel row or scanline independent horizontal scroll.
- 80 Sprites (up to 4x4tiles in size each) in H40 mode, 64 sprites in H32 mode. Read "Sprites" for more information.
- 64K of dedicated Video Memory (VRAM)
- YM2612 FM Sound Chip + SN76489 PSG Sound Chip (Integrated in the VDP)
Setting up
- Assembler: VASM
- Emulator(s): BlastEm ( https://www.retrodev.com/blastem/ )
- Tool(s): MDTools ( https://github.com/sikthehedgehog/mdtools )
- Hardware: Sega Mega Drive / Genesis console with an everdrive.
Cartridges have a ROM header that can normally be ditched entirely, since it's only used by emulators to display information or choose correct emulation settings / peripherals. This is not true for all console revisions, though; one part of the header is mandatory for systems equiped with the TradeMark Security System. Read "ROM Header" and "TMSS" for more information.
CPU Vector table
This must be the very first thing in the ROM. On power up or reset the CPU initializes the stack pointer held at address $000000 and jumps to the reset vector held at address $000004. The rest of the vectors can be discarded competely, since they are only used for exception handling and video interrupts (more information about interrupts can be found in the "VDP" section). If a cartridge doesn't want to process a particular interrupt, or wants to ignore it, standard procedure is to set that vector to a dummy handler that simply returns from the exception (RTE). For our purposes, if only the stack pointer and reset vector will be used, we can use the remaining address space used by the vector table as executable code space.
Below are examples that illustrate how the vector table is conventionally set up and how we might want to do it.
; Example 1: Standard procedure, where all vectors are initialized to something.
; If a particular vector is not to be used, a "null" or "dummy" handler is put.
dc.l 0x00FFE000 ; Initial stack pointer value
dc.l CPU_EntryPoint ; Start of program
dc.l CPU_Exception ; Bus error
dc.l CPU_Exception ; Address error
dc.l CPU_Exception ; Illegal instruction
dc.l CPU_Exception ; Division by zero
dc.l CPU_Exception ; CHK CPU_Exception
dc.l CPU_Exception ; TRAPV CPU_Exception
dc.l CPU_Exception ; Privilege violation
dc.l INT_Null ; TRACE exception
dc.l INT_Null ; Line-A emulator
dc.l INT_Null ; Line-F emulator
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Spurious exception
dc.l INT_Null ; IRQ level 1
dc.l INT_Null ; IRQ level 2
dc.l INT_Null ; IRQ level 3
dc.l INT_HInterrupt ; IRQ level 4 (horizontal retrace interrupt)
dc.l INT_Null ; IRQ level 5
dc.l INT_VInterrupt ; IRQ level 6 (vertical retrace interrupt)
dc.l INT_Null ; IRQ level 7
dc.l INT_Null ; TRAP #00 exception
dc.l INT_Null ; TRAP #01 exception
dc.l INT_Null ; TRAP #02 exception
dc.l INT_Null ; TRAP #03 exception
dc.l INT_Null ; TRAP #04 exception
dc.l INT_Null ; TRAP #05 exception
dc.l INT_Null ; TRAP #06 exception
dc.l INT_Null ; TRAP #07 exception
dc.l INT_Null ; TRAP #08 exception
dc.l INT_Null ; TRAP #09 exception
dc.l INT_Null ; TRAP #10 exception
dc.l INT_Null ; TRAP #11 exception
dc.l INT_Null ; TRAP #12 exception
dc.l INT_Null ; TRAP #13 exception
dc.l INT_Null ; TRAP #14 exception
dc.l INT_Null ; TRAP #15 exception
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
dc.l INT_Null ; Unused (reserved)
; Example 2: For sizecoding purposes, we only specify the initial stack pointer
; and the reset vector, the rest of the space we can use as executable code space.
; In theory this should work, in non TMSS systems. In practice TMSS compatibility should
; be taken into account (read "TMSS" for more information) and some emulators might not like this.
dc.l 0x00FFE000 ; Initial stack pointer value
dc.l CPU_EntryPoint ; Start of program
CPU_EntryPoint:
<our demo>
bra.s CPU_EntryPointThis vector table is then followed by the ROM header at 0x100
ROM Header
The ROM Header is a structure located at 0x100 that specifies some metadata about the ROM, like its name, author, version number, release date, region, and any special peripherals used.
Note that the Mega Drive console itself doesn't read any of this, it's more a convenience for the programmer, but most emulators will read the title and region. Further header information at: https://plutiedev.com/rom-header
Memory map
Start address End address Description 32X SH-2 address
$000000 $3FFFFF Cartridge ROM/RAM $2000000-$23FFFFF
$400000 $7FFFFF Reserved (used by the Mega-CD and 32X)
$800000 $9FFFFF Reserved (used by the 32X)
$840000 $85FFFF 32X frame buffer $4000000-$401FFFF
$860000 $87FFFF 32X frame buffer overwrite mode $4020000-$403FFFF
$880000 $8FFFFF 32X cartridge ROM (first 512kB bank only)
$900000 $9FFFFF 32X cartridge bankswitched ROM (any 512kB bank, controlled by 32X registers)
$A00000 $A0FFFF Z80 memory space
$A10000 $A10001 Version register
$A10002 $A10003 Controller 1 data
$A10004 $A10005 Controller 2 data
$A10006 $A10007 Expansion port data
$A10008 $A10009 Controller 1 control
$A1000A $A1000B Controller 2 control
$A1000C $A1000D Expansion port control
$A1000E $A1000F Controller 1 serial transmit
$A10010 $A10011 Controller 1 serial receive
$A10012 $A10013 Controller 1 serial control
$A10014 $A10015 Controller 2 serial transmit
$A10016 $A10017 Controller 2 serial receive
$A10018 $A10019 Controller 2 serial control
$A1001A $A1001B Expansion port serial transmit
$A1001C $A1001D Expansion port serial receive
$A1001E $A1001F Expansion port serial control
$A10020 $A10FFF Reserved
$A11000 Memory mode register
$A11002 $A110FF Reserved
$A11100 $A11101 Z80 bus request
$A11102 $A111FF Reserved
$A11200 $A11201 Z80 reset
$A11202 $A12FFF Reserved
$A13000 $A130FF TIME registers; used to send signals to the cartridge
$A130EC $A130EF "MARS" when 32X is attached
$A130F1 SRAM access register
$A130F3 Bank register for address $80000-$FFFFF
$A130F5 Bank register for address $100000-$17FFFF
$A130F7 Bank register for address $180000-$1FFFFF
$A130F9 Bank register for address $200000-$27FFFF
$A130FB Bank register for address $280000-$2FFFFF
$A130FD Bank register for address $300000-$37FFFF
$A130FF Bank register for address $380000-$3FFFFF
$A14000 $A14003 TMSS "SEGA"
$A14101 TMSS/cartridge register
$A14102 $BFFFFF Reserved
$C00000 $C00001 VDP data port
$C00002 $C00003 VDP data port (mirror)
$C00004 $C00005 VDP control port
$C00006 $C00007 VDP control port (mirror)
$C00008 $C00009 VDP H/V counter
$C0000A $C0000F VDP H/V counter (mirror)
$C00011 PSG output
$C00013 $C00017 PSG output (mirror)
$C0001C $C0001D Debug register
$C0001E $C0001F Debug register (mirror)
$C00020 $FEFFFF Reserved
$FF0000 $FFFFFF 68000 RAM
Startup
From here it jumps to the CPU entry point and we need to the minimum startup here: The MegaDrive in Supervisor mode already.
The TMSS ROM does is place the SEGA text on screen and run a timer, then I think it just unmaps itself from the address space and does a soft reset.
move.l #'SEGA', ($A14000) ; disable TMSS so that accessing the VDP won't lock up the console
tst.w ($C00004) ; test VDP CONTROL to stop the VDP, this makes it discard anything that was previously in the FIFO
move.w #$2700, sr ; set IPL level 7This setup should give you fully predictable state on every power cycle or reset that's just to play it safe, but getting a spurious interrupt before you even have the chance to disable interrupt processing is pretty much impossible in the real world
Video display
So after the TMSS boot sequence you'll have a black screen but some leftover stuff in VRAM You'll have to enable display which you already need to to when setting the VDP registers to your desired configuration And no, the TMSS ROM doesn't use any interrupts It doesn't even use DMA, the 68k copies all the graphics and all.
The VDP always initializes to 320x224, to get 320x240 you have to enable V30 mode that's why sonic 1 on PAL has borders at the top and bottom they didn't care to change the video height, and PAL systems have a smaller window for 320x224.
VDP Setup
VDP Access is handled by the following 3 ports and VDP Register constants:
VdpCtrl: equ $C00004 ; VDP control port
VdpData: equ $C00000 ; VDP data port
HvCounter: equ $C00008 ; H/V counter
VDPREG_MODE1: equ $8000 ; Mode register #1
VDPREG_MODE2: equ $8100 ; Mode register #2
VDPREG_MODE3: equ $8B00 ; Mode register #3
VDPREG_MODE4: equ $8C00 ; Mode register #4
VDPREG_PLANEA: equ $8200 ; Plane A table address
VDPREG_PLANEB: equ $8400 ; Plane B table address
VDPREG_SPRITE: equ $8500 ; Sprite table address
VDPREG_WINDOW: equ $8300 ; Window table address
VDPREG_HSCROLL: equ $8D00 ; HScroll table address
VDPREG_SIZE: equ $9000 ; Plane A and B size
VDPREG_WINX: equ $9100 ; Window X split position
VDPREG_WINY: equ $9200 ; Window Y split position
VDPREG_INCR: equ $8F00 ; Autoincrement
VDPREG_BGCOL: equ $8700 ; Background color
VDPREG_HRATE: equ $8A00 ; HBlank interrupt rate
VDPREG_DMALEN_L: equ $9300 ; DMA length (low)
VDPREG_DMALEN_H: equ $9400 ; DMA length (high)
VDPREG_DMASRC_L: equ $9500 ; DMA source (low)
VDPREG_DMASRC_M: equ $9600 ; DMA source (mid)
VDPREG_DMASRC_H: equ $9700 ; DMA source (high)A somewhat limited VDP setup can look something like this:
lea (VdpCtrl), a0
move.w #VDPREG_MODE1|$04, (a0) ; Mode register #1
move.w #VDPREG_MODE2|$04, (a0) ; Mode register #2
move.w #VDPREG_MODE3|$00, (a0) ; Mode register #3
move.w #VDPREG_MODE4|$81, (a0) ; Mode register #4
move.w #VDPREG_PLANEA|$30, (a0) ; Plane A address
move.w #VDPREG_PLANEB|$07, (a0) ; Plane B address
move.w #VDPREG_SPRITE|$78, (a0) ; Sprite address
move.w #VDPREG_WINDOW|$34, (a0) ; Window address
move.w #VDPREG_HSCROLL|$3D, (a0) ; HScroll address
move.w #VDPREG_SIZE|$01, (a0) ; Tilemap size
move.w #VDPREG_WINX|$00, (a0) ; Window X split
move.w #VDPREG_WINY|$00, (a0) ; Window Y split
move.w #VDPREG_INCR|$02, (a0) ; Autoincrement
move.w #VDPREG_BGCOL|$00, (a0) ; Background color
move.w #VDPREG_HRATE|$FF, (a0) ; HBlank IRQ rate
For more information, check out https://plutiedev.com/vdp-setup
VDP Register
Here is a detailed explaination of the different VDP registers: https://plutiedev.com/vdp-registers
Sprites
To be added
; to be addedSound
To be added.
; to be added