--- /dev/null
+*.o
+*.swp
+*.d
+*.bin
+*.elf
+*.map
--- /dev/null
+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)
+dep = $(csrc:.c=.d)
+
+name = life
+elf = $(name).elf
+bin = $(name).bin
+
+warn = -pedantic -Wall
+dbg = -g
+opt = -O2
+def = -D__NO_CTYPE
+inc = -I. -Isrc -Isrc/libc
+
+TC = m68k-linux-gnu-
+
+CC = $(TC)gcc
+AS = $(TC)as
+LD = $(TC)ld
+OBJCOPY = $(TC)objcopy
+
+CFLAGS = -m68000 -ffreestanding -fno-builtin -fcommon $(warn) $(dbg) $(opt) $(def) $(inc) -MMD
+CPPFLAGS = $(def)
+ASFLAGS = -m68000 $(inc)
+LDFLAGS = -T megadrive.ld -print-gc-sections \
+ -L/usr/lib/gcc-cross/m68k-linux-gnu/11 -lgcc
+
+$(bin): $(elf)
+ $(OBJCOPY) -O binary $< $@
+
+$(elf): $(obj)
+ $(LD) -o $@ $(obj) -Map link.map $(LDFLAGS)
+
+-include $(dep)
+
+.PHONY: clean
+clean:
+ rm -f $(obj) $(elf) $(bin)
+
+.PHONY: cleandep
+cleandep:
+ rm -f $(dep)
+
+.PHONY: run
+run: $(bin)
+ mednafen $<
+
+.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
--- /dev/null
+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);
+}
--- /dev/null
+| vi:filetype=gas68k:
+| 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
+
+.global enable_intr
+enable_intr:
+ andi.w #0xf8ff, %sr
+ rts
+
+.global disable_intr
+disable_intr:
+ ori.w #0x0300, %sr
+ rts
+
+| interrupt handlers
+intr_fatal:
+ stop #0x2700
+
+| .extern vblank_handler
+
+intr_hblank:
+| move.l #0xc0020000, VDP_PORT_CTL
+|
+| move.w testcol, %d0
+| move.w %d0, VDP_PORT_DATA
+| rol.b #4, %d0
+| move.w %d0, testcol
+|
+ rte
+
+intr_vblank:
+| jsr vblank_handler
+ rte
--- /dev/null
+int main(void)
+{
+ return 0;
+}
--- /dev/null
+| the following will go into the custom .romhdr section which will be placed at
+| address 100h of the binary by the linker (see megadrive.ld).
+ .section .romhdr,"a"
+
+#ifndef GAMENAME
+#define GAMENAME "life"
+#endif
+#ifndef VERSTR
+#define VERSTR "01"
+#endif
+
+ .ascii "SEGA MEGA DRIVE (C)MINDLAPSE2022"
+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
--- /dev/null
+| vi:filetype=gas68k:
+ .text
+ .extern main
+
+ .global start
+ .global halt_cpu
+start:
+ jsr disable_intr
+
+ | write SEGA to 0xa14000 to convince the TMSS to run us
+ move.l 0x100, %d0
+ move.l %d0, 0xa14000
+
+ | 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
+ jsr enable_intr
+
+ jsr main
+halt_cpu:
+ stop #0x2700
--- /dev/null
+| vi:filetype=asm68k:
+ .equ VDP_PORT_DATA, 0xc00000
+ .equ VDP_PORT_CTL, 0xc00004
+
+ .equ VDP_REG_MODE1, 0
+ .equ VDP_REG_MODE2, 1
+
+ .equ VDP_WRITE_VRAM, 0x40000000
+ .equ VDP_WRITE_CRAM, 0xc0000000
+ .equ VDP_WRITE_VSRAM, 0x40000010
+ .equ VDP_READ_VRAM, 0
+ .equ VDP_READ_CRAM, 0x00000020
+ .equ VDP_READ_VSRAM, 0x00000010