# HG changeset patch # User John Tsiombikas # Date 1489463973 -7200 # Node ID ce1b05082ac4a979f27f0240c58369dfbdaf758a initial commit diff -r 000000000000 -r ce1b05082ac4 .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Tue Mar 14 05:59:33 2017 +0200 @@ -0,0 +1,6 @@ +\.o$ +\.swp$ +\.d$ +\.map$ +\.elf$ +\.bin$ diff -r 000000000000 -r ce1b05082ac4 Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Tue Mar 14 05:59:33 2017 +0200 @@ -0,0 +1,51 @@ +csrc = $(wildcard src/*.c) $(wildcard src/libc/*.c) +asrc = $(wildcard src/*.s) +aSsrc = $(wildcard src/*.S) +obj = $(asrc:.s=.o) $(aSsrc:.S=.o) $(csrc:.c=.o) + +name = test2 +elf = $(name).elf +bin = $(name).bin + +warn = -pedantic -Wall +dbg = -g +def = -DGAMENAME=\"testgame\" -DVERSTR=\"01\" -D__NO_CTYPE +inc = -Isrc/libc + +tool_prefix = m68k-linux-gnu- + +CC = $(tool_prefix)gcc +AS = $(tool_prefix)as +LD = $(tool_prefix)ld +OBJCOPY = $(tool_prefix)objcopy + +CFLAGS = -m68000 -ffreestanding -fno-builtin $(warn) $(dbg) $(opt) $(def) $(inc) +CPPFLAGS = $(def) +ASFLAGS = -m68000 +LDFLAGS = -T megadrive.ldscript -print-gc-sections \ + -L/usr/lib/gcc-cross/m68k-linux-gnu/6 -lgcc + +$(bin): $(elf) + $(OBJCOPY) -O binary $< $@ + +$(elf): $(obj) + $(LD) -o $@ $(obj) -Map link.map $(LDFLAGS) + +.PHONY: clean +clean: + rm -f $(obj) $(elf) $(bin) + +.PHONY: run +run: $(bin) + gens-sdl $< + +.PHONY: copy +copy: $(bin) + mount /media/usbmass && cp $(bin) /media/usbmass/$(bin) + umount /media/usbmass + +.PHONY: install +install: $(bin) + mount /media/usbmass + [ -f /media/usbmass/MEGA/MEGA.RBF ] || cp $(bin) /media/usbmass/MEGA/MEGA.BIN + umount /media/usbmass diff -r 000000000000 -r ce1b05082ac4 megadrive.ldscript --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/megadrive.ldscript Tue Mar 14 05:59:33 2017 +0200 @@ -0,0 +1,47 @@ +OUTPUT_ARCH(m68k) + +MEMORY +{ + rom : ORIGIN = 0x00000000, LENGTH = 0x00a00000 + ram : ORIGIN = 0x00ff0000, LENGTH = 0x00010000 +} + +PROVIDE (_stacktop = 0x01000000); + +SECTIONS { + /* ---- start of ROM ---- */ + /* .vect section is used to place the m68k exception vectors at the + * beginning of the address space + */ + .vect : { * (.vect); } >rom + /* .romhdr section is used to place the SEGA ROM header at 0x100 */ + . = 0x100; + .romhdr : { * (.romhdr); } >rom + .text : { * (.text); } >rom + .rodata : { * (.rodata); } >rom + + /* place the load address of the .data section after .rodata */ + . = ALIGN(4); + _data_lma = .; + _rom_end = _data_lma + _data_size; + + /* ---- start of RAM ---- */ + . = 0xff0000; + /* place the .data section at the start of RAM */ + .data ALIGN(4): AT (_data_lma) { + _data_start = .; + * (.data); + . = ALIGN(4); + _data_end = .; + } >ram + _data_size = SIZEOF(.data); + + /* place the .bss section at the end */ + .bss ALIGN(4): { + _bss_start = .; + * (.bss); + . = ALIGN(4); + _bss_end = .; + } >ram + _bss_size = SIZEOF(.bss); +} diff -r 000000000000 -r ce1b05082ac4 src/intr.s --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/intr.s Tue Mar 14 05:59:33 2017 +0200 @@ -0,0 +1,82 @@ +| the following will go into the .vect section which will be placed at the very +| begining of the binary at address 0 by the linker (see lnkscript). + .section .vect,"a" + .extern start +| exception vectors + .long _stacktop | 00 reset - initial SSP + .long start | 01 reset - initial PC + .long intr_fatal | 02 bus error + .long intr_fatal | 03 address error + .long intr_fatal | 04 illegal instruction + .long intr_fatal | 05 zero divide + .long intr_fatal | 06 chk instruction + .long intr_fatal | 07 trapv instruction + .long intr_fatal | 08 privilege violation + .long intr_fatal | 09 trace + .long intr_fatal | 0a line 1010 emulator + .long intr_fatal | 0b line 1111 emulator + .long intr_fatal | 0c reserved + .long intr_fatal | 0d reserved + .long intr_fatal | 0e format error (mc68010 only) + .long intr_fatal | 0f uninitialized interrupt vector + .long intr_fatal | 10 \ + .long intr_fatal | 11 | + .long intr_fatal | 12 | + .long intr_fatal | 13 > reserved + .long intr_fatal | 14 | + .long intr_fatal | 15 | + .long intr_fatal | 16 | + .long intr_fatal | 17 / + .long intr_fatal | 18 spurious interrupt + .long intr_fatal | 19 level 1 interrupt + .long intr_fatal | 1a level 2 interrupt + .long intr_fatal | 1b level 3 interrupt + .long intr_hblank | 1c level 4 interrupt (hblank in the mega drive) + .long intr_fatal | 1d level 5 interrupt + .long intr_vblank | 1e level 6 interrupt (vblank in the mega drive) + .long intr_fatal | 1f level 7 interrupt + .long intr_fatal | 20 trap 0 + .long intr_fatal | 21 trap 1 + .long intr_fatal | 22 trap 2 + .long intr_fatal | 23 trap 3 + .long intr_fatal | 24 trap 4 + .long intr_fatal | 25 trap 5 + .long intr_fatal | 26 trap 6 + .long intr_fatal | 27 trap 7 + .long intr_fatal | 28 trap 8 + .long intr_fatal | 29 trap 9 + .long intr_fatal | 2a trap a + .long intr_fatal | 2b trap b + .long intr_fatal | 2c trap c + .long intr_fatal | 2d trap d + .long intr_fatal | 2e trap e + .long intr_fatal | 2f trap f + .long intr_fatal | 30 \ + .long intr_fatal | 31 | + .long intr_fatal | 32 | + .long intr_fatal | 33 | + .long intr_fatal | 34 | + .long intr_fatal | 35 | + .long intr_fatal | 36 | + .long intr_fatal | 37 | + .long intr_fatal | 38 > reserved + .long intr_fatal | 39 | + .long intr_fatal | 3a | + .long intr_fatal | 3b | + .long intr_fatal | 3c | + .long intr_fatal | 3d | + .long intr_fatal | 3e | + .long intr_fatal | 3f / + +| from here on we continue in the regular .text section since we don't care +| where this code ends up. + .text +| interrupt handlers +intr_fatal: + stop #0x2700 + +| TODO hblank/vblank code +intr_hblank: + rte +intr_vblank: + rte diff -r 000000000000 -r ce1b05082ac4 src/main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main.c Tue Mar 14 05:59:33 2017 +0200 @@ -0,0 +1,13 @@ +#include "vdp.h" + +int main(void) +{ + vdp_init(); + + vdp_set_pal_entry(0, 1, 7, 0, 3); + vdp_set_bgcolor(0, 1); + + for(;;); + + return 0; +} diff -r 000000000000 -r ce1b05082ac4 src/romhdr.S --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/romhdr.S Tue Mar 14 05:59:33 2017 +0200 @@ -0,0 +1,37 @@ +| the following will go into the custom .romhdr section which will be placed at +| address 100h of the binary by the linker (see lnkscript). + .section .romhdr,"a" + +#ifndef GAMENAME +#define GAMENAME "unnamed" +#endif +#ifndef VERSTR +#define VERSTR "00" +#endif + + .ascii "SEGA MEGA DRIVE (C)MINDLAPSE2017" +hdr_game_dom: + .ascii GAMENAME +hdr_game_dom_end: + .fill 48 - (hdr_game_dom_end - hdr_game_dom),1,32 | pad to 48 bytes with spaces +hdr_game_int: + .ascii GAMENAME +hdr_game_int_end: + .fill 48 - (hdr_game_int_end - hdr_game_int),1,32 | pad to 48 bytes with spaces + .ascii "GM" | it's a game (who cares what it is?) + .ascii "0000000-" | product code + .ascii VERSTR | version string + .short 0 | checksum + .ascii "J " | I/O support (joypad) + .long 0 | start address of ROM + .long _rom_end | last address of ROM + .long 0xff0000 | start address of RAM + .long 0xffffff | last address of RAM + .long 0 | SRAM enabled(?) + .long 0 | ??? + .long 0 | start address of SRAM + .long 0 | last address of SRAM + .long 0 | ??? + .long 0 | ??? + .fill 40,1,32 | notes (fill with spaces for now TODO) + .ascii "JUE " | country codes diff -r 000000000000 -r ce1b05082ac4 src/startup.s --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/startup.s Tue Mar 14 05:59:33 2017 +0200 @@ -0,0 +1,47 @@ + .text + .extern main + + .global start + .global halt_cpu +start: + bsr.s disable_intr + + | copy .data section from ROM to RAM + move.l #_data_lma, %a0 + move.l #_data_start, %a1 + move.l #_data_end, %a2 + cmp.l %a1, %a2 + beq.s 1f | skip data copy if the section is empty +0: move.l (%a0)+, (%a1)+ + cmp.l %a1, %a2 + bne.s 0b +1: + + | zero the .bss section + move.l #_bss_start, %a0 + move.l #_bss_end, %a1 + cmp.l %a0, %a1 + beq.s 1f | skip bss zeroing if the section is empty +0: clr.l (%a0)+ + cmp.l %a0, %a1 + bne.s 0b +1: + + | setup the stack pointer stack + move.l #_stacktop, %sp + | now that we have a stack, we can enable interrupts + bsr.s enable_intr + + jsr main +halt_cpu: + stop #0x2700 + +.global enable_intr +enable_intr: + andi.w #0xf8ff, %sr + rts + +.global disable_intr +disable_intr: + ori.w #0x0300, %sr + rts diff -r 000000000000 -r ce1b05082ac4 src/vdp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vdp.c Tue Mar 14 05:59:33 2017 +0200 @@ -0,0 +1,9 @@ +#include "vdp.h" + +void vdp_init(void) +{ + vdp_setreg(VDP_REG_MODE1, VDP_MODE1_BASE); + vdp_setreg(VDP_REG_MODE2, VDP_MODE2_BASE | VDP_MODE2_DISP); + + vdp_set_autoinc(2); +} diff -r 000000000000 -r ce1b05082ac4 src/vdp.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/vdp.h Tue Mar 14 05:59:33 2017 +0200 @@ -0,0 +1,157 @@ +#ifndef VDP_H_ +#define VDP_H_ + +#include + +#define VDP_PORT_DATA *((volatile uint16_t*)0xc00000) +#define VDP_PORT_DATA32 *((volatile uint32_t*)0xc00000) +#define VDP_PORT_CTL *((volatile uint16_t*)0xc00004) +#define VDP_PORT_CTL32 *((volatile uint32_t*)0xc00004) +#define VDP_PORT_HVCNT *((volatile uint16_t*)0xc00008) +#define VDP_PORT_PSG *((volatile uint16_t*)0xc00010) + +#define VDP_PORT_STATUS *((volatile uint16_t*)0xc00004) + +enum { + VDP_REG_MODE1 = 0, + VDP_REG_MODE2 = 1, + VDP_REG_NAMETAB_A = 2, + VDP_REG_NAMETAB_WIN = 3, + VDP_REG_NAMETAB_B = 4, + VDP_REG_SPRITE_TAB = 5, + VDP_REG_BGCOL = 7, + VDP_REG_INTR = 10, + VDP_REG_MODE3 = 11, + VDP_REG_MODE4 = 12, + VDP_REG_SCROLL_TAB = 13, + VDP_REG_AUTOINC = 15, + VDP_REG_SCROLL_SIZE = 16, + VDP_REG_WIN_XPOS = 17, + VDP_REG_WIN_YPOS = 18, + VDP_REG_DMA_LEN_LOW = 19, + VDP_REG_DMA_LEN_HIGH = 20, + VDP_REG_DMA_SRC_LOW = 21, + VDP_REG_DMA_SRC_MID = 22, + VDP_REG_DMA_SRC_HIGH = 23, + + VDP_NUM_REGS +}; + +uint16_t vdp_reg_shadow[VDP_NUM_REGS]; + +/* access VDP memory */ +enum { VDP_MEM_READ, VDP_MEM_WRITE }; +enum { + VDP_MEM_VRAM = 0, + VDP_MEM_CRAM = 0xa, /* CD5->CD0: 0 0 r 0 w 0 */ + VDP_MEM_VSRAM = 4 /* CD5->CD0: 0 0 0 1 0 0 */ +}; + +static inline void vdp_setup_access(uint16_t addr, int rw, int memid) +{ + uint32_t type; + if(rw == VDP_MEM_WRITE) { + type = (memid & 7) | 1; + } else { + type = memid & 0xc; + } + + VDP_PORT_CTL32 = (((uint32_t)addr & 0x3fff) << 16) | ((addr >> 14) & 3) | + ((type << 2) & 0xf0) | (type << 30); +} + + +/* mode register 1 */ +enum { + VDP_MODE1_BASE = 0x4, + VDP_MODE1_HVCNT = 0x2, + VDP_MODE1_HINTR = 0x10 +}; + +/* mode register 2 */ +enum { + VDP_MODE2_BASE = 0x4, + VDP_MODE2_V30CELL = 0x8, + VDP_MODE2_DMA = 0x10, + VDP_MODE2_VINTR = 0x20, + VDP_MODE2_DISP = 0x40 +}; + +/* mode register 3 */ +enum { + VDP_MODE3_BASE = 0, + VDP_MODE3_HSCROLL_CELL = 2, + VDP_MODE3_HSCROLL_LINE = 3, + VDP_MODE3_VSCROLL_2CELL = 4, + VDP_MODE3_EXTINTR = 8 +}; + +/* mode register 4 */ +enum { + VDP_MODE4_BASE = 0, + VDP_MODE4_H40CELL = 0x81, + VDP_MODE4_ILACE = 2, + VDP_MODE4_ILACE_2XRES = 6, + VDP_MODE4_SH = 8 /* shadow/highlight enable */ +}; + +/* scroll size register */ +enum { + VDP_SCROLL_H32 = 0, + VDP_SCROLL_H64 = 1, + VDP_SCROLL_H128 = 3, + VDP_SCROLL_V32 = 0, + VDP_SCROLL_V64 = 0x10, + VDP_SCROLL_V128 = 0x30 +}; + +/* window X/Y position register */ +enum { + VDP_WIN_LEFT = 0, + VDP_WIN_UP = 0, + VDP_WIN_RIGHT = 0x80, + VDP_WIN_DOWN = 0x80, + VDP_WIN_POSMASK = 0x1f +}; + +#define VDP_PACK_RGB(r, g, b) \ + ((((uint16_t)(r) & 7) << 1) | (((uint16_t)(g) & 7) << 5) | (((uint16_t)(b) & 7) << 9)) + + +static inline void vdp_setreg(int reg, uint8_t value) +{ + /*vdp_reg_shadow[reg] = value;*/ + VDP_PORT_CTL = (uint16_t)value | (reg << 8) | (uint16_t)0x8000; +} + +static inline uint16_t vdp_getreg(int reg) +{ + return vdp_reg_shadow[reg]; +} + +static inline uint16_t vdp_status(void) +{ + return VDP_PORT_CTL; +} + +static inline void vdp_set_bgcolor(int palidx, int colidx) +{ + vdp_setreg(VDP_REG_BGCOL, (colidx & 0xf) | (palidx << 4)); +} + +static inline void vdp_set_autoinc(int stride) +{ + vdp_setreg(VDP_REG_AUTOINC, stride); +} + +static inline void vdp_set_pal_entry(int pidx, int cidx, int r, int g, int b) +{ + uint16_t paddr = (pidx << 5) | (cidx << 1); + + vdp_setup_access(paddr, VDP_MEM_WRITE, VDP_MEM_CRAM); + VDP_PORT_DATA = VDP_PACK_RGB(r, g, b); +} + +void vdp_init(void); + +#endif /* VDP_H_ */