diff options
| -rw-r--r-- | 1_hello_world/Makefile | 6 | ||||
| -rw-r--r-- | 1_hello_world/main.c | 2 | ||||
| -rw-r--r-- | 2_scroll_planes/Makefile | 25 | ||||
| -rw-r--r-- | 2_scroll_planes/_rom.ld | 17 | ||||
| -rw-r--r-- | 2_scroll_planes/_sram.ld | 26 | ||||
| -rw-r--r-- | 2_scroll_planes/m68k.ld | 4 | ||||
| -rw-r--r-- | 2_scroll_planes/main.c | 463 | ||||
| -rw-r--r-- | 2_scroll_planes/startup.c | 130 | ||||
| -rw-r--r-- | Readme.md | 11 | 
9 files changed, 679 insertions, 5 deletions
| diff --git a/1_hello_world/Makefile b/1_hello_world/Makefile index 0a4b3a2..da61f6a 100644 --- a/1_hello_world/Makefile +++ b/1_hello_world/Makefile @@ -8,12 +8,12 @@ _LDFLAGS = $(LDFLAGS) $(ARCHFLAGS) $(OPTFLAGS) $(addprefix -T,$(LDSCRIPTS)) -Wl,  OBJECTS=startup.o main.o  .PHONY: all -all: hellorom.bin +all: hello.bin  %.bin: %.elf Makefile  	m68k-none-elf-objcopy -O binary $< $@ -hellorom.elf: $(OBJECTS) $(LDSCRIPTS) Makefile +hello.elf: $(OBJECTS) $(LDSCRIPTS) Makefile  	m68k-none-elf-gcc -o $@ $(OBJECTS) $(_LDFLAGS)  OBJECTS: Makefile @@ -22,4 +22,4 @@ OBJECTS: Makefile  	m68k-none-elf-gcc -c $< -o $@ $(_CFLAGS)  clean: -	rm -rfv hellorom.elf hellorom.bin $(OBJECTS) +	rm -rfv hello.elf hello.bin $(OBJECTS) diff --git a/1_hello_world/main.c b/1_hello_world/main.c index 1a30562..811035b 100644 --- a/1_hello_world/main.c +++ b/1_hello_world/main.c @@ -1,4 +1,4 @@ -// Rewritten from https://github.com/BigEvilCorporation/megadrive_samples/blob/master/1_hello_world/hello.asm +// Rewritten from https://github.com/BigEvilCorporation/megadrive_samples/blob/313e16db9c8cdd0bcd0c98223b3d4245f921b31d/1_hello_world/hello.asm  #include <stdint.h>  #include <stddef.h> diff --git a/2_scroll_planes/Makefile b/2_scroll_planes/Makefile new file mode 100644 index 0000000..75c1747 --- /dev/null +++ b/2_scroll_planes/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) $(ARCHFLAGS) $(OPTFLAGS) $(addprefix -T,$(LDSCRIPTS)) -Wl,--gc-sections -Wl,--build-id=none -nostartfiles + +OBJECTS=startup.o main.o + +.PHONY: all +all: scroll.bin + +%.bin: %.elf Makefile +	m68k-none-elf-objcopy -O binary $< $@ + +scroll.elf: $(OBJECTS) $(LDSCRIPTS) Makefile +	m68k-none-elf-gcc -o $@ $(OBJECTS) $(_LDFLAGS) + +OBJECTS: Makefile + +%.o: %.c Makefile +	m68k-none-elf-gcc -c $< -o $@ $(_CFLAGS) + +clean: +	rm -rfv scroll.elf scroll.bin $(OBJECTS) diff --git a/2_scroll_planes/_rom.ld b/2_scroll_planes/_rom.ld new file mode 100644 index 0000000..916d9d9 --- /dev/null +++ b/2_scroll_planes/_rom.ld @@ -0,0 +1,17 @@ +SECTIONS { +    __rom_start = ORIGIN(ROM); +    . = ORIGIN(ROM); +    .text : { +        KEEP(*(.stack)) +        KEEP(*(.vectors)) +        KEEP(*(.smd_header)) +        KEEP(*(.text)) +        . = ALIGN(4); +        *(.text*) +        . = ALIGN(4); +        KEEP(*(.rodata)) +        *(.rodata*) +        . = ALIGN(4); +    } >ROM +    __rom_end = .; +} diff --git a/2_scroll_planes/_sram.ld b/2_scroll_planes/_sram.ld new file mode 100644 index 0000000..e9fcc24 --- /dev/null +++ b/2_scroll_planes/_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/2_scroll_planes/m68k.ld b/2_scroll_planes/m68k.ld new file mode 100644 index 0000000..dcc5257 --- /dev/null +++ b/2_scroll_planes/m68k.ld @@ -0,0 +1,4 @@ +MEMORY { +    ROM(rx) : ORIGIN = 0x00000000, LENGTH = 4M +    SRAM(rwx) : ORIGIN = 0x00ff0000, LENGTH = 64K +} diff --git a/2_scroll_planes/main.c b/2_scroll_planes/main.c new file mode 100644 index 0000000..421b6c3 --- /dev/null +++ b/2_scroll_planes/main.c @@ -0,0 +1,463 @@ +// Rewritten from https://github.com/BigEvilCorporation/megadrive_samples/blob/313e16db9c8cdd0bcd0c98223b3d4245f921b31d/2_scroll_planes/scroll.asm + +#include <stdint.h> +#include <stddef.h> + +//============================================================== +// CONSTANTS +//============================================================== +// Defines names for commonly used values and addresses to make +// the code more readable. +//============================================================== + +// VDP port addresses +#define VDP_DATA                ((volatile uint16_t*)0x00C00000L) +#define VDP_CONTROL             ((volatile uint16_t*)0x00C00004L) + +// VDP commands +#define VDP_CMD_VRAM_WRITE      ((uint32_t)0x40000000L) +#define VDP_CMD_CRAM_WRITE      ((uint32_t)0xC0000000L) +#define VDP_CMD_VSRAM_WRITE     ((uint32_t)0x40000010L) // NEW to this demo - Vertical Scroll RAM address + +// VDP memory addresses +// according to VDP registers 0x2, 0x4, and 0xD (see table above) +#define VRAM_ADDR_TILES         (0x0000) +#define VRAM_ADDR_PLANE_A       (0xC000) +#define VRAM_ADDR_PLANE_B       (0xE000) +#define VRAM_ADDR_HSCROLL		(0xFC00) // NEW to this demo - Horizonal Scroll table address + +// Screen width and height (in pixels) +#define VDP_SCREEN_WIDTH        (0x0140) +#define VDP_SCREEN_HEIGHT       (0x00F0) + +// The plane width and height (in tiles) +// according to VDP register 0x10 (see table above) +#define VDP_PLANE_WIDTH         (0x40) +#define VDP_PLANE_HEIGHT        (0x20) + +// Hardware version address +#define HARDWARE_VER_ADDRESS    ((uint8_t*)0x00A10001L) + +// TMSS +#define TMSS_ADDRESS            ((uint32_t*)0x00A14000L) +#define TMSS_SIGNATURE          (('S' << 24) | ('E' << 16) | ('G' << 8) | 'A') + +// The size of a word and longword +#define SIZE_WORD               (2) +#define SIZE_LONG               (4) + +// The size of one palette (in bytes, words, and longwords) +#define SIZE_PALETTE_B          (0x20) +#define SIZE_PALETTE_W          (SIZE_PALETTE_B/SIZE_WORD) +#define SIZE_PALETTE_L          (SIZE_PALETTE_B/SIZE_LONG) + +// The size of one graphics tile (in bytes, words, and longwords) +#define SIZE_TILE_B             (0x20) +#define SIZE_TILE_W             (SIZE_TILE_B/SIZE_WORD) +#define SIZE_TILE_L             (SIZE_TILE_B/SIZE_LONG) + +// Text draw position (in tiles) +#define TEXT_POS_X              (0x02) +#define TEXT_POS_Y              (0x04) + +// Speed (in pixels per frame) to move our scroll planes +#define PLANE_A_SCROLL_SPEED_X  (0x2) +#define PLANE_B_SCROLL_SPEED_Y  (0x1) + +//============================================================== +// MEMORY MAP +//============================================================== +// We need to store the current scroll values in RAM and update +// them each frame. There are a few ways to create a memory map, +// but the cleanest, simplest, and easiest to maintain method +// uses the assembler's "RS" keywords. RSSET begins a new table of +// offsets starting from any other offset (here we're starting at +// 0x00FF0000, the start of RAM), and allows us to add named entries +// of any size for the "variables". We can then read/write these +// variables using the offsets' labels (see INT_VInterrupt for use +// cases). +//============================================================== +#define RAM_PLANE_A_SCROLL_X    ((uint16_t*)0x00FF0000) // 1 table entry of word size for plane A's X scroll +#define RAM_PLANE_B_SCROLL_Y    ((uint16_t*)0x00FF0002) // 1 table entry of word size for plane B's Y scroll + +// !! Be careful when adding any table entries of BYTE size, since +// you'll need to start worrying about alignment. More of this in a +// future demo. + +//============================================================== +// 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_P     (0x1) +#define TILE_ID_L     (0x2) +#define TILE_ID_A     (0x3) +#define TILE_ID_N     (0x4) +#define TILE_ID_E     (0x5) +#define TILE_ID_B     (0x6) +#define TILE_COUNT    (0x7) // 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) +}; + +//============================================================== +// PALETTE +//============================================================== +// A single colour palette (16 colours) we'll be using to draw text. +// Colour #0 is always transparent, no matter what colour value +// you specify. +// We only use white (colour 2) and transparent (colour 0) in this +// demo, the rest are just examples. +//============================================================== +// Each colour is in binary format 0000 BBB0 GGG0 RRR0, +// so 0x0000 is black, 0x0EEE is white (NOT 0x0FFF, since the +// bottom bit is discarded), 0x000E is red, 0x00E0 is green, and +// 0x0E00 is blue. +//============================================================== +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, +}; + +//============================================================== +// GRAPHICS TILES +//============================================================== +// The 8x8 pixel graphics tiles that describe the font. +// We only need to specify glyphs for "PLANE B" since the A is reusable. +// 'SPACE' is first, which is unneccessary but it's a good teaching tool for +// why we leave the first tile in memory blank (try changing it +// and see what happens!). +//============================================================== +// 0 = transparent pixel +// 2 = colour 'white' in our palette (see palette above) +//============================================================== +// Change all of the 2's to 3, 4 or 5 to draw the text in red, blue +// or green (see the palette above). +//============================================================== +static uint32_t characters[TILE_COUNT * SIZE_TILE_L] = { +    // Space +    0x00000000L, +    0x00000000L, +    0x00000000L, +    0x00000000L, +    0x00000000L, +    0x00000000L, +    0x00000000L, +    0x00000000L, +    // P +    0x22222200L, +    0x22000220L, +    0x22000220L, +    0x22222200L, +    0x22000000L, +    0x22000000L, +    0x22000000L, +    0x00000000L, +    // L +    0x22000000L, +    0x22000000L, +    0x22000000L, +    0x22000000L, +    0x22000000L, +    0x22000000L, +    0x22222220L, +    0x00000000L, +    // A +    0x22222220L, +    0x22000220L, +    0x22000220L, +    0x22222220L, +    0x22000220L, +    0x22000220L, +    0x22000220L, +    0x00000000L, +    // N +    0x22000220L, +    0x22000220L, +    0x22200220L, +    0x22220220L, +    0x22022220L, +    0x22002220L, +    0x22000220L, +    0x00000000L, +    // E +    0x22222220L, +    0x22000000L, +    0x22000000L, +    0x22222220L, +    0x22000000L, +    0x22000000L, +    0x22222220L, +    0x00000000L, +    // B +    0x22222200L, +    0x22000220L, +    0x22000220L, +    0x22222200L, +    0x22000220L, +    0x22000220L, +    0x22222200L, +    0x00000000L, +}; + +//============================================================== +// VRAM WRITE MACROS +//============================================================== +// Some utility macros to help generate addresses and commands for +// writing data to video memory, since they're tricky (and +// error prone) to calculate manually. +// The resulting command and address is written to the VDP's +// control port, ready to accept data in the data port. +//============================================================== + +// 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); +} + +// Set the VSRAM (vertical scroll RAM) address to write to next +static inline void SetVSRAMWrite(uint16_t addr) +{ +    *(volatile uint32_t*)VDP_CONTROL = VDP_CMD_VSRAM_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]; +    } +} + +int main(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 +    } + +    //============================================================== +    // 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 "PLANE A" and "PLANE B" text to Plane A and B +    //============================================================== + +    // See the Hello World sample for what's going on here + +    // Write "PLANE A" font tile IDs to Plane A +    SetVRAMWrite(VRAM_ADDR_PLANE_A + (((TEXT_POS_Y * VDP_PLANE_WIDTH) + TEXT_POS_X) * SIZE_WORD)); +    *VDP_DATA = TILE_ID_P; +    *VDP_DATA = TILE_ID_L; +    *VDP_DATA = TILE_ID_A; +    *VDP_DATA = TILE_ID_N; +    *VDP_DATA = TILE_ID_E; +    *VDP_DATA = TILE_ID_SPACE; +    *VDP_DATA = TILE_ID_A; + +    // Write "PLANE B" font tile IDs to Plane B +    SetVRAMWrite(VRAM_ADDR_PLANE_B + (((TEXT_POS_Y * VDP_PLANE_WIDTH) + TEXT_POS_X) * SIZE_WORD)); +    *VDP_DATA = TILE_ID_P; +    *VDP_DATA = TILE_ID_L; +    *VDP_DATA = TILE_ID_A; +    *VDP_DATA = TILE_ID_N; +    *VDP_DATA = TILE_ID_E; +    *VDP_DATA = TILE_ID_SPACE; +    *VDP_DATA = TILE_ID_B; + +    //============================================================== +    // Intitialise variables in RAM +    //============================================================== +    *RAM_PLANE_A_SCROLL_X = 0; +    *RAM_PLANE_B_SCROLL_Y = 0; + +    //============================================================== +    // Initialise status register and set interrupt level. +    // This begins firing vertical and horizontal interrupts. +    // +    // Since the vinterrupt does something meaningful in this +    // demo, we start this AFTER setting up the VDP to draw and +    // intialising the variables in RAM. +    //============================================================== +    asm inline volatile ("  move.w #0x2300, %sr"); + +    // Finished! + +    //============================================================== +    // Loop forever +    //============================================================== +    // This loops forever, effectively ending our main routine, +    // but the VDP will continue to run of its own accord and +    // will still fire vertical and horizontal interrupts (which is +    // where our update code is), so the demo continues to run. +    // +    // For a game, it would be better to use this loop for processing +    // input and game code, and wait here until next vblank before +    // looping again. We only use vinterrupt for updates in this demo +    // for simplicity (because we don't yet have any timing code). +    while (1); +} + +//============================================================== +// CODE ENTRY POINT +//============================================================== +// The "main()" function. Your code starts here. Once the CPU +// has finished initialising, it will jump to this entry point +// (specified in the vector table at the top of the file). +//============================================================== +__attribute__((interrupt)) void CPU_EntryPoint(void) +{ +    main(); +} + +__attribute__((interrupt)) void INT_VInterrupt(void) +{ +    // Fetch the current scroll values from RAM. +    // +    // These labels are just named offsets from 0x00FF0000 (start of RAM) +    // so we can read from/write to them like any other address. +    // +    // Animate them +    // +    // Store updated scroll values back into RAM +    *RAM_PLANE_A_SCROLL_X += PLANE_A_SCROLL_SPEED_X; +    *RAM_PLANE_B_SCROLL_Y += PLANE_B_SCROLL_SPEED_Y; + +    // VDP register 0xB specifies how the planes scroll. It's set to per-page mode +    // in this demo, so we only need to write one word value to scroll an entire plane +    // in a particular direction. +    // +    // There are two areas of memory used for scroll values - horizontal scroll +    // is within VRAM (location is specified by VDP register 0xD), and +    // vertical scroll has its own separate memory (VSRAM), and therefore has +    // its own macro for setting the address (it has its own VDP command). + +    // Write Plane A's H-scroll value to horizontal scroll memory (in VRAM). +    // Plane A's horizontal page scroll value is at VRAM 0xFC00, Plane B's is at 0xFC02. +    SetVRAMWrite(VRAM_ADDR_HSCROLL); +    *VDP_DATA = *RAM_PLANE_A_SCROLL_X; + +    // Write Plane B's V-scroll value to vertical scroll memory (VSRAM). +    // Plane A's vertical page scroll value is at VSRAM 0x0000, Plane B's is at 0x0002. +    SetVSRAMWrite(0x0000 + SIZE_WORD); +    *VDP_DATA = *RAM_PLANE_B_SCROLL_Y; +} diff --git a/2_scroll_planes/startup.c b/2_scroll_planes/startup.c new file mode 100644 index 0000000..f2574cd --- /dev/null +++ b/2_scroll_planes/startup.c @@ -0,0 +1,130 @@ +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]; +}; + +void INT_Null(void) __attribute__((weak, interrupt)); +void CPU_EntryPoint(void) __attribute__((weak, alias ("INT_Null"))); +void INT_HInterrupt(void) __attribute__((weak, alias ("INT_Null"))); +void INT_VInterrupt(void) __attribute__((weak, alias ("INT_Null"))); +void CPU_Exception(void) __attribute__((weak, interrupt)); + +extern unsigned __stacktop; +extern unsigned __rom_start; +extern unsigned __rom_end; + +__attribute__((section(".stack"), used)) unsigned *__stack_init = &__stacktop; + +__attribute__((section(".vectors"), used)) ptr_func_t __isr_vectors[] = { +    CPU_EntryPoint, +    CPU_Exception, +    CPU_Exception, +    CPU_Exception, +    CPU_Exception, +    CPU_Exception, +    CPU_Exception, +    CPU_Exception, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_HInterrupt, +    INT_Null, +    INT_VInterrupt, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +    INT_Null, +}; + +__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 = (unsigned long)&__rom_start, +    .rom_end = (unsigned long)&__rom_end-1, +    .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 INT_Null(void) {} + +void CPU_Exception(void) +{ +     asm inline volatile ("  stop #2700"); +} @@ -1,5 +1,14 @@  # SEGA Mega Drive/Genesis Samples in C -An attempt to rewrite [gh:BigEvilCorporation/megadrive_samples](https://github.com/BigEvilCorporation/megadrive_samples) in C. +An attempt to rewrite +[gh:BigEvilCorporation/megadrive_samples](https://github.com/BigEvilCorporation/megadrive_samples) +in C. There is a trade-off of showing some C's best practices of bare metal +projects and keeping the code as close as possible to original assembly code, at +least by structure and sequence.  Inspired by [gh:cortexm/baremetal](https://github.com/cortexm/baremetal). + +Comments are copy-pasted in most cases. Sometimes they tell about assembler +language specific things, but there is no assembly code at all - only C and ld +scripts. Maybe at some point in the future I will rewrite them according to C +code, but IMHO some of them are so much redundant and should be removed. | 
