summaryrefslogtreecommitdiff
path: root/main.c
diff options
context:
space:
mode:
authorOxore <oxore@protonmail.com>2023-05-04 00:16:02 +0300
committerOxore <oxore@protonmail.com>2023-05-04 00:16:02 +0300
commit79ff96cb2ffb140863c48803d21d10349881f907 (patch)
treeb73a97e084eac2551115272272292a657172c714 /main.c
parentf2016e84699c4f8a83e759fecac9e5dacd490a40 (diff)
Add Readme, move hello world into separate dir
Diffstat (limited to 'main.c')
-rw-r--r--main.c332
1 files changed, 0 insertions, 332 deletions
diff --git a/main.c b/main.c
deleted file mode 100644
index 5852765..0000000
--- a/main.c
+++ /dev/null
@@ -1,332 +0,0 @@
-// Rewritten from https://github.com/BigEvilCorporation/megadrive_samples/blob/master/1_hello_world/hello.asm
-
-#include <stdint.h>
-#include <stddef.h>
-
-#define VDP_DATA ((volatile uint16_t*)0x00C00000L)
-#define VDP_CONTROL ((volatile uint16_t*)0x00C00004L)
-#define VDP_CMD_VRAM_WRITE ((uint32_t)0x40000000L)
-#define VDP_CMD_CRAM_WRITE ((uint32_t)0xC0000000L)
-#define VRAM_ADDR_TILES (0x0000)
-#define VRAM_ADDR_PLANE_A (0xC000)
-#define VRAM_ADDR_PLANE_B (0xE000)
-#define VDP_SCREEN_WIDTH (0x0140)
-#define VDP_SCREEN_HEIGHT (0x00F0)
-#define VDP_PLANE_WIDTH (0x40)
-#define VDP_PLANE_HEIGHT (0x20)
-#define HARDWARE_VER_ADDRESS ((uint8_t*)0x00A10001L)
-#define TMSS_ADDRESS ((uint32_t*)0x00A14000L)
-#define TMSS_SIGNATURE (('S' << 24) | ('E' << 16) | ('G' << 8) | 'A')
-#define SIZE_WORD (2)
-#define SIZE_LONG (4)
-#define SIZE_PALETTE_B (0x20)
-#define SIZE_PALETTE_W (SIZE_PALETTE_B/SIZE_WORD)
-#define SIZE_PALETTE_L (SIZE_PALETTE_B/SIZE_LONG)
-#define SIZE_TILE_B (0x20)
-#define SIZE_TILE_W (SIZE_TILE_B/SIZE_WORD)
-#define SIZE_TILE_L (SIZE_TILE_B/SIZE_LONG)
-#define TEXT_POS_X (0x08)
-#define TEXT_POS_Y (0x04)
-
-// ==============================================================
-// TILE IDs
-// ==============================================================
-// The indices of each tile above. Once the tiles have been
-// written to VRAM, the VDP refers to each tile by its index.
-// ==============================================================
-#define TILE_ID_SPACE (0x0)
-#define TILE_ID_H (0x1)
-#define TILE_ID_E (0x2)
-#define TILE_ID_L (0x3)
-#define TILE_ID_O (0x4)
-#define TILE_ID_W (0x5)
-#define TILE_ID_R (0x6)
-#define TILE_ID_D (0x7)
-#define TILE_COUNT (0x8) // Last entry is just the count
-
-static uint8_t vdp_registers[] = {
- 0x14, // 0x00: H interrupt on, palettes on
- 0x74, // 0x01: V interrupt on, display on, DMA on, Genesis mode on
- 0x30, // 0x02: Pattern table for Scroll Plane A at VRAM 0xC000 (bits 3-5 = bits 13-15)
- 0x00, // 0x03: Pattern table for Window Plane at VRAM 0x0000 (disabled) (bits 1-5 = bits 11-15)
- 0x07, // 0x04: Pattern table for Scroll Plane B at VRAM 0xE000 (bits 0-2 = bits 11-15)
- 0x78, // 0x05: Sprite table at VRAM 0xF000 (bits 0-6 = bits 9-15)
- 0x00, // 0x06: Unused
- 0x00, // 0x07: Background colour: bits 0-3 = colour, bits 4-5 = palette
- 0x00, // 0x08: Unused
- 0x00, // 0x09: Unused
- 0x08, // 0x0A: Frequency of Horiz. interrupt in Rasters (number of lines travelled by the beam)
- 0x00, // 0x0B: External interrupts off, V scroll fullscreen, H scroll fullscreen
- 0x81, // 0x0C: Shadows and highlights off, interlace off, H40 mode (320 x 224 screen res)
- 0x3F, // 0x0D: Horiz. scroll table at VRAM 0xFC00 (bits 0-5)
- 0x00, // 0x0E: Unused
- 0x02, // 0x0F: Autoincrement 2 bytes
- 0x01, // 0x10: Scroll plane size: 64x32 tiles
- 0x00, // 0x11: Window Plane X pos 0 left (pos in bits 0-4, left/right in bit 7)
- 0x00, // 0x12: Window Plane Y pos 0 up (pos in bits 0-4, up/down in bit 7)
- 0xFF, // 0x13: DMA length lo byte
- 0xFF, // 0x14: DMA length hi byte
- 0x00, // 0x15: DMA source address lo byte
- 0x00, // 0x16: DMA source address mid byte
- 0x80, // 0x17: DMA source address hi byte, memory-to-VRAM mode (bits 6-7)
-};
-
-static uint16_t palette[SIZE_PALETTE_W] = {
- 0x0000, // Colour 0 = Transparent
- 0x0000, // Colour 1 = Black
- 0x0EEE, // Colour 2 = White
- 0x000E, // Colour 3 = Red
- 0x00E0, // Colour 4 = Blue
- 0x0E00, // Colour 5 = Green
- 0x0E0E, // Colour 6 = Pink
- 0x0000, // Leave the rest black...
- 0x0000,
- 0x0000,
- 0x0000,
- 0x0000,
- 0x0000,
- 0x0000,
- 0x0000,
- 0x0000,
-};
-
-static uint32_t characters[TILE_COUNT * SIZE_TILE_L] = {
- // Space
- 0x00000000L,
- 0x00000000L,
- 0x00000000L,
- 0x00000000L,
- 0x00000000L,
- 0x00000000L,
- 0x00000000L,
- 0x00000000L,
- // H
- 0x22000220L,
- 0x22000220L,
- 0x22000220L,
- 0x22222220L,
- 0x22000220L,
- 0x22000220L,
- 0x22000220L,
- 0x00000000L,
- // E
- 0x22222220L,
- 0x22000000L,
- 0x22000000L,
- 0x22222220L,
- 0x22000000L,
- 0x22000000L,
- 0x22222220L,
- 0x00000000L,
- // L
- 0x22000000L,
- 0x22000000L,
- 0x22000000L,
- 0x22000000L,
- 0x22000000L,
- 0x22000000L,
- 0x22222220L,
- 0x00000000L,
- // O
- 0x22222220L,
- 0x22000220L,
- 0x22000220L,
- 0x22000220L,
- 0x22000220L,
- 0x22000220L,
- 0x22222220L,
- 0x00000000L,
- // W
- 0x22000220L,
- 0x22000220L,
- 0x22000220L,
- 0x22020220L,
- 0x22020220L,
- 0x22020220L,
- 0x22222220L,
- 0x00000000L,
- // R
- 0x22222200L,
- 0x22000220L,
- 0x22000220L,
- 0x22222200L,
- 0x22022000L,
- 0x22002200L,
- 0x22000220L,
- 0x00000000L,
- // D
- 0x22222200L,
- 0x22002220L,
- 0x22000220L,
- 0x22000220L,
- 0x22000220L,
- 0x22002220L,
- 0x22222200L,
- 0x00000000L,
-};
-
-// Set the VRAM (video RAM) address to write to next
-static inline void SetVRAMWrite(uint16_t addr)
-{
- *(volatile uint32_t*)VDP_CONTROL = VDP_CMD_VRAM_WRITE | ((addr & 0x3FFF) << 16) | (addr >> 14);
-}
-
-// Set the CRAM (colour RAM) address to write to next
-static inline void SetCRAMWrite(uint16_t addr)
-{
- *(volatile uint32_t*)VDP_CONTROL = VDP_CMD_CRAM_WRITE | ((addr & 0x3FFF) << 16) | (addr >> 14);
-}
-
-static void VDP_WriteTMSS(void)
-{
- // The TMSS (Trademark Security System) locks up the VDP if we don't
- // write the string 'SEGA' to a special address. This was to discourage
- // unlicensed developers, since doing this displays the "LICENSED BY SEGA
- // ENTERPRISES LTD" message to screen (on Mega Drive models 1 and higher).
- //
- // First, we need to check if we're running on a model 1+, then write
- // 'SEGA' to hardware address 0xA14000.
- const uint8_t ver = (*HARDWARE_VER_ADDRESS) & 0x0f;
- if (ver != 0) {
- *TMSS_ADDRESS = TMSS_SIGNATURE;
- }
-
- // Check VDP
- // Read VDP status register (hangs if no access)
- *VDP_CONTROL;
-}
-
-static void VDP_LoadRegisters(void)
-{
- // To initialise the VDP, we write all of its initial register values from
- // the table at the top of the file, using a loop.
- //
- // To write a register, we write a word to the control port.
- // The top bit must be set to 1 (so 0x8000), bits 8-12 specify the register
- // number to write to, and the bottom byte is the value to set.
- //
- // In binary:
- // 100X XXXX YYYY YYYY
- // X = register number
- // Y = value to write
-
- // Set VDP registers
- for (size_t i = 0; i < sizeof(vdp_registers) / sizeof(*vdp_registers); i++) {
- const uint16_t cmd = 0x8000; // 'Set register 0' command
- const uint16_t reg_num = i << 8;
- *VDP_CONTROL = cmd | reg_num | vdp_registers[i];
- }
-}
-
-void __start(void)
-{
- // ==============================================================
- // Initialise the Mega Drive
- // ==============================================================
-
- // Write the TMSS signature (if a model 1+ Mega Drive)
- VDP_WriteTMSS();
-
- // Load the initial VDP registers
- VDP_LoadRegisters();
-
- //==============================================================
- // Clear VRAM (video memory)
- //==============================================================
-
- // Setup the VDP to write to VRAM address 0x0000 (start of VRAM)
- SetVRAMWrite(0x0000);
-
- // Write 0's across all of VRAM
- const size_t count = (0x00010000 / SIZE_WORD); // Loop counter = 64kb, in words
- for (size_t i = 0; i < count; i++) {
- *VDP_DATA = 0x0; // Write a 0x0000 (word size) to VRAM
- }
-
- //==============================================================
- // Initialise status register and set interrupt level.
- // This begins firing vertical and horizontal interrupts.
- //==============================================================
- asm inline volatile (" move.w #0x2300, %sr");
-
- //==============================================================
- // Write the palette to CRAM (colour memory)
- //==============================================================
-
- // Setup the VDP to write to CRAM address 0x0000 (first palette)
- SetCRAMWrite(0x0000);
-
- // Write the palette to CRAM
- for (size_t i = 0; i < SIZE_PALETTE_W; i++) {
- *VDP_DATA = palette[i]; // Write palette entry
- }
-
- //==============================================================
- // Write the font tiles to VRAM
- //==============================================================
-
- // Setup the VDP to write to VRAM address 0x0000 (the address of the first graphics tile, index 0)
- SetVRAMWrite(VRAM_ADDR_TILES);
-
- // Write the font glyph tiles to VRAM
- for (size_t i = 0; i < TILE_COUNT * SIZE_TILE_L; i++) {
- *(volatile uint32_t*)VDP_DATA = characters[i]; // Write palette entry
- }
-
- //==============================================================
- // Write the tile IDs of "HELLO WORLD" to Plane A's cell grid
- //==============================================================
-
- // Each scroll plane is made up of a 64x32 tile grid (this size is specified in VDP register 0x10),
- // with each cell specifying the index of each tile to draw, the palette to draw it with, and
- // some flags (for priority and flipping).
- //
- // Each plane cell is 1 word in size (2 bytes), in the binary format
- // ABBC DEEE EEEE EEEE, where:
- //
- // A = Draw priority (1 bit)
- // B = Palette index (2 bits, specifies palette 0, 1, 2, or 3)
- // C = Flip tile horizontally (1 bit)
- // D = Flip tile vertically (1 bit)
- // E = Tile index to draw (11 bits, specifies tile index from 0 to 2047)
- //
- // Since we're using priority 0, palette 0, and no flipping, we
- // only need to write the tile ID and leave everything else zero.
-
- // Setup the VDP to write the tile ID at text_pos_x,text_pos_y in plane A's cell grid.
- // Plane A's cell grid starts at address 0xC000, which is specified in VDP register 0x2.
- //
- // Since each cell is 1 word in size, to compute a cell address within plane A:
- // ((y_pos * plane_width) + x_pos) * size_word
- SetVRAMWrite(VRAM_ADDR_PLANE_A + (((TEXT_POS_Y * VDP_PLANE_WIDTH) + TEXT_POS_X) * SIZE_WORD));
-
- // then move the tile ID for "H" to VRAM
- *VDP_DATA = TILE_ID_H;
-
- // Repeat for the remaining characters in the string.
- // We don't need to adjust the VRAM address each time, since the auto-increment
- // register (VDP register 0xF) is set to 2, so the destination address
- // will automatically increment by one word (conveniently the size of a cell)
- // after each write.
- *VDP_DATA = TILE_ID_E;
- *VDP_DATA = TILE_ID_L;
- *VDP_DATA = TILE_ID_L;
- *VDP_DATA = TILE_ID_O;
- *VDP_DATA = TILE_ID_SPACE;
- *VDP_DATA = TILE_ID_W;
- *VDP_DATA = TILE_ID_O;
- *VDP_DATA = TILE_ID_R;
- *VDP_DATA = TILE_ID_L;
- *VDP_DATA = TILE_ID_D;
-
- // Finished!
-
- //==============================================================
- // Loop forever
- //==============================================================
- // This loops forever, effectively ending our code. The VDP will
- // still continue to run (and fire vertical/horizontal interrupts)
- // of its own accord, so it will continue to render our Hello World
- // even though the CPU is stuck in this loop.
- while (1);
-}