*.o
-*.elf
-*.hex
-.*.swp
-cscope.out
-tags
+*.gen
--- /dev/null
+CROSS = m68k-elf-\r
+CC = $(CROSS)gcc\r
+AS = $(CROSS)as\r
+LD = $(CROSS)ld\r
+OBJCOPY = $(CROSS)objcopy\r
+\r
+ASFLAGS += -m68000 --register-prefix-optional --bitwise-or -pic\r
+CPPFLAGS += -Wa,-m68000 -Wa,--register-prefix-optional -Wa,--bitwise-or -Wa,-pic\r
+\r
+TARGET = hexed.bin\r
+OBJS = sega_gcc.o hexed.o transfer.o font.o\r
+\r
+all: $(TARGET)\r
+\r
+$(TARGET): a.out\r
+ $(OBJCOPY) -I elf32-m68k -O binary $^ $@\r
+\r
+a.out: $(OBJS)\r
+ $(LD) -Tmd.ld -Map $(TARGET).map $^\r
+\r
+clean:\r
+ $(RM) $(TARGET) $(OBJS) $(TARGET).map a.out pc_transfer\r
+\r
+\r
+pc_transfer: pc_transfer.c\r
+ gcc -Wall -O2 -ggdb -o $@ $^ -lz\r
+\r
+%.o: %.S\r
+ $(CC) -c -o $@ $^ $(CPPFLAGS)\r
+\r
+%.bin: %.o\r
+ $(OBJCOPY) -I elf32-m68k -O binary $^ $@\r
+\r
+\r
+# ----------- release -----------\r
+ifneq ($(findstring rel,$(MAKECMDGOALS)),)\r
+ifeq ($(VER),)\r
+$(error need VER)\r
+endif\r
+endif\r
+\r
+rel: hexed.bin readme.txt src\r
+ zip -9 -r hexed_r$(VER).zip $^\r
+ rm -rf src\r
+\r
+src: hexed.s font.s sega_gcc.s md.ld Makefile\r
+ mkdir src\r
+ cp $^ src/\r
--- /dev/null
+.text
+.globl font
+
+font:
+ dc.b 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ dc.b 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ dc.b 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ dc.b 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ dc.b 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ dc.b 0x3C,0x42,0x99,0xBD,0xBD,0x99,0x42,0x3C,0x3C,0x42,0x81,0x81,0x81,0x81,0x42,0x3C
+ dc.b 0xFE,0x82,0x8A,0xD2,0xA2,0x82,0xFE,0x00,0xFE,0x82,0x82,0x82,0x82,0x82,0xFE,0x00
+ dc.b 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x64,0x74,0x7C,0x38,0x00,0x00
+ dc.b 0x80,0xC0,0xF0,0xFC,0xF0,0xC0,0x80,0x00,0x01,0x03,0x0F,0x3F,0x0F,0x03,0x01,0x00
+ dc.b 0x18,0x3C,0x7E,0x18,0x7E,0x3C,0x18,0x00,0xEE,0xEE,0xEE,0xCC,0x00,0xCC,0xCC,0x00
+ dc.b 0x00,0x00,0x30,0x68,0x78,0x30,0x00,0x00,0x00,0x38,0x64,0x74,0x7C,0x38,0x00,0x00
+ dc.b 0x3C,0x66,0x7A,0x7A,0x7E,0x7E,0x3C,0x00,0x0E,0x3E,0x3A,0x22,0x26,0x6E,0xE4,0x40
+ dc.b 0x18,0x3C,0x7E,0x3C,0x3C,0x3C,0x3C,0x00,0x3C,0x3C,0x3C,0x3C,0x7E,0x3C,0x18,0x00
+ dc.b 0x08,0x7C,0x7E,0x7E,0x7C,0x08,0x00,0x00,0x10,0x3E,0x7E,0x7E,0x3E,0x10,0x00,0x00
+ dc.b 0x58,0x2A,0xDC,0xC8,0xDC,0x2A,0x58,0x00,0x24,0x66,0xFF,0xFF,0x66,0x24,0x00,0x00
+ dc.b 0x00,0x10,0x10,0x38,0x38,0x7C,0xFE,0x00,0xFE,0x7C,0x38,0x38,0x10,0x10,0x00,0x00
+ dc.b 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x1C,0x1C,0x18,0x00,0x18,0x18,0x00
+ dc.b 0x6C,0x6C,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x28,0x7C,0x28,0x7C,0x28,0x00,0x00
+ dc.b 0x10,0x38,0x60,0x38,0x0C,0x78,0x10,0x00,0x40,0xA4,0x48,0x10,0x24,0x4A,0x04,0x00
+ dc.b 0x18,0x34,0x18,0x3A,0x6C,0x66,0x3A,0x00,0x18,0x18,0x20,0x00,0x00,0x00,0x00,0x00
+ dc.b 0x30,0x60,0x60,0x60,0x60,0x60,0x30,0x00,0x0C,0x06,0x06,0x06,0x06,0x06,0x0C,0x00
+ dc.b 0x10,0x54,0x38,0x7C,0x38,0x54,0x10,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00
+ dc.b 0x00,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00,0x00,0x3E,0x00,0x00,0x00
+ dc.b 0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x04,0x08,0x10,0x20,0x40,0x00,0x00
+ dc.b 0x38,0x4C,0xC6,0xC6,0xC6,0x64,0x38,0x00,0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00
+ dc.b 0x7C,0xC6,0x0E,0x3C,0x78,0xE0,0xFE,0x00,0x7E,0x0C,0x18,0x3C,0x06,0xC6,0x7C,0x00
+ dc.b 0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x00,0xFC,0xC0,0xFC,0x06,0x06,0xC6,0x7C,0x00
+ dc.b 0x3C,0x60,0xC0,0xFC,0xC6,0xC6,0x7C,0x00,0xFE,0xC6,0x0C,0x18,0x30,0x30,0x30,0x00
+ dc.b 0x78,0xC4,0xE4,0x78,0x86,0x86,0x7C,0x00,0x7C,0xC6,0xC6,0x7E,0x06,0x0C,0x78,0x00
+ dc.b 0x00,0x00,0x18,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x18,0x18,0x30
+ dc.b 0x1C,0x38,0x70,0xE0,0x70,0x38,0x1C,0x00,0x00,0x7C,0x00,0x00,0x7C,0x00,0x00,0x00
+ dc.b 0x70,0x38,0x1C,0x0E,0x1C,0x38,0x70,0x00,0x7C,0xC6,0xC6,0x1C,0x18,0x00,0x18,0x00
+ dc.b 0x3C,0x42,0x99,0xA1,0xA5,0x99,0x42,0x3C,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0x00
+ dc.b 0xFC,0xC6,0xC6,0xFC,0xC6,0xC6,0xFC,0x00,0x3C,0x66,0xC0,0xC0,0xC0,0x66,0x3C,0x00
+ dc.b 0xF8,0xCC,0xC6,0xC6,0xC6,0xCC,0xF8,0x00,0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xFE,0x00
+ dc.b 0xFE,0xC0,0xC0,0xFC,0xC0,0xC0,0xC0,0x00,0x3E,0x60,0xC0,0xCE,0xC6,0x66,0x3E,0x00
+ dc.b 0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00,0x7E,0x18,0x18,0x18,0x18,0x18,0x7E,0x00
+ dc.b 0x06,0x06,0x06,0x06,0xC6,0xC6,0x7C,0x00,0xC6,0xCC,0xD8,0xF0,0xF8,0xDC,0xCE,0x00
+ dc.b 0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x00,0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0x00
+ dc.b 0xC6,0xE6,0xF6,0xFE,0xDE,0xCE,0xC6,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00
+ dc.b 0xFC,0xC6,0xC6,0xC6,0xFC,0xC0,0xC0,0x00,0x7C,0xC6,0xC6,0xC6,0xDE,0xCC,0x7A,0x00
+ dc.b 0xFC,0xC6,0xC6,0xCE,0xF8,0xDC,0xCE,0x00,0x78,0xCC,0xC0,0x7C,0x06,0xC6,0x7C,0x00
+ dc.b 0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00
+ dc.b 0xC6,0xC6,0xC6,0xEE,0x7C,0x38,0x10,0x00,0xC6,0xC6,0xD6,0xFE,0xFE,0xEE,0xC6,0x00
+ dc.b 0xC6,0xEE,0x3C,0x38,0x7C,0xEE,0xC6,0x00,0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x00
+ dc.b 0xFE,0x0E,0x1C,0x38,0x70,0xE0,0xFE,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x3C,0x00
+ dc.b 0x60,0x60,0x30,0x18,0x0C,0x06,0x06,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00
+ dc.b 0x18,0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF
+ dc.b 0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x06,0x3E,0x66,0x66,0x3C,0x00
+ dc.b 0x60,0x7C,0x66,0x66,0x66,0x66,0x7C,0x00,0x00,0x3C,0x66,0x60,0x60,0x66,0x3C,0x00
+ dc.b 0x06,0x3E,0x66,0x66,0x66,0x66,0x3E,0x00,0x00,0x3C,0x66,0x66,0x7E,0x60,0x3C,0x00
+ dc.b 0x1C,0x30,0x78,0x30,0x30,0x30,0x30,0x00,0x00,0x3E,0x66,0x66,0x66,0x3E,0x06,0x3C
+ dc.b 0x60,0x7C,0x76,0x66,0x66,0x66,0x66,0x00,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x00
+ dc.b 0x0C,0x00,0x1C,0x0C,0x0C,0x0C,0x0C,0x38,0x60,0x60,0x66,0x6C,0x78,0x6C,0x66,0x00
+ dc.b 0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0xEC,0xFE,0xFE,0xFE,0xD6,0xC6,0x00
+ dc.b 0x00,0x7C,0x76,0x66,0x66,0x66,0x66,0x00,0x00,0x3C,0x66,0x66,0x66,0x66,0x3C,0x00
+ dc.b 0x00,0x7C,0x66,0x66,0x66,0x7C,0x60,0x60,0x00,0x3E,0x66,0x66,0x66,0x3E,0x06,0x06
+ dc.b 0x00,0x7E,0x70,0x60,0x60,0x60,0x60,0x00,0x00,0x3C,0x60,0x3C,0x06,0x66,0x3C,0x00
+ dc.b 0x30,0x78,0x30,0x30,0x30,0x30,0x1C,0x00,0x00,0x66,0x66,0x66,0x66,0x6E,0x3E,0x00
+ dc.b 0x00,0x66,0x66,0x66,0x66,0x3C,0x18,0x00,0x00,0xC6,0xD6,0xFE,0xFE,0x7C,0x6C,0x00
+ dc.b 0x00,0x66,0x3C,0x18,0x3C,0x66,0x66,0x00,0x00,0x66,0x66,0x66,0x66,0x3E,0x06,0x3C
+ dc.b 0x00,0x7E,0x0C,0x18,0x30,0x60,0x7E,0x00,0x0E,0x18,0x0C,0x38,0x0C,0x18,0x0E,0x00
+ dc.b 0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x00,0x70,0x18,0x30,0x1C,0x30,0x18,0x70,0x00
+ dc.b 0x00,0x00,0x76,0xDC,0x00,0x00,0x00,0x00,0x10,0x28,0x10,0x54,0xAA,0x44,0x00,0x00
+
--- /dev/null
+###############################################################################
+#
+# Copyright (c) 2009,2011 Gražvydas Ignotas
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the organization nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Assemble with gas
+# --register-prefix-optional --bitwise-or
+#
+
+.equ USE_VINT, 0
+.equ COPY_TO_EXP, 1
+.equ RELOCATE_TO_RAM, 1
+
+.text
+.globl main
+.globl INT
+.globl VBL
+.globl return_to_main
+
+##################################################
+# #
+# Register and bitmask definitions #
+# #
+##################################################
+
+.equ GFXDATA, 0xc00000
+.equ GFXCNTL, 0xc00004
+
+.equ VDP0_E_HBI, 0x10
+.equ VDP0_E_DISPLAY, 0x02
+.equ VDP0_PLTT_FULL, 0x04
+
+.equ VDP1_SMS_MODE, 0x80
+.equ VDP1_E_DISPLAY, 0x40
+.equ VDP1_E_VBI, 0x20
+.equ VDP1_E_DMA, 0x10
+.equ VDP1_NTSC, 0x00
+.equ VDP1_PAL, 0x08
+.equ VDP1_MODE5, 0x04
+
+.equ VDP12_STE, 0x08
+.equ VDP12_SCREEN_V224, 0x00
+.equ VDP12_SCREEN_V448, 0x04
+.equ VDP12_PROGRESSIVE, 0x00
+.equ VDP12_INTERLACED, 0x02
+.equ VDP12_SCREEN_H256, 0x00
+.equ VDP12_SCREEN_H320, 0x81
+
+.equ VDP16_MAP_V32, 0x00
+.equ VDP16_MAP_V64, 0x10
+.equ VDP16_MAP_V128, 0x30
+.equ VDP16_MAP_H32, 0x00
+.equ VDP16_MAP_H64, 0x01
+.equ VDP16_MAP_H128, 0x03
+
+.equ MMODE_MAIN, 0
+.equ MMODE_VAL_INPUT, 1
+.equ MMODE_EDIT_VAL, 2
+.equ MMODE_GOTO, 3
+.equ MMODE_START_MENU, 4
+.equ MMODE_GOTO_PREDEF, 5
+.equ MMODE_JMP_ADDR, 6
+.equ MMODE_PC, 7
+
+.equ predef_addr_cnt, ((predef_addrs_end-predef_addrs)/4)
+
+##################################################
+# #
+# MACROS #
+# #
+##################################################
+
+
+# Write val to VDP register reg
+.macro write_vdp_r_dst reg val dst
+ move.w #((\reg << 8) + 0x8000 + \val),\dst
+.endm
+
+# Write val to VDP register reg, vdp addr in a3
+.macro write_vdp_reg reg val
+ write_vdp_r_dst \reg, \val, (a3)
+.endm
+
+# Set up address in VDP, control port in dst
+.macro VRAM_ADDR adr dst
+ move.l #(0x40000000 | ((\adr & 0x3fff) << 16) | (\adr >> 14)),\dst
+.endm
+
+.macro VSRAM_ADDR adr dst
+ move.l #(0x40000010 | ((\adr & 0x3fff) << 16) | (\adr >> 14)),\dst
+.endm
+
+
+# make VDP word from address adr and store in d0
+.macro XRAM_ADDR_var adr
+ move.l \adr,d0
+ lsl.l #8,d0
+ lsl.l #8,d0
+ rol.l #2,d0
+ lsl.b #2,d0
+ lsr.l #2,d0
+.endm
+
+
+.macro VRAM_ADDR_var adr
+ XRAM_ADDR_var \adr
+ or.l #0x40000000,d0
+.endm
+
+
+.macro CRAM_ADDR_var adr
+ XRAM_ADDR_var \adr
+ or.l #0xc0000000,d0
+.endm
+
+
+# convert tile coords in d0, d1 to nametable addr to a0
+.macro XY2NT
+ lsl.w #6,d1
+ add.w d1,d0
+ lsl.w #1,d0
+ movea.l #0xe000,a0
+ add.w d0,a0
+.endm
+
+# check if some d-pad button (and modifier) is pressed
+.macro do_dpad bit op val
+ btst.l #\bit,d0
+ beq 0f
+ \op \val,a6
+ bra dpad_end
+0:
+.endm
+
+# convert a6 to normal addr
+# destroys d0
+.macro mk_a6_addr reg
+ move.l a6,\reg
+ moveq.l #0,d0
+ move.b \reg,d0
+ lsr.l #8,\reg
+ add.l d0,\reg
+.endm
+
+.macro change_mode mode_new mode_back
+ and.w #0xc0ff,d7
+ or.w #(\mode_back<<11)|(\mode_new<<8),d7
+.endm
+
+# destroys a0,d0-d2
+.macro menu_text str x y pal
+ lea (\str,pc),a0
+ move.l #\x,d0
+ move.l #\y,d1
+ move.l #0x8000|(\pal<<13),d2
+ jsr print
+.endm
+
+#################################################
+# #
+# DATA #
+# #
+#################################################
+
+colors:
+ dc.w 0x0000,0x0eee,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
+ dc.w 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
+ dc.w 0x0000,0x02e2,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
+ dc.w 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
+ dc.w 0x0000,0x0e44,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
+ dc.w 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
+ dc.w 0x0000,0x044e,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
+ dc.w 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
+colors_end:
+
+
+sprite_data:
+ /* Y size link attr X */
+ dc.w 0; dc.b 0x05; dc.b 0; dc.w 0x6002; dc.w 0
+sprite_data_end:
+
+predef_addrs:
+ dc.l 0x000000, 0x200000, 0x400000, 0xa00000, 0xa10000
+ dc.l 0xa11100, 0xa12000, 0xa13000, 0xa14000, 0xa15100
+ dc.l 0xc00000
+predef_addrs_end:
+
+safe_addrs:
+ dc.l 0x000000, 0x7fffff
+ dc.l 0xe00000, 0xffffff
+ dc.l 0xa00000, 0xa100ff
+ dc.l 0xa11000, 0xa113ff
+ dc.l 0xa12000, 0xa120ff
+ dc.l 0xa13000, 0xa130ff
+safe_addrs_end:
+ dc.l 0xa15100, 0xa1513f
+safe_addrs_end_32x:
+ dc.l 0xa15180, 0xa153ff
+safe_addrs_end_32x_vdp:
+
+sizeof_bin:
+ dc.l _edata
+
+txt_edit:
+ .ascii "- edit -\0"
+txt_a_confirm:
+ .ascii "A-confirm\0"
+txt_about:
+ .ascii "hexed r2\0"
+txt_goto:
+ .ascii "Go to address\0"
+txt_goto_predef:
+ .ascii "Go to (predef)\0"
+txt_jmp_addr:
+ .ascii "Jump to address\0"
+txt_dump:
+ .ascii "PC Transfer\0"
+txt_dtack:
+ .ascii "DTACK safety\0"
+txt_transfer_ready:
+ .ascii "Transfer Ready\0"
+txt_working:
+ .ascii "PC mode \0"
+txt_dtack_err:
+ .ascii "DTACK err?\0"
+txt_exc:
+ .ascii "Exception \0"
+
+##################################################
+# #
+# MAIN PROGRAM #
+# #
+##################################################
+
+# global regs:
+# a6 = page_start[31:8]|cursor_offs[7:0]
+# d7 = old_inputs[31:16]|edit_bytes[15:14]|g_mode_old[13:11]|g_mode[10:8]|irq_cnt[7:0]
+# d6 = edit_word_save[31:15]|edit_done[7]|no_dtack_detect[4]|autorep_cnt[3:0]
+# d5 = main: tmp
+# edit: edit_word[31:8]|edit_pos[4:2]|byte_cnt[1:0]
+# menu: sel
+
+.align 2
+
+main:
+ /* make sure io port 2 is doing inputs */
+ move.b #0,(0xa1000b).l
+ /* make sure irqs are masked */
+ move.w #0x2700,sr
+ /* take care of TMSS */
+ move.b (0xa10000).l,d0
+ andi.b #0x0f,d0
+ beq no_tmss
+ move.l #0x53454741,(0xa14000).l
+ /* want cart, not OS rom if cart pops in */
+ move.w #1,(0xa14100).l
+ /* touch VDP after TMSS setup? */
+ tst.w (0xc00004).l
+no_tmss:
+
+ /* want to do early PC transfer (with RAM/VRAM intact and such)?
+ * also give time PC to see start condition */
+ move.l #0x2000,d0
+0: dbra d0,0b
+
+ move.l #0xa10005,a0
+ btst.b #5,(a0)
+ bne no_early_transfer
+move.b #1,(0)
+ move.b #0x40,(0xa1000b).l /* port 2 ctrl */
+ move.b #0x00,(a0) /* port 2 data - start with TH low */
+ move.l #0x2000,d0
+0:
+ btst.b #4,(a0)
+ beq do_early_transfer
+ dbra d0,0b
+
+move.b #2,(0)
+ move.b #0,(0xa1000b).l
+ bra no_early_transfer /* timeout */
+
+do_early_transfer:
+move.b #9,(0)
+ bsr do_transfer
+
+no_early_transfer:
+
+.if COPY_TO_EXP
+ /* copy to expansion device if magic number is set */
+ move.l #0x400000,a1
+ cmp.w #0x1234,(a1)
+ bne 0f
+
+ move.l #0,a0
+ move.l (sizeof_bin,pc),d0
+ lsr.l #3,d0
+1:
+ move.l (a0)+,(a1)+
+ move.l (a0)+,(a1)+
+ dbra d0,1b
+0:
+.endif
+
+.if RELOCATE_TO_RAM
+ /* we could be relocated by 32x or something else, adjust start addr */
+ lea (pc),a0
+ move.l a0,d0
+ and.l #0xff0000,d0
+ move.l d0,a0
+
+ /* copy, assume 8K size */
+ move.l #0xFF0100,a1
+ move.l (sizeof_bin,pc),d0
+ lsr.l #3,d0
+1:
+ move.l (a0)+,(a1)+
+ move.l (a0)+,(a1)+
+ dbra d0,1b
+
+ /* copy test code */
+ lea (test_code,pc),a0
+ move.l #0xffc000,a1
+ move.w #(test_code_end - test_code)/2-1,d0
+1:
+ move.w (a0)+,(a1)+
+ dbra d0,1b
+
+ lea (0f,pc),a0
+ move.l a0,d0
+ and.l #0x00ffff,d0
+ add.l #0xFF0100,d0
+ move.l d0,a0
+
+ /* patch test code */
+ move.l #0xffc000,a1
+ add.w #(test_code_ret_op-test_code+2),a1
+ move.l a0,(a1)
+
+ jmp (a0)
+0:
+.endif
+
+ movea.l #0,a6
+ move.l #0x8000,d7
+ moveq.l #0,d6
+
+ /* Init pads */
+ move.b #0x40,(0xa10009).l
+ move.b #0x40,(0xa10003).l
+
+ /* Initialize VDP */
+ jsr init_gfx
+
+ /* Clear h/v scroll */
+ movea.l #GFXDATA,a0
+ VRAM_ADDR 0x8000,(GFXCNTL)
+ move.l #0,(a0)
+ VSRAM_ADDR 0,(GFXCNTL)
+ move.l #0,(a0)
+
+ /* Load color data */
+ movea.l #0,a0
+ lea (colors,pc),a1
+ moveq.l #(colors_end-colors)/2,d0
+ jsr load_colors
+
+ /* load font patterns */
+ movea.l #GFXDATA,a0
+ lea (font,pc),a1
+ VRAM_ADDR 0,(GFXCNTL)
+ move.w #128*8,d3
+font_loop:
+ moveq.l #8-1,d2
+ moveq.l #0,d1
+ move.b (a1)+,d0
+0:
+ lsr.b #1,d0
+ roxl.l #1,d1
+ ror.l #5,d1
+ dbra d2,0b
+
+ rol.l #1,d1 /* fixup */
+ move.l d1,(a0)
+ dbra d3,font_loop
+
+ /* generate A layer map */
+ movea.l #0xe000,a1
+ move.l #28-1,d4
+lmaploop0:
+ movea.l a1,a0
+ jsr load_prepare
+
+ move.l #64/2-1,d3
+0: move.l #0x00000000,(a0)
+ dbra d3,0b
+
+ add.l #64*2,a1
+ dbra d4,lmaploop0
+
+ /* generate B layer map */
+ movea.l #0xc000,a0
+ jsr load_prepare
+
+ move.l #64*28/2-1,d3
+0: move.l #0x00000000,(a0)
+ dbra d3,0b
+
+ /* upload sprite data */
+ movea.l #0xfc00,a0
+ jsr load_prepare
+ lea (sprite_data,pc),a1
+
+ move.l #(sprite_data_end-sprite_data)/2-1,d3
+0: move.l (a1)+,(a0)
+ dbra d3,0b
+
+.if USE_VINT
+ /* wait for vsync before unmask */
+ jsr wait_vsync_poll
+
+ /* wait a bit to avoid nested vint */
+ move.w #20,d0
+0:
+ dbra d0,0b /* 10 cycles to go back */
+
+ /* enable and unmask vint */
+ write_vdp_r_dst 1,(VDP1_E_VBI | VDP1_E_DISPLAY | VDP1_MODE5),(GFXCNTL)
+ move.w #0x2000,sr
+.endif
+
+##################################################
+
+forever:
+.if USE_VINT
+ jsr wait_vsync
+.else
+ jsr wait_vsync_poll
+ jsr VBL
+.endif
+ bra forever
+
+
+INT:
+ /* let's hope VRAM is already set up.. */
+ lea (txt_exc,pc),a0
+ move.l #9,d0
+ move.l #27,d1
+ move.l #0xe000,d2
+ jsr print
+ bra forever
+
+##################################################
+
+VBL:
+ addq.b #1,d7
+# movem.l d0-d4/a0-a5,-(a7)
+
+ btst.b #5,(0xa10005).l
+ bne no_auto_transfer
+ change_mode MMODE_PC, MMODE_MAIN
+ write_vdp_r_dst 12,(VDP12_SCREEN_V224 | VDP12_SCREEN_H320 | VDP12_STE),(GFXCNTL)
+
+no_auto_transfer:
+ moveq.l #0,d0
+ move.w d7,d0
+ lsr.w #6,d0
+ and.w #0x1c,d0
+ lea (jumptab,pc,d0),a0
+ jmp (a0)
+jumptab:
+ /* branch insns here because we want to be position independent */
+ bra mode_main
+ bra mode_val_input
+ bra mode_edit_val /* edit val in editor */
+ bra mode_goto
+ bra mode_start_menu
+ bra mode_goto_predef
+ bra mode_jmp_addr
+ bra mode_transfer
+
+##################### main #######################
+
+mode_main:
+ /* assume we will hang */
+ lea (txt_dtack_err,pc),a0
+ move.l #9,d0
+ move.l #27,d1
+ move.l #0xe000,d2
+ jsr print
+
+ moveq.l #0,d1
+ move.l a6,d0
+ move.b d0,d1
+ lsr.l #8,d0
+ move.l d0,a1 /* current addr */
+ lsr.b #3,d1
+ neg.b d1
+ add.b #27-1,d1 /* line where the cursor sits */
+ swap d1
+
+ movea.l #0xe002,a2
+ move.l #27-1,d5 /* line counter for dbra */
+ or.l d1,d5
+
+draw_row:
+ move.l a2,a0
+ jsr load_prepare
+
+ btst.l #15,d7
+ beq 0f
+ move.w #' ',(a0)
+0:
+ /* addr */
+ move.l a1,d2
+ moveq.l #6,d3
+ jsr print_hex_preped
+
+ btst.l #4,d6
+ bne draw_row_safe
+
+ bsr get_safety_mask
+ cmp.b #0xf,d0
+ beq draw_row_safe
+
+# unsafe or partially safe
+draw_row_hsafe:
+ move.l d0,d4
+ swap d4 /* mask in upper word */
+ btst.l #15,d7
+ bne draw_row_hsafe_words_pre
+
+draw_row_hsafe_bytes_pre:
+ /* 8 bytes */
+ moveq.l #2,d3
+ move.w #3,d4
+
+draw_row_hsafe_bytes:
+ move.w #' ',(a0)
+ move.b d4,d0
+ add.b #16,d0
+ btst.l d0,d4
+ bne 0f
+ move.l #'?'|('?'<<16),(a0)
+ move.w #' ',(a0)
+ move.l #'?'|('?'<<16),(a0)
+ bra 1f
+0:
+ move.b (0,a1),d2
+ jsr print_hex_preped
+ move.w #' ',(a0)
+ move.b (1,a1),d2
+ jsr print_hex_preped
+1:
+ addq.l #2,a1
+ dbra d4,draw_row_hsafe_bytes
+
+ move.w #' ',(a0)
+
+ move.l d5,d0
+ swap d0
+ cmp.w d5,d0
+ beq draw_cursor_unsafe_byte
+ bra draw_chars_hsafe_pre
+
+draw_row_hsafe_words_pre:
+ /* 4 shorts */
+ moveq.l #4,d3
+ move.w #3,d4
+
+draw_row_hsafe_words:
+ move.w #' ',(a0)
+ move.b d4,d0
+ add.b #16,d0
+ btst.l d0,d4
+ bne 0f
+ move.l #'?'|('?'<<16),(a0)
+ move.l #'?'|('?'<<16),(a0)
+ bra 1f
+0:
+ move.w (a1),d2
+ jsr print_hex_preped
+1:
+ addq.l #2,a1
+ dbra d4,draw_row_hsafe_words
+
+ move.l #(' '<<16)|' ',(a0)
+
+ move.l d5,d0
+ swap d0
+ cmp.w d5,d0
+ beq draw_cursor_unsafe_word
+
+draw_chars_hsafe_pre:
+ subq.l #8,a1
+ move.w #3,d4
+ moveq.l #0,d0
+
+draw_chars_hsafe:
+ move.b d4,d0
+ add.b #16,d0
+ btst.l d0,d4
+ bne 0f
+ move.l #'?'|('?'<<16),(a0)
+ bra draw_chars_hsafe_next
+0:
+ btst.l #15,d7 /* must perform correct read type */
+ bne 0f /* doing byte reads on security reg hangs */
+ move.b (0,a1),d0
+ lsl.l #8,d0
+ move.b (1,a1),d0
+ bra 1f
+0:
+ move.w (a1),d0
+1:
+ ror.l #8,d0
+ move.b d0,d1
+ sub.b #0x20,d1
+ cmp.b #0x60,d1
+ blo 0f
+ move.b #'.',d0
+0:
+ move.w d0,(a0)
+
+ move.b #0,d0
+ rol.l #8,d0
+ move.b d0,d1
+ sub.b #0x20,d1
+ cmp.b #0x60,d1
+ blo 0f
+ move.b #'.',d0
+0:
+ move.w d0,(a0)
+
+draw_chars_hsafe_next:
+ addq.l #2,a1
+ dbra d4,draw_chars_hsafe
+
+ move.l #(' '<<16)|' ',(a0)
+ add.w #0x80,a2
+ dbra d5,draw_row
+ bra draw_status_bar
+
+
+# normal draw
+draw_row_safe:
+ btst.l #15,d7
+ bne draw_row_words
+
+draw_row_bytes:
+ /* 8 bytes */
+ moveq.l #2,d3
+ moveq.l #8-1,d4
+draw_bytes:
+ move.w #' ',(a0)
+ move.b (a1)+,d2
+ jsr print_hex_preped
+ dbra d4,draw_bytes
+
+ move.w #' ',(a0)
+
+ move.l d5,d0
+ swap d0
+ cmp.w d5,d0
+ beq draw_cursor_byte
+ bra draw_chars_pre
+
+draw_row_words:
+ /* 4 shorts */
+ moveq.l #4,d3
+ moveq.l #4-1,d4
+draw_words:
+ move.w #' ',(a0)
+ move.w (a1)+,d2
+ jsr print_hex_preped
+ dbra d4,draw_words
+
+ move.l #(' '<<16)|' ',(a0)
+
+ move.l d5,d0
+ swap d0
+ cmp.w d5,d0
+ beq draw_cursor_word
+
+draw_chars_pre:
+ /* 8 chars */
+ subq.l #8,a1
+ moveq.l #8-1,d4
+draw_chars:
+ move.b (a1)+,d0
+ move.b d0,d1
+ sub.b #0x20,d1
+ cmp.b #0x60,d1
+ blo 0f
+ move.w #'.',d0
+0:
+ move.w d0,(a0)
+ dbra d4,draw_chars
+
+ move.l #(' '<<16)|' ',(a0)
+
+ add.w #0x80,a2
+ dbra d5,draw_row
+
+
+draw_status_bar:
+ /* status bar */
+ move.l a2,a0
+ jsr load_prepare
+
+ btst.l #15,d7
+ beq 0f
+ move.w #' ',(a0)
+0:
+ mk_a6_addr d2
+ move.l #0x4006,d3
+ jsr print_hex_preped
+
+ /* clear error */
+ moveq.l #5-1,d0
+0:
+ move.l #' '|(' '<<16),(a0)
+ move.l #' '|(' '<<16),(a0)
+ dbra d0,0b
+
+
+ /* handle input */
+ jsr get_input /* x0cbrldu x1sa00du */
+
+ btst.l #16+4,d0 /* A - scroll modifier */
+ beq input_noa
+
+ do_dpad 16+0, sub.l, #0x0800
+ do_dpad 16+1, add.l, #0x0800
+ do_dpad 16+10, sub.l, #0xd800
+ do_dpad 16+11, add.l, #0xd800
+input_noa:
+ moveq.l #0,d1
+ move.w d7,d1
+ lsr.w #7,d1
+ lsr.w #7,d1
+
+ do_dpad 0, subq.l, #0x0008
+ do_dpad 1, addq.l, #0x0008
+ do_dpad 10, sub.l, d1
+ do_dpad 11, add.l, d1
+
+dpad_end:
+ /* update addr */
+ move.l a6,d1
+ cmp.b #0xf0,d1
+ blo 0f
+ sub.l #0xd800,a6
+ add.w #0x00d8,a6
+ bra 1f
+0:
+ cmp.b #0xd8,d1
+ blo 1f
+ add.l #0xd800,a6
+ sub.w #0x00d8,a6
+1:
+
+ /* other btns */
+ moveq.l #0,d1
+ btst.l #12,d0 /* B - switch byte/word mode */
+ beq input_nob
+ bclr.l #15,d7
+ add.w #0x4000,d7 /* changes between 01 10 */
+ move.l a6,d1
+ and.l #1,d1
+ sub.l d1,a6 /* make even, just in case */
+
+input_nob:
+ btst.l #13,d0 /* C - edit selected byte */
+ beq input_noc
+
+ change_mode MMODE_EDIT_VAL, MMODE_MAIN
+ write_vdp_r_dst 12,(VDP12_SCREEN_V224 | VDP12_SCREEN_H320 | VDP12_STE),(GFXCNTL)
+
+input_noc:
+ btst.l #5,d0 /* Start - menu */
+ beq input_nos
+
+ moveq.l #0,d5
+ change_mode MMODE_START_MENU, MMODE_MAIN
+ write_vdp_r_dst 12,(VDP12_SCREEN_V224 | VDP12_SCREEN_H320 | VDP12_STE),(GFXCNTL)
+
+input_nos:
+vbl_end:
+# movem.l (a7)+,d0-d4/a0-a5
+.if USE_VINT
+ rte
+.else
+ rts
+.endif
+
+
+draw_cursor_unsafe_byte:
+ move.l a6,d0
+ and.l #7,d0 /* byte offs */
+ move.b d0,d1
+ add.b d0,d0
+ add.b d1,d0 /* d0 *= 3 (chars) */
+ add.b d0,d0
+ lea (7*2,a2,d0),a0
+ jsr load_prepare
+ move.l #(0x2000|'?'|((0x2000|'?')<<16)),(a0)
+
+ move.l a2,a0
+ add.w #31*2,a0
+ jsr load_prepare /* restore a0 */
+ bra draw_chars_hsafe_pre
+
+draw_cursor_unsafe_word:
+ move.l a6,d0
+ and.l #7,d0 /* byte offs */
+ move.l d0,d1
+ lsr.b #1,d1 /* which word */
+ move.b d1,d2
+ lsl.b #2,d2
+ add.b d2,d1 /* num of chars to skip */
+ add.b d1,d1
+
+ lea (8*2,a2,d1),a0
+ jsr load_prepare
+ move.l #(0x2000|'?'|((0x2000|'?')<<16)),d0
+ move.l d0,(a0)
+ move.l d0,(a0)
+
+ move.l a2,a0
+ add.w #29*2,a0
+ jsr load_prepare /* restore a0 */
+ bra draw_chars_hsafe_pre
+
+
+draw_cursor_byte:
+ move.l a6,d0
+ and.l #7,d0 /* byte offs */
+ move.w #0x2002,d3
+
+ move.b (-8,a1,d0),d2
+ move.b d0,d1
+ add.b d0,d0
+ add.b d1,d0 /* d0 *= 3 (chars) */
+ add.b d0,d0
+ lea (7*2,a2,d0),a0
+ jsr load_prepare
+ jsr print_hex_preped
+
+ move.l a2,a0
+ add.w #31*2,a0
+ jsr load_prepare /* restore a0 */
+
+ bra draw_chars_pre
+
+draw_cursor_word:
+ move.l a6,d0
+ and.l #7,d0 /* byte offs */
+ move.l d0,d1
+ lsr.b #1,d1 /* which word */
+ move.b d1,d2
+ lsl.b #2,d2
+ add.b d2,d1 /* num of chars to skip */
+ add.b d1,d1
+ move.w #0x2004,d3
+
+ move.w (-8,a1,d0),d2
+ lea (8*2,a2,d1),a0
+ jsr load_prepare
+ jsr print_hex_preped
+
+ move.l a2,a0
+ add.w #29*2,a0
+ jsr load_prepare /* restore a0 */
+
+ bra draw_chars_pre
+
+
+#################### hedit #######################
+
+mode_edit_val:
+ btst.l #7,d6
+ bne mode_hedit_finish
+
+ /* read val to edit */
+ moveq.l #0,d5
+ mk_a6_addr d1
+ move.l d1,a0
+ btst.l #15,d7
+ bne 0f
+ move.b (a0),d5
+ lsl.l #8,d5
+ or.b #1,d5
+ bra 1f
+0:
+ move.w (a0),d5
+ lsl.l #8,d5
+ or.b #2,d5
+1:
+
+ change_mode MMODE_VAL_INPUT, MMODE_EDIT_VAL
+ bra vbl_end
+
+mode_hedit_finish:
+ /* write the val */
+ mk_a6_addr d1
+ move.l d1,a0
+ lsr.l #8,d5
+
+ btst.l #15,d7
+ bne 0f
+ move.b d5,(a0)
+ bra 1f
+0:
+ move.w d5,(a0)
+1:
+
+ bra return_to_main
+
+##################### goto #######################
+
+mode_goto:
+ btst.l #7,d6
+ bne mode_goto_finish
+
+ moveq.l #0,d5
+ swap d6
+ move.w d6,d5
+ swap d6
+ swap d5
+ or.b #3,d5 /* 3 bytes */
+ bclr.l #7,d6
+ change_mode MMODE_VAL_INPUT, MMODE_GOTO
+ bra vbl_end
+
+mode_goto_finish:
+ lsr.l #8,d5
+ move.l d5,d0
+ move.l d0,d1
+ and.l #7,d1
+ and.b #0xf8,d0
+ lsl.l #8,d0
+ or.l d1,d0
+ move.l d0,a6
+
+ lsr.l #8,d5
+ swap d6
+ move.w d5,d6
+ swap d6
+
+ bra return_to_main
+
+################### val edit #####################
+
+mode_val_input:
+ /* frame */
+ movea.l #0xe000+14*2+11*64*2,a1
+ moveq.l #6-1,d1
+0:
+ move.w a1,a0
+ jsr load_prepare
+ moveq.l #11-1,d0
+1:
+ move.w #0,(a0)
+ dbra d0,1b
+
+ add.w #64*2,a1
+ dbra d1,0b
+
+ /* text */
+ lea (txt_edit,pc),a0
+ move.l #15,d0
+ move.l #11,d1
+ move.l #0xc000,d2
+ jsr print
+
+ lea (txt_a_confirm,pc),a0
+ move.l #15,d0
+ move.l #15,d1
+ move.l #0xc000,d2
+ jsr print
+
+ /* edit field */
+ moveq.l #0,d0
+ moveq.l #0,d1
+ moveq.l #0,d3
+ move.b d5,d3
+ and.b #3,d3 /* edit field bytes */
+
+ move.b #19,d0
+ sub.b d3,d0
+ move.b #13,d1
+ move.l d5,d2
+ lsr.l #8,d2
+ add.b d3,d3
+ or.w #0x8000,d3
+ jsr print_hex
+
+ /* current char */
+ moveq.l #0,d0
+ moveq.l #0,d1
+
+ and.w #6,d3
+ move.b #19,d0
+ move.b d3,d1
+ lsr.b #1,d1 /* length in bytes */
+ sub.b d1,d0
+ move.b d5,d1
+ lsr.b #2,d1
+ and.b #7,d1 /* nibble to edit */
+ add.b d1,d0
+
+ sub.b d1,d3
+ sub.b #1,d3 /* chars to shift out */
+ lsl.b #2,d3
+ add.b #8,d3
+ move.l d5,d2
+ lsr.l d3,d2
+
+ move.b #13,d1
+ move.w #0xa001,d3
+ jsr print_hex
+
+ /* handle input */
+ jsr get_input /* x0cbrldu x1sa00du */
+
+ move.w d0,d1
+ and.w #0x0f00,d1
+ beq ai_no_dpad
+ move.b d5,d1
+ and.b #3,d1
+ add.b d1,d1 /* nibble count */
+ sub.b #1,d1 /* max n.t.e. val */
+ move.b d5,d2
+ lsr.b #2,d2
+ and.b #7,d2 /* nibble to edit */
+
+ move.b d0,d3
+ and.b #3,d3
+ beq ai_no_ud
+ moveq.l #0,d3
+ moveq.l #0,d4
+ move.b #0xf,d3
+ move.b #0x1,d4
+ sub.b d2,d1
+ lsl.b #2,d1
+ add.b #8,d1
+ lsl.l d1,d3 /* mask */
+ lsl.l d1,d4 /* what to add/sub */
+ move.l d5,d1
+ and.l d3,d1
+ btst.l #8,d0
+ beq 0f
+ add.l d4,d1
+ bra 1f
+0:
+ sub.l d4,d1
+1:
+ and.l d3,d1
+ eor.l #0xffffffff,d3
+ and.l d3,d5
+ or.l d1,d5
+ bra vbl_end
+
+ai_no_ud:
+ btst.l #10,d0
+ bne 0f
+ add.b #1,d2
+ bra 1f
+0:
+ sub.b #1,d2
+1:
+ cmp.b #0,d2
+ bge 0f
+ move.b d1,d2
+0:
+ cmp.b d1,d2
+ ble 0f
+ move.b #0,d2
+0:
+ and.b #0xe3,d5
+ lsl.b #2,d2
+ or.b d2,d5
+ bra vbl_end
+
+ai_no_dpad:
+ move.w d0,d1
+ and.w #0x1020,d1
+ beq ai_no_sb
+
+ bra return_to_main
+
+ai_no_sb:
+ btst.l #4,d0 /* A - confirm */
+ beq ai_no_input
+ bset.l #7,d6
+ move.w d7,d1 /* back to prev mode */
+ and.w #0x3800,d1
+ lsr.w #3,d1
+ and.w #0xc0ff,d7
+ or.w d1,d7
+
+ai_no_input:
+ bra vbl_end
+
+
+################### start menu ###################
+
+mode_start_menu:
+ /* frame */
+ bsr start_menu_box
+
+ /* text */
+ menu_text txt_about, 13, 9, 1
+ menu_text txt_goto, 13, 11, 0
+ menu_text txt_goto_predef, 13, 12, 0
+ menu_text txt_jmp_addr, 13, 13, 0
+ menu_text txt_dump, 13, 14, 0
+ menu_text txt_dtack, 13, 15, 0
+ menu_text txt_a_confirm, 13, 17, 2
+
+ /* dtack safety on/off */
+ movea.l #0xe000+26*2+15*64*2,a0
+ jsr load_prepare
+ move.w #0x8000|'O',(a0)
+ btst.l #4,d6
+ bne 0f
+ move.w #0x8000|'N',(a0)
+ bra 1f
+0:
+ move.w #0x8000|'F',(a0)
+ move.w #0x8000|'F',(a0)
+1:
+
+ /* cursor */
+ movea.l #0xe000+11*2+11*64*2,a0
+ moveq.l #0,d0
+ move.b d5,d0
+ and.b #7,d0
+ lsl.w #7,d0
+ add.w d0,a0
+ jsr load_prepare
+ move.w #'>',(a0)
+
+ /* input */
+ jsr get_input /* x0cbrldu x1sa00du */
+
+ move.w d0,d1
+ and.w #3,d1
+ beq msm_no_ud
+ move.b d5,d1
+ and.b #7,d1
+ btst.l #0,d0
+ sne d2
+ or.b #1,d2 /* up -1, down 1 */
+ add.b d2,d1
+ cmp.b #0,d1
+ bge 0f
+ move.b #4,d1
+0:
+ cmp.b #4,d1
+ ble 0f
+ move.b #0,d1
+0:
+ and.b #0xf8,d5
+ or.b d1,d5
+ bra vbl_end
+
+msm_no_ud:
+ btst.l #4,d0 /* A - confirm */
+ beq msm_no_a
+ move.b d5,d1
+ and.b #7,d1
+ bne 0f
+ change_mode MMODE_GOTO, MMODE_MAIN
+ bsr start_menu_box
+ bra vbl_end
+0:
+ cmp.b #1,d1
+ bne 0f
+ moveq.l #0,d5
+ change_mode MMODE_GOTO_PREDEF, MMODE_MAIN
+ bsr start_menu_box
+ bra vbl_end
+0:
+ cmp.b #2,d1
+ bne 0f
+ change_mode MMODE_JMP_ADDR, MMODE_MAIN
+ bsr start_menu_box
+ bra vbl_end
+0:
+ cmp.b #3,d1
+ bne 0f
+ change_mode MMODE_PC, MMODE_MAIN
+ bsr start_menu_box
+ bra vbl_end
+0:
+ cmp.b #4,d1
+ bne 0f
+ bchg.l #4,d6
+ bra vbl_end
+0:
+
+msm_no_a:
+ move.w d0,d1
+ and.w #0x3000,d1
+ beq msm_no_bc
+ bra return_to_main
+
+msm_no_bc:
+ bra vbl_end
+
+start_menu_box:
+ movea.l #0xe000+10*2+8*64*2,a1
+ move.w #11-1,d1
+0:
+ move.w a1,a0
+ jsr load_prepare
+ move.w #20-1,d0
+1:
+ move.w #0,(a0)
+ dbra d0,1b
+
+ add.w #64*2,a1
+ dbra d1,0b
+ rts
+
+################### goto predef ##################
+
+mode_goto_predef:
+ /* frame */
+ movea.l #0xe000+14*2+8*64*2,a1
+ move.l #predef_addr_cnt+2-1,d1
+0:
+ move.w a1,a0
+ jsr load_prepare
+ moveq.l #10-1,d0
+1:
+ move.w #0,(a0)
+ dbra d0,1b
+
+ add.w #64*2,a1
+ dbra d1,0b
+
+ /* draw addresses */
+ movea.l #0xe000+17*2+9*64*2,a1
+ lea (predef_addrs,pc),a2
+ move.w #predef_addr_cnt-1,d4
+ move.l #0x8006,d3
+mgp_da_loop:
+ move.w a1,a0
+ jsr load_prepare
+ move.l (a2)+,d2
+ jsr print_hex_preped
+ add.w #64*2,a1
+ dbra d4,mgp_da_loop
+
+ /* cursor */
+ movea.l #0xe000+15*2+9*64*2,a0
+ moveq.l #0,d0
+ move.b d5,d0
+ lsl.w #7,d0
+ add.w d0,a0
+ jsr load_prepare
+ move.w #'>',(a0)
+
+ /* input */
+ jsr get_input /* x0cbrldu x1sa00du */
+
+ move.w d0,d1
+ and.w #3,d1
+ beq mgp_no_ud
+ btst.l #0,d0
+ sne d2
+ or.b #1,d2 /* up -1, down 1 */
+ add.b d2,d5
+ cmp.b #0,d5
+ bge 0f
+ move.b #predef_addr_cnt-1,d5
+0:
+ cmp.b #predef_addr_cnt-1,d5
+ ble 0f
+ move.b #0,d5
+0:
+ bra vbl_end
+
+mgp_no_ud:
+ btst.l #4,d0 /* A - confirm */
+ beq mgp_no_a
+ moveq.l #0,d0
+ move.b d5,d0
+ lsl.w #2,d0
+ lea (predef_addrs,pc),a0
+ move.l (a0,d0),d5
+ lsl.l #8,d5
+ bra mode_goto_finish
+
+mgp_no_a:
+ move.w d0,d1
+ and.w #0x3000,d1
+ beq mgp_no_bc
+ bra return_to_main
+
+mgp_no_bc:
+ bra vbl_end
+
+##################### jmp ########################
+
+mode_jmp_addr:
+ btst.l #7,d6
+ bne mode_jmp_finish
+
+ moveq.l #0,d5
+ or.b #3,d5 /* 3 bytes */
+ bclr.l #7,d6
+ change_mode MMODE_VAL_INPUT, MMODE_JMP_ADDR
+ bra vbl_end
+
+mode_jmp_finish:
+ lsr.l #8,d5
+ write_vdp_r_dst 1,(VDP1_E_DISPLAY | VDP1_MODE5),(GFXCNTL) /* disable vint */
+ move.l d5,a0
+ jmp (a0)
+
+mode_transfer:
+ move.b #0x40,(0xa1000b).l /* port 2 ctrl */
+ move.b #0x00,(0xa10005).l /* port 2 data - start with TH low */
+
+ lea (txt_transfer_ready,pc),a0
+ move.l #13,d0
+ move.l #13,d1
+ move.l #0x8000,d2
+ jsr print
+
+wait_tl_low0:
+ move.b (0xa10005),d0
+ btst.b #4,d0
+ bne wait_tl_low0
+
+ menu_text txt_working, 13, 13, 0
+ bsr do_transfer
+ bra return_to_main
+
+# go back to main mode
+return_to_main:
+ bclr.l #7,d6 /* not edited */
+ change_mode MMODE_MAIN, MMODE_MAIN
+ write_vdp_r_dst 12,(VDP12_SCREEN_V224 | VDP12_SCREEN_H320),(GFXCNTL)
+ bra vbl_end
+
+
+#################################################
+# #
+# Initialize VDP registers #
+# #
+#################################################
+
+init_gfx:
+ move.l #GFXCNTL,a3
+ write_vdp_reg 0,(VDP0_E_DISPLAY | VDP0_PLTT_FULL)
+ write_vdp_reg 1,(VDP1_E_DISPLAY | VDP1_MODE5)
+ write_vdp_reg 2,(0xe000 >> 10) /* Screen map a adress */
+ write_vdp_reg 3,(0xe000 >> 10) /* Window address */
+ write_vdp_reg 4,(0xc000 >> 13) /* Screen map b address */
+ write_vdp_reg 5,(0xfc00 >> 9) /* Sprite address */
+ write_vdp_reg 6,0
+ write_vdp_reg 7,0 /* Backdrop color */
+ write_vdp_reg 0x0a,1 /* Lines per hblank interrupt */
+ write_vdp_reg 0x0b,0 /* 2-cell vertical scrolling */
+ write_vdp_reg 0x0c,(VDP12_SCREEN_V224 | VDP12_SCREEN_H320)
+ write_vdp_reg 0x0d,(0x8000 >> 10) /* Horizontal scroll address */
+ write_vdp_reg 0x0e,0
+ write_vdp_reg 0x0f,2
+ write_vdp_reg 0x10,(VDP16_MAP_V32 | VDP16_MAP_H64) /* layer size */
+ write_vdp_reg 0x11,0
+ write_vdp_reg 0x12,0
+ rts
+
+
+# get mask of bits representing safe words
+# a1 - address to check
+# destroys d0-d2, strips upper bits from a1
+get_safety_mask:
+ move.l a1,d1
+ lsl.l #8,d1
+ lsr.l #8,d1
+ lea (safe_addrs,pc),a1
+ move.w #(safe_addrs_end - safe_addrs)/8-1,d2
+ cmp.l #0x4D415253,(0xa130ec) /* 'MARS' */
+ bne no_32x
+ move.w #(safe_addrs_end_32x - safe_addrs)/8-1,d2
+ move.w (0xa15100),d0
+ and.w #3,d0
+ cmp.w #3,d0 /* ADEN and nRES */
+ bne no_32x_vdp
+ btst.b #7,d0 /* FM */
+ bne no_32x_vdp
+ move.w #(safe_addrs_end_32x_vdp - safe_addrs)/8-1,d2
+no_32x_vdp:
+no_32x:
+
+0:
+ move.l (a1)+,d0
+ cmp.l d0,d1
+ blt 1f
+ move.l (a1),d0
+ cmp.l d0,d1
+ ble addr_safe
+1:
+ addq.l #4,a1
+ dbra d2,0b
+
+ move.l d1,a1
+
+ moveq.l #0x0c,d0
+ cmp.l #0xa14000,d1
+ beq gsm_rts
+
+ moveq.l #0x08,d0
+ cmp.l #0xa14100,d1
+ beq gsm_rts
+
+ /* check for VDP address */
+ move.l d1,d0
+ swap d0
+ and.b #0xe0,d0
+ cmp.b #0xc0,d0
+ bne addr_unsafe /* not vdp */
+
+ move.l d1,d0
+ and.l #0x0700e0,d0
+ bne addr_unsafe
+
+ move.l d1,d0
+ and.b #0x1f,d0
+ cmp.b #0x04,d0
+ blt addr_hsafe_3 /* data port */
+ cmp.b #0x10,d0
+ blt addr_safe /* below PSG */
+ cmp.b #0x18,d0
+ bge addr_safe /* above PSG */
+
+addr_unsafe:
+ moveq.l #0,d0 /* skip line */
+ rts
+
+addr_hsafe_3:
+ moveq.l #3,d0 /* skip 2 words */
+ rts
+
+addr_safe:
+ move.l d1,a1
+ moveq.l #0xf,d0
+gsm_rts:
+ rts
+
+
+# read single phase from controller
+# #a0 - addr
+# d0 - result
+# destroys d1,d2
+get_input:
+ move.b #0x40,(0xa10003)
+ nop
+ nop
+ nop
+ move.b (0xa10003),d0
+ move.b #0x00,(0xa10003)
+ lsl.w #8,d0
+ nop
+ move.b (0xa10003),d0
+ eor.w #0xffff,d0
+
+ swap d7
+ move.w d7,d1
+ eor.w d0,d1 /* changed btns */
+ move.w d0,d7 /* old val */
+ swap d7
+ and.w d0,d1 /* what changed now */
+ bne 0f
+
+ addq.b #1,d6
+ move.b d6,d2
+ and.b #0x0f,d2 /* do autorepeat */
+ cmp.b #9,d2
+ bne 1f
+ move.w d0,d1
+0:
+ and.b #0xf0,d6
+1:
+ swap d0
+ move.w d1,d0
+ rts
+
+# Prepare to write to VDP RAM @a0
+# sets a0 to VDP data port for convenience
+# a0: VRAM base
+# destroys d0
+
+load_prepare:
+ VRAM_ADDR_var a0
+ move.l d0,(GFXCNTL).l
+ move.l #GFXDATA,a0
+ rts
+
+
+# Load color data from ROM
+# a0: CRAM base
+# a1: color list address
+# d0: number of colors to load
+# destroys d1
+
+load_colors:
+ move.l d0,d1
+ CRAM_ADDR_var a0
+ move.l d0,(GFXCNTL).l
+
+ move.l #GFXDATA,a0
+ subq.w #1,d1
+0:
+ move.w (a1)+,(a0)
+ dbra d1,0b
+
+ rts
+
+
+# print
+# a0 - string
+# d0 - x
+# d1 - y
+# d2 - tile_bits[15:11]
+# destroys a1
+
+print:
+ move.l a0,a1
+ XY2NT
+ jsr load_prepare
+ move.l d2,d0
+ and.w #0xf800,d0
+
+_print_loop:
+ move.b (a1)+,d0
+ beq _print_end
+
+ move.w d0,(a0)
+ bra _print_loop
+
+_print_end:
+ rts
+
+
+# print_hex
+# d0 - x
+# d1 - y
+# d2 - value
+# d3 - tile_bits[15:11]|digit_cnt[7:0]
+# destroys a0, preserves d3
+
+print_hex:
+ XY2NT
+ jsr load_prepare
+
+print_hex_preped:
+ moveq.l #0,d0
+ move.b d3,d0
+ move.l d0,d1
+ lsl.b #2,d0
+ ror.l d0,d2 /* prep value */
+ subq.l #1,d1 /* count */
+ move.w d3,d0
+ and.w #0xf800,d0 /* keep upper bits in d0 */
+
+_print_hex_loop:
+ rol.l #4,d2
+ move.b d2,d0
+ and.b #0xf,d0
+
+ add.b #'0',d0
+ cmp.b #'9',d0
+ ble 0f
+ addq.b #7,d0
+0:
+ move.w d0,(a0)
+ dbra d1,_print_hex_loop
+
+ rts
+
+
+# wait vertical sync interrupt
+
+wait_vsync:
+ move.b d7,d0
+_wait_change:
+ stop #0x2000
+ cmp.b d7,d0
+ beq _wait_change
+ rts
+
+
+# wait vsync start (polling)
+# destroys a0,d0
+
+wait_vsync_poll:
+ move.l #GFXCNTL,a0
+0:
+ move.w (a0),d0
+ and.b #8,d0
+ nop
+ nop
+ bne 0b
+0:
+ move.w (a0),d0
+ and.b #8,d0
+ nop
+ nop
+ beq 0b
+ rts
+
+
+test_code:
+ nop
+
+test_code_ret_op:
+ jmp 0x123456 /* will be patched */
+test_code_end:
+
+#################################################
+# #
+# RAM DATA #
+# #
+#################################################
+
+.bss
+
+# nothing :)
+
+.end
+
+# vim:filetype=asmM68k
--- /dev/null
+OUTPUT_ARCH(m68k)\r
+SEARCH_DIR(.)\r
+/*GROUP(-lbcc -lc -lgcc)*/\r
+__DYNAMIC = 0;\r
+\r
+/*\r
+ * Setup the memory map of the SEGA Genesis.\r
+ * stack grows down from high memory.\r
+ *\r
+ * The memory map look like this:\r
+ * +--------------------+ <- low memory\r
+ * | .text |\r
+ * | _etext |\r
+ * | ctor list | the ctor and dtor lists are for\r
+ * | dtor list | C++ support\r
+ * +--------------------+\r
+ * | .data | initialized data goes here\r
+ * | _edata |\r
+ * +--------------------+\r
+ * | .bss |\r
+ * | __bss_start | start of bss, cleared by crt0\r
+ * | _end | start of heap, used by sbrk()\r
+ * +--------------------+\r
+ * . .\r
+ * . .\r
+ * . .\r
+ * | __stack | top of stack\r
+ * +--------------------+\r
+ */\r
+/*\r
+MEMORY\r
+{\r
+ rom : ORIGIN = 0x00000000, LENGTH = 0x00400000\r
+ ram : ORIGIN = 0xffff0000, LENGTH = 0x00010000\r
+}\r
+*/\r
+\r
+MEMORY {\r
+ ram : ORIGIN = 0x0, LENGTH = 0xfffffff\r
+}\r
+\r
+/*\r
+ * allocate the stack to be at the top of memory, since the stack\r
+ * grows down\r
+ */\r
+\r
+PROVIDE (__stack = 0x00fffff0);\r
+\r
+PROVIDE (ram = 0xffff0000);\r
+/*\r
+ * Initalize some symbols to be zero so we can reference them in the\r
+ * crt0 without core dumping. These functions are all optional, but\r
+ * we do this so we can have our crt0 always use them if they exist. \r
+ * This is so BSPs work better when using the crt0 installed with gcc.\r
+ * We have to initalize them twice, so we cover a.out (which prepends\r
+ * an underscore) and coff object file formats.\r
+ */\r
+PROVIDE (hardware_init_hook = 0);\r
+PROVIDE (_hardware_init_hook = 0);\r
+PROVIDE (software_init_hook = 0);\r
+PROVIDE (_software_init_hook = 0);\r
+\r
+SECTIONS\r
+{\r
+ .text 0x00000000:\r
+ {\r
+ *(.text)\r
+ . = ALIGN(0x4);\r
+ __CTOR_LIST__ = .;\r
+ LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2)\r
+ *(.ctors)\r
+ LONG(0)\r
+ __CTOR_END__ = .;\r
+ __DTOR_LIST__ = .;\r
+ LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2)\r
+ *(.dtors)\r
+ LONG(0)\r
+ __DTOR_END__ = .;\r
+ *(.rodata)\r
+ *(.gcc_except_table) \r
+\r
+ __INIT_SECTION__ = . ;\r
+ *(.init)\r
+ SHORT (0x4e75) /* rts */\r
+\r
+ __FINI_SECTION__ = . ;\r
+ *(.fini)\r
+ SHORT (0x4e75) /* rts */\r
+\r
+ _etext = .;\r
+ *(.lit)\r
+ } > ram\r
+\r
+ .data BLOCK (0x4) :\r
+ {\r
+ *(.shdata)\r
+ *(.data)\r
+ _edata = .;\r
+ } > ram\r
+\r
+ .bss 0xff0000 :\r
+ {\r
+ __bss_start = . ;\r
+ *(.shbss)\r
+ *(.bss)\r
+ *(COMMON)\r
+ *(.eh_fram)\r
+ *(.eh_frame)\r
+ _end = ALIGN (0x8);\r
+ __end = _end;\r
+ } > ram\r
+\r
+ .stab 0 (NOLOAD) :\r
+ {\r
+ *(.stab)\r
+ }\r
+\r
+ .stabstr 0 (NOLOAD) :\r
+ {\r
+ *(.stabstr)\r
+ }\r
+}\r
--- /dev/null
+/*
+ * Copyright (c) 2011, Gražvydas Ignotas
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the organization nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/io.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <zlib.h>
+
+#include "transfer.h"
+
+/*
+ * PC:
+ * BASE+0 ~ data
+ * BASE+1 ~ status: BAOSEI??
+ * /BUSY, ACK, PE, SLCT, ERROR, IRQ
+ * BASE+2 ~ control: ??MISIAS
+ * bidirrectMODE, IRQ_EN, /SLCT_IN, INIT, /AUTO_FD_XT, /STROBE
+ *
+ * SEGA
+ * ?HRL3210
+ * TH, TR, TL, D3, D2, D1, D0
+ *
+ * SEGA PC
+ * 1 D0 <-> 2 D0
+ * 2 D1 <-> 3 D1
+ * 3 D2 <-> 4 D2
+ * 4 D3 <-> 5 D3
+ * 5 +5V
+ * 6 TL <-- 14 /AUTO_FD_XT
+ * 7 TH --> 10 ACK
+ * 8 GND --- 21 GND
+ * 9 TR <-- 17 /SLCT_IN
+ *
+ * start: TH low/high, TL high
+ *
+ * TH low - lower nibble: MD ready to recv | MD sent to PC
+ * TL low - lower niblle: sent to MD | ready to recv from MD
+ * TH high - upper nibble: MD ready to recv | MD sent to PC
+ * TL high - upper nibble: sent | ready to recv from MD
+ */
+
+#define ACK_TIMEOUT 2000000
+
+#define PORT_DATA 888
+#define PORT_STATUS 889
+#define PORT_CONTROL 890
+
+#define timediff(now, start) \
+ ((now.tv_sec - start.tv_sec) * 1000000 + now.tv_usec - start.tv_usec)
+
+#define PBE2(p) ((*(p) << 8) | *(p+1))
+#define PBE3(p) ((*(p) << 16) | (*(p + 1) << 8) | *(p + 2))
+#define PBE4(p) ((*(p) << 24) | (*(p + 1) << 16) | (*(p + 2) << 8) | *(p + 3))
+
+static void do_exit(const char *msg, const char *where)
+{
+ /* switch TL back to high */
+ outb(0xe0, PORT_CONTROL);
+
+ if (where)
+ fprintf(stderr, "%s: ", where);
+ if (msg)
+ fprintf(stderr, "%s", msg);
+ exit(1);
+}
+
+static void inthandler(int u)
+{
+ do_exit("\n", NULL);
+}
+
+static void wait_th_low(const char *where)
+{
+ struct timeval start, now;
+
+ gettimeofday(&start, NULL);
+
+ while (inb(PORT_STATUS) & 0x40) {
+ gettimeofday(&now, NULL);
+ if (timediff(now, start) > ACK_TIMEOUT)
+ do_exit("timeout waiting TH low\n", where);
+ }
+}
+
+static void wait_th_high(const char *where)
+{
+ struct timeval start, now;
+
+ gettimeofday(&start, NULL);
+
+ while (!(inb(PORT_STATUS) & 0x40)) {
+ gettimeofday(&now, NULL);
+ if (timediff(now, start) > ACK_TIMEOUT)
+ do_exit("timeout waiting TH high\n", where);
+ }
+}
+
+static void output_to_input(void)
+{
+ /* TL high, recv mode; also give time
+ * MD to see TL before we lower it in recv_byte */
+ outb(0xe0, PORT_CONTROL);
+ usleep(4*10); /* must be at least 12+8+8 M68k cycles, 28/7.67M */
+}
+
+static void input_to_output(void)
+{
+ wait_th_low("input_to_output");
+ outb(0xc0, PORT_CONTROL); /* TL high, out mode */
+}
+
+static unsigned int recv_byte(void)
+{
+ unsigned int byte;
+
+ outb(0xe2, PORT_CONTROL); /* TL low */
+
+ wait_th_low("recv_byte");
+
+ byte = inb(PORT_DATA) & 0x0f;
+
+ outb(0xe0, PORT_CONTROL); /* TL high */
+
+ wait_th_high("recv_byte");
+
+ byte |= inb(PORT_DATA) << 4;
+
+ return byte;
+}
+
+static void recv_bytes(unsigned char *b, size_t count)
+{
+ while (count-- > 0)
+ *b++ = recv_byte();
+}
+
+static void send_byte(unsigned int byte)
+{
+ wait_th_low("recv_bytes");
+
+ outb(byte & 0x0f, PORT_DATA);
+ outb(0xc2, PORT_CONTROL); /* TL low */
+
+ wait_th_high("recv_bytes");
+
+ outb((byte >> 4) & 0x0f, PORT_DATA);
+ outb(0xc0, PORT_CONTROL); /* TL high */
+}
+
+static void send_bytes(unsigned char *b, size_t count)
+{
+ while (count-- > 0)
+ send_byte(*b++);
+}
+
+static void send_cmd(unsigned int cmd)
+{
+ send_byte(CMD_PREFIX);
+ send_byte(cmd);
+}
+
+static void usage(const char *argv0)
+{
+ fprintf(stderr, "usage:\n%s <cmd> [args]\n"
+ "\tsend <file> <addr> [size]\n"
+ "\trecv <file> <addr> <size>\n"
+ "\tjump <addr>\n"
+ "\tio {r{8,16,32} <addr>,w{8,16,32} <addr> <data>}*\n"
+ "\tloadstate <picodrive_savestate>\n"
+ "\trecvvram <file>\n", argv0);
+ exit(1);
+}
+
+static unsigned int atoi_or_die(const char *a)
+{
+ char *p = NULL;
+ unsigned int i;
+
+ i = strtoul(a, &p, 0);
+ if (p == NULL || *p != 0) {
+ fprintf(stderr, "atoi: can't convert: %s\n", a);
+ exit(1);
+ }
+
+ return i;
+}
+
+static void checked_gzread(gzFile f, void *data, size_t size)
+{
+ unsigned int ret;
+ ret = gzread(f, data, size);
+ if (ret != size) {
+ fprintf(stderr, "gzread returned %d/%zu\n", ret, size);
+ exit(1);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ unsigned int addr = 0, size = 0;
+ unsigned int count = 0, i = 0;
+ int ret;
+ unsigned char *data;
+ FILE *file = NULL;
+
+ if (argc < 2)
+ usage(argv[0]);
+
+ data = malloc(0x1000000);
+ if (data == NULL) {
+ fprintf(stderr, "can't alloc %d bytes\n", 0x1000000);
+ return 1;
+ }
+
+ /* parse args, read files.. */
+ if (strcmp(argv[1], "send") == 0)
+ {
+ if (argc != 4 && argc != 5)
+ usage(argv[0]);
+
+ file = fopen(argv[2], "rb");
+ if (file == NULL) {
+ fprintf(stderr, "can't open file: %s\n", argv[2]);
+ return 1;
+ }
+
+ addr = atoi_or_die(argv[3]);
+ if (argv[4] == NULL) {
+ fseek(file, 0, SEEK_END);
+ size = ftell(file);
+ fseek(file, 0, SEEK_SET);
+ }
+ else
+ size = atoi_or_die(argv[4]);
+
+ ret = fread(data, 1, size, file);
+ if (ret != size) {
+ fprintf(stderr, "fread returned %d/%d\n", ret, size);
+ perror(NULL);
+ return 1;
+ }
+ }
+ else if (strcmp(argv[1], "recv") == 0)
+ {
+ if (argc != 5)
+ usage(argv[0]);
+
+ file = fopen(argv[2], "wb");
+ if (file == NULL) {
+ fprintf(stderr, "can't open file: %s\n", argv[2]);
+ return 1;
+ }
+
+ addr = atoi_or_die(argv[3]);
+ size = atoi_or_die(argv[4]);
+
+ memset(data, 0, size);
+ }
+ else if (strcmp(argv[1], "jump") == 0)
+ {
+ if (argc != 3)
+ usage(argv[0]);
+
+ addr = atoi_or_die(argv[2]);
+ }
+ else if (strcmp(argv[1], "io") == 0)
+ {
+ unsigned int cmd = 0, value, iosize;
+ unsigned char *p = data;
+
+ for (i = 2; i < argc; ) {
+ if (argv[i][0] == 'r')
+ cmd = IOSEQ_R8;
+ else if (argv[i][0] == 'w')
+ cmd = IOSEQ_W8;
+ else
+ usage(argv[0]);
+
+ iosize = atoi_or_die(&argv[i][1]);
+ if (iosize == 32)
+ cmd += 2;
+ else if (iosize == 16)
+ cmd += 1;
+ else if (iosize != 8)
+ usage(argv[0]);
+ *p++ = cmd;
+ i++;
+
+ addr = atoi_or_die(argv[i]);
+ *p++ = addr >> 16;
+ *p++ = addr >> 8;
+ *p++ = addr >> 0;
+ i++;
+
+ if (cmd == IOSEQ_W8 || cmd == IOSEQ_W16 || cmd == IOSEQ_W32) {
+ value = atoi_or_die(argv[i]);
+ switch (iosize) {
+ case 32:
+ *p++ = value >> 24;
+ *p++ = value >> 16;
+ case 16:
+ *p++ = value >> 8;
+ case 8:
+ *p++ = value >> 0;
+ }
+ i++;
+ }
+
+ count++;
+ }
+ }
+ else if (strcmp(argv[1], "loadstate") == 0)
+ {
+ unsigned char chunk;
+ char header[12];
+ gzFile f;
+ int len;
+
+ if (argc != 3)
+ usage(argv[0]);
+
+ f = gzopen(argv[2], "rb");
+ if (f == NULL) {
+ perror("gzopen");
+ return 1;
+ }
+
+ checked_gzread(f, header, sizeof(header));
+ if (strncmp(header, "PicoSEXT", 8) != 0) {
+ fprintf(stderr, "bad header\n");
+ return 1;
+ }
+
+ while (!gzeof(file))
+ {
+ ret = gzread(f, &chunk, 1);
+ if (ret == 0)
+ break;
+ checked_gzread(f, &len, 4);
+ //printf("%2d %x\n", chunk, len);
+ switch (chunk) {
+ case 3: // VRAM
+ checked_gzread(f, data, len);
+ size += len;
+ break;
+ case 5: // CRAM
+ checked_gzread(f, data + 0x10000, len);
+ size += len;
+ break;
+ case 6: // VSRAM
+ checked_gzread(f, data + 0x10080, len);
+ size += len;
+ break;
+ case 8: // video
+ checked_gzread(f, data + 0x10100, len);
+ data[size+0] &= ~1; // no display disable
+ data[size+1] |= 0x40; // no blanking
+ size += 0x20;
+ break;
+ default:
+ if (chunk > 64+8) {
+ fprintf(stderr, "bad chunk: %d\n", chunk);
+ return 1;
+ }
+ gzseek(f, len, SEEK_CUR);
+ break;
+ }
+ }
+ gzclose(f);
+ if (size != 0x10120) {
+ fprintf(stderr, "bad final size: %x\n", size);
+ return 1;
+ }
+ // unbyteswap *RAMs (stored byteswapped)
+ for (i = 0; i < 0x10100; i += 2) {
+ int tmp = data[i];
+ data[i] = data[i + 1];
+ data[i + 1] = tmp;
+ }
+ }
+ else if (strcmp(argv[1], "recvvram") == 0)
+ {
+ if (argc != 3)
+ usage(argv[0]);
+
+ file = fopen(argv[2], "wb");
+ if (file == NULL) {
+ fprintf(stderr, "can't open file: %s\n", argv[2]);
+ return 1;
+ }
+
+ size = 0x10000;
+ memset(data, 0, size);
+ }
+ else
+ usage(argv[0]);
+
+ ret = ioperm(PORT_DATA, 3, 1);
+ if (ret != 0) {
+ perror("ioperm");
+ return 1;
+ }
+
+ signal(SIGINT, inthandler);
+
+ printf("regs: %02x %02x %02x\n",
+ inb(PORT_DATA), inb(PORT_STATUS), inb(PORT_CONTROL));
+
+ /* wait for start condition */
+ if (!(inb(PORT_STATUS) & 0x40))
+ printf("waiting for TH high..\n");
+ while (!(inb(PORT_STATUS) & 0x40))
+ usleep(10000);
+
+ outb(0xe8, PORT_CONTROL); /* TR low - request for transfer */
+
+ /* wait for request ack */
+ if (inb(PORT_STATUS) & 0x40)
+ printf("waiting for TH low..\n");
+ for (i = 10000; inb(PORT_STATUS) & 0x40; i += 100) {
+ if (i > 100000)
+ i = 100000;
+ usleep(i);
+ }
+
+ outb(0xe0, PORT_CONTROL);
+
+ if (strcmp(argv[1], "send") == 0)
+ {
+ send_cmd(CMD_PC_SEND);
+ send_byte((addr >> 16) & 0xff);
+ send_byte((addr >> 8) & 0xff);
+ send_byte((addr >> 0) & 0xff);
+ send_byte((size >> 16) & 0xff);
+ send_byte((size >> 8) & 0xff);
+ send_byte((size >> 0) & 0xff);
+
+ for (i = 0; i < size; i++)
+ {
+ if ((i & 0xff) == 0) {
+ printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
+ printf("%06x/%06x", i, size);
+ fflush(stdout);
+ }
+
+ send_byte(data[i]);
+ }
+ }
+ else if (strcmp(argv[1], "recv") == 0)
+ {
+ send_cmd(CMD_PC_RECV);
+ send_byte((addr >> 16) & 0xff);
+ send_byte((addr >> 8) & 0xff);
+ send_byte((addr >> 0) & 0xff);
+ send_byte((size >> 16) & 0xff);
+ send_byte((size >> 8) & 0xff);
+ send_byte((size >> 0) & 0xff);
+ output_to_input();
+
+ for (i = 0; i < size; i++)
+ {
+ if ((i & 0xff) == 0) {
+ printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
+ printf("%06x/%06x", i, size);
+ fflush(stdout);
+ }
+
+ data[i] = recv_byte();
+ }
+
+ fwrite(data, 1, size, file);
+ }
+ else if (strcmp(argv[1], "jump") == 0)
+ {
+ send_cmd(CMD_JUMP);
+ send_byte((addr >> 16) & 0xff);
+ send_byte((addr >> 8) & 0xff);
+ send_byte((addr >> 0) & 0xff);
+ }
+ else if (strcmp(argv[1], "io") == 0)
+ {
+ unsigned char *p = data;
+ unsigned char rdata[4];
+ send_cmd(CMD_IOSEQ);
+ send_byte((count >> 8) & 0xff);
+ send_byte((count >> 0) & 0xff);
+
+ for (; count > 0; count--) {
+ input_to_output();
+ send_bytes(p, 4); /* cmd + addr */
+
+ switch (p[0]) {
+ case IOSEQ_R8:
+ output_to_input();
+ recv_bytes(rdata, 1);
+ printf("r8 %06x %02x\n", PBE3(p + 1), rdata[0]);
+ p += 4;
+ break;
+ case IOSEQ_R16:
+ output_to_input();
+ recv_bytes(rdata, 2);
+ printf("r16 %06x %04x\n", PBE3(p + 1), PBE2(rdata));
+ p += 4;
+ break;
+ case IOSEQ_R32:
+ output_to_input();
+ recv_bytes(rdata, 4);
+ printf("r32 %06x %08x\n", PBE3(p + 1), PBE4(rdata));
+ p += 4;
+ break;
+ case IOSEQ_W8:
+ send_bytes(&p[4], 1);
+ printf("w8 %06x %02x\n", PBE3(p + 1), p[4]);
+ p += 5;
+ break;
+ case IOSEQ_W16:
+ send_bytes(&p[4], 2);
+ printf("w16 %06x %04x\n", PBE3(p + 1), PBE2(p + 4));
+ p += 6;
+ break;
+ case IOSEQ_W32:
+ send_bytes(&p[4], 4);
+ printf("w32 %06x %08x\n", PBE3(p + 1), PBE4(p + 4));
+ p += 8;
+ break;
+ default:
+ do_exit("error in ioseq data\n", NULL);
+ break;
+ }
+ }
+ }
+ else if (strcmp(argv[1], "loadstate") == 0)
+ {
+ send_cmd(CMD_LOADSTATE);
+
+ for (i = 0; i < size; i++)
+ {
+ if ((i & 0x1f) == 0) {
+ printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
+ printf("%06x/%06x", i, size);
+ fflush(stdout);
+ }
+
+ send_byte(data[i]);
+ }
+ }
+ else if (strcmp(argv[1], "recvvram") == 0)
+ {
+ send_cmd(CMD_VRAM_RECV);
+ output_to_input();
+
+ for (i = 0; i < size; i++)
+ {
+ if ((i & 0xff) == 0) {
+ printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
+ printf("%06x/%06x", i, size);
+ fflush(stdout);
+ }
+
+ data[i] = recv_byte();
+ }
+
+ fwrite(data, 1, size, file);
+ }
+
+ if (size != 0) {
+ printf("\b\b\b\b\b\b\b\b\b\b\b\b\b");
+ printf("%06x/%06x\n", i, size);
+ }
+ if (file != NULL)
+ fclose(file);
+
+ /* switch TL back to high, disable outputs */
+ outb(0xe0, PORT_CONTROL);
+
+ return 0;
+}
+
--- /dev/null
+-------------------------------------------------------------------------------\r
+hexed - realtime memory viewer for Genesis/Megadrive\r
+ written by Gražvydas "notaz" Ignotas\r
+-------------------------------------------------------------------------------\r
+\r
+This is rather simple address space viewer for Genesis/Megadrive. It is capable\r
+of displaying 216 bytes of memory simultaneously in a manner similar to some\r
+hex editors for the PC, with refresh of 50/60Hz. It was created for hardware\r
+research purposes.\r
+\r
+Here are the key bindings:\r
+ A + up, A + down - scroll one line up/down.\r
+ A + left, A + right - scroll one page up/down.\r
+ B - toggle between byte/word display/edit mode.\r
+ C - edit highlighted value.\r
+ Start - enter menu.\r
+\r
+\r
+hexed is released under the BSD license (see included source code for details).\r
--- /dev/null
+ dc.l 0x0,0x200\r
+ dc.l INT,INT,INT,INT,INT,INT,INT\r
+ dc.l INT,INT,INT,INT,INT,INT,INT,INT\r
+ dc.l INT,INT,INT,INT,INT,INT,INT,INT\r
+ dc.l INT,INT,INT,HBL,INT,VBL,INT,INT\r
+ dc.l INT,INT,INT,INT,INT,INT,INT,INT\r
+ dc.l INT,INT,INT,INT,INT,INT,INT,INT\r
+ dc.l INT,INT,INT,INT,INT,INT,INT,INT\r
+ dc.l INT,INT,INT,INT,INT,INT,INT\r
+ .ascii "SEGA GENESIS "\r
+ .ascii "hexed (c) notaz, 2009-2011 "\r
+ .ascii "HEXED (C) NOTAZ, 2009-2011 "\r
+ .ascii "GM 00000000-00"\r
+ .byte 0x00,0x00\r
+ .ascii "JD "\r
+ .byte 0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00\r
+ .byte 0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff\r
+ .ascii " "\r
+ .ascii " "\r
+ .ascii " "\r
+ .ascii "JUE "\r
+\r
+ moveq #0,%d0\r
+ movea.l %d0,%a7\r
+ move %a7,%usp\r
+ bra main\r
+\r
+* INT:\r
+* rte\r
+\r
+HBL:\r
+ rte\r
+\r
+* VBL:\r
+* addq.l #1,(vtimer).l\r
+* rte\r
+\r
+* Standard 32X startup code for MD side at 0x3F0\r
+ .org 0x3F0\r
+\r
+ .word 0x287C,0xFFFF,0xFFC0,0x23FC,0x0000,0x0000,0x00A1,0x5128\r
+ .word 0x46FC,0x2700,0x4BF9,0x00A1,0x0000,0x7001,0x0CAD,0x4D41\r
+ .word 0x5253,0x30EC,0x6600,0x03E6,0x082D,0x0007,0x5101,0x67F8\r
+ .word 0x4AAD,0x0008,0x6710,0x4A6D,0x000C,0x670A,0x082D,0x0000\r
+ .word 0x5101,0x6600,0x03B8,0x102D,0x0001,0x0200,0x000F,0x6706\r
+ .word 0x2B78,0x055A,0x4000,0x7200,0x2C41,0x4E66,0x41F9,0x0000\r
+ .word 0x04D4,0x6100,0x0152,0x6100,0x0176,0x47F9,0x0000,0x04E8\r
+ .word 0x43F9,0x00A0,0x0000,0x45F9,0x00C0,0x0011,0x3E3C,0x0100\r
+ .word 0x7000,0x3B47,0x1100,0x3B47,0x1200,0x012D,0x1100,0x66FA\r
+ .word 0x7425,0x12DB,0x51CA,0xFFFC,0x3B40,0x1200,0x3B40,0x1100\r
+ .word 0x3B47,0x1200,0x149B,0x149B,0x149B,0x149B,0x41F9,0x0000\r
+ .word 0x04C0,0x43F9,0x00FF,0x0000,0x22D8,0x22D8,0x22D8,0x22D8\r
+ .word 0x22D8,0x22D8,0x22D8,0x22D8,0x41F9,0x00FF,0x0000,0x4ED0\r
+ .word 0x1B7C,0x0001,0x5101,0x41F9,0x0000,0x06BC,0xD1FC,0x0088\r
+ .word 0x0000,0x4ED0,0x0404,0x303C,0x076C,0x0000,0x0000,0xFF00\r
+ .word 0x8137,0x0002,0x0100,0x0000,0xAF01,0xD91F,0x1127,0x0021\r
+ .word 0x2600,0xF977,0xEDB0,0xDDE1,0xFDE1,0xED47,0xED4F,0xD1E1\r
+ .word 0xF108,0xD9C1,0xD1E1,0xF1F9,0xF3ED,0x5636,0xE9E9,0x9FBF\r
+ .word 0xDFFF,0x4D41,0x5253,0x2049,0x6E69,0x7469,0x616C,0x2026\r
+ .word 0x2053,0x6563,0x7572,0x6974,0x7920,0x5072,0x6F67,0x7261\r
+ .word 0x6D20,0x2020,0x2020,0x2020,0x2020,0x2043,0x6172,0x7472\r
+ .word 0x6964,0x6765,0x2056,0x6572,0x7369,0x6F6E,0x2020,0x2020\r
+ .word 0x436F,0x7079,0x7269,0x6768,0x7420,0x5345,0x4741,0x2045\r
+ .word 0x4E54,0x4552,0x5052,0x4953,0x4553,0x2C4C,0x5444,0x2E20\r
+ .word 0x3139,0x3934,0x2020,0x2020,0x2020,0x2020,0x2020,0x2020\r
+ .word 0x2020,0x2020,0x2020,0x2020,0x2020,0x2020,0x2020,0x2020\r
+ .word 0x2020,0x2020,0x2020,0x524F,0x4D20,0x5665,0x7273,0x696F\r
+ .word 0x6E20,0x312E,0x3000,0x48E7,0xC040,0x43F9,0x00C0,0x0004\r
+ .word 0x3011,0x303C,0x8000,0x323C,0x0100,0x3E3C,0x0012,0x1018\r
+ .word 0x3280,0xD041,0x51CF,0xFFF8,0x4CDF,0x0203,0x4E75,0x48E7\r
+ .word 0x81C0,0x41F9,0x0000,0x063E,0x43F9,0x00C0,0x0004,0x3298\r
+ .word 0x3298,0x3298,0x3298,0x3298,0x3298,0x3298,0x2298,0x3341\r
+ .word 0xFFFC,0x3011,0x0800,0x0001,0x66F8,0x3298,0x3298,0x7000\r
+ .word 0x22BC,0xC000,0x0000,0x7E0F,0x3340,0xFFFC,0x3340,0xFFFC\r
+ .word 0x3340,0xFFFC,0x3340,0xFFFC,0x51CF,0xFFEE,0x22BC,0x4000\r
+ .word 0x0010,0x7E09,0x3340,0xFFFC,0x3340,0xFFFC,0x3340,0xFFFC\r
+ .word 0x3340,0xFFFC,0x51CF,0xFFEE,0x4CDF,0x0381,0x4E75,0x8114\r
+ .word 0x8F01,0x93FF,0x94FF,0x9500,0x9600,0x9780,0x4000,0x0080\r
+ .word 0x8104,0x8F02,0x48E7,0xC140,0x43F9,0x00A1,0x5180,0x08A9\r
+ .word 0x0007,0xFF80,0x66F8,0x3E3C,0x00FF,0x7000,0x7200,0x337C\r
+ .word 0x00FF,0x0004,0x3341,0x0006,0x3340,0x0008,0x4E71,0x0829\r
+ .word 0x0001,0x000B,0x66F8,0x0641,0x0100,0x51CF,0xFFE8,0x4CDF\r
+ .word 0x0283,0x4E75,0x48E7,0x8180,0x41F9,0x00A1,0x5200,0x08A8\r
+ .word 0x0007,0xFF00,0x66F8,0x3E3C,0x001F,0x20C0,0x20C0,0x20C0\r
+ .word 0x20C0,0x51CF,0xFFF6,0x4CDF,0x0181,0x4E75,0x41F9,0x00FF\r
+ .word 0x0000,0x3E3C,0x07FF,0x7000,0x20C0,0x20C0,0x20C0,0x20C0\r
+ .word 0x20C0,0x20C0,0x20C0,0x20C0,0x51CF,0xFFEE,0x3B7C,0x0000\r
+ .word 0x1200,0x7E0A,0x51CF,0xFFFE,0x43F9,0x00A1,0x5100,0x7000\r
+ .word 0x2340,0x0020,0x2340,0x0024,0x1B7C,0x0003,0x5101,0x2E79\r
+ .word 0x0088,0x0000,0x0891,0x0007,0x66FA,0x7000,0x3340,0x0002\r
+ .word 0x3340,0x0004,0x3340,0x0006,0x2340,0x0008,0x2340,0x000C\r
+ .word 0x3340,0x0010,0x3340,0x0030,0x3340,0x0032,0x3340,0x0038\r
+ .word 0x3340,0x0080,0x3340,0x0082,0x08A9,0x0000,0x008B,0x66F8\r
+ .word 0x6100,0xFF12,0x08E9,0x0000,0x008B,0x67F8,0x6100,0xFF06\r
+ .word 0x08A9,0x0000,0x008B,0x6100,0xFF3C,0x303C,0x0040,0x2229\r
+ .word 0x0020,0x0C81,0x5351,0x4552,0x6700,0x0092,0x303C,0x0080\r
+ .word 0x2229,0x0020,0x0C81,0x5344,0x4552,0x6700,0x0080,0x21FC\r
+ .word 0x0088,0x02A2,0x0070,0x303C,0x0002,0x7200,0x122D,0x0001\r
+ .word 0x1429,0x0080,0xE14A,0x8242,0x0801,0x000F,0x660A,0x0801\r
+ .word 0x0006,0x6700,0x0058,0x6008,0x0801,0x0006,0x6600,0x004E\r
+ .word 0x7020,0x41F9,0x0088,0x0000,0x3C28,0x018E,0x4A46,0x6700\r
+ .word 0x0010,0x3429,0x0028,0x0C42,0x0000,0x67F6,0xB446,0x662C\r
+ .word 0x7000,0x2340,0x0028,0x2340,0x002C,0x3E14,0x2C7C,0xFFFF\r
+ .word 0xFFC0,0x4CD6,0x7FF9,0x44FC,0x0000,0x6014,0x43F9,0x00A1\r
+ .word 0x5100,0x3340,0x0006,0x303C,0x8000,0x6004,0x44FC,0x0001\r
+\r
--- /dev/null
+###############################################################################
+#
+# Copyright (c) 2011, Gražvydas Ignotas
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the organization nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# Assemble with gas
+# --register-prefix-optional --bitwise-or
+#
+
+#include "transfer.h"
+
+.text
+.globl do_transfer
+
+
+# receive 1 byte to d0
+# in: a1 - data port
+# trash: d1
+.macro recv_one_byte is_last=0
+ move.b #0,(a1) /* clear TH */
+
+0: /*L_wait_tl_low*/
+ move.b (a1),d1
+ btst.b #4,d1
+ bne 0b /*L_wait_tl_low*/
+
+ move.b #0x40,(a1) /* set TH */
+ and.b #0x0f,d1
+
+0: /*L_wait_tl_hi*/
+ move.b (a1),d0
+ btst.b #4,d0
+ beq 0b /*L_wait_tl_hi*/
+
+.if !\is_last
+ move.b #0,(a1) /* clear TH - ready for next */
+.endif
+ lsl.b #4,d0
+ or.b d1,d0
+.endm
+
+# send 1 byte in d0
+# in: a1 - data port
+# trash: d1,d2
+.macro send_one_byte
+ move.b d0,d2
+ and.b #0x0f,d2
+
+0: /*Lwait_tl_low:*/
+ move.b (a1),d1
+ btst.b #4,d1
+ bne 0b /*Lwait_tl_low*/
+
+ move.b d2,(a1) /* clears TH and writes data */
+
+ move.b d0,d2
+ lsr.b #4,d2
+ bset.b #6,d2 /* prepare TH */
+
+0: /*wait_tl_hi*/
+ move.b (a1),d1
+ btst.b #4,d1
+ beq 0b /*wait_tl_hi1*/
+
+ move.b d2,(a1)
+.endm
+
+recv_byte:
+ moveq.l #0,d0
+ recv_one_byte 1
+ rts
+
+# receive 1 16bit word to d0
+# in: a1 - data port
+# trash: d1,d2
+recv_word:
+ recv_one_byte
+ move.b d0,d2
+ recv_one_byte
+ lsl.w #8,d2
+ move.b d0,d2
+ move.w d2,d0
+ rts
+
+# receive address/size to d0 (3 bytes BE)
+# in: a1 - data port
+# trash: d1,d2
+recv_ad:
+ moveq.l #0,d2
+ bsr recv_byte
+ move.b d0,d2
+ bsr recv_byte
+ lsl.l #8,d2
+ move.b d0,d2
+ bsr recv_byte
+ lsl.l #8,d2
+ move.b d0,d2
+ move.l d2,d0
+ rts
+
+# send 1 byte in d0
+# in: a1 - data port
+# trash: d1,d2
+send_byte:
+ send_one_byte
+ rts
+
+.macro switch_to_output
+0: /*Lwait_tl_low: PC should switch to rx mode before lowering tl */
+ move.b (a1),d0
+ btst.b #4,d0
+ bne 0b /*Lwait_tl_low*/
+
+ move.b #0x4f,(0xa1000b).l
+ move.b #0x40,(a1)
+.endm
+
+.equ sat_maxsize, (80*8+0x200) /* sprites+max_align */
+
+# make sure cache is invalidated
+# note: VRAM copy doesn't seem to help here
+# note2: cache is updated as data is written
+# in: d0 - vdp reg5, a0 = 0xc00000
+# trash: d1,d2,a2
+invalidate_sprite_cache:
+ move.w #0x8f02,4(a0) /* auto increment 2 */
+ lsl.b #1,d0 /* upper byte of sat address */
+ move.b d0,d1
+ lsr.b #6,d1 /* 15:14 dst addr */
+ and.b #0x3f,d0 /* assemble cmd */
+ lsl.w #8,d0
+ swap d0
+ move.b d1,d0
+ move.l d0,4(a0)
+
+ move.l #0xffe000,a2
+ move.l #sat_maxsize/2-1,d2
+0:
+ move.w (a0),(a2)+
+ dbra d2,0b
+
+ bset #30,d0 /* VRAM write */
+ move.l d0,4(a0)
+
+ move.l #0xffe000,a2
+ move.l #sat_maxsize/2-1,d2
+0:
+ move.w (a2)+,(a0)
+ dbra d2,0b
+ rts
+
+
+
+do_transfer:
+ lea 0xa10005,a1
+ move.b #0x40,(0xa1000b).l /* ctrl - all inputs except TH */
+ move.b #0x00,(a1)
+
+ bsr recv_byte
+ cmp.b #CMD_PREFIX,d0
+ bne return
+
+ bsr recv_byte
+ cmp.b #CMD_FIRST,d0
+ bcs return
+ cmp.b #CMD_LAST+1,d0
+ bcc return
+ sub.b #CMD_FIRST,d0
+
+ lsl.w #2,d0
+ lea (jumptab,pc,d0),a0
+ jmp (a0)
+jumptab:
+ bra pcc_transfer_recv /* sent to us */
+ bra pcc_transfer_send /* recv from us */
+ bra pcc_jump
+ bra pcc_io
+ bra pcc_loadstate
+ bra pcc_vram_send
+ bra pcc_test_code
+
+
+/* receive data from PC */
+pcc_transfer_recv:
+ bsr recv_ad
+ move.l d0,a0
+ bsr recv_ad
+ move.l d0,d3
+
+tr_recv_loop:
+ recv_one_byte
+ move.b d0,(a0)+
+ subq.l #1,d3
+ bgt tr_recv_loop
+ bra return
+
+
+/* send data to PC */
+pcc_transfer_send:
+ bsr recv_ad
+ move.l d0,a0
+ bsr recv_ad
+ move.l d0,d3
+
+ switch_to_output
+
+tr_send_loop:
+ move.b (a0)+,d0
+ send_one_byte
+ subq.l #1,d3
+ bgt tr_send_loop
+ bra return
+
+
+/* call specified location */
+pcc_jump:
+ bsr recv_ad
+ move.l d0,a0
+ jsr (a0)
+ bra return
+
+
+/* do simple i/o commands */
+pcc_io:
+ moveq.l #0,d4
+ bsr recv_byte
+ move.b d0,d4
+ bsr recv_byte
+ lsl.l #8,d4
+ move.b d0,d4
+
+pcc_io_loop:
+ move.b #0x40,(0xa1000b).l /* input mode */
+
+ sub.w #1,d4
+ bmi return
+
+ bsr recv_byte
+ move.b d0,d3 /* cmd */
+
+ bsr recv_ad
+ move.l d0,a2 /* addr */
+
+ cmp.b #IOSEQ_W32, d3
+ beq pcc_io_w32
+ cmp.b #IOSEQ_W16, d3
+ beq pcc_io_w16
+ cmp.b #IOSEQ_W8, d3
+ bne pcc_io_rx
+
+pcc_io_w8:
+ bsr recv_byte
+ move.b d0,(a2)
+ bra pcc_io_loop
+
+pcc_io_w16:
+ bsr recv_byte
+ move.b d0,d3
+ bsr recv_byte
+ lsl.w #8,d3
+ move.b d0,d3
+ move.w d3,(a2)
+ bra pcc_io_loop
+
+pcc_io_w32:
+ bsr recv_byte
+ move.b d0,d3
+ bsr recv_byte
+ lsl.w #8,d3
+ move.b d0,d3
+ bsr recv_byte
+ lsl.l #8,d3
+ move.b d0,d3
+ bsr recv_byte
+ lsl.l #8,d3
+ move.b d0,d3
+ move.l d3,(a2)
+ bra pcc_io_loop
+
+pcc_io_rx:
+ switch_to_output
+
+ cmp.b #IOSEQ_R32, d3
+ beq pcc_io_r32
+ cmp.b #IOSEQ_R16, d3
+ beq pcc_io_r16
+ cmp.b #IOSEQ_R8, d3
+ bne return
+
+pcc_io_r8:
+ move.b (a2),d0
+ bsr send_byte
+ bra pcc_io_loop
+
+pcc_io_r16:
+ move.w (a2),d3
+ move.w d3,d0
+ lsr.w #8,d0
+ bsr send_byte
+ move.b d3,d0
+ bsr send_byte
+ bra pcc_io_loop
+
+pcc_io_r32:
+ move.l (a2),d3
+ move.l d3,d0
+ swap d0
+ lsr.l #8,d0
+ bsr send_byte
+ move.l d3,d0
+ swap d0
+ bsr send_byte
+ move.w d3,d0
+ lsr.w #8,d0
+ bsr send_byte
+ move.b d3,d0
+ bsr send_byte
+ bra pcc_io_loop
+
+
+/* PicoDrive savestate load */
+pcc_loadstate:
+ /* write VRAM */
+ move.l #0xc00000,a0
+ move.w #0x8f02,4(a0) /* auto increment 2 */
+
+ move.l #0x40000000,4(a0)
+ move.l #0x10000/2-1,d3
+tr_do_vram_loop:
+ bsr recv_word
+ move.w d0,(a0)
+ dbra d3, tr_do_vram_loop
+
+ /* write cram */
+ move.l #0xc0000000,4(a0)
+ move.l #0x80/2-1,d3
+tr_do_cram_loop:
+ bsr recv_word
+ move.w d0,(a0)
+ dbra d3, tr_do_cram_loop
+
+ /* write vsram */
+ move.l #0x40000010,4(a0)
+ move.l #0x80/2-1,d3
+tr_do_vsram_loop:
+ bsr recv_word
+ move.w d0,(a0)
+ dbra d3, tr_do_vsram_loop
+
+ /* recv and write regs */
+ lea 0xffe000,a3
+ move.l a3,a2
+ moveq.l #0x20-1,d3
+tr_do_vdpreg_recv_loop:
+ bsr recv_byte
+ move.b d0,(a2)+
+ dbra d3, tr_do_vdpreg_recv_loop
+
+ move.l a3,a2
+ moveq.l #0,d3
+tr_do_vdpreg_loop:
+ move.b d3,d1
+ or.b #0x80,d1
+ lsl.w #8,d1
+ move.b (d3,a2),d1
+ move.w d1,4(a0)
+ addq.l #1,d3
+ cmp.b #0x17,d3 /* FIXME: r23 might cause DMA or.. */
+ bne 0f /* ..something and hang VDP.. */
+ add.b #1,d3 /* ..so we skip it */
+0:
+ cmp.b #0x20,d3
+ blt tr_do_vdpreg_loop
+
+ moveq.l #0,d0
+ move.b 5(a3),d0
+ bsr invalidate_sprite_cache
+
+
+0: bra 0b
+
+ bra return
+
+
+pcc_vram_send:
+ /* write VRAM */
+ move.l #0xc00000,a0
+ move.w #0x8f02,4(a0) /* auto increment 2 */
+ move.l #0,4(a0) /* VRAM read, addr 0 */
+ move.l #0x10000/2-1,d4
+
+ switch_to_output
+
+tr_vram_send_loop:
+ move.w (a0),d3
+ move.w d3,d0
+ lsr.w #8,d0
+ bsr send_byte
+ move.b d3,d0
+ bsr send_byte
+ dbra d4,tr_vram_send_loop
+
+ bra return
+
+
+
+/* some random code */
+pcc_test_code:
+ bra return
+
+
+
+return:
+ move.b #0,(0xa1000b).l /* all inputs */
+ move.l #0xffe000,a1
+ move.l d0,(a1)+ /* last state for debug */
+ move.l d1,(a1)+
+ move.l d2,(a1)+
+ move.l d3,(a1)+
+ move.l a0,(a1)+
+ rts
+# bra return_to_main
+
+
+# vim:filetype=asmM68k
--- /dev/null
+/* all data is big endian */
+
+#define CMD_PREFIX 0x5a
+#define CMD_PC_SEND 0xc1 /* send to MD: addr[3], len[3], data[] */
+#define CMD_PC_RECV 0xc2 /* recv from MD: addr[3], len[3], data[] */
+#define CMD_JUMP 0xc3 /* jump to addr: addr[3] */
+#define CMD_IOSEQ 0xc4 /* perform i/o ops: count[2], [type[1], addr[3], data[{0,1,2,4}]]* */
+#define CMD_LOADSTATE 0xc5 /* load PD state: vram[64k], cram[128], vsram[128], vdp[32] */
+#define CMD_VRAM_RECV 0xc6 /* recv from MD: vram[64k] */
+#define CMD_TEST 0xc7 /* test code */
+
+#define CMD_FIRST CMD_PC_SEND
+#define CMD_LAST CMD_TEST
+
+#define IOSEQ_R8 0xb0
+#define IOSEQ_R16 0xb1
+#define IOSEQ_R32 0xb2
+#define IOSEQ_W8 0xb3
+#define IOSEQ_W16 0xb4
+#define IOSEQ_W32 0xb5
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <linux/input.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/io.h>
+
+int main()
+{
+ int i, ret, fd;
+ int dpad = 0;
+
+ for (i = 0;; i++)
+ {
+ int support = 0;
+ char name[64];
+
+ snprintf(name, sizeof(name), "/dev/input/event%d", i);
+ fd = open(name, O_RDONLY);
+ if (fd == -1)
+ break;
+
+ /* check supported events */
+ ret = ioctl(fd, EVIOCGBIT(0, sizeof(support)), &support);
+ if (ret == -1) {
+ printf("ioctl failed on %s\n", name);
+ goto skip;
+ }
+
+ if (!(support & (1 << EV_KEY)))
+ goto skip;
+ if (!(support & (1 << EV_ABS)))
+ goto skip;
+
+ ioctl(fd, EVIOCGNAME(sizeof(name)), name);
+ printf("found \"%s\" (type %08x)\n", name, support);
+ break;
+
+skip:
+ close(fd);
+ fd = -1;
+ }
+
+ if (fd == -1) {
+ printf("no suitable devs\n");
+ return 1;
+ }
+
+ ret = ioperm(888, 3, 1);
+ if (ret != 0) {
+ perror("ioperm");
+ return 1;
+ }
+
+ outb(0xc0, 890); /* switch to output mode, TL and TR high */
+ outb(0x0f, 888); /* Dx high */
+
+ while (1)
+ {
+ struct input_event ev;
+ int rd;
+
+ rd = read(fd, &ev, sizeof(ev));
+ if (rd < (int) sizeof(ev)) {
+ perror("in_evdev: error reading");
+ return 1;
+ }
+
+ if (ev.type == EV_KEY) {
+ int val = 0xc0;
+ if (ev.code & 1) {
+ if (ev.value)
+ val |= 2;
+ else
+ val &= ~2;
+ } else {
+ if (ev.value)
+ val |= 8;
+ else
+ val &= ~8;
+ }
+ outb(val, 890);
+ }
+ else if (ev.type == EV_ABS)
+ {
+ if (ev.code == ABS_X) {
+ if (ev.value < 128)
+ dpad |= 4;
+ else if (ev.value > 128)
+ dpad |= 8;
+ else
+ dpad &= ~0x0c;
+ }
+ if (ev.code == ABS_Y) {
+ if (ev.value < 128)
+ dpad |= 1;
+ else if (ev.value > 128)
+ dpad |= 2;
+ else
+ dpad &= ~3;
+ }
+ outb(~dpad & 0x0f, 888);
+ }
+ }
+
+ return 0;
+}
+
--- /dev/null
+CFLAGS += -Wall -ggdb
+ifndef DEBUG
+CFLAGS += -O2
+endif
+
+all: mega-usb
+
+clean:
+ $(RM) mega-usb
--- /dev/null
+/*
+ * Tool for USB communication with Mega Everdrive
+ * Copyright (c) 2013,2014 notaz
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define _BSD_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <arpa/inet.h> // hton
+#include <termios.h>
+#include <unistd.h>
+
+static int setup(int fd)
+{
+ struct termios tty;
+ int ret;
+
+ memset(&tty, 0, sizeof(tty));
+
+ ret = tcgetattr(fd, &tty);
+ if (ret != 0)
+ {
+ perror("tcgetattr");
+ return 1;
+ }
+
+ tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
+ | INLCR | IGNCR | ICRNL | IXON);
+ tty.c_oflag &= ~OPOST;
+ tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+ tty.c_cflag &= ~(CSIZE | PARENB);
+ tty.c_cflag |= CS8;
+
+ //tty.c_cc[VMIN] = 1;
+ //tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
+
+ ret = tcsetattr(fd, TCSANOW, &tty);
+ if (ret != 0) {
+ perror("tcsetattr");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int write_to_cart(int fd, const void *data, size_t size)
+{
+ int ret;
+
+ ret = write(fd, data, size);
+ if (ret != size) {
+ fprintf(stderr, "write %d/%zd: ", ret, size);
+ perror("");
+ exit(1);
+ //return -1;
+ }
+
+ return 0;
+}
+
+static int read_from_cart(int fd, void *data, size_t size)
+{
+ size_t got = 0;
+ int ret;
+
+ while (got < size) {
+ ret = read(fd, (char *)data + got, size - got);
+ if (ret <= 0) {
+ fprintf(stderr, "read %d %zd/%zd: ",
+ ret, got, size);
+ perror("");
+ exit(1);
+ //return -1;
+ }
+ got += ret;
+ }
+
+ return 0;
+}
+
+#define send_cmd(fd, cmd) \
+ write_to_cart(fd, cmd, strlen(cmd))
+
+static int read_check_byte(int fd, char expect)
+{
+ char r = '0';
+ int ret;
+
+ ret = read_from_cart(fd, &r, 1);
+ if (ret != 0) {
+ fprintf(stderr, "missing response, need '%c'\n", expect);
+ return -1;
+ }
+
+ if (r != expect) {
+ fprintf(stderr, "unexpected response: '%c', need '%c'\n",
+ r, expect);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int write_with_check(int fd, const void *data, size_t size, char chk)
+{
+ int ret;
+
+ ret = write_to_cart(fd, data, size);
+ if (ret)
+ return ret;
+
+ ret = read_check_byte(fd, chk);
+ if (ret != 0) {
+ if (size < 16)
+ fprintf(stderr, "data sent: '%16s'\n",
+ (const char *)data);
+ exit(1);
+ //return -1;
+ }
+
+ return 0;
+}
+
+#define send_cmd_check_k(fd, cmd) \
+ write_with_check(fd, cmd, strlen(cmd), 'k')
+
+static int send_file(int fd, const char *fname, const char *cmd)
+{
+ char buf[0x10000];
+ int retval = -1;
+ FILE *f = NULL;
+ size_t blocksz;
+ size_t size;
+ int blocks;
+ int ret;
+ int i;
+
+ f = fopen(fname, "rb");
+ if (f == NULL) {
+ fprintf(stderr, "fopen %s: ", fname);
+ perror("");
+ return -1;
+ }
+
+ fseek(f, 0, SEEK_END);
+ size = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ if (size > 0xf00000) {
+ fprintf(stderr, "size too large: %zd\n", size);
+ goto out;
+ }
+
+ if (cmd[1] == 'f')
+ blocksz = 1024;
+ else
+ blocksz = sizeof(buf);
+
+ blocks = (size + blocksz - 1) / blocksz;
+
+ send_cmd(fd, cmd);
+ ret = write_with_check(fd, &blocks, 1, 'k');
+ if (ret)
+ return ret;
+
+ // at this point, 'd' will arrive even if we don't
+ // write any more data, what's the point of that?
+ // probably a timeout?
+
+ for (i = 0; i < blocks; i++) {
+ ret = fread(buf, 1, blocksz, f);
+ if (i != blocks - 1 && ret != blocksz) {
+ fprintf(stderr, "failed to read block %d/%d\n",
+ i, blocks);
+ }
+ if (ret < blocksz)
+ memset(buf + ret, 0, blocksz - ret);
+
+ write_to_cart(fd, buf, blocksz);
+ }
+
+ ret = read_check_byte(fd, 'd');
+ if (ret)
+ goto out;
+
+ retval = 0;
+out:
+ if (f != NULL)
+ fclose(f);
+
+ return retval;
+}
+
+static int recv_file(int fd, const char *fname, unsigned int addr,
+ unsigned int size)
+{
+ int retval = -1;
+ char *buf = NULL;
+ FILE *f = NULL;
+ struct {
+ unsigned int addr;
+ unsigned int size;
+ } addr_size;
+ int ret;
+
+ f = fopen(fname, "wb");
+ if (f == NULL) {
+ fprintf(stderr, "fopen %s: ", fname);
+ perror("");
+ return -1;
+ }
+
+ buf = malloc(size);
+ if (buf == NULL) {
+ fprintf(stderr, "OOM?\n");
+ goto out;
+ }
+
+ send_cmd(fd, "*xd");
+
+ addr_size.addr = htonl(addr);
+ addr_size.size = htonl(size);
+ ret = write_with_check(fd, &addr_size, sizeof(addr_size), 'k');
+ if (ret)
+ return ret;
+
+ ret = read_from_cart(fd, buf, size);
+ if (ret)
+ goto out;
+
+ ret = fwrite(buf, 1, size, f);
+ if (ret != size) {
+ fprintf(stderr, "fwrite %d/%d: ", ret, size);
+ perror("");
+ goto out;
+ }
+
+ retval = 0;
+out:
+ if (f != NULL)
+ fclose(f);
+ free(buf);
+
+ return retval;
+}
+
+static void usage(const char *argv0)
+{
+ printf("usage:\n"
+ "%s [-d <ttydevice>] [-f <file>] command(s)\n"
+ "known commands for ED OS:\n"
+ " *g - upload game\n"
+ " *w - upload and start OS\n"
+ " *o - upload and start OS\n"
+ " *f - upload (and start?) FPGA firmware\n"
+ " *s - start last ROM (same as pressing start in menu)\n"
+ " *r<x> - run SDRAM contents as mapper x:\n"
+ " s - sms, m - md, o - OS app, c - cd BIOS, M - md10m\n"
+ "upload commands must be followed by -f <file>\n"
+ "custom OS commands:\n"
+ " *xd <addr> <size> <file> - download memory\n"
+ , argv0);
+ exit(1);
+}
+
+static void invarg(int argc, char *argv[], int arg)
+{
+ if (arg < argc)
+ fprintf(stderr, "invalid arg %d: \"%s\"\n", arg, argv[arg]);
+ else
+ fprintf(stderr, "missing required argument %d\n", arg);
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ const char *portname = "/dev/ttyUSB0";
+ const char *pending_cmd = NULL;
+ int ret = -1;
+ int arg = 1;
+ int fd;
+
+ if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
+ usage(argv[0]);
+
+ if (!strcmp(argv[arg], "-d")) {
+ arg++;
+ if (argv[arg] != NULL)
+ portname = argv[arg];
+ else
+ invarg(argc, argv, arg);
+ arg++;
+ }
+
+ fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
+ if (fd < 0) {
+ fprintf(stderr, "open %s: ", portname);
+ perror("");
+ return 1;
+ }
+
+ setup(fd);
+
+ send_cmd_check_k(fd, " *T");
+
+ for (; arg < argc; arg++) {
+ if (!strcmp(argv[arg], "-f")) {
+ arg++;
+ if (argv[arg] == NULL)
+ invarg(argc, argv, argc);
+ if (pending_cmd == NULL)
+ // assume game upload
+ pending_cmd = "*g";
+
+ ret = send_file(fd, argv[arg], pending_cmd);
+ if (ret)
+ return ret;
+
+ pending_cmd = NULL;
+ continue;
+ }
+ if (argv[arg + 1] && !strcmp(argv[arg + 1], "-f")) {
+ /* we'll avoid sending command if there are
+ * problems with specified file */
+ pending_cmd = argv[arg];
+ continue;
+ }
+ /* custom OS commands */
+ if (!strcmp(argv[arg], "*xd")) {
+ unsigned int addr, size;
+ if (arg + 3 >= argc)
+ invarg(argc, argv, argc);
+ addr = strtoul(argv[++arg], NULL, 0);
+ size = strtoul(argv[++arg], NULL, 0);
+ ret = recv_file(fd, argv[++arg], addr, size);
+ if (ret)
+ return ret;
+
+ continue;
+ }
+
+ /* passthrough */
+ ret = send_cmd_check_k(fd, argv[arg]);
+ }
+
+ return ret;
+}
--- /dev/null
+CROSS = m68k-elf-\r
+CC = $(CROSS)gcc\r
+AS = $(CROSS)as\r
+LD = $(CROSS)ld\r
+OBJCOPY = $(CROSS)objcopy\r
+\r
+ASFLAGS += -m68000 --register-prefix-optional --bitwise-or -pic\r
+ASFLAGS_CC += -Wa,-m68000 -Wa,--register-prefix-optional -Wa,--bitwise-or -Wa,-pic\r
+CFLAGS += -Wall -g -O1 -m68000 -fomit-frame-pointer\r
+LDLIBS += $(shell $(CC) -print-file-name=libgcc.a)\r
+\r
+TARGET = megaed_stop_md\r
+OBJS = sega_gcc.o main.o data.o\r
+\r
+all: $(TARGET).bin\r
+\r
+$(TARGET).elf: $(OBJS)\r
+ $(LD) -o $@ -Tsega.ld -Map $(TARGET).map $^ $(LDLIBS)\r
+\r
+clean:\r
+ $(RM) $(TARGET).bin $(OBJS) $(TARGET).elf $(TARGET).map\r
+ $(RM) maketest test.bin\r
+\r
+%.bin: %.elf\r
+ $(OBJCOPY) -I elf32-m68k -O binary $^ $@\r
+\r
+%.o: %.S\r
+ $(CC) -c -o $@ $^ $(ASFLAGS_CC)\r
+\r
+data.s: test.bin\r
+\r
+test.bin:\r
+ gcc -o maketest maketest.c\r
+ ./maketest\r
+\r
+rel: $(TARGET).bin\r
+ mkdir -p /tmp/$(TARGET)/src/\r
+ cp $^ /tmp/$(TARGET)/\r
+ $(MAKE) clean\r
+ cp -a * /tmp/$(TARGET)/src/\r
--- /dev/null
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
--- /dev/null
+.section .rodata
+
+# I don't know why multiple different .align don't work..
+.align 0x8000
+
+.global font_base
+font_base:
+.incbin "font.bin"
+
+.align 0x8000
+.global test_data
+test_data:
+.incbin "test.bin"
+.global test_data_end
+test_data_end:
+
+# vim:filetype=asmM68k:ts=4:sw=4:expandtab
--- /dev/null
+/*
+ * This software is released into the public domain.
+ * See UNLICENSE file in top level directory.
+ */
+#include <stdlib.h>
+#include <stdarg.h>
+
+#define u8 unsigned char
+#define u16 unsigned short
+#define u32 unsigned int
+
+#define GFX_DATA_PORT 0xC00000
+#define GFX_CTRL_PORT 0xC00004
+
+#define TILE_MEM_END 0xB000
+
+#define FONT_LEN 128
+#define TILE_FONT_BASE (TILE_MEM_END - FONT_LEN * 32)
+
+/* note: using ED menu's layout here.. */
+#define WPLANE (TILE_MEM_END + 0x0000)
+#define HSCRL (TILE_MEM_END + 0x0800)
+#define SLIST (TILE_MEM_END + 0x0C00)
+#define APLANE (TILE_MEM_END + 0x1000)
+#define BPLANE (TILE_MEM_END + 0x3000)
+
+#define read8(a) \
+ *((volatile u8 *) (a))
+#define read16(a) \
+ *((volatile u16 *) (a))
+#define read32(a) \
+ *((volatile u32 *) (a))
+#define write16(a, d) \
+ *((volatile u16 *) (a)) = (d)
+#define write32(a, d) \
+ *((volatile u32 *) (a)) = (d)
+
+#define GFX_WRITE_VRAM_ADDR(adr) \
+ (((0x4000 | ((adr) & 0x3FFF)) << 16) | ((adr) >> 14) | 0x00)
+#define GFX_WRITE_VSRAM_ADDR(adr) \
+ (((0x4000 | ((adr) & 0x3FFF)) << 16) | ((adr) >> 14) | 0x10)
+#define GFX_WRITE_CRAM_ADDR(adr) \
+ (((0xC000 | ((adr) & 0x3FFF)) << 16) | ((adr) >> 14) | 0x00)
+
+#define VDP_setReg(r, v) \
+ write16(GFX_CTRL_PORT, 0x8000 | ((r) << 8) | (v))
+
+enum {
+ VDP_MODE1 = 0x00,
+ VDP_MODE2 = 0x01,
+ VDP_NT_SCROLLA = 0x02,
+ VDP_NT_WIN = 0x03,
+ VDP_NT_SCROLLB = 0x04,
+ VDP_SAT_BASE = 0x05,
+ VDP_BACKDROP = 0x07,
+ VDP_MODE3 = 0x0b,
+ VDP_MODE4 = 0x0c,
+ VDP_HSCROLL = 0x0d,
+ VDP_AUTOINC = 0x0f,
+ VDP_SCROLLSZ = 0x10,
+};
+
+/* cell counts */
+#define LEFT_BORDER 1 /* lame TV */
+#define PLANE_W 64
+#define PLANE_H 32
+#define CSCREEN_H 28
+
+static void VDP_drawTextML(const char *str, u16 plane_base,
+ u16 x, u16 y)
+{
+ const u8 *src = (const u8 *)str;
+ u16 basetile = 0;
+ int max_len = 40 - LEFT_BORDER;
+ int len;
+ u32 addr;
+
+ x += LEFT_BORDER;
+
+ for (len = 0; str[len] && len < max_len; len++)
+ ;
+ if (len > (PLANE_W - x))
+ len = PLANE_W - x;
+
+ addr = plane_base + ((x + (PLANE_W * y)) << 1);
+ write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(addr));
+
+ while (len-- > 0) {
+ write16(GFX_DATA_PORT,
+ basetile | ((*src++) - 32 + TILE_FONT_BASE / 32));
+ }
+}
+
+static int printf_ypos;
+
+static void printf_line(int x, const char *buf)
+{
+ u32 addr;
+ int i;
+
+ VDP_drawTextML(buf, APLANE, x, printf_ypos++ & (PLANE_H - 1));
+
+ if (printf_ypos >= CSCREEN_H) {
+ /* clear next line */
+ addr = APLANE;
+ addr += (PLANE_W * (printf_ypos & (PLANE_H - 1))) << 1;
+ write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(addr));
+ for (i = 0; i < 40 / 2; i++)
+ write32(GFX_DATA_PORT, 0);
+
+ /* scroll plane */
+ write32(GFX_CTRL_PORT, GFX_WRITE_VSRAM_ADDR(0));
+ write16(GFX_DATA_PORT, (printf_ypos - CSCREEN_H + 1) * 8);
+ }
+}
+
+#define PRINTF_LEN 40
+
+static int printf(const char *fmt, ...)
+{
+ static const char hexchars[] = "0123456789abcdef";
+ static int printf_xpos;
+ char c, buf[PRINTF_LEN + 11 + 1];
+ const char *s;
+ va_list ap;
+ int ival;
+ u32 uval;
+ int d = 0;
+ int i, j;
+
+ va_start(ap, fmt);
+ for (d = 0; *fmt; ) {
+ int prefix0 = 0;
+ int fwidth = 0;
+
+ c = *fmt++;
+ if (d < PRINTF_LEN)
+ buf[d] = c;
+
+ if (c != '%') {
+ if (c == '\n') {
+ buf[d] = 0;
+ printf_line(printf_xpos, buf);
+ d = 0;
+ printf_xpos = 0;
+ continue;
+ }
+ d++;
+ continue;
+ }
+ if (d >= PRINTF_LEN)
+ continue;
+
+ if (*fmt == '0') {
+ prefix0 = 1;
+ fmt++;
+ }
+
+ while ('1' <= *fmt && *fmt <= '9') {
+ fwidth = fwidth * 10 + *fmt - '0';
+ fmt++;
+ }
+
+ switch (*fmt++) {
+ case '%':
+ d++;
+ break;
+ case 'd':
+ case 'i':
+ ival = va_arg(ap, int);
+ if (ival < 0) {
+ buf[d++] = '-';
+ ival = -ival;
+ }
+ for (i = 1000000000; i >= 10; i /= 10)
+ if (ival >= i)
+ break;
+ for (; i >= 10; i /= 10) {
+ buf[d++] = '0' + ival / i;
+ ival %= i;
+ }
+ buf[d++] = '0' + ival;
+ break;
+ case 'x':
+ uval = va_arg(ap, int);
+ while (fwidth > 1 && uval < (1 << (fwidth - 1) * 4)) {
+ buf[d++] = prefix0 ? '0' : ' ';
+ fwidth--;
+ }
+ for (j = 1; j < 8 && uval >= (1 << j * 4); j++)
+ ;
+ for (j--; j >= 0; j--)
+ buf[d++] = hexchars[(uval >> j * 4) & 0x0f];
+ break;
+ case 's':
+ s = va_arg(ap, char *);
+ while (*s && d < PRINTF_LEN)
+ buf[d++] = *s++;
+ break;
+ default:
+ // don't handle, for now
+ d++;
+ va_arg(ap, void *);
+ break;
+ }
+ }
+ buf[d] = 0;
+ va_end(ap);
+
+ if (d != 0) {
+ // line without \n
+ VDP_drawTextML(buf, APLANE, printf_xpos,
+ printf_ypos & (PLANE_H - 1));
+ printf_xpos += d;
+ }
+
+ return d; // wrong..
+}
+
+extern u32 font_base[];
+extern u8 test_data[];
+extern u8 test_data_end[];
+
+int main()
+{
+ volatile u32 *vptr;
+ u32 old;
+ u8 bad = 0;
+ u8 *p, val;
+ int i, t, len;
+
+ /* z80 */
+ write16(0xa11100, 0x100);
+ write16(0xa11200, 0);
+
+ /* setup VDP */
+ while (read16(GFX_CTRL_PORT) & 2)
+ ;
+
+ VDP_setReg(VDP_MODE1, 0x04);
+ VDP_setReg(VDP_MODE2, 0x64);
+ VDP_setReg(VDP_MODE3, 0x00);
+ VDP_setReg(VDP_MODE4, 0x81);
+ VDP_setReg(VDP_NT_SCROLLA, APLANE >> 10);
+ VDP_setReg(VDP_NT_SCROLLB, BPLANE >> 13);
+ VDP_setReg(VDP_SAT_BASE, SLIST >> 9);
+ VDP_setReg(VDP_HSCROLL, HSCRL >> 10);
+ VDP_setReg(VDP_AUTOINC, 2);
+ VDP_setReg(VDP_SCROLLSZ, 0x01);
+ VDP_setReg(VDP_BACKDROP, 0);
+
+ /* clear name tables */
+ write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(APLANE));
+ for (i = 0; i < PLANE_W * PLANE_H / 2; i++)
+ write32(GFX_DATA_PORT, 0);
+
+ write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(BPLANE));
+ for (i = 0; i < PLANE_W * PLANE_H / 2; i++)
+ write32(GFX_DATA_PORT, 0);
+
+ /* SAT, h. scroll */
+ write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(SLIST));
+ write32(GFX_DATA_PORT, 0);
+
+ write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(HSCRL));
+ write32(GFX_DATA_PORT, 0);
+
+ /* scroll plane vscroll */
+ write32(GFX_CTRL_PORT, GFX_WRITE_VSRAM_ADDR(0));
+ write32(GFX_DATA_PORT, 0);
+ printf_ypos = 0;
+
+ /* load font */
+ write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(TILE_FONT_BASE));
+ for (i = 0; i < FONT_LEN * 32 / 4; i++)
+ write32(GFX_DATA_PORT, font_base[i]);
+
+ /* set colors */
+ write32(GFX_CTRL_PORT, GFX_WRITE_CRAM_ADDR(0));
+ write32(GFX_DATA_PORT, 0);
+ write32(GFX_CTRL_PORT, GFX_WRITE_CRAM_ADDR(15 * 2)); // font
+ write16(GFX_DATA_PORT, 0xeee);
+
+ printf("\n");
+ printf("MD version: %02x\n", read8(0xa10001));
+ printf("ROM witable? ");
+
+ vptr = (void *)0x120;
+ old = *vptr;
+ *vptr ^= ~0;
+ printf(*vptr == old ? "no" : "yes");
+ printf("\n\n");
+
+ p = test_data;
+ len = test_data_end - test_data;
+
+ for (t = 1; ; t++) {
+ printf("executing stop.. ");
+ for (i = 0; i < 5 * 60; i++)
+ asm volatile("stop #0x2000" ::: "cc");
+ printf("done\n");
+
+ printf("checking memory..\n");
+
+ val = 0;
+ for (i = 0; i < len; i++) {
+ if (p[i] != val) {
+ printf("bad: %06x: got %02x, expected %02x\n",
+ &p[i], p[i], val);
+ bad = 1;
+ }
+ val++;
+ }
+
+ printf("done. Try %d: test ", t);
+ if (bad) {
+ printf("FAILED\n");
+ break;
+ }
+ printf("PASSED\n");
+ }
+
+ write32(GFX_CTRL_PORT, GFX_WRITE_CRAM_ADDR(0));
+ write16(GFX_DATA_PORT, 0x008);
+
+ /* there are not enough STOP opcodes in this world :D */
+ while (1)
+ asm volatile("stop #0x2000" ::: "cc");
+
+ return 0;
+}
+
+// vim:ts=4:sw=4:expandtab
--- /dev/null
+#include <stdio.h>
+
+int main()
+{
+ FILE *f;
+ int i;
+
+ f = fopen("test.bin", "wb");
+ if (f == NULL)
+ return 1;
+
+ for (i = 0; i < 0x200000 - 0x10000; i++)
+ fwrite(&i, 1, 1, f);
+ fclose(f);
+
+ return 0;
+}
--- /dev/null
+OUTPUT_ARCH(m68k)\r
+SEARCH_DIR(.)\r
+/*GROUP(-lbcc -lc -lgcc)*/\r
+__DYNAMIC = 0;\r
+\r
+/*\r
+ * Setup the memory map of the SEGA Genesis.\r
+ * stack grows down from high memory.\r
+ *\r
+ * The memory map look like this:\r
+ * +--------------------+ <- low memory\r
+ * | .text |\r
+ * | _etext |\r
+ * | ctor list | the ctor and dtor lists are for\r
+ * | dtor list | C++ support\r
+ * +--------------------+\r
+ * | .data | initialized data goes here\r
+ * | _edata |\r
+ * +--------------------+\r
+ * | .bss |\r
+ * | __bss_start | start of bss, cleared by crt0\r
+ * | _end | start of heap, used by sbrk()\r
+ * +--------------------+\r
+ * . .\r
+ * . .\r
+ * . .\r
+ * | __stack | top of stack\r
+ * +--------------------+\r
+ */\r
+MEMORY\r
+{\r
+ rom : ORIGIN = 0x00000000, LENGTH = 0x00400000\r
+ ram : ORIGIN = 0x00ff0000, LENGTH = 0x00010000\r
+}\r
+\r
+/*\r
+ * allocate the stack to be at the top of memory, since the stack\r
+ * grows down\r
+ */\r
+\r
+PROVIDE (__stack = 0x00fffff0);\r
+\r
+/*\r
+ * Initalize some symbols to be zero so we can reference them in the\r
+ * crt0 without core dumping. These functions are all optional, but\r
+ * we do this so we can have our crt0 always use them if they exist. \r
+ * This is so BSPs work better when using the crt0 installed with gcc.\r
+ * We have to initalize them twice, so we cover a.out (which prepends\r
+ * an underscore) and coff object file formats.\r
+ */\r
+PROVIDE (hardware_init_hook = 0);\r
+PROVIDE (_hardware_init_hook = 0);\r
+PROVIDE (software_init_hook = 0);\r
+PROVIDE (_software_init_hook = 0);\r
+\r
+SECTIONS\r
+{\r
+ .text 0x00000000:\r
+ {\r
+ *(.text)\r
+ . = ALIGN(0x4);\r
+ __CTOR_LIST__ = .;\r
+ LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2)\r
+ *(.ctors)\r
+ LONG(0)\r
+ __CTOR_END__ = .;\r
+ __DTOR_LIST__ = .;\r
+ LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2)\r
+ *(.dtors)\r
+ LONG(0)\r
+ __DTOR_END__ = .;\r
+ *(.rodata*)\r
+/*\r
+ *(.gcc_except_table)\r
+\r
+ __INIT_SECTION__ = . ;\r
+ *(.init)\r
+ SHORT (0x4e75)\r
+\r
+ __FINI_SECTION__ = . ;\r
+ *(.fini)\r
+ SHORT (0x4e75)\r
+*/\r
+ _etext = .;\r
+ *(.lit)\r
+ } > rom\r
+\r
+ .data 0xff0000 :\r
+ {\r
+ *(.shdata)\r
+ *(.data)\r
+ _edata = .;\r
+ } > ram\r
+\r
+ /* .bss 0xff0100 : */\r
+ .bss BLOCK (0x4) :\r
+ {\r
+ __bss_start = . ;\r
+ *(.shbss)\r
+ *(.bss)\r
+ *(COMMON)\r
+ *(.eh_fram)\r
+ *(.eh_frame)\r
+ _end = ALIGN (0x8);\r
+ __end = _end;\r
+ } > ram\r
+\r
+ .stab 0 (NOLOAD) :\r
+ {\r
+ *(.stab)\r
+ }\r
+\r
+ .stabstr 0 (NOLOAD) :\r
+ {\r
+ *(.stabstr)\r
+ }\r
+}\r
--- /dev/null
+ dc.l 0, RST, exc__, exc__, exc__, exc__, exc__, exc__\r
+ dc.l exc__, exc__, exc__, exc__, exc__, exc__, exc__, exc__\r
+ dc.l exc__, exc__, exc__, exc__, exc__, exc__, exc__, exc__\r
+ dc.l exc__, exc__, exc__, exc__, HBL, exc__, VBL, exc__\r
+ dc.l exc__, exc__, exc__, exc__, exc__, exc__, exc__, exc__\r
+ dc.l exc__, exc__, exc__, exc__, exc__, exc__, exc__, exc__\r
+ dc.l exc__, exc__, exc__, exc__, exc__, exc__, exc__, exc__\r
+ dc.l exc__, exc__, exc__, exc__, exc__, exc__, exc__, exc__\r
+\r
+ .ascii "SEGA "\r
+ .ascii "MEGA-ED SDRAM vs STOP "\r
+ .ascii "MEGA-ED SDRAM vs STOP "\r
+ .ascii "GM 00000000-00"\r
+ .byte 0x00,0x00\r
+ .ascii "JD "\r
+ .byte 0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00\r
+ .byte 0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff\r
+ .ascii " "\r
+ .ascii " "\r
+ .ascii " "\r
+ .ascii "JUE "\r
+\r
+RST:\r
+ move.w #0x2700, %sr\r
+\r
+ move.b (0xA10001), %d0\r
+ andi.b #0x0F, %d0\r
+ beq.s 0f\r
+ move.l #0x53454741, (0xA14000) /* 'SEGA' */\r
+0:\r
+ moveq.l #0, %d0\r
+ movea.l %d0, %a7\r
+ move.l %a7, %usp\r
+ bra main\r
+\r
+HBL:\r
+VBL:\r
+exc__:\r
+ rte\r
+\r
+# vim:filetype=asmM68k:ts=4:sw=4:expandtab\r
--- /dev/null
+CROSS = m68k-elf-\r
+CC = $(CROSS)gcc\r
+AS = $(CROSS)as\r
+LD = $(CROSS)ld\r
+OBJCOPY = $(CROSS)objcopy\r
+\r
+ASFLAGS += -m68000 --register-prefix-optional --bitwise-or -pic\r
+ASFLAGS_CC += -Wa,-m68000 -Wa,--register-prefix-optional -Wa,--bitwise-or -Wa,-pic\r
+CFLAGS += -Wall -g -O1 -m68000 -fomit-frame-pointer\r
+LDLIBS += $(shell $(CC) -print-file-name=libgcc.a)\r
+\r
+TARGET = megaed_stop\r
+OBJS = sega_gcc.o main.o\r
+\r
+all: $(TARGET).bin\r
+\r
+$(TARGET).elf: $(OBJS)\r
+ $(LD) -o $@ -Ted_app.ld -Map $(TARGET).map $^ $(LDLIBS)\r
+\r
+$(TARGET)_e.bin: $(TARGET).bin\r
+ dd if=/dev/zero of=$@ bs=1M count=1\r
+ dd if=$^ of=$@ bs=1M seek=1\r
+ dd if=$^ of=$@ conv=notrunc\r
+\r
+clean:\r
+ $(RM) $(TARGET).bin $(OBJS) $(TARGET).elf $(TARGET).map\r
+\r
+\r
+%.bin: %.elf\r
+ $(OBJCOPY) -I elf32-m68k -O binary $^ $@\r
+\r
+%.o: %.S\r
+ $(CC) -c -o $@ $^ $(ASFLAGS_CC)\r
+\r
+rel: $(TARGET).bin\r
+ mkdir -p /tmp/$(TARGET)/src/\r
+ cp $^ /tmp/$(TARGET)/\r
+ $(MAKE) clean\r
+ cp -a * /tmp/$(TARGET)/src/\r
--- /dev/null
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
--- /dev/null
+OUTPUT_ARCH(m68k)\r
+SEARCH_DIR(.)\r
+/*GROUP(-lbcc -lc -lgcc)*/\r
+__DYNAMIC = 0;\r
+\r
+/*\r
+ * Setup the memory map of the SEGA Genesis.\r
+ * stack grows down from high memory.\r
+ *\r
+ * The memory map look like this:\r
+ * +--------------------+ <- low memory\r
+ * | .text |\r
+ * | _etext |\r
+ * | ctor list | the ctor and dtor lists are for\r
+ * | dtor list | C++ support\r
+ * +--------------------+\r
+ * | .data | initialized data goes here\r
+ * | _edata |\r
+ * +--------------------+\r
+ * | .bss |\r
+ * | __bss_start | start of bss, cleared by crt0\r
+ * | _end | start of heap, used by sbrk()\r
+ * +--------------------+\r
+ * . .\r
+ * . .\r
+ * . .\r
+ * | __stack | top of stack\r
+ * +--------------------+\r
+ */\r
+MEMORY\r
+{\r
+ rom : ORIGIN = 0x00100000, LENGTH = 0x00300000\r
+ ram : ORIGIN = 0x00ff0100, LENGTH = 0x0000FF00\r
+}\r
+\r
+/*\r
+ * allocate the stack to be at the top of memory, since the stack\r
+ * grows down\r
+ */\r
+\r
+PROVIDE (__stack = 0x00fffff0);\r
+\r
+/*\r
+ * Initalize some symbols to be zero so we can reference them in the\r
+ * crt0 without core dumping. These functions are all optional, but\r
+ * we do this so we can have our crt0 always use them if they exist. \r
+ * This is so BSPs work better when using the crt0 installed with gcc.\r
+ * We have to initalize them twice, so we cover a.out (which prepends\r
+ * an underscore) and coff object file formats.\r
+ */\r
+PROVIDE (hardware_init_hook = 0);\r
+PROVIDE (_hardware_init_hook = 0);\r
+PROVIDE (software_init_hook = 0);\r
+PROVIDE (_software_init_hook = 0);\r
+\r
+SECTIONS\r
+{\r
+ .text 0x00100000:\r
+ {\r
+ *(.text)\r
+ . = ALIGN(0x4);\r
+ __CTOR_LIST__ = .;\r
+ LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2)\r
+ *(.ctors)\r
+ LONG(0)\r
+ __CTOR_END__ = .;\r
+ __DTOR_LIST__ = .;\r
+ LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2)\r
+ *(.dtors)\r
+ LONG(0)\r
+ __DTOR_END__ = .;\r
+ *(.rodata*)\r
+ *(.gcc_except_table)\r
+\r
+ __INIT_SECTION__ = . ;\r
+ *(.init)\r
+ SHORT (0x4e75) /* rts */\r
+\r
+ __FINI_SECTION__ = . ;\r
+ *(.fini)\r
+ SHORT (0x4e75) /* rts */\r
+\r
+ _etext = .;\r
+ *(.lit)\r
+ } > rom\r
+\r
+ .data 0xff0100 :\r
+ {\r
+ *(.shdata)\r
+ *(.data)\r
+ _edata = .;\r
+ } > ram\r
+\r
+ /* .bss 0xff0100 : */\r
+ .bss BLOCK (0x4) :\r
+ {\r
+ __bss_start = . ;\r
+ *(.shbss)\r
+ *(.bss)\r
+ *(COMMON)\r
+ *(.eh_fram)\r
+ *(.eh_frame)\r
+ _end = ALIGN (0x8);\r
+ __end = _end;\r
+ } > ram\r
+\r
+ .stab 0 (NOLOAD) :\r
+ {\r
+ *(.stab)\r
+ }\r
+\r
+ .stabstr 0 (NOLOAD) :\r
+ {\r
+ *(.stabstr)\r
+ }\r
+}\r
--- /dev/null
+/*
+ * File: edos.h
+ * Author: krik
+ *
+ * Created on 29 Ìà ðò 2012 ã., 1:29
+ */
+
+#ifndef _EDOS_H
+#define _EDOS_H
+
+//#include "segalib.h"
+
+/*
+ * saddr: address in sectors
+ * slen: lenght in sectors
+ * wr_slen: how many sectors will be written
+ * sector size 512 bytes
+ * dma reading of usb and SD available only if data destination located in rom area (0-0x3ffffff). dma reading not available for genesis ram
+ * destination address for dma reading must be aligned to 512b
+ * one mbyte of sdram can be mapped to one of four banks in rom space. osSetMemMap(u16) can be used for mapping configuration.
+ * os code located in last mbyte of sdram and should be mapped in bank 0
+ * user code located in begin of sdram. default mapping 0x210f means that end of sdram (OS) mapped to area 0-0x0fffff, first mbyte of user code mapped to 0x100000-0x1fffff,
+ * memory configuration:
+ * genesis address: 0x000000-0x0fffff, physical sdram address: 0xf00000-0xffffff OS
+ * genesis address: 0x100000-0x3fffff, physical sdram address: 0x000000-0x2fffff user code (current application)
+ * 0xff0000-0xff00ff OS dma code
+ * first 256bytes of genesis ram must be reserved for OS
+ * */
+
+
+#define MEM_ERR_SPI_RD_TIMEOUT 120
+
+#define ERR_FILE_TOO_BIG 140
+#define ERR_WRON_OS_SIZE 142
+#define ERR_OS_VERIFY 143
+#define ERR_OS_VERIFY2 144
+#define ERR_BAD_DMA_ADDRESS 145
+#define ERR_MUST_NOT_RET 146
+
+//100 - 119 fat errors
+#define FAT_ERR_NOT_EXIST 100
+#define FAT_ERR_EXIST 101
+#define FAT_ERR_NAME 102
+#define FAT_ERR_OUT_OF_FILE 103
+#define FAT_ERR_BAD_BASE_CLUSTER 104;
+#define FAT_ERR_NO_FRE_SPACE 105
+#define FAT_ERR_NOT_FILE 106
+#define FAT_ERR_FILE_MODE 107
+#define FAT_ERR_ROT_OVERFLOW 108
+#define FAT_ERR_OUT_OF_TABLE 109
+#define FAT_ERR_INIT 110
+#define FAT_LFN_BUFF_OVERFLOW 111
+#define FAT_DISK_NOT_READY 112
+#define FAT_ERR_SIZE 113
+#define FAT_ERR_RESIZE 114
+
+
+#define DISK_ERR_INIT 50
+#define DISK_ERR_RD1 62
+#define DISK_ERR_RD2 63
+
+#define DISK_ERR_WR1 64
+#define DISK_ERR_WR2 65
+#define DISK_ERR_WR3 66
+#define DISK_ERR_WR4 67
+#define DISK_ERR_WR5 68
+
+typedef struct {
+ u32 entry_cluster;
+ u32 size;
+ u32 hdr_sector;
+ u16 hdr_idx;
+ u8 name[256];
+ u16 is_dir;
+} FatFullRecord;
+
+typedef struct {
+ u8(*diskWrite)(u32 saddr, u8 *buff, u16 slen);
+ u8(*diskRead)(u32 saddr, void *buff, u16 slen);//*dst must be alligned to 512bytes and be in range 0x100000-0x3fffff. *dst can be located in genesis ram also, but transfer will be slow in this case
+ u8(*usbReadByte)();//loop inside of function waiting until usbRdReady != 0
+ void (*usbWriteByte)(u8 dat);//loop inside of function waiting until usbWrReady != 0
+ u8(*usbReadDma)(u16 *dst, u16 slen);//*dst must be alligned to 512bytes and be in range 0x100000-0x3fffff
+ u8(*usbReadPio)(u16 *dst, u16 slen);
+ u8(*usbRdReady)(); //return 1 if some data comes from pc
+ u8(*usbWrReady)(); //return 1 usb ready to send byte
+ void (*osSetMemMap)(u16 map); //memoty configuration 4x1Mb
+ u16(*osGetOsVersion)();
+ u16(*osGetFirmVersion)();
+ void (*osGetSerial)(u32 * buff); // 8bytes uniq serial number
+ void (*VDP_setReg)(u8 reg, u8 value);//os vdp functions should be used, otherwise system may hang while udb/sd dma
+ void (*VDP_vintOn)();
+ void (*VDP_vintOff)();
+ void (*memInitDmaCode)();//copy dma routine to genesis ram
+ u8(*fatReadDir)(FatFullRecord * frec);//get next record in current dir
+ void(*fatOpenDir)(u32 entry_cluster);//arg is entry cluster ot dir or 0. zero arg means that need to open root dir
+ u8(*fatOpenFileByeName)(u8 *name, u32 wr_slen);//wr_slen write len, or 0 if reading
+ u8(*fatOpenFile)(FatFullRecord *rec, u32 wr_slen);//wr_slen is write len, or 0 if reading
+ u8(*fatReadFile)(void *dst, u32 slen);
+ u8(*fatWriteFile)(void *src, u32 slen);
+ u8(*fatCreateRecIfNotExist)(u8 *name, u8 is_dir);
+ u8(*fatSkipSectors)(u32 slen);
+} OsRoutine;
+
+
+extern OsRoutine *ed_os;
+void edInit();
+
+
+//stereo dac. 8bit for each channel.
+//any writing to this port enables dac output, any reading from this port disables dac output.
+#define REG_DAC *((volatile u16*) (0xA1300E))
+
+
+//stuff for direct access to usb and spi. not recommended for use. OS function should be used for best compatibility
+#define _SPI_SS 0
+#define _SPI_FULL_SPEED 1
+#define _SPI_SPI16 2
+#define _SPI_AREAD 3
+
+//spi port (sd interface)
+#define SPI_PORT *((volatile u16*) (0xA13000))
+#define SPI_CFG_PORT *((volatile u16*) (0xA13002))
+
+//16bit or 8bit spi mode
+#define SPI16_ON SPI_CFG_PORT |= (1 << _SPI_SPI16)
+#define SPI16_OFF SPI_CFG_PORT &= ~(1 << _SPI_SPI16)
+
+//spi autoread. means that not need to write something to spi port before than read
+#define SPI_AR_ON SPI_CFG_PORT |= (1 << _SPI_AREAD)
+#define SPI_AR_OFF SPI_CFG_PORT &= ~(1 << _SPI_AREAD)
+
+//spi chip select
+#define SPI_SS_OFF SPI_CFG_PORT |= (1 << _SPI_SS)
+#define SPI_SS_ON SPI_CFG_PORT &= ~(1 << _SPI_SS)
+
+//spi speed. low speed need only for sd init
+#define SPI_HI_SPEED_ON SPI_CFG_PORT |= (1 << _SPI_FULL_SPEED)
+#define SPI_HI_SPEED_OFF SPI_CFG_PORT &= ~(1 << _SPI_FULL_SPEED)
+
+//usb-serial port
+#define FIFO_PORT *((volatile u16*) (0xA1300A))
+
+//spi and usb state
+#define STATE_PORT *((volatile u16*) (0xA1300C))
+#define _STATE_SPI_READY 0
+#define _STATE_FIFO_WR_READY 1
+#define _STATE_FIFO_RD_READY 2
+#define IS_FIFO_RD_READY (STATE_PORT & (1 << _STATE_FIFO_RD_READY))
+#define IS_FIFO_WR_READY (STATE_PORT & (1 << _STATE_FIFO_WR_READY))
+
+
+#endif /* _EDOS_H */
+
--- /dev/null
+/*
+ * This software is released into the public domain.
+ * See UNLICENSE file in top level directory.
+ */
+#include <stdlib.h>
+#include <stdarg.h>
+
+#define u8 unsigned char
+#define u16 unsigned short
+#define u32 unsigned int
+
+#include "edos.h"
+
+#define GFX_DATA_PORT 0xC00000
+#define GFX_CTRL_PORT 0xC00004
+
+#define TILE_MEM_END 0xB000
+
+#define FONT_LEN 128
+#define TILE_FONT_BASE (TILE_MEM_END / 32 - FONT_LEN)
+
+/* note: using ED menu's layout here.. */
+#define WPLANE (TILE_MEM_END + 0x0000)
+#define HSCRL (TILE_MEM_END + 0x0800)
+#define SLIST (TILE_MEM_END + 0x0C00)
+#define APLANE (TILE_MEM_END + 0x1000)
+#define BPLANE (TILE_MEM_END + 0x3000)
+
+#define read8(a) \
+ *((volatile u8 *) (a))
+#define read16(a) \
+ *((volatile u16 *) (a))
+#define read32(a) \
+ *((volatile u32 *) (a))
+#define write16(a, d) \
+ *((volatile u16 *) (a)) = (d)
+#define write32(a, d) \
+ *((volatile u32 *) (a)) = (d)
+
+#define GFX_WRITE_VRAM_ADDR(adr) \
+ (((0x4000 | ((adr) & 0x3FFF)) << 16) | ((adr) >> 14) | 0x00)
+#define GFX_WRITE_VSRAM_ADDR(adr) \
+ (((0x4000 | ((adr) & 0x3FFF)) << 16) | ((adr) >> 14) | 0x10)
+
+enum {
+ VDP_MODE1 = 0x00,
+ VDP_MODE2 = 0x01,
+ VDP_BACKDROP = 0x07,
+ VDP_MODE3 = 0x0b,
+ VDP_MODE4 = 0x0c,
+ VDP_AUTOINC = 0x0f,
+ VDP_SCROLLSZ = 0x10,
+};
+
+/* cell counts */
+#define LEFT_BORDER 1 /* lame TV */
+#define PLANE_W 64
+#define PLANE_H 32
+#define CSCREEN_H 28
+
+static void VDP_drawTextML(const char *str, u16 plane_base,
+ u16 x, u16 y)
+{
+ const u8 *src = (const u8 *)str;
+ u16 basetile = 0;
+ int max_len = 40 - LEFT_BORDER;
+ int len;
+ u32 addr;
+
+ x += LEFT_BORDER;
+
+ for (len = 0; str[len] && len < max_len; len++)
+ ;
+ if (len > (PLANE_W - x))
+ len = PLANE_W - x;
+
+ addr = plane_base + ((x + (PLANE_W * y)) << 1);
+ write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(addr));
+
+ while (len-- > 0) {
+ write16(GFX_DATA_PORT,
+ basetile | ((*src++) - 32 + TILE_FONT_BASE));
+ }
+}
+
+static int printf_ypos;
+
+static void printf_line(int x, const char *buf)
+{
+ u32 addr;
+ int i;
+
+ VDP_drawTextML(buf, APLANE, x, printf_ypos++ & (PLANE_H - 1));
+
+ if (printf_ypos >= CSCREEN_H) {
+ /* clear next line */
+ addr = APLANE;
+ addr += (PLANE_W * (printf_ypos & (PLANE_H - 1))) << 1;
+ write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(addr));
+ for (i = 0; i < 40 / 2; i++)
+ write32(GFX_DATA_PORT, 0);
+
+ /* scroll plane */
+ write32(GFX_CTRL_PORT, GFX_WRITE_VSRAM_ADDR(0));
+ write16(GFX_DATA_PORT, (printf_ypos - CSCREEN_H + 1) * 8);
+ }
+}
+
+#define PRINTF_LEN 40
+
+static int printf(const char *fmt, ...)
+{
+ static const char hexchars[] = "0123456789abcdef";
+ static int printf_xpos;
+ char c, buf[PRINTF_LEN + 11 + 1];
+ const char *s;
+ va_list ap;
+ int ival;
+ u32 uval;
+ int d = 0;
+ int i, j;
+
+ va_start(ap, fmt);
+ for (d = 0; *fmt; ) {
+ int prefix0 = 0;
+ int fwidth = 0;
+
+ c = *fmt++;
+ if (d < PRINTF_LEN)
+ buf[d] = c;
+
+ if (c != '%') {
+ if (c == '\n') {
+ buf[d] = 0;
+ printf_line(printf_xpos, buf);
+ d = 0;
+ printf_xpos = 0;
+ continue;
+ }
+ d++;
+ continue;
+ }
+ if (d >= PRINTF_LEN)
+ continue;
+
+ if (*fmt == '0') {
+ prefix0 = 1;
+ fmt++;
+ }
+
+ while ('1' <= *fmt && *fmt <= '9') {
+ fwidth = fwidth * 10 + *fmt - '0';
+ fmt++;
+ }
+
+ switch (*fmt++) {
+ case '%':
+ d++;
+ break;
+ case 'd':
+ case 'i':
+ ival = va_arg(ap, int);
+ if (ival < 0) {
+ buf[d++] = '-';
+ ival = -ival;
+ }
+ for (i = 1000000000; i >= 10; i /= 10)
+ if (ival >= i)
+ break;
+ for (; i >= 10; i /= 10) {
+ buf[d++] = '0' + ival / i;
+ ival %= i;
+ }
+ buf[d++] = '0' + ival;
+ break;
+ case 'x':
+ uval = va_arg(ap, int);
+ while (fwidth > 1 && uval < (1 << (fwidth - 1) * 4)) {
+ buf[d++] = prefix0 ? '0' : ' ';
+ fwidth--;
+ }
+ for (j = 1; j < 8 && uval >= (1 << j * 4); j++)
+ ;
+ for (j--; j >= 0; j--)
+ buf[d++] = hexchars[(uval >> j * 4) & 0x0f];
+ break;
+ case 's':
+ s = va_arg(ap, char *);
+ while (*s && d < PRINTF_LEN)
+ buf[d++] = *s++;
+ break;
+ default:
+ // don't handle, for now
+ d++;
+ va_arg(ap, void *);
+ break;
+ }
+ }
+ buf[d] = 0;
+ va_end(ap);
+
+ if (d != 0) {
+ // line without \n
+ VDP_drawTextML(buf, APLANE, printf_xpos,
+ printf_ypos & (PLANE_H - 1));
+ printf_xpos += d;
+ }
+
+ return d; // wrong..
+}
+
+int main()
+{
+ OsRoutine *ed;
+ u8 seed, bad = 0;
+ u8 *p, val;
+ int i, t, len;
+
+ ed = (OsRoutine *) *(u32 *)0x1A0;
+ ed->memInitDmaCode();
+
+ ed->VDP_setReg(VDP_MODE1, 0x04);
+ ed->VDP_setReg(VDP_MODE2, 0x64);
+ ed->VDP_setReg(VDP_AUTOINC, 2);
+ ed->VDP_setReg(VDP_SCROLLSZ, 0x01);
+
+ /* clear name tables */
+ write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(APLANE));
+ for (i = 0; i < PLANE_W * PLANE_H / 2; i++)
+ write32(GFX_DATA_PORT, 0);
+
+ write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(BPLANE));
+ for (i = 0; i < PLANE_W * PLANE_H / 2; i++)
+ write32(GFX_DATA_PORT, 0);
+
+ /* scroll planes */
+ write32(GFX_CTRL_PORT, GFX_WRITE_VSRAM_ADDR(0));
+ write32(GFX_DATA_PORT, 0);
+
+ /* note: relying on ED menu's font setup here.. */
+
+ printf("\n");
+ printf("MD version: %02x\n", read8(0xa10001));
+ printf("ED os/fw: %d/%d\n\n", ed->osGetOsVersion(),
+ ed->osGetFirmVersion());
+
+ seed = read8(0xC00009);
+ p = (void *)0x200000;
+ len = 0x200000;
+ printf("filling SDRAM, seed=%02x.. ", seed);
+
+ val = seed;
+ for (i = 0; i < len; i++)
+ p[i] = val++;
+
+ printf("done.\n");
+
+ for (t = 1; ; t++) {
+ printf("executing stop.. ");
+ for (i = 0; i < 5 * 60; i++)
+ asm volatile("stop #0x2000");
+ printf("done\n");
+
+ printf("checking memory..\n");
+
+ val = seed;
+ for (i = 0; i < len; i++) {
+ if (p[i] != val) {
+ printf("bad: %06x: got %02x, expected %02x\n",
+ &p[i], p[i], val);
+ bad = 1;
+ }
+ val++;
+ }
+
+ printf("done. Try %d: test ", t);
+ if (bad) {
+ printf("FAILED\n");
+ break;
+ }
+ printf("PASSED\n");
+ }
+
+ /* there are not enough STOP opcodes in this world :D */
+ while (1)
+ asm volatile("stop #0x2000");
+
+ return 0;
+}
+
+// vim:ts=4:sw=4:expandtab
--- /dev/null
+ dc.l 0, 0x200, exc__, exc__, exc__, exc__, exc__, exc__\r
+ dc.l exc__, exc__, exc__, exc__, exc__, exc__, exc__, exc__\r
+ dc.l exc__, exc__, exc__, exc__, exc__, exc__, exc__, exc__\r
+ dc.l exc__, exc__, exc__, exc__, HBL, exc__, VBL, exc__\r
+ dc.l exc__, exc__, exc__, exc__, exc__, exc__, exc__, exc__\r
+ dc.l exc__, exc__, exc__, exc__, exc__, exc__, exc__, exc__\r
+ dc.l exc__, exc__, exc__, exc__, exc__, exc__, exc__, exc__\r
+ dc.l exc__, exc__, exc__, exc__, exc__, exc__, exc__, exc__\r
+\r
+ .ascii "SEGA EVERDRIVE "\r
+ .ascii "MEGA-ED SDRAM vs STOP "\r
+ .ascii "MEGA-ED SDRAM vs STOP "\r
+ .ascii "GM 00000000-00"\r
+ .byte 0x00,0x00\r
+ .ascii "JD "\r
+ .byte 0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00\r
+ .byte 0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff\r
+ .ascii " "\r
+ .ascii " "\r
+ .ascii " "\r
+ .ascii "JUE "\r
+\r
+RST:\r
+ move.w #0x2700, %sr\r
+/* magic ED app init */\r
+ move.w #0x0000, (0xA13006)\r
+ jmp init_ed.l\r
+init_ed:\r
+ move.w #0x210f, (0xA13006)\r
+ move.l #HBL, (0x70)\r
+ move.l #VBL, (0x78)\r
+\r
+ moveq #0, %d0\r
+ movea.l %d0, %a7\r
+ move %a7, %usp\r
+ bra main\r
+\r
+HBL:\r
+VBL:\r
+exc__:\r
+ rte\r
+\r
+# vim:filetype=asmM68k:ts=4:sw=4:expandtab\r
--- /dev/null
+CROSS = m68k-elf-\r
+CC = $(CROSS)gcc\r
+AS = $(CROSS)as\r
+LD = $(CROSS)ld\r
+OBJCOPY = $(CROSS)objcopy\r
+\r
+ASFLAGS += -m68000 --register-prefix-optional --bitwise-or -pic\r
+ASFLAGS_CC += -Wa,-m68000 -Wa,--register-prefix-optional -Wa,--bitwise-or -Wa,-pic\r
+CFLAGS += -Wall -g -O1 -m68000 -fomit-frame-pointer\r
+LDLIBS += $(shell $(CC) -print-file-name=libgcc.a)\r
+\r
+TARGET = megaedsv\r
+OBJS = sega_gcc.o main.o asmtools.o\r
+\r
+all: $(TARGET).bin\r
+\r
+$(TARGET).elf: $(OBJS)\r
+ $(LD) -o $@ -Ted_app.ld -Map $(TARGET).map $^ $(LDLIBS)\r
+\r
+$(TARGET)_e.bin: $(TARGET).bin\r
+ dd if=/dev/zero of=$@ bs=1M count=1\r
+ dd if=$^ of=$@ bs=1M seek=1\r
+ dd if=$^ of=$@ conv=notrunc\r
+\r
+clean:\r
+ $(RM) $(TARGET).bin $(OBJS) $(TARGET).elf $(TARGET).map\r
+\r
+\r
+%.bin: %.elf\r
+ $(OBJCOPY) -I elf32-m68k -O binary $^ $@\r
+\r
+%.o: %.S\r
+ $(CC) -c -o $@ $^ $(ASFLAGS_CC)\r
--- /dev/null
+void read_joy_responses(u8 resp[8*5]);
+void test_joy_read_log(u8 *dest, int size, int do_sync);
+void test_joy_read_log_vsync(u8 *dest, int size);
+void test_byte_write(u8 *dest, int size, int seed);
+void run_game(u16 mapper, int tas_sync);
--- /dev/null
+# Assemble with gas
+# --register-prefix-optional --bitwise-or
+
+.macro ldarg arg, stacksz, reg
+ move.l (4 + \arg * 4 + \stacksz)(%sp), \reg
+.endm
+
+
+.global read_joy_responses /* u8 *rbuf */
+read_joy_responses:
+ ldarg 0, 0, a1
+ movem.l d2-d7, -(sp)
+ movea.l #0xa10003, a0
+ move.b #0x40, (6,a0)
+ move.b #0x40, (a0)
+
+.macro one_test val
+ move.l #100/12-1, d0
+0:
+ dbra d0, 0b
+ move.b \val, d0
+ move.b d0, (a0)
+ move.b (a0), d0
+ move.b (a0), d1
+ move.b (a0), d2
+ move.b (a0), d3
+ move.b (a0), d4
+ move.b (a0), d5
+ move.b (a0), d6
+ move.b (a0), d7
+ move.b d0, (a1)+
+ move.b d1, (a1)+
+ move.b d2, (a1)+
+ move.b d3, (a1)+
+ move.b d4, (a1)+
+ move.b d5, (a1)+
+ move.b d6, (a1)+
+ move.b d7, (a1)+
+.endm
+
+ move.w #0x2700, sr
+ one_test #0x00
+ one_test #0x40
+ one_test #0x00
+ one_test #0x40
+ one_test #0x00
+ move.w #0x2000, sr
+ movem.l (sp)+, d2-d7
+ rts
+
+
+/* expects:
+ * a0 = #0xa10003
+ * d0 = #0
+ * d1 = #0x40
+ * trashes d2, d3
+ */
+sync_with_teensy:
+0: /* wait for special code */
+ move.b d1, (a0)
+ move.b (a0), d2
+ move.b d0, (a0)
+ move.b (a0), d3
+ and.b #0x3f, d2
+ cmp.b d2, d3
+ bne 0b
+ cmp.b #0x25, d2
+ bne 0b
+
+0: /* wait for special code to end */
+ cmp.b (a0), d2
+ beq 0b
+
+ move.b d1, (a0)
+ move.l #8000000/50/18, d2
+
+0: /* wait enough for teensy to setup it's stuff */
+ subq.l #1, d2 /* 8 */
+ bgt.s 0b /* 10 */
+
+ rts
+
+
+.macro t_nop
+ /*
+ * when communicating with 3.3V teensy:
+ * - no nops: see old value on multiple pins randomly
+ * - 1 nop: only TR often shows old value
+ * - 2 nops: ?
+ */
+ nop
+ nop
+.endm
+
+
+.global test_joy_read_log /* u8 *dest, int size, int do_sync */
+test_joy_read_log:
+ ldarg 0, 0, a1
+ ldarg 1, 0, d0
+ ldarg 2, 0, d1
+ movem.l d2-d7, -(sp)
+ movea.l #0xa10003, a0
+ move.l d0, d7
+ move.l d1, d6
+
+ moveq.l #0, d0
+ move.l #0x40, d1
+ move.b d1, (6,a0)
+ move.b d1, (a0)
+
+ tst.l d6
+ beq.s 2f
+ bsr sync_with_teensy
+
+2: /* save data */
+ move.b d0, (a0)
+ t_nop
+ move.b (a0), d2
+ move.b d1, (a0)
+ t_nop
+ move.b (a0), d3
+ move.b d0, (a0)
+ t_nop
+ move.b (a0), d4
+ move.b d1, (a0)
+ t_nop
+ move.b (a0), d5
+.if 0
+ /* broken on Mega-ED v9?? */
+ move.b d2, (a1)+
+ move.b d3, (a1)+
+ move.b d4, (a1)+
+ move.b d5, (a1)+
+.else
+ lsl.w #8, d2
+ move.b d3, d2
+ move.w d2, (a1)+
+ lsl.w #8, d4
+ move.b d5, d4
+ move.w d4, (a1)+
+.endif
+
+ /* delay for teensy, 128 not enough.. */
+ move.l #256, d2
+0:
+ dbra d2, 0b
+
+ subq.l #4, d7
+ bgt.s 2b
+
+ movem.l (sp)+, d2-d7
+ rts
+
+
+.global test_joy_read_log_vsync /* u8 *dest, int size */
+test_joy_read_log_vsync:
+ ldarg 0, 0, a1
+ ldarg 1, 0, d0
+ movem.l d2-d7/a2, -(sp)
+ movea.l #0xa10003, a0
+ movea.l #0xc00005, a2
+ move.l d0, d7
+
+ move.l #0x40, d1
+ moveq.l #0, d0
+ move.b d1, (6,a0)
+ move.b d1, (a0)
+
+ bsr sync_with_teensy
+
+2: /* save data */
+ move.b d0, (a0)
+ move.b (a0), d2
+ move.b d1, (a0)
+ move.b (a0), d3
+ move.b d2, (a1)+
+ move.b d3, (a1)+
+
+ /* wait for next vsync */
+ moveq.l #3, d2
+0:
+ btst d2, (a2)
+ bne.s 0b
+0:
+ btst d2, (a2)
+ beq.s 0b
+
+ subq.l #2, d7
+ bgt.s 2b
+
+ movem.l (sp)+, d2-d7/a2
+ rts
+
+
+.global test_byte_write /* u8 *dest, int size, int seed */
+test_byte_write:
+ ldarg 0, 0, a0
+ ldarg 1, 0, d0
+ ldarg 2, 0, d1
+ movem.l d2-d7, -(sp)
+
+ move.l a0, a1
+ add.l d0, a1
+ move.l d1, d7
+0:
+ move.b d7, d0
+ addq.b #1, d7
+ move.b d7, d1
+ addq.b #1, d7
+ move.b d7, d2
+ addq.b #1, d7
+ move.b d7, d3
+ addq.b #1, d7
+ move.b d7, d4
+ addq.b #1, d7
+ move.b d7, d5
+ addq.b #1, d7
+ move.b d7, d6
+ addq.b #1, d7
+
+ move.b d0, (a0)+
+ move.b d1, (a0)+
+ move.b d2, (a0)+
+ move.b d3, (a0)+
+ move.b d4, (a0)+
+ move.b d5, (a0)+
+ move.b d6, (a0)+
+ move.b d7, (a0)+
+ addq.b #1, d7
+ cmp.l a1, a0
+ blt.s 0b
+
+ movem.l (sp)+, d2-d7
+ rts
+
+
+.global run_game /* u16 mapper, int tas_sync */
+run_game:
+ move.w #0x2700, sr
+ ldarg 0, 0, d7
+ ldarg 1, 0, d6
+ movea.l #0xa10000, a6
+ movea.l #0xc00000, a5
+ movea.l #0xc00005, a4
+ movea.l #0xc00004, a3
+ moveq.l #0x00, d0
+ move.b #0x40, d1 /* d2 is tmp */
+ move.b #0xff, d3 /* d4 is temp */
+ moveq.l #0x00, d5 /* progress cnt */
+ movea.l d0, a7
+ move.b d1, (0x09,a6) /* CtrlA */
+ move.b d0, (0x0b,a6) /* CtrlB */
+ move.b d0, (0x0d,a6) /* CtrlC */
+ move.b d0, (0x13,a6) /* S-CtrlA */
+ move.b d3, (0x0f,a6) /* TxDataA */
+ move.b d0, (0x19,a6) /* S-CtrlB */
+ move.b d3, (0x15,a6) /* TxDataB */
+ move.b d0, (0x1f,a6) /* S-CtrlC */
+ move.b d3, (0x1b,a6) /* TxDataC */
+
+ move.w #0xcbaf, (0xA13006) /* some scratch area */
+
+ move.l #0xff0000, a1
+ move.l #0x10000/4/4-1, d2
+0:
+ move.l d0, (a1)+
+ move.l d0, (a1)+
+ move.l d0, (a1)+
+ move.l d0, (a1)+
+ dbra d2, 0b
+
+ lea (run_game_r,pc), a0
+ move.l #0xffff80, a1
+ move.l #(run_game_r_end - run_game_r)/2-1, d2
+0:
+ move.w (a0)+, (a1)+
+ dbra d2, 0b
+
+ tst.l d6
+ beq.s sync_hvc
+
+ movea.l #0xa10003, a0
+ bsr sync_with_teensy /* trashes d3 */
+ move.l d0, (-4,a7)
+
+sync_hvc:
+ addq.l #1, d6 /* attempt counter */
+
+ /* set up for progress vram write (x,y - tile #) */
+ /* GFX_WRITE_VRAM_ADDR(0xc000 + (x + 64 * y) * 2) */
+ /* d = d5 + '0' - 32 + 0xB000/32 - 128 = d5 + 0x510 */
+ move.l #(0x40000003 | ((36 + 64*1) << 17)), (a3)
+ add.w #0x510, d5
+ move.w d5, (a5)
+ move.w #('/'+0x4e0), (a5)
+ move.w #('4'+0x4e0), (a5)
+
+ lea hexchars, a1
+ move.l #(0x40000003 | ((31 + 64*2) << 17)), (a3)
+ moveq.l #8-1, d5
+0:
+ rol.l #4, d3
+ move.b d3, d4
+ and.l #0x0f, d4
+ move.b (d4,a1), d4
+ add.w #0x4e0, d4
+ move.w d4, (a5)
+ dbra d5, 0b
+
+ movea.l #0xc00008, a0
+ movea.l #0x3ff000, a1
+ movea.l #0xffffe0, a2
+
+ /* wait for active display */
+ moveq.l #3, d2
+0:
+ btst d2, (a4) /* 8 */
+ beq.s 0b /* 10 */
+0:
+ btst d2, (a4)
+ bne.s 0b
+
+ /* flood the VDP FIFO */
+.rept 5
+ move.w d0, (a5)
+.endr
+
+ /* these seem stable for both 50Hz/60Hz */
+ move.l (a0), (a1)+ /* #0xff07ff09 */
+ move.l (a0), (a1)+ /* #0xff00ff11 */
+ move.l (a0), (a1)+ /* #0xff18ff1a */
+ move.l (a0), (a1)+ /* #0xff21ff23 */
+ move.l (a0), (a1)+ /* #0xff2aff28 */
+ move.l (a0), (a1)+ /* #0xff33ff34 */
+ move.l (a0), (a1)+ /* #0xff3cff3e */
+ move.l (a0), (a1)+ /* #0xff45ff47 */
+
+ /* as long as exactly 8 or more RAM writes are performed here, */
+ /* after multiple tries RAM refresh somehow eventually syncs */
+ /* after cold boot, only 50Hz syncs to always same values though, */
+ /* so values below are 50Hz */
+ move.l (a0), (a2)+ /* #0xff4eff4f */
+ move.l (a0), (a2)+ /* #0xff58ff59 */
+ move.l (a0), (a2)+ /* #0xff60ff62 */
+ move.l (a0), (a2)+ /* #0xff69ff6b */
+ move.l (a0), (a2)+ /* #0xff72ff74 */
+ move.l (a0), (a2)+ /* #0xff7bff7c */
+ move.l (a0), (a2)+ /* #0xff83ff85 */
+ move.l (a0), (a2)+ /* #0xff8eff8f */
+
+ sub.l #4*8, a1
+ sub.l #4*8, a2
+
+ moveq.l #1, d5
+ move.l (0x00,a1), d3
+ cmp.l #0xff07ff09, d3
+ bne.w sync_hvc
+
+ moveq.l #2, d5
+ move.l (0x04,a1), d3
+ cmp.l #0xff00ff11, d3 /* mystery value */
+ bne.w sync_hvc
+
+ moveq.l #3, d5
+ move.l (0x1c,a1), d3
+ cmp.l #0xff45ff47, d3
+ bne.w sync_hvc
+
+ btst.b #6, (0xa10001)
+ bne.s sync_hvc_50hz
+
+sync_hvc_60hz:
+ /* unable to get stable RAM between cold boots :( */
+ moveq.l #4, d5
+ move.l (0x1c,a2), d3
+ cmp.l #0xff8dff8f, d3 /* unstable */
+ beq.s sync_hvc_end
+ cmp.l #0xff8cff8e, d3 /* stable? */
+ beq.s sync_hvc_end
+ cmp.l #0xff8eff90, d3
+ beq.s sync_hvc_end
+ bra.w sync_hvc
+
+sync_hvc_50hz:
+ moveq.l #4, d5
+ move.l (0x1c,a2), d3
+ cmp.l #0xff8eff8f, d3 /* RAM */
+ bne.w sync_hvc
+
+sync_hvc_end:
+ movea.l d0, a0
+ movea.l #0xA13000, a1
+
+ move.b d0, (0x09,a6) /* CtrlA */
+ move.b d1, (0x03,a6)
+
+ jmp 0xffff80
+
+run_game_r:
+ move.w #0x3210, (0x06,a1) /* 0xA13006 */
+ move.w d7, (0x10,a1) /* 0xA13010 */
+ move.w d0, (a1) /* 0xA13000 */
+
+ move.l (a0)+, a7
+ move.l (a0), a0
+
+ jmp (a0)
+run_game_r_end:
+
+hexchars:
+ dc.b '0','1','2','3','4','5','6','7'
+ dc.b '8','9','a','b','c','d','e','f'
+
+# vim:filetype=asmM68k:ts=4:sw=4:expandtab
--- /dev/null
+OUTPUT_ARCH(m68k)\r
+SEARCH_DIR(.)\r
+/*GROUP(-lbcc -lc -lgcc)*/\r
+__DYNAMIC = 0;\r
+\r
+/*\r
+ * Setup the memory map of the SEGA Genesis.\r
+ * stack grows down from high memory.\r
+ *\r
+ * The memory map look like this:\r
+ * +--------------------+ <- low memory\r
+ * | .text |\r
+ * | _etext |\r
+ * | ctor list | the ctor and dtor lists are for\r
+ * | dtor list | C++ support\r
+ * +--------------------+\r
+ * | .data | initialized data goes here\r
+ * | _edata |\r
+ * +--------------------+\r
+ * | .bss |\r
+ * | __bss_start | start of bss, cleared by crt0\r
+ * | _end | start of heap, used by sbrk()\r
+ * +--------------------+\r
+ * . .\r
+ * . .\r
+ * . .\r
+ * | __stack | top of stack\r
+ * +--------------------+\r
+ */\r
+MEMORY\r
+{\r
+ rom : ORIGIN = 0x00100000, LENGTH = 0x00300000\r
+ ram : ORIGIN = 0x00ff0100, LENGTH = 0x0000FF00\r
+}\r
+\r
+/*\r
+ * allocate the stack to be at the top of memory, since the stack\r
+ * grows down\r
+ */\r
+\r
+PROVIDE (__stack = 0x00fffff0);\r
+\r
+/*\r
+ * Initalize some symbols to be zero so we can reference them in the\r
+ * crt0 without core dumping. These functions are all optional, but\r
+ * we do this so we can have our crt0 always use them if they exist. \r
+ * This is so BSPs work better when using the crt0 installed with gcc.\r
+ * We have to initalize them twice, so we cover a.out (which prepends\r
+ * an underscore) and coff object file formats.\r
+ */\r
+PROVIDE (hardware_init_hook = 0);\r
+PROVIDE (_hardware_init_hook = 0);\r
+PROVIDE (software_init_hook = 0);\r
+PROVIDE (_software_init_hook = 0);\r
+\r
+SECTIONS\r
+{\r
+ .text 0x00100000:\r
+ {\r
+ *(.text)\r
+ . = ALIGN(0x4);\r
+ __CTOR_LIST__ = .;\r
+ LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2)\r
+ *(.ctors)\r
+ LONG(0)\r
+ __CTOR_END__ = .;\r
+ __DTOR_LIST__ = .;\r
+ LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2)\r
+ *(.dtors)\r
+ LONG(0)\r
+ __DTOR_END__ = .;\r
+ *(.rodata*)\r
+ *(.gcc_except_table)\r
+\r
+ __INIT_SECTION__ = . ;\r
+ *(.init)\r
+ SHORT (0x4e75) /* rts */\r
+\r
+ __FINI_SECTION__ = . ;\r
+ *(.fini)\r
+ SHORT (0x4e75) /* rts */\r
+\r
+ _etext = .;\r
+ *(.lit)\r
+ } > rom\r
+\r
+ .data 0xff0100 :\r
+ {\r
+ *(.shdata)\r
+ *(.data)\r
+ _edata = .;\r
+ } > ram\r
+\r
+ /* .bss 0xff0100 : */\r
+ .bss BLOCK (0x4) :\r
+ {\r
+ __bss_start = . ;\r
+ *(.shbss)\r
+ *(.bss)\r
+ *(COMMON)\r
+ *(.eh_fram)\r
+ *(.eh_frame)\r
+ _end = ALIGN (0x8);\r
+ __end = _end;\r
+ } > ram\r
+\r
+ .stab 0 (NOLOAD) :\r
+ {\r
+ *(.stab)\r
+ }\r
+\r
+ .stabstr 0 (NOLOAD) :\r
+ {\r
+ *(.stabstr)\r
+ }\r
+}\r
--- /dev/null
+/*
+ * File: edos.h
+ * Author: krik
+ *
+ * Created on 29 Ìà ðò 2012 ã., 1:29
+ */
+
+#ifndef _EDOS_H
+#define _EDOS_H
+
+//#include "segalib.h"
+
+/*
+ * saddr: address in sectors
+ * slen: lenght in sectors
+ * wr_slen: how many sectors will be written
+ * sector size 512 bytes
+ * dma reading of usb and SD available only if data destination located in rom area (0-0x3ffffff). dma reading not available for genesis ram
+ * destination address for dma reading must be aligned to 512b
+ * one mbyte of sdram can be mapped to one of four banks in rom space. osSetMemMap(u16) can be used for mapping configuration.
+ * os code located in last mbyte of sdram and should be mapped in bank 0
+ * user code located in begin of sdram. default mapping 0x210f means that end of sdram (OS) mapped to area 0-0x0fffff, first mbyte of user code mapped to 0x100000-0x1fffff,
+ * memory configuration:
+ * genesis address: 0x000000-0x0fffff, physical sdram address: 0xf00000-0xffffff OS
+ * genesis address: 0x100000-0x3fffff, physical sdram address: 0x000000-0x2fffff user code (current application)
+ * 0xff0000-0xff00ff OS dma code
+ * first 256bytes of genesis ram must be reserved for OS
+ * */
+
+
+#define MEM_ERR_SPI_RD_TIMEOUT 120
+
+#define ERR_FILE_TOO_BIG 140
+#define ERR_WRON_OS_SIZE 142
+#define ERR_OS_VERIFY 143
+#define ERR_OS_VERIFY2 144
+#define ERR_BAD_DMA_ADDRESS 145
+#define ERR_MUST_NOT_RET 146
+
+//100 - 119 fat errors
+#define FAT_ERR_NOT_EXIST 100
+#define FAT_ERR_EXIST 101
+#define FAT_ERR_NAME 102
+#define FAT_ERR_OUT_OF_FILE 103
+#define FAT_ERR_BAD_BASE_CLUSTER 104;
+#define FAT_ERR_NO_FRE_SPACE 105
+#define FAT_ERR_NOT_FILE 106
+#define FAT_ERR_FILE_MODE 107
+#define FAT_ERR_ROT_OVERFLOW 108
+#define FAT_ERR_OUT_OF_TABLE 109
+#define FAT_ERR_INIT 110
+#define FAT_LFN_BUFF_OVERFLOW 111
+#define FAT_DISK_NOT_READY 112
+#define FAT_ERR_SIZE 113
+#define FAT_ERR_RESIZE 114
+
+
+#define DISK_ERR_INIT 50
+#define DISK_ERR_RD1 62
+#define DISK_ERR_RD2 63
+
+#define DISK_ERR_WR1 64
+#define DISK_ERR_WR2 65
+#define DISK_ERR_WR3 66
+#define DISK_ERR_WR4 67
+#define DISK_ERR_WR5 68
+
+typedef struct {
+ u32 entry_cluster;
+ u32 size;
+ u32 hdr_sector;
+ u16 hdr_idx;
+ u8 name[256];
+ u16 is_dir;
+} FatFullRecord;
+
+typedef struct {
+ u8(*diskWrite)(u32 saddr, u8 *buff, u16 slen);
+ u8(*diskRead)(u32 saddr, void *buff, u16 slen);//*dst must be alligned to 512bytes and be in range 0x100000-0x3fffff. *dst can be located in genesis ram also, but transfer will be slow in this case
+ u8(*usbReadByte)();//loop inside of function waiting until usbRdReady != 0
+ void (*usbWriteByte)(u8 dat);//loop inside of function waiting until usbWrReady != 0
+ u8(*usbReadDma)(u16 *dst, u16 slen);//*dst must be alligned to 512bytes and be in range 0x100000-0x3fffff
+ u8(*usbReadPio)(u16 *dst, u16 slen);
+ u8(*usbRdReady)(); //return 1 if some data comes from pc
+ u8(*usbWrReady)(); //return 1 usb ready to send byte
+ void (*osSetMemMap)(u16 map); //memoty configuration 4x1Mb
+ u16(*osGetOsVersion)();
+ u16(*osGetFirmVersion)();
+ void (*osGetSerial)(u32 * buff); // 8bytes uniq serial number
+ void (*VDP_setReg)(u8 reg, u8 value);//os vdp functions should be used, otherwise system may hang while udb/sd dma
+ void (*VDP_vintOn)();
+ void (*VDP_vintOff)();
+ void (*memInitDmaCode)();//copy dma routine to genesis ram
+ u8(*fatReadDir)(FatFullRecord * frec);//get next record in current dir
+ void(*fatOpenDir)(u32 entry_cluster);//arg is entry cluster ot dir or 0. zero arg means that need to open root dir
+ u8(*fatOpenFileByeName)(u8 *name, u32 wr_slen);//wr_slen write len, or 0 if reading
+ u8(*fatOpenFile)(FatFullRecord *rec, u32 wr_slen);//wr_slen is write len, or 0 if reading
+ u8(*fatReadFile)(void *dst, u32 slen);
+ u8(*fatWriteFile)(void *src, u32 slen);
+ u8(*fatCreateRecIfNotExist)(u8 *name, u8 is_dir);
+ u8(*fatSkipSectors)(u32 slen);
+} OsRoutine;
+
+
+extern OsRoutine *ed_os;
+void edInit();
+
+
+//stereo dac. 8bit for each channel.
+//any writing to this port enables dac output, any reading from this port disables dac output.
+#define REG_DAC *((volatile u16*) (0xA1300E))
+
+
+//stuff for direct access to usb and spi. not recommended for use. OS function should be used for best compatibility
+#define _SPI_SS 0
+#define _SPI_FULL_SPEED 1
+#define _SPI_SPI16 2
+#define _SPI_AREAD 3
+
+//spi port (sd interface)
+#define SPI_PORT *((volatile u16*) (0xA13000))
+#define SPI_CFG_PORT *((volatile u16*) (0xA13002))
+
+//16bit or 8bit spi mode
+#define SPI16_ON SPI_CFG_PORT |= (1 << _SPI_SPI16)
+#define SPI16_OFF SPI_CFG_PORT &= ~(1 << _SPI_SPI16)
+
+//spi autoread. means that not need to write something to spi port before than read
+#define SPI_AR_ON SPI_CFG_PORT |= (1 << _SPI_AREAD)
+#define SPI_AR_OFF SPI_CFG_PORT &= ~(1 << _SPI_AREAD)
+
+//spi chip select
+#define SPI_SS_OFF SPI_CFG_PORT |= (1 << _SPI_SS)
+#define SPI_SS_ON SPI_CFG_PORT &= ~(1 << _SPI_SS)
+
+//spi speed. low speed need only for sd init
+#define SPI_HI_SPEED_ON SPI_CFG_PORT |= (1 << _SPI_FULL_SPEED)
+#define SPI_HI_SPEED_OFF SPI_CFG_PORT &= ~(1 << _SPI_FULL_SPEED)
+
+//usb-serial port
+#define FIFO_PORT *((volatile u16*) (0xA1300A))
+
+//spi and usb state
+#define STATE_PORT *((volatile u16*) (0xA1300C))
+#define _STATE_SPI_READY 0
+#define _STATE_FIFO_WR_READY 1
+#define _STATE_FIFO_RD_READY 2
+#define IS_FIFO_RD_READY (STATE_PORT & (1 << _STATE_FIFO_RD_READY))
+#define IS_FIFO_WR_READY (STATE_PORT & (1 << _STATE_FIFO_WR_READY))
+
+
+#endif /* _EDOS_H */
+
--- /dev/null
+#include <stdlib.h>
+#include <stdarg.h>
+
+#define u8 unsigned char
+#define u16 unsigned short
+#define u32 unsigned int
+
+#define noinline __attribute__((noinline))
+#define _packed __attribute__((packed))
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+
+#include "edos.h"
+#include "asmtools.h"
+
+extern u16 start_hvc;
+
+#define GFX_DATA_PORT 0xC00000
+#define GFX_CTRL_PORT 0xC00004
+
+#define TILE_MEM_END 0xB000
+
+#define FONT_LEN 128
+#define TILE_FONT_BASE (TILE_MEM_END / 32 - FONT_LEN)
+
+/* note: using ED menu's layout here.. */
+#define WPLANE (TILE_MEM_END + 0x0000)
+#define HSCRL (TILE_MEM_END + 0x0800)
+#define SLIST (TILE_MEM_END + 0x0C00)
+#define APLANE (TILE_MEM_END + 0x1000)
+#define BPLANE (TILE_MEM_END + 0x3000)
+
+#define read8(a) \
+ *((volatile u8 *) (a))
+#define read16(a) \
+ *((volatile u16 *) (a))
+#define read32(a) \
+ *((volatile u32 *) (a))
+#define write8(a, d) \
+ *((volatile u8 *) (a)) = (d)
+#define write16(a, d) \
+ *((volatile u16 *) (a)) = (d)
+#define write32(a, d) \
+ *((volatile u32 *) (a)) = (d)
+
+#define GFX_WRITE_VRAM_ADDR(adr) \
+ (((0x4000 | ((adr) & 0x3FFF)) << 16) | ((adr) >> 14) | 0x00)
+#define GFX_WRITE_VSRAM_ADDR(adr) \
+ (((0x4000 | ((adr) & 0x3FFF)) << 16) | ((adr) >> 14) | 0x10)
+
+enum {
+ VDP_MODE1 = 0x00,
+ VDP_MODE2 = 0x01,
+ VDP_BACKDROP = 0x07,
+ VDP_MODE3 = 0x0b,
+ VDP_MODE4 = 0x0c,
+ VDP_AUTOINC = 0x0f,
+ VDP_SCROLLSZ = 0x10,
+};
+
+/* cell counts */
+#define LEFT_BORDER 1 /* lame TV */
+#define PLANE_W 64
+#define PLANE_H 32
+#define CSCREEN_H 28
+
+static noinline void VDP_drawTextML(const char *str, u16 plane_base,
+ u16 x, u16 y)
+{
+ const u8 *src = (const u8 *)str;
+ u16 basetile = 0;
+ int max_len = 40 - LEFT_BORDER;
+ int len;
+ u32 addr;
+
+ x += LEFT_BORDER;
+
+ for (len = 0; str[len] && len < max_len; len++)
+ ;
+ if (len > (PLANE_W - x))
+ len = PLANE_W - x;
+
+ addr = plane_base + ((x + (PLANE_W * y)) << 1);
+ write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(addr));
+
+ while (len-- > 0) {
+ write16(GFX_DATA_PORT,
+ basetile | ((*src++) - 32 + TILE_FONT_BASE));
+ }
+}
+
+static int printf_ypos;
+
+static void printf_line(int x, const char *buf)
+{
+ u32 addr;
+ int i;
+
+ VDP_drawTextML(buf, APLANE, x, printf_ypos++ & (PLANE_H - 1));
+
+ if (printf_ypos >= CSCREEN_H) {
+ /* clear next line */
+ addr = APLANE;
+ addr += (PLANE_W * (printf_ypos & (PLANE_H - 1))) << 1;
+ write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(addr));
+ for (i = 0; i < 40 / 2; i++)
+ write32(GFX_DATA_PORT, 0);
+
+ /* scroll plane */
+ write32(GFX_CTRL_PORT, GFX_WRITE_VSRAM_ADDR(0));
+ write16(GFX_DATA_PORT, (printf_ypos - CSCREEN_H + 1) * 8);
+ }
+}
+
+#define PRINTF_LEN 40
+
+static noinline int printf(const char *fmt, ...)
+{
+ static const char hexchars[] = "0123456789abcdef";
+ static int printf_xpos;
+ char c, buf[PRINTF_LEN + 11 + 1];
+ const char *s;
+ va_list ap;
+ int ival;
+ u32 uval;
+ int d = 0;
+ int i, j;
+
+ va_start(ap, fmt);
+ for (d = 0; *fmt; ) {
+ int prefix0 = 0;
+ int fwidth = 0;
+
+ c = *fmt++;
+ if (d < PRINTF_LEN)
+ buf[d] = c;
+
+ if (c != '%') {
+ if (c == '\n') {
+ buf[d] = 0;
+ printf_line(printf_xpos, buf);
+ d = 0;
+ printf_xpos = 0;
+ continue;
+ }
+ d++;
+ continue;
+ }
+ if (d >= PRINTF_LEN)
+ continue;
+
+ if (*fmt == '0') {
+ prefix0 = 1;
+ fmt++;
+ }
+
+ while ('1' <= *fmt && *fmt <= '9') {
+ fwidth = fwidth * 10 + *fmt - '0';
+ fmt++;
+ }
+
+ switch (*fmt++) {
+ case '%':
+ d++;
+ break;
+ case 'd':
+ case 'i':
+ ival = va_arg(ap, int);
+ if (ival < 0) {
+ buf[d++] = '-';
+ ival = -ival;
+ }
+ for (i = 1000000000; i >= 10; i /= 10)
+ if (ival >= i)
+ break;
+ for (; i >= 10; i /= 10) {
+ buf[d++] = '0' + ival / i;
+ ival %= i;
+ }
+ buf[d++] = '0' + ival;
+ break;
+ case 'x':
+ uval = va_arg(ap, int);
+ while (fwidth > 1 && uval < (1 << (fwidth - 1) * 4)) {
+ buf[d++] = prefix0 ? '0' : ' ';
+ fwidth--;
+ }
+ for (j = 1; j < 8 && uval >= (1 << j * 4); j++)
+ ;
+ for (j--; j >= 0; j--)
+ buf[d++] = hexchars[(uval >> j * 4) & 0x0f];
+ break;
+ case 's':
+ s = va_arg(ap, char *);
+ while (*s && d < PRINTF_LEN)
+ buf[d++] = *s++;
+ break;
+ default:
+ // don't handle, for now
+ d++;
+ va_arg(ap, void *);
+ break;
+ }
+ }
+ buf[d] = 0;
+ va_end(ap);
+
+ if (d != 0) {
+ // line without \n
+ VDP_drawTextML(buf, APLANE, printf_xpos,
+ printf_ypos & (PLANE_H - 1));
+ printf_xpos += d;
+ }
+
+ return d; // wrong..
+}
+
+static u8 gethex(char c)
+{
+ if ('0' <= c && c <= '9')
+ return c - '0';
+ if ('a' <= c && c <= 'f')
+ return c - 'a' + 10;
+ if ('A' <= c && c <= 'F')
+ return c - 'A' + 10;
+ return 0;
+}
+
+static const char *exc_names[] = {
+ NULL,
+ NULL,
+ "Bus Error",
+ "Address Error",
+ "Illegal Instruction",
+ "Zero Divide",
+ "CHK Instruction",
+ "TRAPV Instruction",
+ "Privilege Violation", /* 8 8 */
+ "Trace",
+ "Line 1010 Emulator",
+ "Line 1111 Emulator",
+ NULL,
+ NULL,
+ NULL,
+ "Uninitialized Interrupt",
+ NULL, /* 10 16 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Spurious Interrupt", /* 18 24 */
+ "l1 irq",
+ "l2 irq",
+ "l3 irq",
+ "l4 irq",
+ "l5 irq",
+ "l6 irq",
+ "l7 irq",
+};
+
+struct exc_frame {
+ u32 dr[8];
+ u32 ar[8];
+ u16 ecxnum; // from handler
+ union {
+ struct {
+ u16 sr;
+ u32 pc;
+ } g _packed;
+ struct {
+ u16 fc;
+ u32 addr;
+ u16 ir;
+ u16 sr;
+ u32 pc;
+ } bae _packed; // bus/address error frame
+ };
+} _packed;
+
+int xtttt(void) { return sizeof(struct exc_frame); }
+
+void exception(const struct exc_frame *f)
+{
+ int i;
+
+ while (read16(GFX_CTRL_PORT) & 2)
+ ;
+ write16(GFX_CTRL_PORT, 0x8000 | (VDP_MODE1 << 8) | 0x04);
+ write16(GFX_CTRL_PORT, 0x8000 | (VDP_MODE2 << 8) | 0x44);
+ /* adjust scroll */
+ write32(GFX_CTRL_PORT, GFX_WRITE_VSRAM_ADDR(0));
+ write16(GFX_DATA_PORT,
+ printf_ypos >= CSCREEN_H ?
+ (printf_ypos - CSCREEN_H + 1) * 8 : 0);
+
+ printf("exception %i ", f->ecxnum);
+ if (f->ecxnum < ARRAY_SIZE(exc_names) && exc_names[f->ecxnum] != NULL)
+ printf("(%s)", exc_names[f->ecxnum]);
+ if (f->ecxnum < 4)
+ printf(" (%s)", (f->bae.fc & 0x10) ? "r" : "w");
+ printf(" \n");
+
+ if (f->ecxnum < 4) {
+ printf(" PC: %08x SR: %04x \n", f->bae.pc, f->bae.sr);
+ printf("addr: %08x IR: %04x FC: %02x \n",
+ f->bae.addr, f->bae.ir, f->bae.fc);
+ }
+ else {
+ printf(" PC: %08x SR: %04x \n", f->g.pc, f->g.sr);
+ }
+ for (i = 0; i < 8; i++)
+ printf(" D%d: %08x A%d: %08x \n", i, f->dr[i], i, f->ar[i]);
+ printf(" \n");
+}
+
+void vbl(void)
+{
+}
+
+static int usb_read_while_ready(OsRoutine *ed,
+ void *buf_, unsigned int maxlen)
+{
+ u8 *buf = buf_;
+ unsigned int r = 0;
+
+ while (ed->usbRdReady() && r < maxlen)
+ buf[r++] = ed->usbReadByte();
+
+ return r;
+}
+
+static int usb_read(OsRoutine *ed, void *buf_, unsigned int maxlen)
+{
+ u8 *buf = buf_;
+ unsigned int r = 0;
+
+ while (r < maxlen)
+ buf[r++] = ed->usbReadByte();
+
+ return r;
+}
+
+static int usb_write(OsRoutine *ed, const void *buf_, unsigned int maxlen)
+{
+ const u8 *buf = buf_;
+ unsigned int r = 0;
+
+ while (r < maxlen)
+ ed->usbWriteByte(buf[r++]);
+
+ return r;
+}
+
+/*
+ * TH = 1 : ?1CBRLDU 3-button pad return value (not read)
+ * TH = 0 : ?0SA00DU 3-button pad return value
+ * TH = 1 : ?1CBRLDU 3-button pad return value
+ * TH = 0 : ?0SA0000 D3-0 are forced to '0'
+ * TH = 1 : ?1CBMXYZ Extra buttons returned in D3-0
+ * TH = 0 : ?0SA1111 D3-0 are forced to '1'
+ */
+static void test_joy_latency(int *min_out, int *max_out)
+{
+ u8 rbuf[8 * 6];
+ int min = 8;
+ int max = 0;
+ int i, v, b, e;
+
+ for (i = 0; i < 64; i++) {
+ read_joy_responses(rbuf);
+
+ for (b = 0; b < 8 * 4; b++) {
+ v = b & 7;
+ e = (b & 0x08) ? 0x0c : 0;
+ if ((rbuf[b] & 0x0c) == e) {
+ if (v < min)
+ min = v;
+ }
+ else if (v > max)
+ max = v;
+ }
+ }
+
+ /* print out the last test */
+ for (b = 0; b < 8 * 5; b++) {
+ printf(" %02x", rbuf[b]);
+ if ((b & 7) == 7)
+ printf("\n");
+ }
+ printf("\n");
+
+ *min_out = min;
+ *max_out = max;
+}
+
+static int do_test(OsRoutine *ed, u8 b3)
+{
+ int min = 0, max = 0;
+ int i, t, len, seed;
+ u8 *p, v;
+
+ switch (b3)
+ {
+ case '0':
+ printf("reading..\n");
+ test_joy_read_log((void *)0x200000, 0x20000, 1);
+ //test_joy_read_log((void *)0xff0200, 0x0f000, 1);
+ printf("done\n");
+ return 0;
+ case '1':
+ printf("reading w/vsync..\n");
+ test_joy_read_log_vsync((void *)0x200000, 3600 * 2);
+ printf("done\n");
+ return 0;
+ case '2':
+ case '3':
+ printf("3btn_idle test..\n");
+ p = (void *)0x200000;
+ len = 0x20000;
+ test_joy_read_log(p, len, b3 == '3');
+ for (i = 0; i < len; i++) {
+ static const u8 e[2] = { 0x33, 0x7f };
+ v = e[i & 1];
+ if (p[i] != v)
+ printf("%06x: bad: %02x %02x\n", &p[i], p[i], v);
+ }
+ printf("done\n");
+ return 0;
+ case '4':
+ printf("3btn_idle data test..\n");
+ p = (void *)0x200000;
+ len = 0x20000;
+ for (i = 0; i < len; i++) {
+ static const u8 e[2] = { 0x33, 0x7f };
+ v = e[i & 1];
+ if (p[i] != v)
+ printf("%06x: bad: %02x %02x\n", &p[i], p[i], v);
+ }
+ printf("done\n");
+ return 0;
+ case '5':
+ seed = read8(0xC00009);
+ printf("testing, seed=%02x\n", seed);
+ p = (void *)0x200000;
+ len = 0x100000;
+ test_byte_write(p, len, seed);
+ for (t = 0; t < 2; t++) {
+ for (i = 0; i < len; i++) {
+ v = (u8)(i + seed);
+ if (p[i] != v)
+ printf("%06x: bad: %02x %02x\n", &p[i], p[i], v);
+ }
+ printf("done (%d)\n", t);
+ }
+ return 0;
+ case 'j':
+ test_joy_latency(&min, &max);
+ printf("latency: %d - %d\n\n", min, max);
+ return 0;
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+static int do_custom(OsRoutine *ed, u8 b3)
+{
+ struct {
+ unsigned int addr;
+ unsigned int size;
+ } d;
+
+ switch (b3)
+ {
+ case 'd':
+ usb_read(ed, &d, sizeof(d));
+ ed->usbWriteByte('k');
+ printf("sending %i bytes from %06x..\n", d.size, d.addr);
+ usb_write(ed, (void *)d.addr, d.size);
+ printf("done.\n");
+ return 1;
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+#define MTYPE_OS 0
+#define MTYPE_MD 1
+#define MTYPE_SSF 2
+#define MTYPE_CD 3
+#define MTYPE_SMS 4
+#define MTYPE_10M 5
+#define MTYPE_32X 6
+
+static int do_run(OsRoutine *ed, u8 b3, int tas_sync)
+{
+ u8 mapper = 0;
+
+ switch (b3)
+ {
+ case 's':
+ mapper = MTYPE_SMS | (7 << 4);
+ break;
+ case 'm':
+ mapper = MTYPE_MD;
+ break;
+ case 'o':
+ mapper = MTYPE_OS;
+ break;
+ case 'c':
+ mapper = MTYPE_CD;
+ break;
+ case '3':
+ mapper = MTYPE_32X;
+ break;
+ case 'M':
+ mapper = MTYPE_10M;
+ break;
+ case 'n':
+ // raw numer: hex XX: mtype | x;
+ // x: bits [4-7]: SRAM_ON, SRAM_3M_ON, SNAP_SAVE_ON, MKEY
+ mapper = gethex(ed->usbReadByte()) << 4;
+ mapper |= gethex(ed->usbReadByte());
+ break;
+ default:
+ return -1;
+ }
+
+ printf("syncing and starting mapper %x..\n", mapper);
+
+ while (read16(GFX_CTRL_PORT) & 2)
+ ;
+ ed->VDP_setReg(VDP_MODE1, 0x04);
+ ed->VDP_setReg(VDP_MODE2, 0x44);
+
+ ed->usbWriteByte('k');
+
+ run_game(mapper, tas_sync);
+ /* should not get here.. */
+
+ return -1;
+}
+
+void setup_z80(void)
+{
+ u8 *mem = (u8 *)0xa00000;
+ int i;
+
+ write8(0xa11100, 1);
+ write8(0xa11200, 1);
+
+ while (read8(0xa11100) & 1)
+ ;
+
+ /* must use byte access */
+ for (i = 0x2000; i > 0; i--)
+ *mem++ = 0;
+
+ /* console starts with reset on, busreq off,
+ * gens starts with busreq on, keep that for gmv.. */
+}
+
+int main()
+{
+ OsRoutine *ed;
+ u8 buf[16];
+ int len;
+ int i, d, ret;
+
+ ed = (OsRoutine *) *(u32 *)0x1A0;
+ ed->memInitDmaCode();
+
+ /* setup VDP */
+ while (read16(GFX_CTRL_PORT) & 2)
+ ;
+
+ ed->VDP_setReg(VDP_MODE1, 0x04);
+ ed->VDP_setReg(VDP_MODE2, 0x64);
+ ed->VDP_setReg(VDP_AUTOINC, 2);
+ ed->VDP_setReg(VDP_SCROLLSZ, 0x01);
+
+ /* clear name tables */
+ write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(APLANE));
+ for (i = 0; i < PLANE_W * PLANE_H / 2; i++)
+ write32(GFX_DATA_PORT, 0);
+
+ write32(GFX_CTRL_PORT, GFX_WRITE_VRAM_ADDR(BPLANE));
+ for (i = 0; i < PLANE_W * PLANE_H / 2; i++)
+ write32(GFX_DATA_PORT, 0);
+
+ /* scroll planes */
+ write32(GFX_CTRL_PORT, GFX_WRITE_VSRAM_ADDR(0));
+ write32(GFX_DATA_PORT, 0);
+
+ /* note: relying on ED menu's font setup here.. */
+
+ printf("\n");
+ printf("version: %02x, hvc: %04x %04x\n",
+ read8(0xa10001), start_hvc, read16(0xc00008));
+ printf("ED os/fw: %d/%d\n\n", ed->osGetOsVersion(),
+ ed->osGetFirmVersion());
+
+ setup_z80();
+
+ for (;;) {
+ if (!ed->usbRdReady()) {
+ /* note: stop corrupts SDRAM */
+ //asm volatile("stop #0x2000");
+ asm volatile(
+ "move.l #1000/10, %0\n"
+ "0: dbra %0, 0b\n" : "=r" (i) :: "cc");
+ continue;
+ }
+
+ buf[0] = ed->usbReadByte();
+ if (buf[0] == ' ')
+ continue;
+ if (buf[0] != '*') {
+ d = 1;
+ goto bad_input;
+ }
+
+ /* note: OS uses Twofgsr */
+ buf[1] = ed->usbReadByte();
+ switch (buf[1]) {
+ case 'T':
+ ed->usbWriteByte('k');
+ break;
+ case 'g':
+ len = ed->usbReadByte() * 128;
+ printf("loading %d bytes.. ", len * 512);
+ ed->usbWriteByte('k');
+ ed->usbReadDma((void *)0x200000, len);
+ ed->usbWriteByte('d');
+ printf("done\n");
+ break;
+ case 'r':
+ case 'R':
+ buf[2] = ed->usbReadByte();
+ ret = do_run(ed, buf[2], buf[1] == 'R');
+ if (ret != 0) {
+ d = 3;
+ goto bad_input;
+ }
+ printf("run returned??\n");
+ break;
+
+ /* custom */
+ case 't':
+ buf[2] = ed->usbReadByte();
+ ret = do_test(ed, buf[2]);
+ if (ret != 0) {
+ d = 3;
+ goto bad_input;
+ }
+ ed->usbWriteByte('k');
+ break;
+ case 'x':
+ buf[2] = ed->usbReadByte();
+ ret = do_custom(ed, buf[2]);
+ if (ret == 1)
+ break;
+ if (ret != 0) {
+ d = 3;
+ goto bad_input;
+ }
+ ed->usbWriteByte('k');
+ break;
+ default:
+ d = 2;
+ goto bad_input;
+ }
+
+ continue;
+
+bad_input:
+ ret = usb_read_while_ready(ed, buf + d, sizeof(buf) - d);
+ buf[d + ret] = 0;
+ printf("bad cmd: %s\n", buf);
+ /* consume all remaining data */
+ while (ed->usbRdReady())
+ usb_read_while_ready(ed, buf, sizeof(buf));
+
+ ed->usbWriteByte('b');
+ }
+
+ return 0;
+}
+
+// vim:ts=4:sw=4:expandtab
--- /dev/null
+exc_tab:\r
+ dc.l 0, 0x200, exc02, exc03, exc04, exc05, exc06, exc07\r
+ dc.l exc08, exc09, exc0a, exc0b, exc0c, exc0d, exc0e, exc0f\r
+ dc.l exc10, exc11, exc12, exc13, exc14, exc15, exc16, exc17\r
+ dc.l exc18, exc19, exc1a, exc1b, HBL, exc1d, VBL, exc1f\r
+ dc.l exc20, exc21, exc22, exc23, exc24, exc25, exc26, exc27\r
+ dc.l exc28, exc29, exc2a, exc2b, exc2c, exc2d, exc2e, exc2f\r
+ dc.l exc30, exc31, exc32, exc33, exc34, exc35, exc3e, exc37\r
+ dc.l exc38, exc39, exc3a, exc3b, exc3c, exc3d, exc3e, exc3f\r
+\r
+ .ascii "SEGA EVERDRIVE "\r
+ .ascii "MEGA-ED host "\r
+ .ascii "MEGA-ED host "\r
+ .ascii "GM 00000000-00"\r
+ .byte 0x00,0x00\r
+ .ascii "JD "\r
+ .byte 0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00\r
+ .byte 0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff\r
+ .ascii " "\r
+ .ascii " "\r
+ .ascii " "\r
+ .ascii "JUE "\r
+\r
+RST:\r
+ move.w (0xc00008), start_hvc\r
+ move.w #0x2700, %sr\r
+/* magic ED app init */\r
+ move.w #0x0000, (0xA13006)\r
+ jmp init_ed.l\r
+init_ed:\r
+/* relocate to bank a, so that other ROMs can be loaded */\r
+ move.w #0x0a0f, (0xA13006)\r
+ movea.l #0x100000, %a0\r
+ movea.l #0x200000, %a1\r
+ move.l #0x100000/4/4-1, %d0\r
+0:\r
+ move.l (a0)+, (a1)+\r
+ move.l (a0)+, (a1)+\r
+ move.l (a0)+, (a1)+\r
+ move.l (a0)+, (a1)+\r
+ dbra %d0, 0b\r
+\r
+ move.w #0x10af, (0xA13006)\r
+\r
+ lea exc_tab, %a0\r
+ adda.l #4*2, %a0\r
+ movea.l #4*2, %a1\r
+ move.l #64-2-1, %d0\r
+0:\r
+ move.l (a0)+, (a1)+\r
+ dbra %d0, 0b\r
+\r
+ moveq #0, %d0\r
+ movea.l %d0, %a7\r
+ move %a7, %usp\r
+ move.w #0x2000, %sr\r
+ bra main\r
+\r
+HBL:\r
+ rte\r
+\r
+VBL:\r
+ movem.l %d0-%d1/%a0-%a1,-(%sp)\r
+ jsr vbl\r
+ movem.l (%sp)+,%d0-%d1/%a0-%a1\r
+ rte\r
+\r
+pre_exception:\r
+ move.w #0x2700, %sr\r
+ movem.l %d0-%d7/%a0-%a7,-(%sp)\r
+ move.l %sp, %d0\r
+ move.l %d0,-(%sp)\r
+ jsr exception\r
+0:\r
+ bra 0b\r
+\r
+\r
+* Standard 32X startup code for MD side at 0x3F0\r
+ .org 0x3F0\r
+\r
+ .word 0x287C,0xFFFF,0xFFC0,0x23FC,0x0000,0x0000,0x00A1,0x5128\r
+ .word 0x46FC,0x2700,0x4BF9,0x00A1,0x0000,0x7001,0x0CAD,0x4D41\r
+ .word 0x5253,0x30EC,0x6600,0x03E6,0x082D,0x0007,0x5101,0x67F8\r
+ .word 0x4AAD,0x0008,0x6710,0x4A6D,0x000C,0x670A,0x082D,0x0000\r
+ .word 0x5101,0x6600,0x03B8,0x102D,0x0001,0x0200,0x000F,0x6706\r
+ .word 0x2B78,0x055A,0x4000,0x7200,0x2C41,0x4E66,0x41F9,0x0000\r
+ .word 0x04D4,0x6100,0x0152,0x6100,0x0176,0x47F9,0x0000,0x04E8\r
+ .word 0x43F9,0x00A0,0x0000,0x45F9,0x00C0,0x0011,0x3E3C,0x0100\r
+ .word 0x7000,0x3B47,0x1100,0x3B47,0x1200,0x012D,0x1100,0x66FA\r
+ .word 0x7425,0x12DB,0x51CA,0xFFFC,0x3B40,0x1200,0x3B40,0x1100\r
+ .word 0x3B47,0x1200,0x149B,0x149B,0x149B,0x149B,0x41F9,0x0000\r
+ .word 0x04C0,0x43F9,0x00FF,0x0000,0x22D8,0x22D8,0x22D8,0x22D8\r
+ .word 0x22D8,0x22D8,0x22D8,0x22D8,0x41F9,0x00FF,0x0000,0x4ED0\r
+ .word 0x1B7C,0x0001,0x5101,0x41F9,0x0000,0x06BC,0xD1FC,0x0088\r
+ .word 0x0000,0x4ED0,0x0404,0x303C,0x076C,0x0000,0x0000,0xFF00\r
+ .word 0x8137,0x0002,0x0100,0x0000,0xAF01,0xD91F,0x1127,0x0021\r
+ .word 0x2600,0xF977,0xEDB0,0xDDE1,0xFDE1,0xED47,0xED4F,0xD1E1\r
+ .word 0xF108,0xD9C1,0xD1E1,0xF1F9,0xF3ED,0x5636,0xE9E9,0x9FBF\r
+ .word 0xDFFF,0x4D41,0x5253,0x2049,0x6E69,0x7469,0x616C,0x2026\r
+ .word 0x2053,0x6563,0x7572,0x6974,0x7920,0x5072,0x6F67,0x7261\r
+ .word 0x6D20,0x2020,0x2020,0x2020,0x2020,0x2043,0x6172,0x7472\r
+ .word 0x6964,0x6765,0x2056,0x6572,0x7369,0x6F6E,0x2020,0x2020\r
+ .word 0x436F,0x7079,0x7269,0x6768,0x7420,0x5345,0x4741,0x2045\r
+ .word 0x4E54,0x4552,0x5052,0x4953,0x4553,0x2C4C,0x5444,0x2E20\r
+ .word 0x3139,0x3934,0x2020,0x2020,0x2020,0x2020,0x2020,0x2020\r
+ .word 0x2020,0x2020,0x2020,0x2020,0x2020,0x2020,0x2020,0x2020\r
+ .word 0x2020,0x2020,0x2020,0x524F,0x4D20,0x5665,0x7273,0x696F\r
+ .word 0x6E20,0x312E,0x3000,0x48E7,0xC040,0x43F9,0x00C0,0x0004\r
+ .word 0x3011,0x303C,0x8000,0x323C,0x0100,0x3E3C,0x0012,0x1018\r
+ .word 0x3280,0xD041,0x51CF,0xFFF8,0x4CDF,0x0203,0x4E75,0x48E7\r
+ .word 0x81C0,0x41F9,0x0000,0x063E,0x43F9,0x00C0,0x0004,0x3298\r
+ .word 0x3298,0x3298,0x3298,0x3298,0x3298,0x3298,0x2298,0x3341\r
+ .word 0xFFFC,0x3011,0x0800,0x0001,0x66F8,0x3298,0x3298,0x7000\r
+ .word 0x22BC,0xC000,0x0000,0x7E0F,0x3340,0xFFFC,0x3340,0xFFFC\r
+ .word 0x3340,0xFFFC,0x3340,0xFFFC,0x51CF,0xFFEE,0x22BC,0x4000\r
+ .word 0x0010,0x7E09,0x3340,0xFFFC,0x3340,0xFFFC,0x3340,0xFFFC\r
+ .word 0x3340,0xFFFC,0x51CF,0xFFEE,0x4CDF,0x0381,0x4E75,0x8114\r
+ .word 0x8F01,0x93FF,0x94FF,0x9500,0x9600,0x9780,0x4000,0x0080\r
+ .word 0x8104,0x8F02,0x48E7,0xC140,0x43F9,0x00A1,0x5180,0x08A9\r
+ .word 0x0007,0xFF80,0x66F8,0x3E3C,0x00FF,0x7000,0x7200,0x337C\r
+ .word 0x00FF,0x0004,0x3341,0x0006,0x3340,0x0008,0x4E71,0x0829\r
+ .word 0x0001,0x000B,0x66F8,0x0641,0x0100,0x51CF,0xFFE8,0x4CDF\r
+ .word 0x0283,0x4E75,0x48E7,0x8180,0x41F9,0x00A1,0x5200,0x08A8\r
+ .word 0x0007,0xFF00,0x66F8,0x3E3C,0x001F,0x20C0,0x20C0,0x20C0\r
+ .word 0x20C0,0x51CF,0xFFF6,0x4CDF,0x0181,0x4E75,0x41F9,0x00FF\r
+ .word 0x0000,0x3E3C,0x07FF,0x7000,0x20C0,0x20C0,0x20C0,0x20C0\r
+ .word 0x20C0,0x20C0,0x20C0,0x20C0,0x51CF,0xFFEE,0x3B7C,0x0000\r
+ .word 0x1200,0x7E0A,0x51CF,0xFFFE,0x43F9,0x00A1,0x5100,0x7000\r
+ .word 0x2340,0x0020,0x2340,0x0024,0x1B7C,0x0003,0x5101,0x2E79\r
+ .word 0x0088,0x0000,0x0891,0x0007,0x66FA,0x7000,0x3340,0x0002\r
+ .word 0x3340,0x0004,0x3340,0x0006,0x2340,0x0008,0x2340,0x000C\r
+ .word 0x3340,0x0010,0x3340,0x0030,0x3340,0x0032,0x3340,0x0038\r
+ .word 0x3340,0x0080,0x3340,0x0082,0x08A9,0x0000,0x008B,0x66F8\r
+ .word 0x6100,0xFF12,0x08E9,0x0000,0x008B,0x67F8,0x6100,0xFF06\r
+ .word 0x08A9,0x0000,0x008B,0x6100,0xFF3C,0x303C,0x0040,0x2229\r
+ .word 0x0020,0x0C81,0x5351,0x4552,0x6700,0x0092,0x303C,0x0080\r
+ .word 0x2229,0x0020,0x0C81,0x5344,0x4552,0x6700,0x0080,0x21FC\r
+ .word 0x0088,0x02A2,0x0070,0x303C,0x0002,0x7200,0x122D,0x0001\r
+ .word 0x1429,0x0080,0xE14A,0x8242,0x0801,0x000F,0x660A,0x0801\r
+ .word 0x0006,0x6700,0x0058,0x6008,0x0801,0x0006,0x6600,0x004E\r
+ .word 0x7020,0x41F9,0x0088,0x0000,0x3C28,0x018E,0x4A46,0x6700\r
+ .word 0x0010,0x3429,0x0028,0x0C42,0x0000,0x67F6,0xB446,0x662C\r
+ .word 0x7000,0x2340,0x0028,0x2340,0x002C,0x3E14,0x2C7C,0xFFFF\r
+ .word 0xFFC0,0x4CD6,0x7FF9,0x44FC,0x0000,0x6014,0x43F9,0x00A1\r
+ .word 0x5100,0x3340,0x0006,0x303C,0x8000,0x6004,0x44FC,0x0001\r
+\r
+.macro exc_stub num\r
+exc\num:\r
+ move.w #0x\num, -(%sp)\r
+ jmp pre_exception\r
+.endm\r
+\r
+exc_stub 02\r
+exc_stub 03\r
+exc_stub 04\r
+exc_stub 05\r
+exc_stub 06\r
+exc_stub 07\r
+exc_stub 08\r
+exc_stub 09\r
+exc_stub 0a\r
+exc_stub 0b\r
+exc_stub 0c\r
+exc_stub 0d\r
+exc_stub 0e\r
+exc_stub 0f\r
+exc_stub 10\r
+exc_stub 11\r
+exc_stub 12\r
+exc_stub 13\r
+exc_stub 14\r
+exc_stub 15\r
+exc_stub 16\r
+exc_stub 17\r
+exc_stub 18\r
+exc_stub 19\r
+exc_stub 1a\r
+exc_stub 1b\r
+# exc_stub 1c\r
+exc_stub 1d\r
+# exc_stub 1e\r
+exc_stub 1f\r
+exc_stub 20\r
+exc_stub 21\r
+exc_stub 22\r
+exc_stub 23\r
+exc_stub 24\r
+exc_stub 25\r
+exc_stub 26\r
+exc_stub 27\r
+exc_stub 28\r
+exc_stub 29\r
+exc_stub 2a\r
+exc_stub 2b\r
+exc_stub 2c\r
+exc_stub 2d\r
+exc_stub 2e\r
+exc_stub 2f\r
+exc_stub 30\r
+exc_stub 31\r
+exc_stub 32\r
+exc_stub 33\r
+exc_stub 34\r
+exc_stub 35\r
+exc_stub 36\r
+exc_stub 37\r
+exc_stub 38\r
+exc_stub 39\r
+exc_stub 3a\r
+exc_stub 3b\r
+exc_stub 3c\r
+exc_stub 3d\r
+exc_stub 3e\r
+exc_stub 3f\r
+\r
+.bss\r
+.align 2\r
+.global start_hvc\r
+start_hvc:\r
+ .word 0\r
+\r
+# vim:filetype=asmM68k:ts=4:sw=4:expandtab\r
--- /dev/null
+
+CC ?= $(CROSS)gcc
+
+CFLAGS += -Wall -O2 -ggdb -fno-strict-aliasing
+LDLIBS += -lusb
+TARGET = mx_flasher
+
+all: $(TARGET)
+
+clean:
+ $(RM) $(TARGET)
--- /dev/null
+/*
+ * Copyright (c) 2009, Gražvydas Ignotas
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the organization nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <usb.h>
+
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+#define array_size(x) (sizeof(x) / sizeof(x[0]))
+
+static const struct {
+ unsigned short vendor;
+ unsigned short product;
+ const char *name;
+} g_devices[] = {
+ { 0x03eb, 0x202a, "16MX+U Game Device" },
+ { 0x03eb, 0x202b, "32MX+U Game Device" },
+ { 0x03eb, 0x202c, "16MX+US Game Device" },
+ { 0x03eb, 0x202d, "32MX+UF Game Device" },
+};
+
+#define VERSION "0.91"
+
+//#define IO_BLK_SIZE 0x2000 /* 8K - unreliable? */
+#define IO_BLK_SIZE 0x800
+#define IO_RAM_BLK_SIZE 256
+
+#define CMD_ATM_READY 0x22
+#define CMD_SEC_GET_NAME 'G' /* filename r/w */
+#define CMD_SEC_PUT_NAME 'P'
+#define CMD_SEC_DEVID 'L' /* read flash device ID */
+#define CMD_SEC_ERASE 'E'
+#define CMD_SEC_READY 'C' /* is flash ready? */
+#define CMD_SEC_READ 'R'
+#define CMD_SEC_WRITE 'W'
+#define CMD_SEC_RAM_READ 'D' /* not implemented? */
+#define CMD_SEC_RAM_WRITE 'U'
+#define CMD_SEC_COMPAT '$' /* set RAM mode */
+
+/* bus controllers */
+#define CTL_DATA_BUS 0x55
+#define CTL_ADDR_BUS 0xAA
+
+#define W_COUNTER 0xA0
+#define W_CNT_WRITE 0x01
+#define W_CNT_READ 0x00
+
+#define FILENAME_ROM0 0
+#define FILENAME_ROM1 1
+#define FILENAME_RAM 2
+
+/* windows app sets to 0x80 on init
+ * checkboxes use 0x41 0x42 0x43 (?)
+ * r,w Ram/ROM uses 0x23/0x21
+ */
+#define C_MODE_4M_NORAM 0x41 /* RAM always off */
+#define C_MODE_4M_RAM 0x42 /* RAM switched by game */
+#define C_MODE_2M_RAM 0x43
+#define C_RAM_TMP_OFF 0x21
+#define C_RAM_TMP_ON 0x23
+
+typedef struct {
+ u8 magic[4];
+ u8 reserved[8];
+ u8 write_flag;
+ u8 reserved2[2];
+ u8 magic2;
+ u8 mx_cmd;
+ union { /* 0x11 */
+ struct {
+ u8 which_device;
+ } dev_info;
+ struct {
+ u8 addrb2; /* most significant (BE) */
+ u8 addrb1;
+ u8 addrb0;
+ u8 param; /* 64 byte usb packets for i/o */
+ u8 param2;
+ } rom_rw;
+ struct {
+ u8 which;
+ } filename, mode;
+ struct {
+ u8 cmd;
+ u8 action;
+ u8 b0; /* LE */
+ u8 b1;
+ u8 b2;
+ u8 b3;
+ } write_cnt;
+ struct {
+ u8 which;
+ u8 dev_id;
+ } rom_id;
+ };
+ u8 pad[8];
+} dev_cmd_t;
+
+typedef struct {
+ u8 firmware_ver[4];
+ u8 bootloader_ver[4];
+ char names[56];
+} dev_info_t;
+
+typedef struct {
+ u32 start_addr;
+ u32 end_addr;
+ u32 page_size;
+} page_table_t;
+
+static const page_table_t p_AM29LV320DB[] =
+{
+ { 0x000000, 0x00ffff, 0x002000 },
+ { 0x010000, 0x3fffff, 0x010000 },
+ { 0x000000, 0x000000, 0x000000 },
+};
+
+static const page_table_t p_AM29LV320DT[] =
+{
+ { 0x000000, 0x3effff, 0x010000 },
+ { 0x3f0000, 0x3fffff, 0x002000 },
+ { 0x000000, 0x000000, 0x000000 },
+};
+
+static const page_table_t p_2x_16[] =
+{
+ { 0x000000, 0x003fff, 0x004000 },
+ { 0x004000, 0x007fff, 0x002000 },
+ { 0x008000, 0x00ffff, 0x008000 },
+ { 0x010000, 0x1fffff, 0x010000 },
+ { 0x200000, 0x203fff, 0x004000 },
+ { 0x204000, 0x207fff, 0x002000 },
+ { 0x208000, 0x20ffff, 0x008000 },
+ { 0x210000, 0x3fffff, 0x010000 },
+ { 0x000000, 0x000000, 0x000000 },
+};
+
+/*****************************************************************************/
+
+static void prepare_cmd(dev_cmd_t *dev_cmd, u8 cmd)
+{
+ memset(dev_cmd, 0, sizeof(*dev_cmd));
+
+ memcpy(dev_cmd->magic, "USBC", 4);
+ dev_cmd->magic2 = 0x67; /* MySCSICommand, EXCOMMAND */
+ dev_cmd->mx_cmd = cmd;
+}
+
+static int write_data(struct usb_dev_handle *dev, void *data, int len)
+{
+ int ret = usb_bulk_write(dev, 0x03, data, len, 2000);
+ if (ret < 0) {
+ fprintf(stderr, "failed to write:\n");
+ fprintf(stderr, "%s (%d)\n", usb_strerror(), ret);
+ } else if (ret != len)
+ printf("write_cmd: wrote only %d of %d bytes\n", ret, len);
+
+ return ret;
+}
+
+static int write_cmd(struct usb_dev_handle *dev, dev_cmd_t *cmd)
+{
+ return write_data(dev, cmd, sizeof(*cmd));
+}
+
+static int read_data(struct usb_dev_handle *dev, void *buff, int size)
+{
+ int ret = usb_bulk_read(dev, 0x82, buff, size, 2000);
+ if (ret < 0) {
+ fprintf(stderr, "failed to read:\n");
+ fprintf(stderr, "%s (%d)\n", usb_strerror(), ret);
+ }
+/*
+ else if (ret != size)
+ printf("read_data: read only %d of %d bytes\n", ret, size);
+*/
+ return ret;
+}
+
+static int read_info(struct usb_dev_handle *device, u8 ctl_id, dev_info_t *info)
+{
+ dev_cmd_t cmd;
+ int ret;
+
+ prepare_cmd(&cmd, CMD_ATM_READY);
+ cmd.dev_info.which_device = ctl_id;
+ memset(info, 0, sizeof(*info));
+
+ ret = write_cmd(device, &cmd);
+ if (ret < 0)
+ return ret;
+
+ ret = read_data(device, info, sizeof(*info));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static void printf_info(dev_info_t *info)
+{
+ printf(" firmware version: %X.%X.%X%c\n", info->firmware_ver[0],
+ info->firmware_ver[1], info->firmware_ver[2], info->firmware_ver[3]);
+ printf(" bootloader version: %X.%X.%X%c\n", info->bootloader_ver[0],
+ info->bootloader_ver[1], info->bootloader_ver[2], info->bootloader_ver[3]);
+ info->names[sizeof(info->names) - 1] = 0;
+ printf(" device name: %s\n", info->names);
+}
+
+static void print_progress(u32 done, u32 total)
+{
+ int i, step;
+
+ printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
+ printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); /* 20 */
+ printf("\b\b\b\b\b\b");
+ printf("%06x/%06x |", done, total);
+
+ step = total / 20;
+ for (i = step; i <= total; i += step)
+ printf("%c", done >= i ? '=' : '-');
+ printf("| %3d%%", done * 100 / total);
+ fflush(stdout);
+}
+
+static int read_filename(struct usb_dev_handle *dev, char *dst, int len, u8 which)
+{
+ char buff[65];
+ dev_cmd_t cmd;
+ int ret;
+
+ prepare_cmd(&cmd, CMD_SEC_GET_NAME);
+ cmd.filename.which = which;
+ memset(buff, 0, sizeof(buff));
+
+ ret = write_cmd(dev, &cmd);
+ if (ret < 0)
+ return ret;
+
+ ret = read_data(dev, buff, 64);
+ if (ret < 0)
+ return ret;
+
+ strncpy(dst, buff, len);
+ dst[len - 1] = 0;
+
+ return 0;
+}
+
+static int write_filename(struct usb_dev_handle *dev, const char *fname, u8 which)
+{
+ dev_cmd_t cmd;
+ char buff[64];
+ int ret, len;
+
+ len = strlen(fname);
+ if (len > 63)
+ len = 63;
+ strncpy(buff, fname, len);
+ buff[len] = 0;
+
+ prepare_cmd(&cmd, CMD_SEC_PUT_NAME);
+ cmd.filename.which = which;
+
+ ret = write_cmd(dev, &cmd);
+ if (ret < 0)
+ return ret;
+
+ return write_data(dev, buff, len + 1);
+}
+
+static int read_erase_counter(struct usb_dev_handle *dev, u32 *val)
+{
+ dev_info_t dummy_info;
+ dev_cmd_t cmd;
+ u8 buff[4];
+ int ret;
+
+ /* must perform dummy info read here,
+ * or else device hangs after close (firmware bug?) */
+ ret = read_info(dev, CTL_DATA_BUS, &dummy_info);
+ if (ret < 0)
+ return ret;
+
+ prepare_cmd(&cmd, CMD_ATM_READY);
+ cmd.write_cnt.cmd = W_COUNTER;
+ cmd.write_cnt.action = W_CNT_READ;
+
+ ret = write_cmd(dev, &cmd);
+ if (ret < 0)
+ return ret;
+
+ ret = read_data(dev, buff, sizeof(buff));
+ if (ret < 0)
+ return ret;
+
+ *val = *(u32 *)buff;
+ return 0;
+}
+
+static int read_flash_rom_id(struct usb_dev_handle *dev, int is_second, u32 *val)
+{
+ dev_cmd_t cmd;
+ u8 buff[2];
+ int ret;
+
+ prepare_cmd(&cmd, CMD_SEC_DEVID);
+ cmd.rom_id.which = is_second ? 0x10 : 0;
+ cmd.rom_id.dev_id = 0;
+
+ ret = write_cmd(dev, &cmd);
+ if (ret < 0)
+ return ret;
+
+ ret = read_data(dev, buff, sizeof(buff));
+ if (ret < 0)
+ return ret;
+
+ *val = *(u16 *)buff << 16;
+
+ cmd.rom_id.dev_id = 1;
+ ret = write_cmd(dev, &cmd);
+ if (ret < 0)
+ return ret;
+
+ ret = read_data(dev, buff, sizeof(buff));
+ if (ret < 0)
+ return ret;
+
+ *val |= *(u16 *)buff;
+ return 0;
+}
+
+static const page_table_t *get_page_table(u32 rom_id)
+{
+ switch (rom_id) {
+ case 0x0100F922:
+ return p_AM29LV320DB;
+ case 0x0100F422:
+ return p_AM29LV320DT;
+ case 0x01004922:
+ case 0xC2004922:
+ return p_2x_16;
+ default:
+ fprintf(stderr, "unrecognized ROM id: %08x\n", rom_id);
+ }
+
+ return NULL;
+}
+
+static int get_page_size(const page_table_t *table, u32 addr, u32 *size)
+{
+ const page_table_t *t;
+
+ for (t = table; t->end_addr != 0; t++) {
+ if (addr >= t->start_addr && addr <= t->end_addr) {
+ *size = t->page_size;
+ return 0;
+ }
+ }
+
+ if (addr == t[-1].end_addr + 1)
+ return 1; /* no more */
+
+ fprintf(stderr, "get_page_size: failed on addr %06x\n", addr);
+ return -1;
+}
+
+static int set_ram_mode(struct usb_dev_handle *dev, u8 mode)
+{
+ dev_cmd_t cmd;
+ u8 buff[2];
+ int ret;
+
+ prepare_cmd(&cmd, CMD_SEC_COMPAT);
+ cmd.write_flag = 1;
+ cmd.mode.which = mode;
+
+ ret = write_cmd(dev, &cmd);
+ if (ret < 0)
+ goto end;
+
+ ret = read_data(dev, buff, sizeof(buff));
+
+end:
+ if (ret < 0)
+ fprintf(stderr, "warning: failed to set RAM mode\n");
+ return ret;
+}
+
+/* limitations:
+ * - bytes must be multiple of 64
+ * - bytes must be less than 16k
+ * - must perform even number of reads, or dev hangs on exit (firmware bug?) */
+static int rw_dev_block(struct usb_dev_handle *dev, u32 addr, void *buffer, int bytes, int mx_cmd)
+{
+ dev_cmd_t cmd;
+ int ret;
+
+ prepare_cmd(&cmd, mx_cmd);
+ if (mx_cmd == CMD_SEC_WRITE || mx_cmd == CMD_SEC_RAM_WRITE)
+ cmd.write_flag = 1;
+ cmd.rom_rw.addrb2 = addr >> (16 + 1);
+ cmd.rom_rw.addrb1 = addr >> (8 + 1);
+ cmd.rom_rw.addrb0 = addr >> 1;
+ cmd.rom_rw.param = bytes / 64;
+ if (mx_cmd == CMD_SEC_WRITE || mx_cmd == CMD_SEC_RAM_WRITE)
+ cmd.rom_rw.param2 = 1; /* ? */
+
+ ret = write_cmd(dev, &cmd);
+ if (ret < 0)
+ return ret;
+
+ bytes &= ~63;
+
+ if (mx_cmd == CMD_SEC_WRITE || mx_cmd == CMD_SEC_RAM_WRITE)
+ ret = write_data(dev, buffer, bytes);
+ else
+ ret = read_data(dev, buffer, bytes);
+ if (ret < 0)
+ return ret;
+
+ if (ret != bytes)
+ fprintf(stderr, "rw_dev_block warning: done only %d/%d bytes\n", ret, bytes);
+
+ return ret;
+}
+
+static int read_write_rom(struct usb_dev_handle *dev, u32 addr, void *buffer, int bytes, int is_write)
+{
+ int mx_cmd = is_write ? CMD_SEC_WRITE : CMD_SEC_READ;
+ int total_bytes = bytes;
+ u8 *buff = buffer;
+ u8 dummy[64 * 4];
+ int count, ret;
+
+ if (addr & 1)
+ fprintf(stderr, "read_write_rom: can't handle odd address %06x, "
+ "LSb will be ignored\n", addr);
+ if (bytes & 63)
+ fprintf(stderr, "read_write_rom: byte count must be multiple of 64, "
+ "last %d bytes will not be handled\n", bytes & 63);
+
+ set_ram_mode(dev, C_RAM_TMP_OFF);
+
+ printf("%s flash ROM...\n", is_write ? "writing to" : "reading");
+
+ /* do i/o in blocks */
+ for (count = 0; bytes >= IO_BLK_SIZE; count++) {
+ print_progress(buff - (u8 *)buffer, total_bytes);
+
+ ret = rw_dev_block(dev, addr, buff, IO_BLK_SIZE, mx_cmd);
+ if (ret < 0)
+ return ret;
+ buff += IO_BLK_SIZE;
+ addr += IO_BLK_SIZE;
+ bytes -= IO_BLK_SIZE;
+ }
+ print_progress(buff - (u8 *)buffer, total_bytes);
+
+ ret = 0;
+ if (bytes != 0) {
+ ret = rw_dev_block(dev, addr, buff, bytes, mx_cmd);
+ count++;
+ print_progress(total_bytes, total_bytes);
+ }
+
+ if ((count & 1) && !is_write)
+ /* work around rw_dev_block() limitation 3 (works for reads only?) */
+ rw_dev_block(dev, 0, dummy, sizeof(dummy), 0);
+
+ printf("\n");
+ return ret;
+}
+
+static int read_write_ram(struct usb_dev_handle *dev, void *buffer, int bytes, int is_write)
+{
+ int mx_cmd = is_write ? CMD_SEC_RAM_WRITE : CMD_SEC_READ;
+ int total_bytes = bytes;
+ u8 *buff = buffer;
+ u32 addr = 0x200000;
+ int i, ret = 0;
+
+ if (bytes % IO_RAM_BLK_SIZE)
+ fprintf(stderr, "read_write_ram: byte count must be multiple of %d, "
+ "last %d bytes will not be handled\n", IO_RAM_BLK_SIZE,
+ bytes % IO_RAM_BLK_SIZE);
+
+ set_ram_mode(dev, C_RAM_TMP_ON);
+
+ printf("%s RAM...\n", is_write ? "writing to" : "reading");
+
+ /* do i/o in blocks */
+ while (bytes >= IO_RAM_BLK_SIZE) {
+ print_progress(buff - (u8 *)buffer, total_bytes);
+
+ ret = rw_dev_block(dev, addr, buff, IO_RAM_BLK_SIZE, mx_cmd);
+ if (ret < 0)
+ return ret;
+ buff += IO_RAM_BLK_SIZE;
+ addr += IO_RAM_BLK_SIZE;
+ bytes -= IO_RAM_BLK_SIZE;
+ }
+ print_progress(buff - (u8 *)buffer, total_bytes);
+
+ /* only D0-D7 connected.. */
+ for (i = 0; i < total_bytes; i += 2)
+ ((u8 *)buffer)[i] = 0;
+
+ printf("\n");
+ return ret;
+
+}
+
+static int increment_erase_cnt(struct usb_dev_handle *dev)
+{
+ dev_cmd_t cmd;
+ u8 buff[4];
+ u32 cnt;
+ int ret;
+
+ ret = read_erase_counter(dev, &cnt);
+ if (ret != 0)
+ return ret;
+
+ if (cnt == (u32)-1) {
+ fprintf(stderr, "flash erase counter maxed out!\n");
+ fprintf(stderr, "(wow, did you really erase so many times?)\n");
+ return -1;
+ }
+
+ cnt++;
+
+ prepare_cmd(&cmd, CMD_ATM_READY);
+ cmd.write_cnt.cmd = W_COUNTER;
+ cmd.write_cnt.action = W_CNT_WRITE;
+ cmd.write_cnt.b3 = cnt >> 24;
+ cmd.write_cnt.b2 = cnt >> 16;
+ cmd.write_cnt.b1 = cnt >> 8;
+ cmd.write_cnt.b0 = cnt;
+
+ ret = write_cmd(dev, &cmd);
+ if (ret < 0)
+ return ret;
+
+ ret = read_data(dev, buff, sizeof(buff));
+ if (ret < 0)
+ return ret;
+
+ return cnt;
+}
+
+static int erase_page(struct usb_dev_handle *dev, u32 addr, int whole)
+{
+ dev_cmd_t cmd;
+ u8 buff[5];
+ int i, ret;
+
+ prepare_cmd(&cmd, CMD_SEC_ERASE);
+ cmd.write_flag = 1;
+ cmd.rom_rw.addrb2 = addr >> (16 + 1);
+ cmd.rom_rw.addrb1 = addr >> (8 + 1);
+ cmd.rom_rw.addrb0 = addr >> 1;
+ cmd.rom_rw.param = whole ? 0x10 : 0;
+
+ ret = write_cmd(dev, &cmd);
+ if (ret < 0)
+ return ret;
+
+ ret = read_data(dev, buff, sizeof(buff));
+ if (ret < 0)
+ return ret;
+
+ prepare_cmd(&cmd, CMD_SEC_READY);
+ cmd.rom_rw.addrb2 = addr >> (16 + 1);
+ cmd.rom_rw.addrb1 = addr >> (8 + 1);
+ cmd.rom_rw.addrb0 = addr >> 1;
+
+ for (i = 0; i < 100; i++) {
+ ret = write_cmd(dev, &cmd);
+ if (ret < 0)
+ return ret;
+
+ ret = read_data(dev, buff, sizeof(buff));
+ if (ret < 0)
+ return ret;
+
+ if (ret > 4 && buff[4] == 1)
+ break;
+
+ usleep((whole ? 600 : 20) * 1000);
+ }
+
+ if (i == 100) {
+ fprintf(stderr, "\ntimeout waiting for erase to complete\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int erase_seq(struct usb_dev_handle *dev, u32 size)
+{
+ const page_table_t *table;
+ u32 addr, page_size = 0;
+ u32 rom0_id, rom1_id;
+ int count, ret;
+
+ ret = read_flash_rom_id(dev, 0, &rom0_id);
+ if (ret < 0)
+ return ret;
+
+ ret = read_flash_rom_id(dev, 1, &rom1_id);
+ if (ret < 0)
+ return ret;
+
+ if (rom0_id != rom1_id)
+ fprintf(stderr, "Warning: flash ROM ids differ: %08x %08x\n",
+ rom0_id, rom1_id);
+
+ table = get_page_table(rom0_id);
+ if (table == NULL)
+ return -1;
+
+ ret = increment_erase_cnt(dev);
+ if (ret < 0)
+ fprintf(stderr, "warning: coun't increase erase counter\n");
+
+ printf("erasing flash... (erase count=%u)\n", ret);
+
+ for (addr = 0, count = 0; addr < size; addr += page_size, count++) {
+ print_progress(addr, size);
+
+ ret = erase_page(dev, addr, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = get_page_size(table, addr, &page_size);
+ if (ret != 0)
+ break;
+ }
+
+ if (count & 1)
+ /* ??? */
+ /* must submit even number of erase commands (fw bug?) */
+ erase_page(dev, 0, 0);
+
+ print_progress(addr, size);
+ printf("\n");
+
+ return ret;
+}
+
+static int erase_all(struct usb_dev_handle *dev, u32 size)
+{
+ int ret;
+
+ ret = increment_erase_cnt(dev);
+ if (ret < 0)
+ fprintf(stderr, "warning: couldn't increase erase counter\n");
+
+ printf("erasing flash0, count=%u ...", ret);
+ fflush(stdout);
+
+ ret = erase_page(dev, 0xaaa, 1);
+ if (ret != 0)
+ return ret;
+
+ if (size > 0x200000) {
+ printf(" done.\n");
+ printf("erasing flash1...");
+ fflush(stdout);
+
+ ret = erase_page(dev, 0x200aaa, 1);
+ }
+
+ printf(" done.\n");
+ return ret;
+}
+
+static int print_device_info(struct usb_dev_handle *dev)
+{
+ u32 counter, rom0_id, rom1_id;
+ dev_info_t info;
+ int ret;
+
+ printf("data bus controller:\n");
+ ret = read_info(dev, CTL_DATA_BUS, &info);
+ if (ret < 0)
+ return ret;
+ printf_info(&info);
+
+ printf("address bus controller:\n");
+ ret = read_info(dev, CTL_ADDR_BUS, &info);
+ if (ret < 0)
+ return ret;
+ printf_info(&info);
+
+ ret = read_erase_counter(dev, &counter);
+ if (ret < 0)
+ return ret;
+ printf("flash erase count: %u\n", counter);
+
+ ret = read_flash_rom_id(dev, 0, &rom0_id);
+ if (ret < 0)
+ return ret;
+ printf("flash rom0 id: %08x\n", rom0_id);
+
+ ret = read_flash_rom_id(dev, 1, &rom1_id);
+ if (ret < 0)
+ return ret;
+ printf("flash rom1 id: %08x\n", rom1_id);
+
+ return 0;
+}
+
+static int print_game_info(struct usb_dev_handle *dev)
+{
+ char fname[65];
+ int ret;
+
+ ret = read_filename(dev, fname, sizeof(fname), FILENAME_ROM0);
+ if (ret < 0)
+ return ret;
+ printf("ROM filename: %s\n", fname);
+
+ ret = read_filename(dev, fname, sizeof(fname), FILENAME_RAM);
+ if (ret < 0)
+ return ret;
+ printf("SRAM filename: %s\n", fname);
+
+ return 0;
+}
+
+static int read_file(const char *fname, void **buff_out, int *size, int limit)
+{
+ int file_size, ret;
+ void *data;
+ FILE *file;
+
+ file = fopen(fname, "rb");
+ if (file == NULL) {
+ fprintf(stderr, "can't open file: %s\n", fname);
+ return -1;
+ }
+
+ fseek(file, 0, SEEK_END);
+ file_size = ftell(file);
+ fseek(file, 0, SEEK_SET);
+ if (file_size > limit)
+ fprintf(stderr, "warning: input file \"%s\" too large\n", fname);
+ if (file_size < 0) {
+ fprintf(stderr, "bad/empty file: %s\n", fname);
+ goto fail;
+ }
+
+ data = malloc(file_size);
+ if (data == NULL) {
+ fprintf(stderr, "low memory\n");
+ goto fail;
+ }
+
+ ret = fread(data, 1, file_size, file);
+ if (ret != file_size) {
+ fprintf(stderr, "failed to read file: %s", fname);
+ perror("");
+ goto fail;
+ }
+
+ *buff_out = data;
+ *size = file_size;
+ fclose(file);
+ return 0;
+
+fail:
+ fclose(file);
+ return -1;
+}
+
+static int write_file(const char *fname, void *buff, int size)
+{
+ FILE *file;
+ int ret;
+
+ file = fopen(fname, "wb");
+ if (file == NULL) {
+ fprintf(stderr, "can't open for writing: %s\n", fname);
+ return -1;
+ }
+
+ ret = fwrite(buff, 1, size, file);
+ if (ret != size) {
+ fprintf(stderr, "write failed to %s", fname);
+ perror("");
+ } else
+ printf("saved to \"%s\".\n", fname);
+ fclose(file);
+
+ return 0;
+}
+
+static usb_dev_handle *get_device(void)
+{
+ struct usb_dev_handle *handle;
+ struct usb_device *dev;
+ struct usb_bus *bus;
+ int i, ret;
+
+ ret = usb_find_busses();
+ if (ret <= 0) {
+ fprintf(stderr, "Can't find USB busses\n");
+ return NULL;
+ }
+
+ ret = usb_find_devices();
+ if (ret <= 0) {
+ fprintf(stderr, "Can't find USB devices\n");
+ return NULL;
+ }
+
+ bus = usb_get_busses();
+ for (; bus; bus = bus->next)
+ {
+ for (dev = bus->devices; dev; dev = dev->next)
+ {
+ for (i = 0; i < array_size(g_devices); i++)
+ {
+ if (dev->descriptor.idVendor == g_devices[i].vendor &&
+ dev->descriptor.idProduct == g_devices[i].product)
+ goto found;
+ }
+ }
+ }
+
+ fprintf(stderr, "device not found.\n");
+ return NULL;
+
+found:
+ printf("found %s.\n", g_devices[i].name);
+
+ handle = usb_open(dev);
+ if (handle == NULL) {
+ fprintf(stderr, "failed to open device:\n");
+ fprintf(stderr, "%s\n", usb_strerror());
+ return NULL;
+ }
+
+ ret = usb_set_configuration(handle, 1);
+ if (ret != 0) {
+ fprintf(stderr, "couldn't set configuration for /*/bus/usb/%s/%s:\n",
+ bus->dirname, dev->filename);
+ fprintf(stderr, "%s (%d)\n", usb_strerror(), ret);
+ return NULL;
+ }
+
+ ret = usb_claim_interface(handle, 0);
+ if (ret != 0) {
+ fprintf(stderr, "couldn't claim /*/bus/usb/%s/%s:\n",
+ bus->dirname, dev->filename);
+ fprintf(stderr, "%s (%d)\n", usb_strerror(), ret);
+ return NULL;
+ }
+
+ return handle;
+}
+
+static void release_device(struct usb_dev_handle *device)
+{
+ usb_release_interface(device, 0);
+ usb_close(device);
+}
+
+static void usage(const char *app_name)
+{
+ printf("Flasher tool for MX game devices\n"
+ "written by Grazvydas \"notaz\" Ignotas\n");
+ printf("v" VERSION " (" __DATE__ ")\n\n");
+ printf("Usage:\n"
+ "%s [-i] [-g] [-e] [-r [file]] [-w <file>] ...\n"
+ " -i print some info about connected device\n"
+ " -g print some info about game ROM inside device\n"
+ " -e[1] erase whole flash ROM in device, '1' uses different erase method\n"
+ " -m[1-3] set MX mode: 2M+RAM, 4M no RAM, 4M+RAM\n"
+ " -f skip file check\n"
+ " -r [file] copy game image from device to file; can autodetect filename\n"
+ " -w <file> write file to device; also does erase\n"
+ " -sr [file] read save RAM to file\n"
+ " -sw <file> write save RAM file to device\n"
+ " -sc clear save RAM\n"
+ " -v with -w or -sw: verify written file\n",
+ app_name);
+}
+
+int main(int argc, char *argv[])
+{
+ char *r_fname = NULL, *w_fname = NULL, *sr_fname = NULL, *sw_fname = NULL;
+ void *r_fdata = NULL, *w_fdata = NULL, *sr_fdata = NULL, *sw_fdata = NULL;
+ int do_read_ram = 0, do_clear_ram = 0, do_verify = 0, do_check = 1;
+ int pr_dev_info = 0, pr_rom_info = 0, do_read = 0, mx_mode = 0;
+ int erase_method = 0, do_erase_size = 0;
+ int w_fsize = 0, sw_fsize = 0;
+ struct usb_dev_handle *device;
+ char fname_buff[65];
+ int i, ret = 0;
+
+ for (i = 1; i < argc; i++)
+ {
+ if (argv[i][0] != '-')
+ break;
+
+ switch (argv[i][1]) {
+ case 'i':
+ pr_dev_info = 1;
+ break;
+ case 'g':
+ pr_rom_info = 1;
+ break;
+ case 'e':
+ do_erase_size = 0x400000;
+ if (argv[i][2] == '1')
+ erase_method = 1;
+ break;
+ case 'f':
+ do_check = 0;
+ break;
+ case 'v':
+ do_verify = 1;
+ break;
+ case 'm':
+ mx_mode = argv[i][2];
+ break;
+ case 'r':
+ do_read = 1;
+ if (argv[i+1] && argv[i+1][0] != '-')
+ r_fname = argv[++i];
+ break;
+ case 'w':
+ if (argv[i+1] && argv[i+1][0] != '-')
+ w_fname = argv[++i];
+ else
+ goto breakloop;
+ break;
+ case 's':
+ switch (argv[i][2]) {
+ case 'r':
+ do_read_ram = 1;
+ if (argv[i+1] && argv[i+1][0] != '-')
+ sr_fname = argv[++i];
+ break;
+ case 'w':
+ if (argv[i+1] && argv[i+1][0] != '-')
+ sw_fname = argv[++i];
+ else
+ goto breakloop;
+ break;
+ case 'c':
+ do_clear_ram = 1;
+ break;
+ default:
+ goto breakloop;
+ }
+ break;
+ default:
+ goto breakloop;
+ }
+ }
+
+breakloop:
+ if (i <= 1 || i < argc) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ /* preparations */
+ if (w_fname != NULL) {
+ /* check extension */
+ ret = strlen(w_fname);
+ if (do_check && (w_fname[ret - 4] == '.' || w_fname[ret - 3] == '.' ||
+ w_fname[ret - 2] == '.') &&
+ strcasecmp(&w_fname[ret - 4], ".gen") != 0 &&
+ strcasecmp(&w_fname[ret - 4], ".bin") != 0 &&
+ strcasecmp(&w_fname[ret - 4], ".32x") != 0) {
+ fprintf(stderr, "\"%s\" doesn't look like a game ROM, aborting "
+ "(use -f to disable this check)\n", w_fname);
+ return 1;
+ }
+
+ ret = read_file(w_fname, &w_fdata, &w_fsize, 0x400000);
+ if (ret < 0)
+ return 1;
+
+ /* align size to 64 */
+ ret = (w_fsize + 63) & ~63;
+ if (w_fsize != ret) {
+ printf("ROM image size is %d, padding to %d\n", w_fsize, ret);
+ w_fdata = realloc(w_fdata, ret);
+ if (w_fdata == NULL) {
+ fprintf(stderr, "low mem\n");
+ return 1;
+ }
+ memset((char *)w_fdata + w_fsize, 0, ret - w_fsize);
+ w_fsize = ret;
+ }
+
+ if (do_erase_size < w_fsize)
+ do_erase_size = w_fsize;
+ }
+ if (sw_fname != NULL) {
+ ret = read_file(sw_fname, &sw_fdata, &sw_fsize, 0x8000*2);
+ if (ret < 0)
+ return 1;
+ }
+ if (sw_fdata != NULL || do_clear_ram) {
+ if (sw_fsize < 0x8000*2) {
+ sw_fdata = realloc(sw_fdata, 0x8000*2);
+ if (sw_fdata == NULL) {
+ fprintf(stderr, "low mem\n");
+ return 1;
+ }
+ memset((u8 *)sw_fdata + sw_fsize, 0, 0x8000*2 - sw_fsize);
+ }
+ sw_fsize = 0x8000*2;
+ }
+ if (w_fname == NULL && sw_fname == NULL && do_verify) {
+ fprintf(stderr, "warning: -w or -sw not specified, -v ignored.\n");
+ do_verify = 0;
+ }
+
+ /* init */
+ usb_init();
+
+ device = get_device();
+ if (device == NULL)
+ return 1;
+
+ /* info */
+ if (pr_dev_info) {
+ ret = print_device_info(device);
+ if (ret < 0)
+ goto end;
+ }
+
+ if (pr_rom_info) {
+ ret = print_game_info(device);
+ if (ret < 0)
+ goto end;
+ }
+
+ /* erase */
+ if (do_erase_size != 0) {
+ if (erase_method)
+ ret = erase_all(device, do_erase_size);
+ else
+ ret = erase_seq(device, do_erase_size);
+ if (ret < 0)
+ goto end;
+ }
+
+ /* write flash */
+ if (w_fdata != NULL) {
+ char *p;
+
+ ret = read_write_rom(device, 0, w_fdata, w_fsize, 1);
+ if (ret < 0)
+ goto end;
+
+ p = strrchr(w_fname, '/');
+ p = (p == NULL) ? w_fname : p + 1;
+
+ ret = write_filename(device, p, FILENAME_ROM0);
+ if (ret < 0)
+ fprintf(stderr, "warning: failed to save ROM filename\n");
+ }
+
+ /* write ram */
+ if (sw_fdata != NULL) {
+ char *p, *t;
+
+ ret = read_write_ram(device, sw_fdata, sw_fsize, 1);
+ if (ret < 0)
+ goto end;
+
+ memset(fname_buff, 0, sizeof(fname_buff));
+ p = fname_buff;
+ if (sw_fname != NULL) {
+ p = strrchr(sw_fname, '/');
+ p = (p == NULL) ? sw_fname : p + 1;
+ } else if (w_fname != NULL) {
+ t = strrchr(w_fname, '/');
+ t = (t == NULL) ? w_fname : t + 1;
+
+ strncpy(fname_buff, t, sizeof(fname_buff));
+ fname_buff[sizeof(fname_buff) - 1] = 0;
+ ret = strlen(fname_buff);
+ if (ret > 4 && fname_buff[ret - 4] == '.')
+ strcpy(&fname_buff[ret - 4], ".srm");
+ }
+
+ ret = write_filename(device, p, FILENAME_RAM);
+ if (ret < 0)
+ fprintf(stderr, "warning: failed to save RAM filename\n");
+ }
+
+ /* set mode */
+ if (mx_mode || w_fsize > 0x200000) {
+ if (mx_mode == 0)
+ mx_mode = '3';
+ printf("MX mode set to ");
+ switch (mx_mode) {
+ case '1':
+ printf("2M with RAM.\n");
+ mx_mode = C_MODE_2M_RAM;
+ break;
+ case '2':
+ printf("4M, no RAM.\n");
+ mx_mode = C_MODE_4M_NORAM;
+ break;
+ default:
+ printf("4M with RAM.\n");
+ mx_mode = C_MODE_4M_RAM;
+ break;
+ }
+ set_ram_mode(device, mx_mode);
+ }
+
+ /* read flash */
+ if (do_read && r_fname == NULL) {
+ ret = read_filename(device, fname_buff, sizeof(fname_buff), FILENAME_ROM0);
+ if (ret < 0)
+ return ret;
+ r_fname = fname_buff;
+ if (r_fname[0] == 0)
+ r_fname = "rom.gen";
+ }
+
+ if (r_fname != NULL || do_verify) {
+ r_fdata = malloc(0x400000);
+ if (r_fdata == NULL) {
+ fprintf(stderr, "low mem\n");
+ goto end;
+ }
+
+ ret = read_write_rom(device, 0, r_fdata, 0x400000, 0);
+ if (ret < 0)
+ goto end;
+ }
+
+ if (r_fname != NULL)
+ write_file(r_fname, r_fdata, 0x400000);
+
+ /* read ram */
+ if (do_read_ram && sr_fname == NULL) {
+ ret = read_filename(device, fname_buff, sizeof(fname_buff), FILENAME_RAM);
+ if (ret < 0)
+ return ret;
+ sr_fname = fname_buff;
+ if (sr_fname[0] == 0)
+ sr_fname = "rom.srm";
+ }
+
+ if (sr_fname != NULL || do_verify) {
+ sr_fdata = malloc(0x8000*2);
+ if (sr_fdata == NULL) {
+ fprintf(stderr, "low mem\n");
+ goto end;
+ }
+
+ ret = read_write_ram(device, sr_fdata, 0x8000*2, 0);
+ if (ret < 0)
+ goto end;
+ }
+
+ if (sr_fname != NULL)
+ write_file(sr_fname, sr_fdata, 0x8000*2);
+
+ /* verify */
+ if (do_verify && w_fdata != NULL && r_fdata != NULL) {
+ ret = memcmp(w_fdata, r_fdata, w_fsize);
+ if (ret == 0)
+ printf("flash verification passed.\n");
+ else
+ printf("flash verification FAILED!\n");
+ }
+
+ if (do_verify && sw_fdata != NULL && sr_fdata != NULL) {
+ ret = memcmp(sw_fdata, sr_fdata, 0x8000*2);
+ if (ret == 0)
+ printf("RAM verification passed.\n");
+ else
+ printf("RAM verification FAILED!\n");
+ }
+
+ printf("all done.\n");
+ ret = 0;
+
+end:
+ if (w_fdata != NULL)
+ free(w_fdata);
+ if (r_fdata != NULL)
+ free(r_fdata);
+
+ release_device(device);
+
+ return ret;
+}
+
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+
+#define IN_SIZE 0x8000
+#define OUT_SIZE 0x400000
+static unsigned char buff[OUT_SIZE], buff_in[IN_SIZE];
+
+int main(int argc, char *argv[])
+{
+ FILE *fi, *fo;
+ int size, bsize;
+ int i, o;
+
+ if (argc != 3) {
+ fprintf(stderr, "usage:\n%s <sms ROM> <mx img>\n", argv[0]);
+ return 1;
+ }
+
+ fi = fopen(argv[1], "rb");
+ if (fi == NULL) {
+ fprintf(stderr, "can't open: %s\n", argv[1]);
+ return 1;
+ }
+
+ fo = fopen(argv[2], "wb");
+ if (fo == NULL) {
+ fprintf(stderr, "can't open: %s\n", argv[2]);
+ return 1;
+ }
+
+ fseek(fi, 0, SEEK_END);
+ size = ftell(fi);
+ fseek(fi, 0, SEEK_SET);
+
+ if (size > IN_SIZE) {
+ fprintf(stderr, "ROMs > 32k won't work\n");
+ size = IN_SIZE;
+ }
+
+ if (fread(buff_in, 1, size, fi) != size)
+ fprintf(stderr, "failed to read %s\n", argv[1]);
+
+ memset(buff, 0, sizeof(buff));
+
+ for (bsize = 1; bsize < size; bsize <<= 1)
+ ;
+
+ for (i = o = 0; o < sizeof(buff); i = (i + 1) & (bsize - 1), o += 2)
+ buff[o+1] = buff_in[i];
+
+ if (fwrite(buff, 1, OUT_SIZE, fo) != OUT_SIZE)
+ fprintf(stderr, "failed to write to %s\n", argv[2]);
+ fclose(fi);
+ fclose(fo);
+
+ return 0;
+}
+
--- /dev/null
+CROSS = m68k-elf-\r
+AS = $(CROSS)as\r
+LD = $(CROSS)ld\r
+OBJCOPY = $(CROSS)objcopy\r
+\r
+ASFLAGS = -m68000 --register-prefix-optional --bitwise-or \r
+\r
+TARGET = nshtest.bin\r
+OBJS = sega_gcc.o test.o\r
+\r
+all : $(TARGET)\r
+\r
+$(TARGET) : a.out\r
+ $(OBJCOPY) -I elf32-m68k -O binary $^ $@\r
+\r
+a.out : $(OBJS)\r
+ $(LD) -Tmd.ld -Map $(TARGET).map $^\r
+\r
+clean:\r
+ $(RM) $(TARGET) $(OBJS) $(TARGET).map a.out\r
+\r
--- /dev/null
+OUTPUT_ARCH(m68k)\r
+SEARCH_DIR(.)\r
+/*GROUP(-lbcc -lc -lgcc)*/\r
+__DYNAMIC = 0;\r
+\r
+/*\r
+ * Setup the memory map of the SEGA Genesis.\r
+ * stack grows down from high memory.\r
+ *\r
+ * The memory map look like this:\r
+ * +--------------------+ <- low memory\r
+ * | .text |\r
+ * | _etext |\r
+ * | ctor list | the ctor and dtor lists are for\r
+ * | dtor list | C++ support\r
+ * +--------------------+\r
+ * | .data | initialized data goes here\r
+ * | _edata |\r
+ * +--------------------+\r
+ * | .bss |\r
+ * | __bss_start | start of bss, cleared by crt0\r
+ * | _end | start of heap, used by sbrk()\r
+ * +--------------------+\r
+ * . .\r
+ * . .\r
+ * . .\r
+ * | __stack | top of stack\r
+ * +--------------------+\r
+ */\r
+/*\r
+MEMORY\r
+{\r
+ rom : ORIGIN = 0x00000000, LENGTH = 0x00400000\r
+ ram : ORIGIN = 0xffff0000, LENGTH = 0x00010000\r
+}\r
+*/\r
+\r
+MEMORY {\r
+ ram : ORIGIN = 0x0, LENGTH = 0xfffffff\r
+}\r
+\r
+/*\r
+ * allocate the stack to be at the top of memory, since the stack\r
+ * grows down\r
+ */\r
+\r
+PROVIDE (__stack = 0x00fffff0);\r
+\r
+PROVIDE (ram = 0xffff0000);\r
+/*\r
+ * Initalize some symbols to be zero so we can reference them in the\r
+ * crt0 without core dumping. These functions are all optional, but\r
+ * we do this so we can have our crt0 always use them if they exist. \r
+ * This is so BSPs work better when using the crt0 installed with gcc.\r
+ * We have to initalize them twice, so we cover a.out (which prepends\r
+ * an underscore) and coff object file formats.\r
+ */\r
+PROVIDE (hardware_init_hook = 0);\r
+PROVIDE (_hardware_init_hook = 0);\r
+PROVIDE (software_init_hook = 0);\r
+PROVIDE (_software_init_hook = 0);\r
+\r
+SECTIONS\r
+{\r
+ .text 0x00000000:\r
+ {\r
+ *(.text)\r
+ . = ALIGN(0x4);\r
+ __CTOR_LIST__ = .;\r
+ LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2)\r
+ *(.ctors)\r
+ LONG(0)\r
+ __CTOR_END__ = .;\r
+ __DTOR_LIST__ = .;\r
+ LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2)\r
+ *(.dtors)\r
+ LONG(0)\r
+ __DTOR_END__ = .;\r
+ *(.rodata)\r
+ *(.gcc_except_table) \r
+\r
+ __INIT_SECTION__ = . ;\r
+ *(.init)\r
+ SHORT (0x4e75) /* rts */\r
+\r
+ __FINI_SECTION__ = . ;\r
+ *(.fini)\r
+ SHORT (0x4e75) /* rts */\r
+\r
+ _etext = .;\r
+ *(.lit)\r
+ } > ram\r
+\r
+ .data BLOCK (0x4) :\r
+ {\r
+ *(.shdata)\r
+ *(.data)\r
+ _edata = .;\r
+ } > ram\r
+\r
+ .bss 0xff0000 :\r
+ {\r
+ __bss_start = . ;\r
+ *(.shbss)\r
+ *(.bss)\r
+ *(COMMON)\r
+ *(.eh_fram)\r
+ *(.eh_frame)\r
+ _end = ALIGN (0x8);\r
+ __end = _end;\r
+ } > ram\r
+\r
+ .stab 0 (NOLOAD) :\r
+ {\r
+ *(.stab)\r
+ }\r
+\r
+ .stabstr 0 (NOLOAD) :\r
+ {\r
+ *(.stabstr)\r
+ }\r
+}\r
--- /dev/null
+*-------------------------------------------------------\r
+*\r
+* Sega startup code for the GNU Assembler\r
+* Translated from:\r
+* Sega startup code for the Sozobon C compiler\r
+* Written by Paul W. Lee\r
+* Modified from Charles Coty's code\r
+*\r
+*-------------------------------------------------------\r
+\r
+ dc.l 0x0,0x200\r
+ dc.l INT,INT,INT,INT,INT,INT,INT\r
+ dc.l INT,INT,INT,INT,INT,INT,INT,INT\r
+ dc.l INT,INT,INT,INT,INT,INT,INT,INT\r
+ dc.l INT,INT,INT,HBL,INT,VBL,INT,INT\r
+ dc.l INT,INT,INT,INT,INT,INT,INT,INT\r
+ dc.l INT,INT,INT,INT,INT,INT,INT,INT\r
+ dc.l INT,INT,INT,INT,INT,INT,INT,INT\r
+ dc.l INT,INT,INT,INT,INT,INT,INT\r
+ .ascii "SEGA GENESIS "\r
+ .ascii "notaz's Shadow / Hilight test "\r
+ .ascii "NOTAZ'S SHADOW HILIGHT TEST "\r
+ .ascii "GM 00000000-00"\r
+ .byte 0xa5,0xfb\r
+ .ascii "JD "\r
+ .byte 0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00\r
+ .byte 0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff\r
+ .ascii " "\r
+ .ascii " "\r
+ .ascii " "\r
+ .ascii "JUE "\r
+*debugee:\r
+* bra debugee\r
+ tst.l 0xa10008\r
+ bne SkipJoyDetect \r
+ tst.w 0xa1000c\r
+SkipJoyDetect:\r
+ bne SkipSetup\r
+ lea Table,%a5 \r
+ movem.w (%a5)+,%d5-%d7\r
+ movem.l (%a5)+,%a0-%a4 \r
+* Check Version Number \r
+ move.b -0x10ff(%a1),%d0\r
+ andi.b #0x0f,%d0 \r
+ beq WrongVersion \r
+* Sega Security Code (SEGA) \r
+ move.l #0x53454741,0x2f00(%a1)\r
+WrongVersion:\r
+ move.w (%a4),%d0\r
+ moveq #0x00,%d0 \r
+ movea.l %d0,%a6 \r
+ move %a6,%usp\r
+* Set VDP registers\r
+ moveq #0x17,%d1\r
+FillLoop: \r
+ move.b (%a5)+,%d5\r
+ move.w %d5,(%a4) \r
+ add.w %d7,%d5 \r
+ dbra %d1,FillLoop \r
+ move.l (%a5)+,(%a4) \r
+ move.w %d0,(%a3) \r
+ move.w %d7,(%a1) \r
+ move.w %d7,(%a2) \r
+L0250:\r
+ btst %d0,(%a1)\r
+ bne L0250 \r
+* Put initial values into a00000 \r
+ moveq #0x25,%d2\r
+Filla: \r
+ move.b (%a5)+,(%a0)+\r
+ dbra %d2,Filla\r
+ move.w %d0,(%a2) \r
+ move.w %d0,(%a1) \r
+ move.w %d7,(%a2) \r
+L0262:\r
+ move.l %d0,-(%a6)\r
+ dbra %d6,L0262 \r
+ move.l (%a5)+,(%a4) \r
+ move.l (%a5)+,(%a4) \r
+* Put initial values into c00000 \r
+ moveq #0x1f,%d3\r
+Filc0: \r
+ move.l %d0,(%a3)\r
+ dbra %d3,Filc0\r
+ move.l (%a5)+,(%a4) \r
+* Put initial values into c00000 \r
+ moveq #0x13,%d4\r
+Fillc1: \r
+ move.l %d0,(%a3)\r
+ dbra %d4,Fillc1\r
+* Put initial values into c00011 \r
+ moveq #0x03,%d5\r
+Fillc2: \r
+ move.b (%a5)+,0x0011(%a3) \r
+ dbra %d5,Fillc2 \r
+ move.w %d0,(%a2) \r
+ movem.l (%a6),%d0-%d7/%a0-%a6 \r
+ move #0x2700,%sr \r
+SkipSetup:\r
+ bra Continue\r
+Table:\r
+ dc.w 0x8000, 0x3fff, 0x0100, 0x00a0, 0x0000, 0x00a1, 0x1100, 0x00a1\r
+ dc.w 0x1200, 0x00c0, 0x0000, 0x00c0, 0x0004, 0x0414, 0x302c, 0x0754\r
+ dc.w 0x0000, 0x0000, 0x0000, 0x812b, 0x0001, 0x0100, 0x00ff, 0xff00 \r
+ dc.w 0x0080, 0x4000, 0x0080, 0xaf01, 0xd91f, 0x1127, 0x0021, 0x2600\r
+ dc.w 0xf977, 0xedb0, 0xdde1, 0xfde1, 0xed47, 0xed4f, 0xd1e1, 0xf108 \r
+ dc.w 0xd9c1, 0xd1e1, 0xf1f9, 0xf3ed, 0x5636, 0xe9e9, 0x8104, 0x8f01 \r
+ dc.w 0xc000, 0x0000, 0x4000, 0x0010, 0x9fbf, 0xdfff \r
+\r
+Continue:\r
+ tst.w 0x00C00004\r
+\r
+* set stack pointer\r
+* clr.l %a7\r
+ move.w #0,%a7\r
+\r
+* user mode\r
+ move.w #0x2300,%sr\r
+\r
+* clear Genesis RAM\r
+ lea 0xff0000,%a0\r
+ moveq #0,%d0\r
+clrram: move.w #0,(%a0)+\r
+ subq.w #2,%d0\r
+ bne clrram\r
+\r
+*---------------------------------------------------------- \r
+*\r
+* Load driver into the Z80 memory\r
+*\r
+*---------------------------------------------------------- \r
+\r
+* halt the Z80\r
+ move.w #0x100,0xa11100\r
+* reset it\r
+ move.w #0x100,0xa11200\r
+\r
+ lea Z80Driver,%a0\r
+ lea 0xa00000,%a1\r
+ move.l #Z80DriverEnd,%d0\r
+ move.l #Z80Driver,%d1\r
+ sub.l %d1,%d0\r
+Z80loop:\r
+ move.b (%a0)+,(%a1)+\r
+ subq.w #1,%d0\r
+ bne Z80loop\r
+\r
+* enable the Z80\r
+ move.w #0x0,0xa11100\r
+\r
+*---------------------------------------------------------- \r
+ jmp main\r
+\r
+INT: \r
+ rte\r
+\r
+HBL:\r
+ /* addq.l #1,htimer */\r
+ rte\r
+\r
+VBL:\r
+ /* addq.l #1,vtimer */\r
+ move.l #vtimer,a0\r
+ addq.l #1,(a0)\r
+ rte\r
+\r
+*------------------------------------------------\r
+*\r
+* Get a random number. This routine\r
+* was found in TOS.\r
+*\r
+* Output\r
+* ------\r
+* d0 = random number\r
+*\r
+*------------------------------------------------\r
+\r
+ .globl random\r
+\r
+random:\r
+ move.l rand_num,%d0\r
+ tst.l %d0\r
+ bne .L1\r
+ moveq #16,%d1\r
+ lsl.l %d1,%d0\r
+ or.l htimer,%d0\r
+ move.l %d0,rand_num\r
+.L1:\r
+ move.l #-1153374675,-(%sp)\r
+ move.l rand_num,-(%sp)\r
+ bsr lmul\r
+ addq.w #8,%sp\r
+ addq.l #1,%d0\r
+ move.l %d0,rand_num\r
+\r
+ lsr.l #8,%d0\r
+ and.l #16777215,%d0\r
+ rts\r
+\r
+\r
+*------------------------------------------------\r
+*\r
+* Copyright (c) 1988 by Sozobon, Limited. Author: Johann Ruegg\r
+*\r
+* Permission is granted to anyone to use this software for any purpose\r
+* on any computer system, and to redistribute it freely, with the\r
+* following restrictions:\r
+* 1) No charge may be made other than reasonable charges for reproduction.\r
+* 2) Modified versions must be clearly marked as such.\r
+* 3) The authors are not responsible for any harmful consequences\r
+* of using this software, even if they result from defects in it.\r
+*\r
+*------------------------------------------------\r
+\r
+ldiv:\r
+ move.l 4(%a7),%d0\r
+ bpl ld1\r
+ neg.l %d0\r
+ld1:\r
+ move.l 8(%a7),%d1\r
+ bpl ld2\r
+ neg.l %d1\r
+ eor.b #0x80,4(%a7)\r
+ld2:\r
+ bsr i_ldiv /* d0 = d0/d1 */\r
+ tst.b 4(%a7)\r
+ bpl ld3\r
+ neg.l %d0\r
+ld3:\r
+ rts\r
+\r
+lmul:\r
+ move.l 4(%a7),%d0\r
+ bpl lm1\r
+ neg.l %d0\r
+lm1:\r
+ move.l 8(%a7),%d1\r
+ bpl lm2\r
+ neg.l %d1\r
+ eor.b #0x80,4(%a7)\r
+lm2:\r
+ bsr i_lmul /* d0 = d0*d1 */\r
+ tst.b 4(%a7)\r
+ bpl lm3\r
+ neg.l %d0\r
+lm3:\r
+ rts\r
+\r
+lrem:\r
+ move.l 4(%a7),%d0\r
+ bpl lr1\r
+ neg.l %d0\r
+lr1:\r
+ move.l 8(%a7),%d1\r
+ bpl lr2\r
+ neg.l %d1\r
+lr2:\r
+ bsr i_ldiv /* d1 = d0%d1 */\r
+ move.l %d1,%d0\r
+ tst.b 4(%a7)\r
+ bpl lr3\r
+ neg.l %d0\r
+lr3:\r
+ rts\r
+\r
+ldivu:\r
+ move.l 4(%a7),%d0\r
+ move.l 8(%a7),%d1\r
+ bsr i_ldiv\r
+ rts\r
+\r
+lmulu:\r
+ move.l 4(%a7),%d0\r
+ move.l 8(%a7),%d1\r
+ bsr i_lmul\r
+ rts\r
+\r
+lremu:\r
+ move.l 4(%a7),%d0\r
+ move.l 8(%a7),%d1\r
+ bsr i_ldiv\r
+ move.l %d1,%d0\r
+ rts\r
+*\r
+* A in d0, B in d1, return A*B in d0\r
+*\r
+i_lmul:\r
+ move.l %d3,%a2 /* save d3 */\r
+ move.w %d1,%d2\r
+ mulu %d0,%d2 /* d2 = Al * Bl */\r
+\r
+ move.l %d1,%d3\r
+ swap %d3\r
+ mulu %d0,%d3 /* d3 = Al * Bh */\r
+\r
+ swap %d0\r
+ mulu %d1,%d0 /* d0 = Ah * Bl */\r
+\r
+ add.l %d3,%d0 /* d0 = (Ah*Bl + Al*Bh) */\r
+ swap %d0\r
+ clr.w %d0 /* d0 = (Ah*Bl + Al*Bh) << 16 */\r
+\r
+ add.l %d2,%d0 /* d0 = A*B */\r
+ move.l %a2,%d3 /* restore d3 */\r
+ rts\r
+*\r
+*A in d0, B in d1, return A/B in d0, A%B in d1\r
+*\r
+i_ldiv:\r
+ tst.l %d1\r
+ bne nz1\r
+\r
+* divide by zero\r
+* divu #0,%d0 /* cause trap */\r
+ move.l #0x80000000,%d0\r
+ move.l %d0,%d1\r
+ rts\r
+nz1:\r
+ move.l %d3,%a2 /* save d3 */\r
+ cmp.l %d1,%d0\r
+ bhi norm\r
+ beq is1\r
+* A<B, so ret 0, rem A\r
+ move.l %d0,%d1\r
+ clr.l %d0\r
+ move.l %a2,%d3 /* restore d3 */\r
+ rts\r
+* A==B, so ret 1, rem 0\r
+is1:\r
+ moveq.l #1,%d0\r
+ clr.l %d1\r
+ move.l %a2,%d3 /* restore d3 */\r
+ rts\r
+* A>B and B is not 0\r
+norm:\r
+ cmp.l #1,%d1\r
+ bne not1\r
+* B==1, so ret A, rem 0\r
+ clr.l %d1\r
+ move.l %a2,%d3 /* restore d3 */\r
+ rts\r
+* check for A short (implies B short also)\r
+not1:\r
+ cmp.l #0xffff,%d0\r
+ bhi slow\r
+* A short and B short -- use 'divu'\r
+ divu %d1,%d0 /* d0 = REM:ANS */\r
+ swap %d0 /* d0 = ANS:REM */\r
+ clr.l %d1\r
+ move.w %d0,%d1 /* d1 = REM */\r
+ clr.w %d0\r
+ swap %d0\r
+ move.l %a2,%d3 /* restore d3 */\r
+ rts\r
+* check for B short\r
+slow:\r
+ cmp.l #0xffff,%d1\r
+ bhi slower\r
+* A long and B short -- use special stuff from gnu\r
+ move.l %d0,%d2\r
+ clr.w %d2\r
+ swap %d2\r
+ divu %d1,%d2 /* d2 = REM:ANS of Ahi/B */\r
+ clr.l %d3\r
+ move.w %d2,%d3 /* d3 = Ahi/B */\r
+ swap %d3\r
+\r
+ move.w %d0,%d2 /* d2 = REM << 16 + Alo */\r
+ divu %d1,%d2 /* d2 = REM:ANS of stuff/B */\r
+\r
+ move.l %d2,%d1\r
+ clr.w %d1\r
+ swap %d1 /* d1 = REM */\r
+\r
+ clr.l %d0\r
+ move.w %d2,%d0\r
+ add.l %d3,%d0 /* d0 = ANS */\r
+ move.l %a2,%d3 /* restore d3 */\r
+ rts\r
+* A>B, B > 1\r
+slower:\r
+ move.l #1,%d2\r
+ clr.l %d3\r
+moreadj:\r
+ cmp.l %d0,%d1\r
+ bhs adj\r
+ add.l %d2,%d2\r
+ add.l %d1,%d1\r
+ bpl moreadj\r
+* we shifted B until its >A or sign bit set\r
+* we shifted #1 (d2) along with it\r
+adj:\r
+ cmp.l %d0,%d1\r
+ bhi ltuns\r
+ or.l %d2,%d3\r
+ sub.l %d1,%d0\r
+ltuns:\r
+ lsr.l #1,%d1\r
+ lsr.l #1,%d2\r
+ bne adj\r
+* d3=answer, d0=rem\r
+ move.l %d0,%d1\r
+ move.l %d3,%d0\r
+ move.l %a2,%d3 /* restore d3 */\r
+ rts\r
+*---------------------------------------------------------- \r
+*\r
+* Z80 Sound Driver\r
+*\r
+*---------------------------------------------------------- \r
+Z80Driver:\r
+ dc.b 0xc3,0x46,0x00,0x00,0x00,0x00,0x00,0x00\r
+ dc.b 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\r
+ dc.b 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\r
+ dc.b 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\r
+ dc.b 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\r
+ dc.b 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\r
+ dc.b 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\r
+ dc.b 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00\r
+ dc.b 0x00,0x00,0x00,0x00,0x00,0x00,0xf3,0xed\r
+ dc.b 0x56,0x31,0x00,0x20,0x3a,0x39,0x00,0xb7\r
+ dc.b 0xca,0x4c,0x00,0x21,0x3a,0x00,0x11,0x40\r
+ dc.b 0x00,0x01,0x06,0x00,0xed,0xb0,0x3e,0x00\r
+ dc.b 0x32,0x39,0x00,0x3e,0xb4,0x32,0x02,0x40\r
+ dc.b 0x3e,0xc0,0x32,0x03,0x40,0x3e,0x2b,0x32\r
+ dc.b 0x00,0x40,0x3e,0x80,0x32,0x01,0x40,0x3a\r
+ dc.b 0x43,0x00,0x4f,0x3a,0x44,0x00,0x47,0x3e\r
+ dc.b 0x06,0x3d,0xc2,0x81,0x00,0x21,0x00,0x60\r
+ dc.b 0x3a,0x41,0x00,0x07,0x77,0x3a,0x42,0x00\r
+ dc.b 0x77,0x0f,0x77,0x0f,0x77,0x0f,0x77,0x0f\r
+ dc.b 0x77,0x0f,0x77,0x0f,0x77,0x0f,0x77,0x3a\r
+ dc.b 0x40,0x00,0x6f,0x3a,0x41,0x00,0xf6,0x80\r
+ dc.b 0x67,0x3e,0x2a,0x32,0x00,0x40,0x7e,0x32\r
+ dc.b 0x01,0x40,0x21,0x40,0x00,0x7e,0xc6,0x01\r
+ dc.b 0x77,0x23,0x7e,0xce,0x00,0x77,0x23,0x7e\r
+ dc.b 0xce,0x00,0x77,0x3a,0x39,0x00,0xb7,0xc2\r
+ dc.b 0x4c,0x00,0x0b,0x78,0xb1,0xc2,0x7f,0x00\r
+ dc.b 0x3a,0x45,0x00,0xb7,0xca,0x4c,0x00,0x3d\r
+ dc.b 0x3a,0x45,0x00,0x06,0xff,0x0e,0xff,0xc3\r
+ dc.b 0x7f,0x00\r
+Z80DriverEnd:\r
+\r
+\r
--- /dev/null
+##################################################
+# #
+# Assemble with gas #
+# --register-prefix-optional --bitwise-or #
+# #
+##################################################
+
+.text
+.globl main
+
+##################################################
+# #
+# Register and bitmask definitions #
+# #
+##################################################
+
+.equ GFXDATA, 0xc00000
+.equ GFXCNTL, 0xc00004
+
+.equ VDP0_E_HBI, 0x10
+.equ VDP0_E_DISPLAY, 0x02
+.equ VDP0_PLTT_FULL, 0x04
+
+.equ VDP1_SMS_MODE, 0x80
+.equ VDP1_E_DISPLAY, 0x40
+.equ VDP1_E_VBI, 0x20
+.equ VDP1_E_DMA, 0x10
+.equ VDP1_NTSC, 0x00
+.equ VDP1_PAL, 0x08
+.equ VDP1_RESERVED, 0x04
+
+.equ VDP12_SPR_SHADOWS, 0x08
+.equ VDP12_SCREEN_V224, 0x00
+.equ VDP12_SCREEN_V448, 0x04
+.equ VDP12_PROGRESSIVE, 0x00
+.equ VDP12_INTERLACED, 0x02
+.equ VDP12_SCREEN_H256, 0x00
+.equ VDP12_SCREEN_H320, 0x81
+
+.equ VDP16_MAP_V32, 0x00
+.equ VDP16_MAP_V64, 0x10
+.equ VDP16_MAP_V128, 0x30
+.equ VDP16_MAP_H32, 0x00
+.equ VDP16_MAP_H64, 0x01
+.equ VDP16_MAP_H128, 0x03
+
+
+
+##################################################
+# #
+# MACROS #
+# #
+##################################################
+
+
+/* Write val to VDP register reg */
+.macro write_vdp_reg reg val
+ move.w #((\reg << 8) + 0x8000 + \val),(a3)
+.endm
+
+
+/* For immediate addresses */
+.macro VRAM_ADDR reg adr
+ move.l #(((0x4000 + (\adr & 0x3fff)) << 16) + (\adr >> 14)),\reg
+.endm
+
+
+/* For indirect (variable) addresses.
+ Destroys d6-d7. */
+.macro VRAM_ADDR_var reg adr
+ move.l \adr,d6
+ move.l \adr,d7
+ and.w #0x3fff,d6
+ lsr.w #7,d7
+ lsr.w #7,d7
+ add.w #0x4000,d6
+ lsl.l #7,d6
+ lsl.l #7,d6
+ lsl.l #2,d6
+ or.l d7,d6
+ move.l d6,\reg
+.endm
+
+
+.macro CRAM_ADDR reg adr
+ move.l #(((0xc000 + (\adr & 0x3fff)) << 16) + (\adr >> 14)),\reg
+.endm
+
+
+/* For indirect (variable) addresses */
+.macro CRAM_ADDR_var reg adr
+ move.l \adr,d6
+ move.l \adr,d7
+ and.w #0x3fff,d6
+ lsr.w #7,d7
+ lsr.w #7,d7
+ add.w #0xc000,d6
+ lsl.l #7,d6
+ lsl.l #7,d6
+ lsl.l #2,d6
+ or.l d7,d6
+ move.l d6,\reg
+.endm
+
+
+.macro VSCROLL_ADDR reg adr
+ move.l #(((0x4000 + (\adr & 0x3fff)) << 16) + ((\adr >> 14) | 0x10)),\reg
+.endm
+
+
+.macro HSCROLL_ADDR reg adr
+ move.l #(((0x4000 + (\adr & 0x3fff)) << 16) + (\adr >> 14)),\reg
+.endm
+
+
+#################################################
+# #
+# DATA #
+# #
+#################################################
+
+colors:
+ dc.w 0x0040,0x0080,0x000e,0x00e0,0x0e00,0x00ee
+pattern:
+ dc.l 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000
+ dc.l 0x22334455,0x22334455,0x22334455,0x22334455,0x22334455,0x22334455,0x22334455,0x22334455
+ /* shadow sprite */
+ dc.l 0x00000fff
+ dc.l 0x0000ffff
+ dc.l 0x00ffffff
+ dc.l 0x00ffffff
+ dc.l 0x0fffffff
+ dc.l 0xffffffff
+ dc.l 0xffffffff
+ dc.l 0xffffffff
+ /* */
+ dc.l 0xffffffff
+ dc.l 0xffffffff
+ dc.l 0xffffffff
+ dc.l 0x0fffffff
+ dc.l 0x00ffffff
+ dc.l 0x00ffffff
+ dc.l 0x0000ffff
+ dc.l 0x00000fff
+ /* */
+ dc.l 0xfff00000
+ dc.l 0xffff0000
+ dc.l 0xffffff00
+ dc.l 0xffffff00
+ dc.l 0xfffffff0
+ dc.l 0xffffffff
+ dc.l 0xffffffff
+ dc.l 0xffffffff
+ /* */
+ dc.l 0xffffffff
+ dc.l 0xffffffff
+ dc.l 0xffffffff
+ dc.l 0xfffffff0
+ dc.l 0xffffff00
+ dc.l 0xffffff00
+ dc.l 0xffff0000
+ dc.l 0xfff00000
+ /* hilight sprite */
+ dc.l 0x00000eee
+ dc.l 0x0000eeee
+ dc.l 0x00eeeeee
+ dc.l 0x00eeeeee
+ dc.l 0x0eeeeeee
+ dc.l 0xeeeeeeee
+ dc.l 0xeeeeeeee
+ dc.l 0xeeeeeeee
+ /* */
+ dc.l 0xeeeeeeee
+ dc.l 0xeeeeeeee
+ dc.l 0xeeeeeeee
+ dc.l 0x0eeeeeee
+ dc.l 0x00eeeeee
+ dc.l 0x00eeeeee
+ dc.l 0x0000eeee
+ dc.l 0x00000eee
+ /* */
+ dc.l 0xeee00000
+ dc.l 0xeeee0000
+ dc.l 0xeeeeee00
+ dc.l 0xeeeeee00
+ dc.l 0xeeeeeee0
+ dc.l 0xeeeeeeee
+ dc.l 0xeeeeeeee
+ dc.l 0xeeeeeeee
+ /* */
+ dc.l 0xeeeeeeee
+ dc.l 0xeeeeeeee
+ dc.l 0xeeeeeeee
+ dc.l 0xeeeeeee0
+ dc.l 0xeeeeee00
+ dc.l 0xeeeeee00
+ dc.l 0xeeee0000
+ dc.l 0xeee00000
+
+
+sprite_data:
+ /* Y size link attr X */
+ dc.w 10+128; dc.b 0x05; dc.b 1; dc.w 0x6002; dc.w 0
+ dc.w 30+128; dc.b 0x05; dc.b 2; dc.w 0x6006; dc.w 0
+ dc.w 60+128; dc.b 0x05; dc.b 3; dc.w 0xe002; dc.w 0
+ dc.w 80+128; dc.b 0x05; dc.b 4; dc.w 0xe006; dc.w 0
+ dc.w 120+128; dc.b 0x05; dc.b 5; dc.w 0x6002; dc.w 0
+ dc.w 140+128; dc.b 0x05; dc.b 6; dc.w 0x6006; dc.w 0
+ dc.w 170+128; dc.b 0x05; dc.b 7; dc.w 0xe002; dc.w 0
+ dc.w 190+128; dc.b 0x05; dc.b 0; dc.w 0xe006; dc.w 0
+sprite_data_end:
+
+
+##################################################
+# #
+# MAIN PROGRAM #
+# #
+##################################################
+
+main:
+ /* Initialize VDP */
+ jsr init_gfx
+
+ /* Load color data */
+ movea.l #0,a3
+ move.l #colors,a4
+ moveq.l #6,d4
+ jsr load_colors
+
+ /* load patterns */
+ movea.l #0,a3
+ movea.l #pattern,a4
+ move.l #10,d4
+ jsr load_tiles
+
+ /* generate A layer map */
+ movea.l #0xe000+10*2,a6
+ move.l #28-1,d4
+lmaploop0:
+ movea.l a6,a3
+ jsr load_prepare
+
+ moveq.l #6-1,d3
+0: move.l #0x00010001,(a3)
+ dbra d3,0b
+
+ moveq.l #9-1,d3
+0: move.l #0x80018001,(a3)
+ dbra d3,0b
+
+ add.l #64*2,a6
+ dbra d4,lmaploop0
+
+ /* generate B layer map */
+ movea.l #0xc000+64*14*2,a3
+ jsr load_prepare
+
+ move.l #64*14/2-1,d3
+0: move.l #0x80008000,(a3)
+ dbra d3,0b
+
+ /* upload sprite data */
+ movea.l #0xfc00,a3
+ jsr load_prepare
+ movea.l #sprite_data,a0
+
+ move.l #(sprite_data_end-sprite_data)/2-1,d3
+0: move.l (a0)+,(a3)
+ dbra d3,0b
+
+ jsr wait_vsync
+
+##################################################
+# #
+# MAIN LOOP #
+# #
+##################################################
+
+forever:
+ movea.l #vtimer,a0
+ move.l (a0),d4
+ and.w #0x1ff,d4
+ movea.l #0xfc06,a6
+ moveq.l #8-1,d5
+
+0:
+ movea.l a6,a3
+ jsr load_prepare
+ move.w d4,(a3)
+ addq.w #8,a6
+ dbra d5,0b
+
+ jsr wait_vsync
+ bra forever
+
+
+
+#################################################
+# #
+# Initialize VDP registers #
+# #
+#################################################
+
+init_gfx:
+ move.l #GFXCNTL,a3
+ write_vdp_reg 0,(VDP0_E_DISPLAY + VDP0_PLTT_FULL)
+ write_vdp_reg 1,(VDP1_E_VBI + VDP1_E_DISPLAY + VDP1_E_DMA + VDP1_RESERVED)
+ write_vdp_reg 2,(0xe000 >> 10) /* Screen map a adress */
+ write_vdp_reg 3,(0xe000 >> 10) /* Window address */
+ write_vdp_reg 4,(0xc000 >> 13) /* Screen map b address */
+ write_vdp_reg 5,(0xfc00 >> 9) /* Sprite address */
+ write_vdp_reg 6,0
+ write_vdp_reg 7,1 /* Border color */
+ write_vdp_reg 10,1 /* Lines per hblank interrupt */
+ write_vdp_reg 11,0 /* 2-cell vertical scrolling */
+ write_vdp_reg 12,(VDP12_SCREEN_V224 | VDP12_SCREEN_H320 | VDP12_SPR_SHADOWS)
+ write_vdp_reg 13,(0x6000 >> 10) /* Horizontal scroll address */
+ write_vdp_reg 15,2
+ write_vdp_reg 16,(VDP16_MAP_V32 + VDP16_MAP_H64)
+ write_vdp_reg 17,0
+ write_vdp_reg 18,0xff
+ rts
+
+
+
+#################################################
+# #
+# Load tile data from ROM #
+# #
+# Parameters: #
+# a3: VRAM base #
+# a4: pattern address #
+# d4: number of tiles to load #
+# Destroys a2,d0,d6-d7... #
+# #
+#################################################
+
+load_tiles:
+ move.l #GFXCNTL,a2
+ VRAM_ADDR_var d0,a3
+ move.l d0,(a2)
+ lsl #3,d4
+
+ move.l #GFXDATA,a3
+ subq.l #1,d4
+_copy_tile_data:
+ move.l (a4)+,(a3)
+ dbra d4,_copy_tile_data
+
+ rts
+
+
+load_prepare:
+ move.l #GFXCNTL,a2
+ VRAM_ADDR_var d0,a3
+ move.l d0,(a2)
+ move.l #GFXDATA,a3
+
+ rts
+
+
+#################################################
+# #
+# Clear one of the screen maps #
+# #
+# Parameters: #
+# a0: Map address #
+# d0: Data to write to each map entry #
+# #
+#################################################
+
+clear_map:
+ move.l #GFXCNTL,a4
+ VRAM_ADDR_var d1,a0
+ move.l d1,(a4)
+ move.l #GFXDATA,a3
+ move.w #1023,d1 /* Loop counter */
+_clear_map_loop:
+ move.w d0,(a3)
+ move.w d0,(a3)
+ dbra d1,_clear_map_loop
+ rts
+
+
+#################################################
+# #
+# Load color data from ROM #
+# #
+# Parameters: #
+# a3: CRAM base #
+# a4: color list address #
+# d4: number of colors to load #
+# #
+#################################################
+
+load_colors:
+ move.l #GFXCNTL,a2
+ CRAM_ADDR_var d0,a3
+ move.l d0,(a2)
+
+ move.l #GFXDATA,a3
+ subq.w #1,d4
+_copy_color_data:
+ move.w (a4)+,(a3)
+ dbra d4,_copy_color_data
+
+ rts
+
+
+#################################################
+# #
+# Wait for next VBlank interrupt #
+# #
+#################################################
+
+wait_vsync:
+ movea.l #vtimer,a0
+ move.l (a0),a1
+_wait_change:
+ stop #0x2000
+ cmp.l (a0),a1
+ beq _wait_change
+ rts
+
+
+#################################################
+# #
+# RAM DATA #
+# #
+#################################################
+
+.bss
+.globl htimer
+.globl vtimer
+.globl rand_num
+htimer: .long 0
+vtimer: .long 0
+rand_num: .long 0
+scrollx: .long 0
+
+.end
+
+# vim:filetype=asmM68k
--- /dev/null
+*.o
+*.elf
+*.hex
+.*.swp
+cscope.out
+tags