From 79ff96cb2ffb140863c48803d21d10349881f907 Mon Sep 17 00:00:00 2001 From: Oxore Date: Thu, 4 May 2023 00:16:02 +0300 Subject: Add Readme, move hello world into separate dir --- .gitignore | 1 - 1_hello_world/Makefile | 25 ++++ 1_hello_world/_rom.ld | 15 +++ 1_hello_world/_sram.ld | 26 ++++ 1_hello_world/m68k.ld | 4 + 1_hello_world/main.c | 332 ++++++++++++++++++++++++++++++++++++++++++++++++ 1_hello_world/startup.c | 122 ++++++++++++++++++ Makefile | 25 ---- Readme.md | 3 + _rom.ld | 15 --- _sram.ld | 26 ---- m68k.ld | 4 - main.c | 332 ------------------------------------------------ startup.c | 122 ------------------ 14 files changed, 527 insertions(+), 525 deletions(-) create mode 100644 1_hello_world/Makefile create mode 100644 1_hello_world/_rom.ld create mode 100644 1_hello_world/_sram.ld create mode 100644 1_hello_world/m68k.ld create mode 100644 1_hello_world/main.c create mode 100644 1_hello_world/startup.c delete mode 100644 Makefile create mode 100644 Readme.md delete mode 100644 _rom.ld delete mode 100644 _sram.ld delete mode 100644 m68k.ld delete mode 100644 main.c delete mode 100644 startup.c diff --git a/.gitignore b/.gitignore index 67bcd8c..91ec0ac 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,3 @@ *.elf *.bin compile_commands.json -disasm.dir/ diff --git a/1_hello_world/Makefile b/1_hello_world/Makefile new file mode 100644 index 0000000..c66436d --- /dev/null +++ b/1_hello_world/Makefile @@ -0,0 +1,25 @@ +WARNFLAGS = -Wall -Wextra -pedantic -Wshadow -Wlogical-op +OPTFLAGS = -Os +ARCHFLAGS = -m68000 +_CFLAGS = $(CFLAGS) $(WARNFLAGS) $(ARCHFLAGS) $(OPTFLAGS) -g3 -ffunction-sections -fdata-sections +LDSCRIPTS = m68k.ld _sram.ld _rom.ld +_LDFLAGS = $(LDFLAGS) $(OPTFLAGS) $(addprefix -T,$(LDSCRIPTS)) --gc-sections + +OBJECTS=startup.o main.o + +.PHONY: all +all: hellorom.bin + +%.bin: %.elf Makefile + m68k-none-elf-objcopy -O binary $< $@ + +hellorom.elf: $(OBJECTS) $(LDSCRIPTS) Makefile + m68k-none-elf-ld -o $@ $(OBJECTS) $(_LDFLAGS) + +OBJECTS: Makefile + +%.o: %.c Makefile + m68k-none-elf-gcc -m68000 -c $< -o $@ $(_CFLAGS) + +clean: + rm -rfv hellorom.elf hellorom.bin $(OBJECTS) diff --git a/1_hello_world/_rom.ld b/1_hello_world/_rom.ld new file mode 100644 index 0000000..55106be --- /dev/null +++ b/1_hello_world/_rom.ld @@ -0,0 +1,15 @@ +SECTIONS { + . = ORIGIN(ROM); + .text : { + KEEP(*(.stack)) + KEEP(*(.vectors)) + KEEP(*(.smd_header)) + KEEP(*(.text)) + . = ALIGN(4); + *(.text*) + . = ALIGN(4); + KEEP(*(.rodata)) + *(.rodata*) + . = ALIGN(4); + } >ROM +} diff --git a/1_hello_world/_sram.ld b/1_hello_world/_sram.ld new file mode 100644 index 0000000..e9fcc24 --- /dev/null +++ b/1_hello_world/_sram.ld @@ -0,0 +1,26 @@ +SECTIONS { + __stacktop = ORIGIN(SRAM) + 0xe000; + __data_load = LOADADDR(.data); + . = ORIGIN(SRAM); + + .data ALIGN(4) : { + __data_start = .; + *(.data) + *(.data*) + . = ALIGN(4); + __data_end = .; + } >SRAM AT >ROM + + .bss ALIGN(4) (NOLOAD) : { + __bss_start = .; + *(.bss) + *(.bss*) + . = ALIGN(4); + __bss_end = .; + *(.noinit) + *(.noinit*) + } >SRAM + + . = ALIGN(4); + __heap_start = .; +} diff --git a/1_hello_world/m68k.ld b/1_hello_world/m68k.ld new file mode 100644 index 0000000..dcc5257 --- /dev/null +++ b/1_hello_world/m68k.ld @@ -0,0 +1,4 @@ +MEMORY { + ROM(rx) : ORIGIN = 0x00000000, LENGTH = 4M + SRAM(rwx) : ORIGIN = 0x00ff0000, LENGTH = 64K +} diff --git a/1_hello_world/main.c b/1_hello_world/main.c new file mode 100644 index 0000000..5852765 --- /dev/null +++ b/1_hello_world/main.c @@ -0,0 +1,332 @@ +// Rewritten from https://github.com/BigEvilCorporation/megadrive_samples/blob/master/1_hello_world/hello.asm + +#include +#include + +#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); +} diff --git a/1_hello_world/startup.c b/1_hello_world/startup.c new file mode 100644 index 0000000..3b3c017 --- /dev/null +++ b/1_hello_world/startup.c @@ -0,0 +1,122 @@ +typedef void (*ptr_func_t)(); + +struct smd_header { + char console_name[16]; + char copyright[16]; + char domestic_name[48]; + char international_name[48]; + char version[14]; + unsigned short checksum; + char io_support[16]; + unsigned long rom_start; + unsigned long rom_end; + unsigned long ram_start; + unsigned long ram_end; + unsigned long sram_enabled; + unsigned long unused1; + unsigned long sram_start; + unsigned long sram_end; + unsigned long unused2; + unsigned long unused3; + char notes[40]; + char country_codes[16]; +}; + +extern void __start(void); +static void HSYNC_handler(void); +static void VSYNC_handler(void); + +extern unsigned __stacktop; + +__attribute__((section(".stack"), used)) unsigned *__stack_init = &__stacktop; + +__attribute__((section(".vectors"), used)) ptr_func_t __isr_vectors[] = { + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + HSYNC_handler, + __start, + VSYNC_handler, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, + __start, +}; + +__attribute__((section(".smd_header"), used)) struct smd_header __smd_header = { + .console_name = "SEGA MEGA DRIVE ", + .copyright = "BIGEVILCORP. ", + .domestic_name = "HELLO WORLD ", + .international_name = "HELLO WORLD ", + .version = "GM XXXXXXXX-XX", + .checksum = 0x0000, + .io_support = "J ", + .rom_start = 0, + .rom_end = 0xfffff, + .ram_start = 0xff0000, + .ram_end = 0xffffff, + .sram_enabled = 0x00000000, + .unused1 = 0x00000000, + .sram_start = 0x00000000, + .sram_end = 0x00000000, + .unused2 = 0x00000000, + .unused3 = 0x00000000, + .notes = " ", + .country_codes = " E ", +}; + +void HSYNC_handler(void) {} +void VSYNC_handler(void) {} diff --git a/Makefile b/Makefile deleted file mode 100644 index c66436d..0000000 --- a/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -WARNFLAGS = -Wall -Wextra -pedantic -Wshadow -Wlogical-op -OPTFLAGS = -Os -ARCHFLAGS = -m68000 -_CFLAGS = $(CFLAGS) $(WARNFLAGS) $(ARCHFLAGS) $(OPTFLAGS) -g3 -ffunction-sections -fdata-sections -LDSCRIPTS = m68k.ld _sram.ld _rom.ld -_LDFLAGS = $(LDFLAGS) $(OPTFLAGS) $(addprefix -T,$(LDSCRIPTS)) --gc-sections - -OBJECTS=startup.o main.o - -.PHONY: all -all: hellorom.bin - -%.bin: %.elf Makefile - m68k-none-elf-objcopy -O binary $< $@ - -hellorom.elf: $(OBJECTS) $(LDSCRIPTS) Makefile - m68k-none-elf-ld -o $@ $(OBJECTS) $(_LDFLAGS) - -OBJECTS: Makefile - -%.o: %.c Makefile - m68k-none-elf-gcc -m68000 -c $< -o $@ $(_CFLAGS) - -clean: - rm -rfv hellorom.elf hellorom.bin $(OBJECTS) diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..bcb1cb4 --- /dev/null +++ b/Readme.md @@ -0,0 +1,3 @@ +# SEGA Mega Drive/Genesis Samples in C + +An attempt to rewrite [BigEvilCorporation/megadrive_samples](https://github.com/BigEvilCorporation/megadrive_samples) in C. diff --git a/_rom.ld b/_rom.ld deleted file mode 100644 index 55106be..0000000 --- a/_rom.ld +++ /dev/null @@ -1,15 +0,0 @@ -SECTIONS { - . = ORIGIN(ROM); - .text : { - KEEP(*(.stack)) - KEEP(*(.vectors)) - KEEP(*(.smd_header)) - KEEP(*(.text)) - . = ALIGN(4); - *(.text*) - . = ALIGN(4); - KEEP(*(.rodata)) - *(.rodata*) - . = ALIGN(4); - } >ROM -} diff --git a/_sram.ld b/_sram.ld deleted file mode 100644 index e9fcc24..0000000 --- a/_sram.ld +++ /dev/null @@ -1,26 +0,0 @@ -SECTIONS { - __stacktop = ORIGIN(SRAM) + 0xe000; - __data_load = LOADADDR(.data); - . = ORIGIN(SRAM); - - .data ALIGN(4) : { - __data_start = .; - *(.data) - *(.data*) - . = ALIGN(4); - __data_end = .; - } >SRAM AT >ROM - - .bss ALIGN(4) (NOLOAD) : { - __bss_start = .; - *(.bss) - *(.bss*) - . = ALIGN(4); - __bss_end = .; - *(.noinit) - *(.noinit*) - } >SRAM - - . = ALIGN(4); - __heap_start = .; -} diff --git a/m68k.ld b/m68k.ld deleted file mode 100644 index dcc5257..0000000 --- a/m68k.ld +++ /dev/null @@ -1,4 +0,0 @@ -MEMORY { - ROM(rx) : ORIGIN = 0x00000000, LENGTH = 4M - SRAM(rwx) : ORIGIN = 0x00ff0000, LENGTH = 64K -} 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 -#include - -#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); -} diff --git a/startup.c b/startup.c deleted file mode 100644 index 3b3c017..0000000 --- a/startup.c +++ /dev/null @@ -1,122 +0,0 @@ -typedef void (*ptr_func_t)(); - -struct smd_header { - char console_name[16]; - char copyright[16]; - char domestic_name[48]; - char international_name[48]; - char version[14]; - unsigned short checksum; - char io_support[16]; - unsigned long rom_start; - unsigned long rom_end; - unsigned long ram_start; - unsigned long ram_end; - unsigned long sram_enabled; - unsigned long unused1; - unsigned long sram_start; - unsigned long sram_end; - unsigned long unused2; - unsigned long unused3; - char notes[40]; - char country_codes[16]; -}; - -extern void __start(void); -static void HSYNC_handler(void); -static void VSYNC_handler(void); - -extern unsigned __stacktop; - -__attribute__((section(".stack"), used)) unsigned *__stack_init = &__stacktop; - -__attribute__((section(".vectors"), used)) ptr_func_t __isr_vectors[] = { - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - HSYNC_handler, - __start, - VSYNC_handler, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, - __start, -}; - -__attribute__((section(".smd_header"), used)) struct smd_header __smd_header = { - .console_name = "SEGA MEGA DRIVE ", - .copyright = "BIGEVILCORP. ", - .domestic_name = "HELLO WORLD ", - .international_name = "HELLO WORLD ", - .version = "GM XXXXXXXX-XX", - .checksum = 0x0000, - .io_support = "J ", - .rom_start = 0, - .rom_end = 0xfffff, - .ram_start = 0xff0000, - .ram_end = 0xffffff, - .sram_enabled = 0x00000000, - .unused1 = 0x00000000, - .sram_start = 0x00000000, - .sram_end = 0x00000000, - .unused2 = 0x00000000, - .unused3 = 0x00000000, - .notes = " ", - .country_codes = " E ", -}; - -void HSYNC_handler(void) {} -void VSYNC_handler(void) {} -- cgit v1.2.3