diff --git a/HISTORY.txt b/HISTORY.txt index ca2cba6..3d25f19 100644 --- a/HISTORY.txt +++ b/HISTORY.txt @@ -1,3 +1,50 @@ +--------------------------------------------------------------------------------------------------------- +Genesis Plus GX 1.5.0 (31/03/2011) (Eke-Eke) +--------------------------------------------------------------------------------------------------------- + +[Core/VDP] +--------------- +* added support for Master System compatibility mode (Z80 ports access mode), incl. Mode 5 rendering. +* added Mode 4 rendering for both Genesis & Master System modes. +* added alternate BG planes rendering functions (should be faster on PPC architectures). + +[Core/IO] +--------------- +* added support for Master System compatibility mode (Z80 ports access mode). +* added Master System peripherals emulation (Control Pad, Paddle, Sports Pad & Light Phaser). +* added XE-1AP (analog controller) emulation. +* added Activator emulation. + +[Core/Extra] +--------------- +* added support for all known Master System cartridge mappers. +* added copy-protection hardware emulation for a few MD unlicensed games: fixes 777 Casino (crash when talking to bunny girls). +(NB: most of those unlicensed games seem to have been already patched by ROM dumpers, main purpose is documenting them) +* added support for Top Shooter arcade board controller. (A=Shoot, B=Bet, C/RIGHT=Coins, START=Start, hold UP on startup to enter service mode) +* improved King of Fighters 98 mapper emulation (registers address decoding is now 100% accurate) +* fixed Game Genie when several codes affect same ROM address. +* fixed EEPROM types for Brian Lara Cricket & NBA Jam TE (verified on real cartridges) + +[Core/General] +--------------- +* added Master System compatibility mode emulation (automatically enabled when loading ROM file with .sms extension). +* improved savestate stability & compatibility (support for old 1.4.x savestates is preserved) +* various code cleanup & comments. + +[Gamecube/Wii] +--------------- +* fixed cheat codes handling when several codes affect same ROM address. +* improved input controller detection on menu exit. +* improved key remapping dialog box to match emulated device +* changed Menu key for Gamecube controller to allow MODE button mapping +* fixed DVD not being unmounted on swap (memory leak) + +[Wii only] +--------------- +* added USB mouse support for Sega Mouse emulation +* compiled with latest libogc: improves USB compatibility & fixes stability issues with Wiimotes. + + --------------------------------------------------------------------------------------------------------- Genesis Plus GX 1.4.1 (04/12/2010) (Eke-Eke) --------------------------------------------------------------------------------------------------------- diff --git a/Makefile.gc b/Makefile.gc index c57ea4b..6978400 100644 --- a/Makefile.gc +++ b/Makefile.gc @@ -17,9 +17,9 @@ include $(DEVKITPPC)/gamecube_rules #--------------------------------------------------------------------------------- TARGET := genplus_cube BUILD := build_cube -SOURCES := source source/m68k source/z80 source/sound source/ntsc source/cart_hw source/cart_hw/svp \ +SOURCES := source source/m68k source/z80 source/sound source/ntsc source/input_hw source/cart_hw source/cart_hw/svp \ source/gx source/gx/utils source/gx/gui source/gx/fileio source/gx/images source/gx/sounds -INCLUDES := source source/m68k source/z80 source/sound source/ntsc source/cart_hw source/cart_hw/svp \ +INCLUDES := source source/m68k source/z80 source/sound source/ntsc source/input_hw source/cart_hw source/cart_hw/svp \ source/gx source/gx/utils source/gx/gui source/gx/fileio source/gx/images source/gx/sounds \ $(BUILD) @@ -27,7 +27,7 @@ INCLUDES := source source/m68k source/z80 source/sound source/ntsc source/cart_h # options for code generation #--------------------------------------------------------------------------------- -CFLAGS = -O3 -fomit-frame-pointer -Wall -Wno-strict-aliasing $(MACHDEP) $(INCLUDE) -DWORDS_BIGENDIAN -DNGC -DHW_DOL +CFLAGS = -O3 -fomit-frame-pointer -Wall -Wno-strict-aliasing $(MACHDEP) $(INCLUDE) -DALT_RENDERER -DNGC -DHW_DOL CXXFLAGS = $(CFLAGS) LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map diff --git a/Makefile.wii b/Makefile.wii index 9ec01ba..a8bd665 100644 --- a/Makefile.wii +++ b/Makefile.wii @@ -17,9 +17,9 @@ include $(DEVKITPPC)/wii_rules #--------------------------------------------------------------------------------- TARGET := genplus_wii BUILD := build_wii -SOURCES := source source/m68k source/z80 source/sound source/ntsc source/cart_hw source/cart_hw/svp \ +SOURCES := source source/m68k source/z80 source/sound source/ntsc source/input_hw source/cart_hw source/cart_hw/svp \ source/gx source/gx/utils source/gx/gui source/gx/fileio source/gx/images source/gx/sounds -INCLUDES := source source/m68k source/z80 source/sound source/ntsc source/cart_hw source/cart_hw/svp \ +INCLUDES := source source/m68k source/z80 source/sound source/ntsc source/input_hw source/cart_hw source/cart_hw/svp \ source/gx source/gx/utils source/gx/gui source/gx/fileio source/gx/images source/gx/sounds \ $(BUILD) @@ -27,7 +27,7 @@ INCLUDES := source source/m68k source/z80 source/sound source/ntsc source/cart_h # options for code generation #--------------------------------------------------------------------------------- -CFLAGS = -O3 -fomit-frame-pointer -Wall -Wno-strict-aliasing $(MACHDEP) $(INCLUDE) -DWORDS_BIGENDIAN -DNGC -DHW_RVL +CFLAGS = -O3 -fomit-frame-pointer -Wall -Wno-strict-aliasing $(MACHDEP) $(INCLUDE) -DALT_RENDERER -DNGC -DHW_RVL CXXFLAGS = $(CFLAGS) LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map diff --git a/source/cart_hw/eeprom.c b/source/cart_hw/eeprom.c index 58b5bc0..94a3229 100644 --- a/source/cart_hw/eeprom.c +++ b/source/cart_hw/eeprom.c @@ -38,10 +38,10 @@ static const T_GAME_ENTRY database[GAME_CNT] = /* 24C02 (old mapper) */ {{"T-081326" }, 0, {8, 0xFF, 0xFF, 0x200001, 0x200001, 0x200001, 0, 1, 1}}, /* NBA Jam (UE) */ {{"T-81033" }, 0, {8, 0xFF, 0xFF, 0x200001, 0x200001, 0x200001, 0, 1, 1}}, /* NBA Jam (J) */ - /* 24C02 */ - {{"T-81406" }, 0, {8, 0xFF, 0xFF, 0x200001, 0x200001, 0x200000, 0, 0, 0}}, /* NBA Jam TE */ {{"T-081276" }, 0, {8, 0xFF, 0xFF, 0x200001, 0x200001, 0x200000, 0, 0, 0}}, /* NFL Quarterback Club */ + /* 24C04 */ + {{"T-81406" }, 0, {8, 0x1FF, 0x1FF, 0x200001, 0x200001, 0x200000, 0, 0, 0}}, /* NBA Jam TE */ /* 24C16 */ {{"T-081586" }, 0, {8, 0x7FF, 0x7FF, 0x200001, 0x200001, 0x200000, 0, 0, 0}}, /* NFL Quarterback Club '96 */ /* 24C65 */ @@ -68,9 +68,8 @@ static const T_GAME_ENTRY database[GAME_CNT] = {{"G-4524" }, 0, {7, 0x7F, 0x7F, 0x200001, 0x200001, 0x200001, 0, 0, 1}}, /* Ninja Burai Densetsu */ /* CODEMASTERS mapper */ - /* 24C01 */ - {{"T-120106"}, 0, {7, 0x7F, 0x7F, 0x300000, 0x380001, 0x300000, 0, 7, 1}}, /* Brian Lara Cricket */ /* 24C08 */ + {{"T-120106" }, 0, {8, 0x3FF, 0x3FF, 0x300000, 0x380001, 0x300000, 0, 7, 1}}, /* Brian Lara Cricket */ {{"T-120096" }, 0, {8, 0x3FF, 0x3FF, 0x300000, 0x380001, 0x300000, 0, 7, 1}}, /* Micro Machines 2 - Turbo Tournament (E) */ {{"00000000-00"}, 0x168B, {8, 0x3FF, 0x3FF, 0x300000, 0x380001, 0x300000, 0, 7, 1}}, /* Micro Machines Military */ {{"00000000-00"}, 0xCEE0, {8, 0x3FF, 0x3FF, 0x300000, 0x380001, 0x300000, 0, 7, 1}}, /* Micro Machines Military (Bad)*/ diff --git a/source/cart_hw/ggenie.c b/source/cart_hw/ggenie.c index 853a9fe..55a175f 100644 --- a/source/cart_hw/ggenie.c +++ b/source/cart_hw/ggenie.c @@ -118,7 +118,7 @@ void ggenie_reset(int hard) void ggenie_switch(int enable) { - int i,j; + int i; if (enable) { /* enable cheats */ diff --git a/source/cart_hw/md_cart.c b/source/cart_hw/md_cart.c new file mode 100644 index 0000000..6691c13 --- /dev/null +++ b/source/cart_hw/md_cart.c @@ -0,0 +1,1200 @@ + +/**************************************************************************** + * Genesis Plus + * Mega Drive cartridge hardware support + * + * Copyright (C) 2007-2011 Eke-Eke (GCN/Wii port) + * + * Most cartridge protections documented by Haze + * (http://haze.mameworld.info/) + * + * Realtec mapper documented by TascoDeluxe + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +#include "shared.h" +#include "gamepad.h" + +#define CART_CNT (44) + +/* Cart database entry */ +typedef struct +{ + uint16 chk_1; /* header checksum */ + uint16 chk_2; /* real checksum */ + uint8 bank_start; /* first mapped bank in $400000-$7fffff region */ + uint8 bank_end; /* last mapped bank in $400000-$7fffff region */ + T_CART_HW cart_hw; /* hardware description */ +} T_CART_ENTRY; + +extern int emulate_address_error; + +/* Cartridge Hardware structure */ +T_CART cart; + +/* Function prototypes */ +static void mapper_sega_w(uint32 data); +static void mapper_ssf2_w(uint32 address, uint32 data); +static void mapper_realtec_w(uint32 address, uint32 data); +static void mapper_seganet_w(uint32 address, uint32 data); +static void mapper_32k_w(uint32 data); +static void mapper_64k_w(uint32 data); +static void mapper_64k_multi_w(uint32 address); +static uint32 mapper_radica_r(uint32 address); +static void default_time_w(uint32 address, uint32 data); +static void default_regs_w(uint32 address, uint32 data); +static uint32 default_regs_r(uint32 address); +static uint32 default_regs_r_16(uint32 address); +static void custom_regs_w(uint32 address, uint32 data); +static void custom_alt_regs_w(uint32 address, uint32 data); +static uint32 topshooter_read(uint32 address); +static void topshooter_write(uint32 address, uint32 data); + +/* Games that need extra hardware emulation: + - copy protection device + - custom ROM banking device +*/ +static const T_CART_ENTRY rom_database[CART_CNT] = +{ +/* Funny World & Balloon Boy */ + {0x0000,0x06ab,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},1,0,NULL,NULL,NULL,mapper_realtec_w}}, +/* Whac-a-Critter */ + {0xffff,0xf863,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},1,0,NULL,NULL,NULL,mapper_realtec_w}}, +/* Earth Defense */ + {0xffff,0x44fb,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},1,0,NULL,NULL,NULL,mapper_realtec_w}}, +/* RADICA (Volume 1) */ + {0x0000,0x2326,0x00,0x00,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},0,0,mapper_radica_r,NULL,NULL,NULL}}, +/* RADICA (Volume 2) */ + {0x4f10,0x0836,0x00,0x00,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},0,0,mapper_radica_r,NULL,NULL,NULL}}, +/* RADICA (Volume 1) (byteswapped version) */ + {0xf424,0x9f82,0x00,0x00,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},0,0,mapper_radica_r,NULL,NULL,NULL}}, +/* Chinese Fighters III */ + {0x9490,0x8180,0x40,0x6f,{{0x00,0x00,0x00,0x00},{0xf0000c,0xf0000c,0xf0000c,0xf0000c},{0x400000,0x400004,0x400008,0x40000c},0,1,NULL,NULL,default_regs_r,custom_alt_regs_w}}, +/* Top Fighter */ + {0x4eb9,0x5d8b,0x60,0x7f,{{0x00,0x00,0x00,0x00},{0xf00007,0xf00007,0xf00007,0xffffff},{0x600001,0x600003,0x600005,0x000000},0,1,NULL,NULL,default_regs_r,custom_regs_w}}, +/* Mulan */ + {0x0404,0x1b40,0x60,0x7f,{{0x00,0x00,0x00,0x00},{0xf00007,0xf00007,0xf00007,0xffffff},{0x600001,0x600003,0x600005,0x000000},0,1,NULL,NULL,default_regs_r,custom_regs_w}}, +/* Pocket Monsters II */ + {0x47f9,0x17e5,0x60,0x7f,{{0x00,0x00,0x00,0x00},{0xf00007,0xf00007,0xf00007,0xffffff},{0x600001,0x600003,0x600005,0x000000},0,1,NULL,NULL,default_regs_r,custom_regs_w}}, +/* Lion King 3 */ + {0x0000,0x507c,0x60,0x7f,{{0x00,0x00,0x00,0x00},{0xf00007,0xf00007,0xf00007,0xffffff},{0x600001,0x600003,0x600005,0x000000},0,1,NULL,NULL,default_regs_r,custom_regs_w}}, +/* Super King Kong 99 */ + {0x0000,0x7d6e,0x60,0x7f,{{0x00,0x00,0x00,0x00},{0xf00007,0xf00007,0xf00007,0xffffff},{0x600001,0x600003,0x600005,0x000000},0,1,NULL,NULL,default_regs_r,custom_regs_w}}, +/* Pokemon Stadium */ + {0x0000,0x843c,0x70,0x7f,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},0,1,NULL,NULL,NULL,custom_regs_w}}, +/* Lion King 2 */ + {0xffff,0x1d9b,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xfffffd,0xfffffd,0xffffff,0xffffff},{0x400000,0x400004,0x000000,0x000000},0,0,NULL,NULL,default_regs_r,default_regs_w}}, +/* Squirell King */ + {0x0000,0x8ec8,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xfffffd,0xfffffd,0xffffff,0xffffff},{0x400000,0x400004,0x000000,0x000000},0,0,NULL,NULL,default_regs_r,default_regs_w}}, +/* Lian Huan Pao - Barver Battle Saga (registers accessed by Z80, related to sound engine ?) */ + {0x30b9,0x1c2a,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xfffffd,0xfffffd,0xffffff,0xffffff},{0x400000,0x400004,0x000000,0x000000},0,0,NULL,NULL,default_regs_r,default_regs_w}}, +/* Shui Hu Zhuan (registers accessed by Z80, related to sound engine ?) */ + {0x6001,0x0211,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xfffffd,0xfffffd,0xffffff,0xffffff},{0x400000,0x400004,0x000000,0x000000},0,0,NULL,NULL,default_regs_r,default_regs_w}}, +/* Feng Shen Ying Jie Chuan (registers accessed by Z80, related to sound engine ?) */ + {0xffff,0x5d98,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xfffffd,0xfffffd,0xffffff,0xffffff},{0x400000,0x400004,0x000000,0x000000},0,0,NULL,NULL,default_regs_r,default_regs_w}}, +/* (*) Shui Hu - Feng Yun Zhuan (patched ROM, unused registers) */ + {0x3332,0x872b,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xfffffd,0xfffffd,0xffffff,0xffffff},{0x400000,0x400004,0x000000,0x000000},0,0,NULL,NULL,default_regs_r,default_regs_w}}, +/* (*) Chao Ji Da Fu Weng (patched ROM, various words witten to register, long word also read from $7E0000, unknown banking hardware ?) */ + {0xa697,0xa697,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x000000,0x000000,0x000000},0,0,NULL,NULL,NULL,default_regs_w}}, +/* (*) Aq Renkan Awa (patched ROM, ON/OFF bit sequence is written to register, unknown banking hardware ?) */ + {0x8104,0x0517,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400001,0x000000,0x000000,0x000000},0,0,NULL,NULL,NULL,default_regs_w}}, +/* (*) Jiu Ji Ma Jiang II - Ye Yan Bian (patched ROM, using expected register value - $0f - crashes the game) (uses 16-bits reads) */ + {0x0c44,0xba81,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x400006},0,0,NULL,NULL,default_regs_r_16,NULL}}, +/* (*) Tun Shi Tian Di III (patched ROM, unused register) */ + {0x0000,0x9c5e,0x40,0x40,{{0xab,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400046,0x000000,0x000000,0x000000},0,0,NULL,NULL,default_regs_r,NULL}}, +/* Ma Jiang Qing Ren - Ji Ma Jiang Zhi */ + {0x0000,0x7037,0x40,0x40,{{0x90,0xd3,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x401000,0x000000,0x000000},0,0,NULL,NULL,default_regs_r,NULL}}, +/* 16 Zhang Ma Jiang (uses 16-bits reads) */ + {0xfb40,0x4bed,0x40,0x40,{{0x00,0xaa,0x00,0xf0},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x400002,0x000000,0x400006},0,0,NULL,NULL,default_regs_r_16,NULL}}, +/* King of Fighter 98 */ + {0x0000,0xd0a0,0x48,0x4f,{{0x00,0x00,0xaa,0xf0},{0xffffff,0xffffff,0xfc0000,0xfc0000},{0x000000,0x000000,0x480000,0x4c0000},0,0,NULL,NULL,default_regs_r,NULL}}, +/* Supper Bubble Bobble */ + {0x0000,0x16cd,0x40,0x40,{{0x55,0x0f,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x400002,0x000000,0x000000},0,0,NULL,NULL,default_regs_r,NULL}}, +/* Huan Le Tao Qi Shu - Smart Mouse */ + {0x0000,0x1a28,0x40,0x40,{{0x55,0x0f,0xaa,0xf0},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x400002,0x400004,0x400006},0,0,NULL,NULL,default_regs_r,NULL}}, +/* (*) Hei Tao 2 - Super Big 2 (patched ROM, unused registers) */ + {0x0000,0x5843,0x40,0x40,{{0x55,0x0f,0xaa,0xf0},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x400002,0x400004,0x400006},0,0,NULL,NULL,default_regs_r,NULL}}, +/* Elf Wor */ + {0x0080,0x3dba,0x40,0x40,{{0x55,0x0f,0xc9,0x18},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x400002,0x400004,0x400006},0,0,NULL,NULL,default_regs_r,NULL}}, +/* Ya-Se Chuanshuo */ + {0xffff,0xd472,0x40,0x40,{{0x63,0x98,0xc9,0x18},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x400002,0x400004,0x400006},0,0,NULL,NULL,default_regs_r,NULL}}, +/* 777 Casino (For first one, 0x55 works as well. Other values are never used so they are guessed from on other unlicensed games using similar mapper) */ + {0x0000,0xf8d9,0x40,0x40,{{0x63,0x98,0xc9,0x18},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x400002,0x400004,0x400006},0,0,NULL,NULL,default_regs_r,NULL}}, +/* Soul Blade */ + {0x0000,0x0c5b,0x40,0x40,{{0x63,0x98,0xc9,0xf0},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x400002,0x400004,0x400006},0,0,NULL,NULL,default_regs_r,NULL}}, +/* Rockman X3 (half-patched ROM, two last register values are not used, 0xaa/0x18 works too) */ + {0x0000,0x9d0e,0x40,0x40,{{0x0c,0x00,0xc9,0xf0},{0xffffff,0xffffff,0xffffff,0xffffff},{0xa13000,0x000000,0x400004,0x400006},0,0,default_regs_r,NULL,default_regs_r,NULL}}, +/* (*) Tekken 3 Special (patched ROM, register value not used, unknown writes to $400000-$40000E, read from $400002) */ + {0x0000,0x8c6e,0x00,0x00,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0xa13000,0x000000,0x000000,0x000000},0,0,default_regs_r,NULL,NULL,NULL}}, +/* (*) Dragon Ball Final Bout (patched ROM, in original code, different switches occurs depending on returned value $00-$0f) */ + {0xc65a,0xc65a,0x00,0x00,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0xa13000,0x000000,0x000000,0x000000},0,0,default_regs_r,NULL,NULL,NULL}}, +/* (*) Yang Jia Jiang - Yang Warrior Family (patched ROM, register value unused) */ + {0x0000,0x96b0,0x00,0x00,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0xa13000,0x000000,0x000000,0x000000},0,0,default_regs_r,NULL,NULL,NULL}}, +/* Super Mario 2 1998 */ + {0xffff,0x0474,0x00,0x00,{{0x0a,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0xa13000,0x000000,0x000000,0x000000},0,0,default_regs_r,NULL,NULL,NULL}}, +/* Super Mario World */ + {0x2020,0xb4eb,0x00,0x00,{{0x1c,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0xa13000,0x000000,0x000000,0x000000},0,0,default_regs_r,NULL,NULL,NULL}}, +/* A Bug's Life */ + {0x7f7f,0x2aad,0x00,0x00,{{0x28,0x1f,0x01,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0xa13000,0xa13002,0xa1303e,0x000000},0,0,default_regs_r,NULL,NULL,NULL}}, +/* King of Fighter 99 */ + {0x0000,0x021e,0x00,0x00,{{0x00,0x01,0x1f,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0xa13000,0xa13002,0xa1303e,0x000000},0,0,default_regs_r,NULL,NULL,NULL}}, +/* Pocket Monster */ + {0xd6fc,0x1eb1,0x00,0x00,{{0x00,0x01,0x1f,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0xa13000,0xa13002,0xa1303e,0x000000},0,0,default_regs_r,NULL,NULL,NULL}}, +/* Game no Kanzume Otokuyou */ + {0x0000,0xf9d1,0x00,0x00,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},0,0,NULL,mapper_seganet_w,NULL,NULL}}, +/* Top Shooter (arcade hardware) */ + {0xffff,0x3632,0x20,0x20,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},0,0,NULL,NULL,topshooter_read,topshooter_write}} +}; + + +/************************************************************ + Cart Hardware initialization +*************************************************************/ + +/* cart hardware detection */ +void md_cart_init(void) +{ + int i; + + /*************************************************************************************************************** + CARTRIDGE ROM MIRRORING + *************************************************************************************************************** + + Cartridge area is mapped to $000000-$3fffff: + + -> when accessing ROM, 68k address lines A1 to A21 are used by the internal cartridge hardware to decode the + full 4MB address range. + -> depending on the ROM total size, some address lines might be ignored, resulting in ROM mirroring. + + + Cartridges can use either 8-bits (x2) or 16-bits (x1, x2) Mask ROM chips, each chip size is a factor of 2 bytes: + + -> two 8-bits chips are equivalent to one 16-bits chip, no specific address decoding is required, needed + address lines are simply connected to each chip, upper address lines are ignored and data lines are + connected appropriately to each chip (D0-D7 to one chip, D8-D15 to the other one). + ROM is mirrored each N bytes where N=2^(k+1) is the total ROM size (ROM1+ROM2,ROM1+ROM2,...). + + -> one single 16-bits chip do not need specific address decoding, address lines are simply connected + depending on the ROM size, upper address lines being ignored. + ROM is mirrored each N bytes where N=2^k is the size of the ROM chip (ROM1,ROM1,ROM1,...). + + -> two 16-bits chips of the same size are equivalent to one chip of double size, address decoding generally + is the same except that specific hardware is used (one address line is generally used for chip selection, + lower ones being used to address the chips and upper ones being ignored). + ROM is mirrored continuously each N bytes where N=2^(k+1) is the total ROM size (ROM1,ROM2,ROM1,ROM2,...). + + -> two 16-bits chips with different size are mapped differently. Address decoding is done the same way as + above (one address line used for chip selection) but the ignored & required address lines differ from + one chip to another, which makes ROM mirroring different. + ROM2 size is generally half of ROM1 size and ROM are mirrored like that : ROM1,ROM2,ROM2,ROM1,ROM2,ROM2,... + + From the emulator point of view, we only need to distinguish 3 cases: + + 1/ total ROM size is a factor of 2: ROM is mirrored each 2^k bytes. + + 2/ total ROM size is not a factor of 2 and cartridge uses one or two chips of the same size (Type A): + ROM is padded up to 2^k and mirrored each 2^k bytes. + + 3/ total ROM size is not a factor of 2 and cartridge uses two chips of different sizes (Type B): + ROM is not padded and the first 2^(k-1) bytes are mirrored each 2^k bytes while the next 2^(k-2) bytes are + mirrored in the last 2^(k-2) bytes. + + ******************************************************************************************************************/ + + /* calculate nearest size with factor of 2 */ + unsigned int size = 0x10000; + while (cart.romsize > size) + size <<= 1; + + /* total ROM size is not a factor of 2 */ + /* TODO: handle more possible ROM configurations (using cartridge database ???) */ + if ((size < MAXROMSIZE) && (cart.romsize < size)) + { + /* two chips with different size */ + if (config.romtype) + { + /* third ROM section is mirrored in the last section */ + memcpy(cart.rom + cart.romsize, cart.rom + 2*cart.romsize - size, size - cart.romsize); + } + else + { + /* ROM is padded up to 2^k bytes */ + memset(cart.rom + cart.romsize, 0xff, size - cart.romsize); + } + } + + /* special case: Sonic & Knuckles */ + /* $200000-$3fffff is mapped to external cartridge */ + if (strstr(rominfo.international,"SONIC & KNUCKLES") != NULL) + { + /* disable ROM mirroring */ + size = 0x400000; + } + + /* ROM is mirrored each 2^k bytes */ + cart.mask = size - 1; + + /********************************************** + DEFAULT CARTRIDGE MAPPING + ***********************************************/ + for (i=0; i<0x40; i++) + { + /* cartridge ROM */ + m68k_memory_map[i].base = cart.rom + ((i<<16) & cart.mask); + m68k_memory_map[i].read8 = NULL; + m68k_memory_map[i].read16 = NULL; + m68k_memory_map[i].write8 = m68k_unused_8_w; + m68k_memory_map[i].write16 = m68k_unused_16_w; + zbank_memory_map[i].read = NULL; + zbank_memory_map[i].write = zbank_unused_w; + } + + for (i=0x40; i<0x80; i++) + { + /* unused area */ + m68k_memory_map[i].base = cart.rom + (i<<16); + m68k_memory_map[i].read8 = m68k_read_bus_8; + m68k_memory_map[i].read16 = m68k_read_bus_16; + m68k_memory_map[i].write8 = m68k_unused_8_w; + m68k_memory_map[i].write16 = m68k_unused_16_w; + zbank_memory_map[i].read = zbank_unused_r; + zbank_memory_map[i].write = zbank_unused_w; + } + + /********************************************** + BACKUP MEMORY + ***********************************************/ + sram_init(); + eeprom_init(); + if (sram.on) + { + if (sram.custom) + { + /* Serial EEPROM */ + m68k_memory_map[eeprom.type.sda_out_adr >> 16].read8 = eeprom_read_byte; + m68k_memory_map[eeprom.type.sda_out_adr >> 16].read16 = eeprom_read_word; + m68k_memory_map[eeprom.type.sda_in_adr >> 16].read8 = eeprom_read_byte; + m68k_memory_map[eeprom.type.sda_in_adr >> 16].read16 = eeprom_read_word; + m68k_memory_map[eeprom.type.scl_adr >> 16].write8 = eeprom_write_byte; + m68k_memory_map[eeprom.type.scl_adr >> 16].write16 = eeprom_write_word; + zbank_memory_map[eeprom.type.sda_out_adr >> 16].read = eeprom_read_byte; + zbank_memory_map[eeprom.type.sda_in_adr >> 16].read = eeprom_read_byte; + zbank_memory_map[eeprom.type.scl_adr >> 16].write = eeprom_write_byte; + } + else + { + /* Static RAM (64k max.) */ + m68k_memory_map[sram.start >> 16].base = sram.sram; + m68k_memory_map[sram.start >> 16].read8 = NULL; + m68k_memory_map[sram.start >> 16].read16 = NULL; + m68k_memory_map[sram.start >> 16].write8 = NULL; + m68k_memory_map[sram.start >> 16].write16 = NULL; + zbank_memory_map[sram.start >> 16].read = NULL; + zbank_memory_map[sram.start >> 16].write = NULL; + } + } + + /********************************************** + SVP CHIP + ***********************************************/ + svp = NULL; + if (strstr(rominfo.international,"Virtua Racing") != NULL) + { + svp_init(); + + m68k_memory_map[0x30].base = svp->dram; + m68k_memory_map[0x30].read16 = NULL; + m68k_memory_map[0x30].write16 = svp_write_dram; + + m68k_memory_map[0x31].base = svp->dram + 0x10000; + m68k_memory_map[0x31].read16 = NULL; + m68k_memory_map[0x31].write16 = svp_write_dram; + + m68k_memory_map[0x39].read16 = svp_read_cell_1; + m68k_memory_map[0x3a].read16 = svp_read_cell_2; + } + + /********************************************** + SPECIFIC PERIPHERAL SUPPORT + ***********************************************/ + + /* restore previous input settings */ + if (old_system[0] != -1) + { + input.system[0] = old_system[0]; + } + if (old_system[1] != -1) + { + input.system[1] = old_system[1]; + } + + /* default GUN settings */ + input.x_offset = 0x00; + input.y_offset = 0x00; + + /* SEGA Menacer */ + if (strstr(rominfo.international,"MENACER") != NULL) + { + /* save current setting */ + if (old_system[0] == -1) + { + old_system[0] = input.system[0]; + } + if (old_system[1] == -1) + { + old_system[1] = input.system[1]; + } + + input.system[0] = SYSTEM_MD_GAMEPAD; + input.system[1] = SYSTEM_MENACER; + input.x_offset = 0x52; + input.y_offset = 0x00; + } + else if (strstr(rominfo.international,"T2 ; THE ARCADE GAME") != NULL) + { + /* save current setting */ + if (old_system[0] == -1) + { + old_system[0] = input.system[0]; + } + if (old_system[1] == -1) + { + old_system[1] = input.system[1]; + } + + input.system[0] = SYSTEM_MD_GAMEPAD; + input.system[1] = SYSTEM_MENACER; + input.x_offset = 0x84; + input.y_offset = 0x08; + } + else if (strstr(rominfo.international,"BODY COUNT") != NULL) + { + /* save current setting */ + if (old_system[0] == -1) + { + old_system[0] = input.system[0]; + } + if (old_system[1] == -1) + { + old_system[1] = input.system[1]; + } + + input.system[0] = SYSTEM_MOUSE; + input.system[1] = SYSTEM_MENACER; + input.x_offset = 0x44; + input.y_offset = 0x18; + } + + /* KONAMI Justifiers */ + else if (strstr(rominfo.international,"LETHAL ENFORCERSII") != NULL) + { + /* save current setting */ + if (old_system[0] == -1) + { + old_system[0] = input.system[0]; + } + if (old_system[1] == -1) + { + old_system[1] = input.system[1]; + } + + input.system[0] = SYSTEM_MD_GAMEPAD; + input.system[1] = SYSTEM_JUSTIFIER; + input.x_offset = 0x18; + input.y_offset = 0x00; + } + else if (strstr(rominfo.international,"LETHAL ENFORCERS") != NULL) + { + /* save current setting */ + if (old_system[0] == -1) + { + old_system[0] = input.system[0]; + } + if (old_system[1] == -1) + { + old_system[1] = input.system[1]; + } + + input.system[0] = SYSTEM_MD_GAMEPAD; + input.system[1] = SYSTEM_JUSTIFIER; + input.x_offset = 0x00; + input.y_offset = 0x00; + } + + cart.special = 0; + + /********************************************** + J-CART + ***********************************************/ + if (((strstr(rominfo.product,"00000000") != NULL) && (rominfo.checksum == 0x168b)) || /* Super Skidmarks, Micro Machines Military*/ + ((strstr(rominfo.product,"00000000") != NULL) && (rominfo.checksum == 0x165e)) || /* Pete Sampras Tennis (1991), Micro Machines 96 */ + ((strstr(rominfo.product,"00000000") != NULL) && (rominfo.checksum == 0xcee0)) || /* Micro Machines Military (bad) */ + ((strstr(rominfo.product,"00000000") != NULL) && (rominfo.checksum == 0x2c41)) || /* Micro Machines 96 (bad) */ + ((strstr(rominfo.product,"XXXXXXXX") != NULL) && (rominfo.checksum == 0xdf39)) || /* Sampras Tennis 96 */ + ((strstr(rominfo.product,"T-123456") != NULL) && (rominfo.checksum == 0x1eae)) || /* Sampras Tennis 96 */ + ((strstr(rominfo.product,"T-120066") != NULL) && (rominfo.checksum == 0x16a4)) || /* Pete Sampras Tennis (1994)*/ + (strstr(rominfo.product,"T-120096") != NULL)) /* Micro Machines 2 */ + { + if (cart.romsize <= 0x380000) /* just to be sure (checksum might not be enough) */ + { + cart.special |= HW_J_CART; + + /* set default port 1 setting */ + if (input.system[1] != SYSTEM_WAYPLAY) + { + old_system[1] = input.system[1]; + input.system[1] = SYSTEM_MD_GAMEPAD; + } + + /* extra connectors mapped at $38xxxx or $3Fxxxx */ + m68k_memory_map[0x38].read16 = jcart_read; + m68k_memory_map[0x38].write16 = jcart_write; + m68k_memory_map[0x3f].read16 = jcart_read; + m68k_memory_map[0x3f].write16 = jcart_write; + } + } + + /********************************************** + LOCK-ON + ***********************************************/ + + /* clear existing patches */ + ggenie_shutdown(); + areplay_shutdown(); + + /* initialize extra hardware */ + switch (config.lock_on) + { + case TYPE_GG: + { + ggenie_init(); + break; + } + + case TYPE_AR: + { + areplay_init(); + break; + } + + case TYPE_SK: + { + /* store S&K ROM above cartridge ROM + SRAM */ + if (cart.romsize > 0x600000) break; + + /* load Sonic & Knuckles ROM (2 MBytes) */ + FILE *f = fopen(SK_ROM,"r+b"); + if (!f) break; + int done = 0; + while (done < 0x200000) + { + fread(cart.rom + 0x600000 + done, 2048, 1, f); + done += 2048; + } + fclose(f); + + /* load Sonic 2 UPMEM ROM (256 KBytes) */ + f = fopen(SK_UPMEM,"r+b"); + if (!f) break; + done = 0; + while (done < 0x40000) + { + fread(cart.rom + 0x800000 + done, 2048, 1, f); + done += 2048; + } + fclose(f); + +#ifdef LSB_FIRST + /* Byteswap ROM */ + int i; + uint8 temp; + for(i = 0; i < 0x240000; i += 2) + { + temp = cart.rom[i + 0x600000]; + cart.rom[i + 0x600000] = cart.rom[i + 0x600000 + 1]; + cart.rom[i + 0x600000 + 1] = temp; + } +#endif + + /*$000000-$1FFFFF is mapped to S&K ROM */ + for (i=0x00; i<0x20; i++) + { + m68k_memory_map[i].base = (cart.rom + 0x600000) + (i<<16); + } + + cart.special |= HW_LOCK_ON; + break; + } + + default: + { + break; + } + } + + /********************************************** + Cartridge Extra Hardware + ***********************************************/ + memset(&cart.hw, 0, sizeof(T_CART_HW)); + + /* search for game into database */ + for (i=0; i < CART_CNT + 1; i++) + { + /* known cart found ! */ + if ((rominfo.checksum == rom_database[i].chk_1) && + (rominfo.realchecksum == rom_database[i].chk_2)) + { + /* retrieve hardware information */ + memcpy(&cart.hw, &(rom_database[i].cart_hw), sizeof(T_CART_HW)); + + /* initialize memory handlers for $400000-$7FFFFF region */ + int j = rom_database[i].bank_start; + while (j <= rom_database[i].bank_end) + { + if (cart.hw.regs_r) + { + m68k_memory_map[j].read8 = cart.hw.regs_r; + m68k_memory_map[j].read16 = cart.hw.regs_r; + zbank_memory_map[j].read = cart.hw.regs_r; + } + if (cart.hw.regs_w) + { + m68k_memory_map[j].write8 = cart.hw.regs_w; + m68k_memory_map[j].write16 = cart.hw.regs_w; + zbank_memory_map[j].write = cart.hw.regs_w; + } + j++; + } + + /* leave loop */ + i = CART_CNT + 1; + } + } + + /* Realtec mapper */ + if (cart.hw.realtec) + { + /* 8k BOOT ROM */ + for (i=0; i<8; i++) + { + memcpy(cart.rom + 0x900000 + i*0x2000, cart.rom + 0x7e000, 0x2000); + } + + /* BOOT ROM is mapped to $000000-$3FFFFF */ + for (i=0x00; i<0x40; i++) + { + m68k_memory_map[i].base = cart.rom + 0x900000; + } + } + +#if M68K_EMULATE_ADDRESS_ERROR + /* default behavior */ + emulate_address_error = config.addr_error; +#endif + + /* detect special cartridges */ + if (cart.romsize > 0x800000) + { + /* Ultimate MK3 (hack) */ + for (i=0x40; i<0xA0; i++) + { + m68k_memory_map[i].base = cart.rom + (i<<16); + m68k_memory_map[i].read8 = NULL; + m68k_memory_map[i].read16 = NULL; + zbank_memory_map[i].read = NULL; + } + +#if M68K_EMULATE_ADDRESS_ERROR + /* this game does not work properly on real hardware */ + emulate_address_error = 0; +#endif + } + else if (cart.romsize > 0x400000) + { + /* assume SSF2 mapper */ + cart.hw.bankshift = 1; + cart.hw.time_w = mapper_ssf2_w; + } + + /* default write handler for !TIME range ($A130xx)*/ + if (!cart.hw.time_w) + { + cart.hw.time_w = default_time_w; + } +} + +/* hardware that need to be reseted on power on */ +void md_cart_reset(int hard_reset) +{ + int i; + + /* reset cartridge mapping */ + if (cart.hw.bankshift) + { + for (i=0x00; i<0x40; i++) + { + m68k_memory_map[i].base = cart.rom + ((i<<16) & cart.mask); + } + } + + /* SVP chip */ + if (svp) svp_reset(); + + /* Lock-ON */ + switch (config.lock_on) + { + case TYPE_GG: + { + ggenie_reset(hard_reset); + break; + } + + case TYPE_AR: + { + areplay_reset(hard_reset); + break; + } + + case TYPE_SK: + { + if (cart.special & HW_LOCK_ON) + { + /* disable UPMEM chip at $300000-$3fffff */ + for (i=0x30; i<0x40; i++) + { + m68k_memory_map[i].base = cart.rom + ((i<<16) & cart.mask); + } + } + break; + } + + default: + { + break; + } + } + + /* save default cartridge slot mapping */ + cart.base = m68k_memory_map[0].base; +} + +int md_cart_context_save(uint8 *state) +{ + int i; + int bufferptr = 0; + uint8 *base; + + /* cartridge mapping */ + for (i=0; i<0x40; i++) + { + /* get base address */ + base = m68k_memory_map[i].base; + + if (base == sram.sram) + { + /* SRAM */ + state[bufferptr++] = 0xff; + } + else + { + /* ROM */ + state[bufferptr++] = ((base - cart.rom) >> 16) & 0xff; + } + } + + /* hardware registers */ + save_param(cart.hw.regs, sizeof(cart.hw.regs)); + + /* SVP */ + if (svp) + { + save_param(svp->iram_rom, 0x800); + save_param(svp->dram,sizeof(svp->dram)); + save_param(&svp->ssp1601,sizeof(ssp1601_t)); + } + + return bufferptr; +} + +int md_cart_context_load(uint8 *state, char *version) +{ + int i; + int bufferptr = 0; + uint8 offset; + + /* extended state (from 1.4.1 and above) */ + if ((version[11] > 0x31) || (version[13] > 0x34) || (version[15] > 0x30)) + { + /* cartridge mapping */ + for (i=0; i<0x40; i++) + { + /* get offset */ + offset = state[bufferptr++]; + + if (offset == 0xff) + { + /* SRAM */ + m68k_memory_map[i].base = sram.sram; + } + else + { + /* ROM */ + m68k_memory_map[i].base = cart.rom + (offset << 16); + } + } + + /* hardware registers */ + load_param(cart.hw.regs, sizeof(cart.hw.regs)); + + /* SVP */ + if (svp) + { + load_param(svp->iram_rom, 0x800); + load_param(svp->dram,sizeof(svp->dram)); + load_param(&svp->ssp1601,sizeof(ssp1601_t)); + } + } + + return bufferptr; +} + +/************************************************************ + MAPPER handlers +*************************************************************/ + +/* + ROM/SRAM Bankswitch (Phantasy Star IV, Story of Thor/Beyond Oasis, Sonic 3 & Knuckles) +*/ +static void mapper_sega_w(uint32 data) +{ + int i; + + if (data & 1) + { + /* Only if SRAM is detected */ + if (sram.on) + { + /* $200000-$3fffff is mapped to SRAM */ + for (i=0x20; i<0x40; i++) + { + m68k_memory_map[i].base = sram.sram; + } + + if (data & 2) + { + /* SRAM write disabled */ + for (i=0x20; i<0x40; i++) + { + m68k_memory_map[i].write8 = m68k_unused_8_w; + m68k_memory_map[i].write16 = m68k_unused_16_w; + zbank_memory_map[i].write = zbank_unused_w; + } + } + else + { + /* SRAM write enabled */ + for (i=0x20; i<0x40; i++) + { + m68k_memory_map[i].write8 = NULL; + m68k_memory_map[i].write16 = NULL; + zbank_memory_map[i].write = NULL; + } + } + } + + /* S&K lock-on chip */ + if ((cart.special & HW_LOCK_ON) && (config.lock_on == TYPE_SK)) + { + /* $300000-$3fffff is mapped to S2K upmem chip */ + for (i=0x30; i<0x40; i++) + { + m68k_memory_map[i].base = (cart.rom + 0x800000) + ((i & 3)<<16); + } + } + } + else + { + /* $200000-$3fffff is mapped to ROM */ + for (i=0x20; i<0x40; i++) + { + m68k_memory_map[i].base = cart.rom + ((i<<16) & cart.mask); + m68k_memory_map[i].write8 = m68k_unused_8_w; + m68k_memory_map[i].write16 = m68k_unused_16_w; + zbank_memory_map[i].write = zbank_unused_w; + } + } +} + +/* + Super Street Fighter 2 ROM Bankswitch + documented by Bart Trzynadlowski (http://www.trzy.org/files/ssf2.txt) +*/ +static void mapper_ssf2_w(uint32 address, uint32 data) +{ + /* 8 x 512k banks */ + uint32 dst = (address << 2) & 0x38; + + /* bank 0 remains unchanged */ + if (dst) + { + uint32 i; + uint8 *src = cart.rom + (data << 19); + + for (i=0; i<8; i++) + { + m68k_memory_map[dst++].base = src + (i<<16); + } + } +} + +/* + Realtec ROM Bankswitch (Earth Defend, Balloon Boy & Funny World, Whac-A-Critter) + (Note: register usage is inverted in TascoDlx documentation) +*/ +static void mapper_realtec_w(uint32 address, uint32 data) +{ + switch (address) + { + case 0x402000: + { + /* number of mapped 64k blocks (the written value is a number of 128k blocks) */ + cart.hw.regs[2] = data << 1; + return; + } + + case 0x404000: + { + /* 00000xxx */ + cart.hw.regs[0] = data & 7; + return; + } + + case 0x400000: + { + /* 00000yy1 */ + cart.hw.regs[1] = data & 6; + + /* mapped start address is 00yy xxx0 0000 0000 0000 0000 */ + uint32 base = (cart.hw.regs[0] << 1) | (cart.hw.regs[1] << 3); + + /* ensure mapped size is not null */ + if (cart.hw.regs[2]) + { + /* selected blocks are mirrored into the whole cartridge area */ + int i; + for (i=0x00; i<0x40; i++) + m68k_memory_map[i].base = &cart.rom[(base + (i % cart.hw.regs[2])) << 16]; + } + return; + } + } +} + +/* Game no Kanzume Otokuyou ROM Mapper */ +static void mapper_seganet_w(uint32 address, uint32 data) +{ + if ((address & 0xff) == 0xf1) + { + int i; + if (data & 1) + { + /* ROM Write protected */ + for (i=0; i<0x40; i++) + { + m68k_memory_map[i].write8 = m68k_unused_8_w; + m68k_memory_map[i].write16 = m68k_unused_16_w; + zbank_memory_map[i].write = zbank_unused_w; + } + } + else + { + /* ROM Write enabled */ + for (i=0; i<0x40; i++) + { + m68k_memory_map[i].write8 = NULL; + m68k_memory_map[i].write16 = NULL; + zbank_memory_map[i].write = NULL; + } + } + } +} + +/* + Custom ROM Bankswitch used in Top Fighter, Mulan, Pocket Monsters II, Lion King 3, Super King Kong 99, Pokemon Stadium +*/ +static void mapper_32k_w(uint32 data) +{ + int i; + + /* 64 x 32k banks */ + if (data) + { + /* unverified (Top Fighter writes $2A instead $2E) */ + if (data >> 2) data |= 4; + + /* bank is mapped at $000000-$0FFFFF */ + for (i=0; i<16; i++) + { + memcpy(cart.rom + 0x900000 + (i<<16), cart.rom + ((data & 0x3f) << 15), 0x8000); + memcpy(cart.rom + 0x908000 + (i<<16), cart.rom + ((data & 0x3f) << 15), 0x8000); + m68k_memory_map[i].base = cart.rom + 0x900000 + (i<<16); + } + } + else + { + /* reset default $000000-$0FFFFF mapping */ + for (i=0; i<16; i++) + { + m68k_memory_map[i].base = &cart.rom[i << 16]; + } + } +} + +/* + Custom ROM Bankswitch used in Chinese Fighter III +*/ +static void mapper_64k_w(uint32 data) +{ + int i; + + /* 16 x 64k banks */ + if (data) + { + /* bank is mapped at $000000-$0FFFFF */ + for (i=0; i<16; i++) + { + m68k_memory_map[i].base = &cart.rom[(data & 0xf) << 16]; + } + } + else + { + /* reset default $000000-$0FFFFF mapping */ + for (i=0; i<16; i++) + { + m68k_memory_map[i].base = &cart.rom[(i & 0xf) << 16]; + } + } +} + +/* + Custom ROM Bankswitch used in pirate "Multi-in-1" cartridges, A Bug's Life, King of Fighter 99, Pocket Monster, Rockman X3 + */ +static void mapper_64k_multi_w(uint32 address) +{ + int i; + + /* 64 x 64k banks */ + for (i=0; i<64; i++) + { + m68k_memory_map[i].base = &cart.rom[((address++) & 0x3f) << 16]; + } +} + +/* + Custom ROM Bankswitch used in RADICA cartridges +*/ +static uint32 mapper_radica_r(uint32 address) +{ + int i = 0; + address = (address >> 1); + + /* 64 x 64k banks */ + for (i = 0; i < 64; i++) + { + m68k_memory_map[i].base = &cart.rom[((address++)& 0x3f)<< 16]; + } + + return 0xffff; +} + + +/************************************************************ + default !TIME signal handler +*************************************************************/ + +static void default_time_w(uint32 address, uint32 data) +{ + if (address < 0xa13040) + { + /* unlicensed cartridges mapper (default) */ + mapper_64k_multi_w(address); + return; + } + + /* official cartridges mapper (default) */ + mapper_sega_w(data); +} + + +/************************************************************ + Internal register handlers +*************************************************************/ + +static uint32 default_regs_r(uint32 address) +{ + int i; + for (i=0; i<4; i++) + { + if ((address & cart.hw.mask[i]) == cart.hw.addr[i]) + { + return cart.hw.regs[i]; + } + } + return m68k_read_bus_8(address); +} + +static uint32 default_regs_r_16(uint32 address) +{ + int i; + for (i=0; i<4; i++) + { + if ((address & cart.hw.mask[i]) == cart.hw.addr[i]) + { + return (cart.hw.regs[i] << 8); + } + } + return m68k_read_bus_16(address); +} + +static void default_regs_w(uint32 address, uint32 data) +{ + int i; + for (i=0; i<4; i++) + { + if ((address & cart.hw.mask[i]) == cart.hw.addr[i]) + { + cart.hw.regs[i] = data; + return; + } + } + m68k_unused_8_w(address, data); +} + +/* custom register hardware (Top Fighter, Lion King III, Super Donkey Kong 99, Mulan, Pocket Monsters II, Pokemon Stadium) */ +static void custom_regs_w(uint32 address, uint32 data) +{ + /* ROM bankswitch */ + if ((address >> 16) > 0x6f) + { + mapper_32k_w(data); + return; + } + + /* write register */ + default_regs_w(address, data); + + /* bitswapping */ + uint32 temp = cart.hw.regs[0]; + switch (cart.hw.regs[1] & 3) + { + case 0: + cart.hw.regs[2] = (temp << 1); + break; + + case 1: + cart.hw.regs[2] = (temp >> 1); + return; + + case 2: + cart.hw.regs[2] = ((temp >> 4) | ((temp & 0x0F) << 4)); + return; + + default: + cart.hw.regs[2] = (((temp >> 7) & 0x01) | ((temp >> 5) & 0x02) | + ((temp >> 3) & 0x04) | ((temp >> 1) & 0x08) | + ((temp << 1) & 0x10) | ((temp << 3) & 0x20) | + ((temp << 5) & 0x40) | ((temp << 7) & 0x80)); + return; + } +} + +/* alternate custom register hardware (Chinese Fighters III) */ +static void custom_alt_regs_w(uint32 address, uint32 data) +{ + /* ROM bankswitch */ + if ((address >> 16) > 0x5f) + { + mapper_64k_w(data); + return; + } + + /* write regs */ + default_regs_w(address, data); +} + +static uint32 topshooter_read(uint32 address) +{ + if (address < 0x202000) + { + uint8 temp = 0xff; + + switch (address & 0xff) + { + case 0x43: + { + if (input.pad[0] & INPUT_A) temp &= ~0x80; /* Shoot */ + if (input.pad[0] & INPUT_B) temp &= ~0x10; /* Bet */ + if (input.pad[0] & INPUT_START) temp &= ~0x20; /* Start */ + break; + } + + case 0x45: /* ??? (DOWN) & Service Mode (UP) */ + { + if (input.pad[0] & INPUT_UP) temp &= ~0x08; /* Service Mode */ + if (input.pad[0] & INPUT_DOWN) temp &= ~0x10; /* ???, used in service menu to select next option */ + break; + } + + case 0x47: + { + if (input.pad[0] & INPUT_RIGHT) temp &= ~0x03; /* Insert 10 coins */ + break; + } + + case 0x49: + { + if (input.pad[0] & INPUT_LEFT) temp &= ~0x03; /* Clear coins */ + if (input.pad[0] & INPUT_C) temp &= ~0x01; /* Insert XXX coins */ + break; + } + + case 0x51: + { + temp = 0xA5; + break; + } + + default: + { + temp = m68k_read_bus_8(address); + break; + } + } + return temp; + } + + return READ_BYTE(sram.sram , address & 0xffff); +} + +static void topshooter_write(uint32 address, uint32 data) +{ + if (address >= 0x202000) + { + WRITE_BYTE(sram.sram , address & 0xffff, data); + return; + } + m68k_unused_8_w(address, data); +} diff --git a/source/cart_hw/md_cart.h b/source/cart_hw/md_cart.h new file mode 100644 index 0000000..d0d60bc --- /dev/null +++ b/source/cart_hw/md_cart.h @@ -0,0 +1,75 @@ +/**************************************************************************** + * Genesis Plus + * Mega Drive cartridge hardware support + * + * Copyright (C) 2007-2011 Eke-Eke (GCN/Wii port) + * + * Lots of protection mechanism have been discovered by Haze + * (http://haze.mameworld.info/) + * + * Realtec mapper has been figured out by TascoDeluxe + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +#ifndef _MD_CART_H_ +#define _MD_CART_H_ + +/* Lock-On cartridge type */ +#define TYPE_GG 0x01 /* Game Genie */ +#define TYPE_AR 0x02 /* (Pro) Action Replay */ +#define TYPE_SK 0x03 /* Sonic & Knuckles */ + +/* Special hardware (0x01 reserved for SMS 3-D glasses) */ +#define HW_J_CART 0x02 +#define HW_LOCK_ON 0x04 + +/* Cartridge extra hardware */ +typedef struct +{ + uint8 regs[4]; /* internal registers (R/W) */ + uint32 mask[4]; /* registers address mask */ + uint32 addr[4]; /* registers address */ + uint16 realtec; /* realtec mapper */ + uint16 bankshift; /* cartridge with bankshift mecanism reseted on software reset */ + unsigned int (*time_r)(unsigned int address); /* !TIME signal ($a130xx) read handler */ + void (*time_w)(unsigned int address, unsigned int data); /* !TIME signal ($a130xx) write handler */ + unsigned int (*regs_r)(unsigned int address); /* cart hardware registers read handler */ + void (*regs_w)(unsigned int address, unsigned int data); /* cart hardware registers write handler */ +} T_CART_HW; + +/* Cartridge type */ +typedef struct +{ + uint8 *rom; /* ROM area */ + uint8 *base; /* ROM base (saved for OS/Cartridge ROM swap) */ + uint32 romsize; /* ROM size */ + uint32 mask; /* ROM mask */ + uint8 special; /* Lock-On, J-Cart or SMS 3-D glasses hardware */ + T_CART_HW hw; /* Extra mapping hardware */ +} T_CART; + +/* global variables */ +extern T_CART cart; + +/* Function prototypes */ +extern void md_cart_init(void); +extern void md_cart_reset(int hard_reset); +extern int md_cart_context_save(uint8 *state); +extern int md_cart_context_load(uint8 *state, char *version); + +#endif + + diff --git a/source/cart_hw/sms_cart.c b/source/cart_hw/sms_cart.c new file mode 100644 index 0000000..a7c2513 --- /dev/null +++ b/source/cart_hw/sms_cart.c @@ -0,0 +1,631 @@ + +/**************************************************************************** + * Genesis Plus + * Master System cartridge hardware support + * + * + * Copyright (C) 1998-2007 Charles MacDonald (SMS Plus original code) + * Eke-Eke (2007-2011), additional code & fixes for the GCN/Wii port + * + * Most cartridge protections documented by Haze + * (http://haze.mameworld.info/) + * + * Realtec mapper documented by TascoDeluxe + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +#include "shared.h" + +#define MAPPER_NONE (0) +#define MAPPER_SEGA (1) +#define MAPPER_CODIES (2) +#define MAPPER_KOREA (3) +#define MAPPER_MSX (4) + +#define GAME_DATABASE_CNT (75) + +typedef struct +{ + uint32 crc; + uint8 glasses_3d; + uint8 peripheral; + uint8 mapper; + uint8 region; +} rominfo_t; + +static struct +{ + uint8 fcr[4]; + uint8 mapper; +} slot; + +/* SMS game database */ +static const rominfo_t game_list[GAME_DATABASE_CNT] = +{ + /* games requiring CODEMASTER mapper (NOTE: extended video modes don't work on Genesis VDP !) */ + {0x29822980, 0, SYSTEM_MS_GAMEPAD, MAPPER_CODIES, REGION_EUROPE}, /* Cosmic Spacehead */ + {0xA577CE46, 0, SYSTEM_MS_GAMEPAD, MAPPER_CODIES, REGION_EUROPE}, /* Micro Machines */ + {0xF7C524F6, 0, SYSTEM_MS_GAMEPAD, MAPPER_CODIES, REGION_EUROPE}, /* Micro Machines [BAD DUMP] */ + {0xDBE8895C, 0, SYSTEM_MS_GAMEPAD, MAPPER_CODIES, REGION_EUROPE}, /* Micro Machines 2 - Turbo Tournament */ + {0xC1756BEE, 0, SYSTEM_MS_GAMEPAD, MAPPER_CODIES, REGION_EUROPE}, /* Pete Sampras Tennis */ + {0x8813514B, 0, SYSTEM_MS_GAMEPAD, MAPPER_CODIES, REGION_EUROPE}, /* Excellent Dizzy Collection, The [Proto] */ + {0xEA5C3A6F, 0, SYSTEM_MS_GAMEPAD, MAPPER_CODIES, REGION_USA}, /* Dinobasher - Starring Bignose the Caveman [Proto] */ + {0x152F0DCC, 0, SYSTEM_MS_GAMEPAD, MAPPER_CODIES, REGION_USA}, /* Drop Zone" */ + {0xAA140C9C, 0, SYSTEM_MS_GAMEPAD, MAPPER_CODIES, REGION_USA}, /* Excellent Dizzy Collection, The [SMS-GG] */ + {0xB9664AE1, 0, SYSTEM_MS_GAMEPAD, MAPPER_CODIES, REGION_USA}, /* Fantastic Dizzy */ + {0xC888222B, 0, SYSTEM_MS_GAMEPAD, MAPPER_CODIES, REGION_USA}, /* Fantastic Dizzy [SMS-GG] */ + {0x76C5BDFB, 0, SYSTEM_MS_GAMEPAD, MAPPER_CODIES, REGION_USA}, /* Jang Pung 2 [SMS-GG] */ + {0xD9A7F170, 0, SYSTEM_MS_GAMEPAD, MAPPER_CODIES, REGION_USA}, /* Man Overboard! */ + + /* games requiring KOREA mappers (NOTE: TMS9918 video modes don't work on Genesis VDP !) */ + {0x17AB6883, 0, SYSTEM_MS_GAMEPAD, MAPPER_NONE, REGION_JAPAN_NTSC}, /* FA Tetris (KR) */ + {0x61E8806F, 0, SYSTEM_MS_GAMEPAD, MAPPER_NONE, REGION_JAPAN_NTSC}, /* Flash Point (KR) */ + {0x445525E2, 0, SYSTEM_MS_GAMEPAD, MAPPER_MSX, REGION_JAPAN_NTSC}, /* Penguin Adventure (KR) */ + {0x83F0EEDE, 0, SYSTEM_MS_GAMEPAD, MAPPER_MSX, REGION_JAPAN_NTSC}, /* Street Master (KR) */ + {0xA05258F5, 0, SYSTEM_MS_GAMEPAD, MAPPER_MSX, REGION_JAPAN_NTSC}, /* Won-Si-In (KR) */ + {0x06965ED9, 0, SYSTEM_MS_GAMEPAD, MAPPER_MSX, REGION_JAPAN_NTSC}, /* F-1 Spirit - The way to Formula-1 (KR) */ + {0x89B79E77, 0, SYSTEM_MS_GAMEPAD, MAPPER_KOREA, REGION_JAPAN_NTSC}, /* Dodgeball King (KR) */ + {0x18FB98A3, 0, SYSTEM_MS_GAMEPAD, MAPPER_KOREA, REGION_JAPAN_NTSC}, /* Jang Pung 3 (KR) */ + {0x97D03541, 0, SYSTEM_MS_GAMEPAD, MAPPER_KOREA, REGION_JAPAN_NTSC}, /* Sangokushi 3 (KR)"} */ + {0x67C2F0FF, 0, SYSTEM_MS_GAMEPAD, MAPPER_KOREA, REGION_JAPAN_NTSC}, /* Super Boy 2 (KR) */ + + /* games requiring PAL timings */ + {0x72420F38, 0, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_EUROPE}, /* Addams Familly */ + {0x2D48C1D3, 0, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_EUROPE}, /* Back to the Future Part III */ + {0x1CBB7BF1, 0, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_EUROPE}, /* Battlemaniacs (BR) */ + {0x1B10A951, 0, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_EUROPE}, /* Bram Stoker's Dracula */ + {0xC0E25D62, 0, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_EUROPE}, /* California Games II */ + {0x45C50294, 0, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_EUROPE}, /* Jogos de Verao II (BR) */ + {0xC9DBF936, 0, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_EUROPE}, /* Home Alone */ + {0x0047B615, 0, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_EUROPE}, /* Predator2 */ + {0xF42E145C, 0, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_EUROPE}, /* Quest for the Shaven Yak Starring Ren Hoek & Stimpy (BR) */ + {0x9F951756, 0, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_EUROPE}, /* RoboCop 3 */ + {0xF8176918, 0, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_EUROPE}, /* Sensible Soccer */ + {0x1575581D, 0, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_EUROPE}, /* Shadow of the Beast */ + {0x96B3F29E, 0, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_EUROPE}, /* Sonic Blast (BR) */ + {0x5B3B922C, 0, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_EUROPE}, /* Sonic the Hedgehog 2 [V0] */ + {0xD6F2BFCA, 0, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_EUROPE}, /* Sonic the Hedgehog 2 [V1] */ + {0xCA1D3752, 0, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_EUROPE}, /* Space Harrier [50 Hz] */ + {0x85CFC9C9, 0, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_EUROPE}, /* Taito Chase H.Q. */ + + /* games requiring 3-D Glasses */ + {0x871562b0, 1, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_JAPAN_NTSC}, /* Maze Walker */ + {0x156948f9, 1, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_JAPAN_NTSC}, /* Space Harrier 3-D (J) */ + {0x6BD5C2BF, 1, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_USA}, /* Space Harrier 3-D */ + {0x8ECD201C, 1, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_USA}, /* Blade Eagle 3-D */ + {0xFBF96C81, 1, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_USA}, /* Blade Eagle 3-D (BR) */ + {0x58D5FC48, 1, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_USA}, /* Blade Eagle 3-D [Proto] */ + {0x31B8040B, 1, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_USA}, /* Maze Hunter 3-D */ + {0xABD48AD2, 1, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_USA}, /* Poseidon Wars 3-D */ + {0xA3EF13CB, 1, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_USA}, /* Zaxxon 3-D */ + {0xBBA74147, 1, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_USA}, /* Zaxxon 3-D [Proto] */ + {0xD6F43DDA, 1, SYSTEM_MS_GAMEPAD, MAPPER_SEGA, REGION_USA}, /* Out Run 3-D */ + + /* games requiring 3-D Glasses & Sega Light Phaser */ + {0xFBE5CFBB, 1, SYSTEM_LIGHTPHASER, MAPPER_SEGA, REGION_USA}, /* Missile Defense 3D */ + {0xE79BB689, 1, SYSTEM_LIGHTPHASER, MAPPER_SEGA, REGION_USA}, /* Missile Defense 3D [BIOS] */ + + /* games requiring Sega Light Phaser */ + {0x861B6E79, 0, SYSTEM_LIGHTPHASER, MAPPER_SEGA, REGION_USA}, /* Assault City [Light Phaser] */ + {0x5FC74D2A, 0, SYSTEM_LIGHTPHASER, MAPPER_SEGA, REGION_USA}, /* Gangster Town */ + {0xE167A561, 0, SYSTEM_LIGHTPHASER, MAPPER_SEGA, REGION_USA}, /* Hang-On / Safari Hunt */ + {0xC5083000, 0, SYSTEM_LIGHTPHASER, MAPPER_SEGA, REGION_USA}, /* Hang-On / Safari Hunt [BAD DUMP] */ + {0x91E93385, 0, SYSTEM_LIGHTPHASER, MAPPER_SEGA, REGION_USA}, /* Hang-On / Safari Hunt [BIOS] */ + {0xE8EA842C, 0, SYSTEM_LIGHTPHASER, MAPPER_SEGA, REGION_USA}, /* Marksman Shooting / Trap Shooting */ + {0xE8215C2E, 0, SYSTEM_LIGHTPHASER, MAPPER_SEGA, REGION_USA}, /* Marksman Shooting / Trap Shooting / Safari Hunt */ + {0x205CAAE8, 0, SYSTEM_LIGHTPHASER, MAPPER_SEGA, REGION_USA}, /* Operation Wolf (can be played with gamepad in port B)*/ + {0x23283F37, 0, SYSTEM_LIGHTPHASER, MAPPER_SEGA, REGION_USA}, /* Operation Wolf [A] (can be played with gamepad in port B) */ + {0xDA5A7013, 0, SYSTEM_LIGHTPHASER, MAPPER_SEGA, REGION_USA}, /* Rambo 3 */ + {0x79AC8E7F, 0, SYSTEM_LIGHTPHASER, MAPPER_SEGA, REGION_USA}, /* Rescue Mission */ + {0x4B051022, 0, SYSTEM_LIGHTPHASER, MAPPER_SEGA, REGION_USA}, /* Shooting Gallery */ + {0xA908CFF5, 0, SYSTEM_LIGHTPHASER, MAPPER_SEGA, REGION_USA}, /* Spacegun */ + {0x5359762D, 0, SYSTEM_LIGHTPHASER, MAPPER_SEGA, REGION_USA}, /* Wanted */ + {0x0CA95637, 0, SYSTEM_LIGHTPHASER, MAPPER_SEGA, REGION_USA}, /* Laser Ghost */ + + /* games requiring Sega Paddle */ + {0xF9DBB533, 0, SYSTEM_PADDLE, MAPPER_SEGA, REGION_JAPAN_NTSC}, /* Alex Kidd BMX Trial */ + {0xA6FA42D0, 0, SYSTEM_PADDLE, MAPPER_SEGA, REGION_JAPAN_NTSC}, /* Galactic Protector */ + {0x29BC7FAD, 0, SYSTEM_PADDLE, MAPPER_SEGA, REGION_JAPAN_NTSC}, /* Megumi Rescue */ + {0x315917D4, 0, SYSTEM_PADDLE, MAPPER_SEGA, REGION_JAPAN_NTSC}, /* Woody Pop */ + + /* games requiring Sega Sport Pad */ + {0x0CB7E21F, 0, SYSTEM_SPORTSPAD, MAPPER_SEGA, REGION_USA}, /* Great Ice Hockey */ + {0xE42E4998, 0, SYSTEM_SPORTSPAD, MAPPER_SEGA, REGION_USA}, /* Sports Pad Football */ + {0x41C948BF, 0, SYSTEM_SPORTSPAD, MAPPER_SEGA, REGION_USA} /* Sports Pad Soccer */ +}; + +/* 1K trash buffer */ +static uint8 dummy[0x400]; + +/* Function prorotypes */ +static void mapper_8k_w(int offset, unsigned int data); +static void mapper_16k_w(int offset, unsigned int data); +static void write_mapper_none(unsigned int address, unsigned char data); +static void write_mapper_sega(unsigned int address, unsigned char data); +static void write_mapper_codies(unsigned int address, unsigned char data); +static void write_mapper_korea(unsigned int address, unsigned char data); +static void write_mapper_msx(unsigned int address, unsigned char data); + + +void sms_cart_init(void) +{ + /* default mapper */ + slot.mapper = MAPPER_SEGA; + + /* default supported peripheral */ + uint8 device = SYSTEM_MS_GAMEPAD; + cart.special = 0; + + /* compute CRC */ + uint32 crc = crc32(0, cart.rom, cart.romsize); + + /* detect cartridge mapper */ + int i; + for (i=0; i> 13); + + /* Save frame control register data */ + slot.fcr[offset] = data; + + /* 4 x 8k banks */ + switch (offset & 3) + { + case 0: /* cartridge ROM bank (8k) at $8000-$9FFF */ + { + for(i = 0x20; i < 0x28; i++) + { + z80_readmap[i] = &cart.rom[(page << 13) | ((i & 0x07) << 10)]; + } + break; + } + + case 1: /* cartridge ROM bank (8k) at $A000-$BFFF */ + { + for(i = 0x28; i < 0x30; i++) + { + z80_readmap[i] = &cart.rom[(page << 13) | ((i & 0x07) << 10)]; + } + break; + } + + case 2: /* cartridge ROM bank (8k) at $4000-$5FFF */ + { + for(i = 0x10; i < 0x18; i++) + { + z80_readmap[i] = &cart.rom[(page << 13) | ((i & 0x07) << 10)]; + } + break; + } + + case 3: /* cartridge ROM bank (8k) at $6000-$7FFF */ + { + for(i = 0x18; i < 0x20; i++) + { + z80_readmap[i] = &cart.rom[(page << 13) | ((i & 0x07) << 10)]; + } + break; + } + } +} + +void mapper_16k_w(int offset, unsigned int data) +{ + int i; + + /* cartridge ROM page (16k) */ + uint8 page = data % (cart.romsize >> 14); + + /* page index increment (SEGA mapper) */ + if (slot.fcr[0] & 0x03) + { + page = (page + ((4 - (slot.fcr[0] & 0x03)) << 3)) % (cart.romsize >> 14); + } + + /* save frame control register data */ + slot.fcr[offset] = data; + + switch (offset) + { + case 0: /* control register (SEGA mapper) */ + { + if(data & 0x08) + { + /* external RAM (upper or lower 16K) mapped at $8000-$BFFF */ + for(i = 0x20; i <= 0x2F; i++) + { + z80_readmap[i] = z80_writemap[i] = &sram.sram[((data & 0x04) << 12) + ((i & 0x0F) << 10)]; + } + } + else + { + /* cartridge ROM page (16k) */ + page = slot.fcr[3] % (cart.romsize >> 14); + + /* page index increment (SEGA mapper) */ + if (data & 0x03) + { + page = (page + ((4 - (data & 0x03)) << 3)) % (cart.romsize >> 14); + } + + /* cartridge ROM mapped at $8000-$BFFF */ + for(i = 0x20; i < 0x30; i++) + { + z80_readmap[i] = &cart.rom[(page << 14) | ((i & 0x0F) << 10)]; + z80_writemap[i] = dummy; + } + } + + if(data & 0x10) + { + /* external RAM (lower 16K) mapped at $C000-$FFFF */ + for(i = 0x30; i < 0x40; i++) + { + z80_readmap[i] = z80_writemap[i] = &sram.sram[(i & 0x0F) << 10]; + } + } + else + { + /* internal RAM (8K mirrorred) mapped at $C000-$FFFF */ + for(i = 0x30; i < 0x40; i++) + { + z80_readmap[i] = z80_writemap[i] = &work_ram[(i & 0x07) << 10]; + } + } + break; + } + + case 1: /* cartridge ROM bank (16k) at $0000-$3FFF */ + { + /* first 1k is not fixed (CODEMASTER mapper) */ + if (slot.mapper == MAPPER_CODIES) + { + z80_readmap[0] = &cart.rom[(page << 14)]; + } + + for(i = 0x01; i < 0x10; i++) + { + z80_readmap[i] = &cart.rom[(page << 14) | ((i & 0x0F) << 10)]; + } + break; + } + + case 2: /* cartridge ROM bank (16k) at $4000-$7FFF */ + { + for(i = 0x10; i < 0x20; i++) + { + z80_readmap[i] = &cart.rom[(page << 14) | ((i & 0x0F) << 10)]; + } + + /* Ernie Elf's Golf external RAM switch */ + if (slot.mapper == MAPPER_CODIES) + { + if (data & 0x80) + { + /* external RAM (8k) mapped at $A000-$BFFF */ + for(i = 0x28; i < 0x30; i++) + { + z80_readmap[i] = z80_writemap[i] = &sram.sram[(i & 0x0F) << 10]; + } + } + else + { + /* cartridge ROM page (16k) */ + page = slot.fcr[3] % (cart.romsize >> 14); + + /* cartridge ROM mapped at $A000-$BFFF */ + for(i = 0x28; i < 0x30; i++) + { + z80_readmap[i] = &cart.rom[(page << 14) | ((i & 0x0F) << 10)]; + z80_writemap[i] = dummy; + } + } + } + break; + } + + case 3: /* cartridge ROM bank (16k) at $8000-$BFFF */ + { + /* check that external RAM (16k) is not mapped at $8000-$BFFF (SEGA mapper) */ + if ((slot.fcr[0] & 0x08)) break; + + /* first 8k */ + for(i = 0x20; i < 0x28; i++) + { + z80_readmap[i] = &cart.rom[(page << 14) | ((i & 0x0F) << 10)]; + } + + /* check that external RAM (8k) is not mapped at $A000-$BFFF (CODEMASTER mapper) */ + if ((slot.mapper == MAPPER_CODIES) && (slot.fcr[2] & 0x80)) break; + + /* last 8k */ + for(i = 0x28; i < 0x30; i++) + { + z80_readmap[i] = &cart.rom[(page << 14) | ((i & 0x0F) << 10)]; + } + break; + } + } +} + +static void write_mapper_none(unsigned int address, unsigned char data) +{ + z80_writemap[address >> 10][address & 0x03FF] = data; +} + +static void write_mapper_sega(unsigned int address, unsigned char data) +{ + if(address >= 0xFFFC) + { + mapper_16k_w(address & 3, data); + } + + z80_writemap[address >> 10][address & 0x03FF] = data; +} + +static void write_mapper_codies(unsigned int address, unsigned char data) +{ + if (address == 0x0000) + { + mapper_16k_w(1,data); + return; + } + + if (address == 0x4000) + { + mapper_16k_w(2,data); + return; + } + + if (address == 0x8000) + { + mapper_16k_w(3,data); + return; + } + + z80_writemap[address >> 10][address & 0x03FF] = data; +} + +static void write_mapper_korea(unsigned int address, unsigned char data) +{ + if (address == 0xA000) + { + mapper_16k_w(3,data); + return; + } + + z80_writemap[address >> 10][address & 0x03FF] = data; +} + +static void write_mapper_msx(unsigned int address, unsigned char data) +{ + if (address <= 0x0003) + { + mapper_8k_w(address,data); + return; + } + + z80_writemap[address >> 10][address & 0x03FF] = data; +} diff --git a/source/cart_hw/sms_cart.h b/source/cart_hw/sms_cart.h new file mode 100644 index 0000000..8b88bae --- /dev/null +++ b/source/cart_hw/sms_cart.h @@ -0,0 +1,38 @@ +/**************************************************************************** + * Genesis Plus + * Master System cartridge hardware support + * + * Copyright (C) 1998-2007 Charles MacDonald (SMS Plus original code) + * Eke-Eke (2007-2011), additional code & fixes for the GCN/Wii port + * + * Realtec mapper has been figured out by TascoDeluxe + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + ***************************************************************************/ + +#ifndef _SMS_CART_H_ +#define _SMS_CART_H_ + +/* Function prototypes */ +extern void sms_cart_init(void); +extern void sms_cart_reset(void); +extern void sms_cart_switch(int enabled); +extern int sms_cart_region_detect(void); +extern int sms_cart_context_save(uint8 *state); +extern int sms_cart_context_load(uint8 *state, char *version); + +#endif + + diff --git a/source/cart_hw/sram.c b/source/cart_hw/sram.c index 1d5fbb9..61cad85 100644 --- a/source/cart_hw/sram.c +++ b/source/cart_hw/sram.c @@ -59,7 +59,9 @@ void sram_init() /* fixe some bad header informations */ if ((sram.start > sram.end) || ((sram.end - sram.start) >= 0x10000)) + { sram.end = sram.start + 0xffff; + } sram.start &= 0xfffffffe; sram.end |= 1; diff --git a/source/genesis.c b/source/genesis.c index a2cb0e5..a7a627f 100644 --- a/source/genesis.c +++ b/source/genesis.c @@ -1,9 +1,9 @@ /*************************************************************************************** * Genesis Plus - * Genesis internals & Bus controller + * Internal Hardware & Bus controllers * * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) - * Eke-Eke (2007,2008,2009), additional code & fixes for the GCN/Wii port + * Eke-Eke (2007-2011), additional code & fixes for the GCN/Wii port * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -42,16 +42,18 @@ void gen_init(void) { int i; - /* initialize CPUs */ + /* initialize 68k */ m68k_set_cpu_type(M68K_CPU_TYPE_68000); m68k_init(); + + /* initialize Z80 */ z80_init(0,z80_irq_callback); - /* initialize 68k mapped memory */ - /* $000000-$7fffff is affected to cartridge area (see cart_hw.c) */ - /* $800000-$ffffff is affected to WRAM (see VDP DMA) */ + /* initialize 68k memory map */ + /* $000000-$7FFFFF is affected to cartridge area (see md_cart.c) */ for (i=0x80; i<0x100; i++) { + /* $800000-$FFFFFF is affected to WRAM (see VDP DMA) */ m68k_memory_map[i].base = work_ram; m68k_memory_map[i].read8 = NULL; m68k_memory_map[i].read16 = NULL; @@ -64,7 +66,7 @@ void gen_init(void) /* initialize 68k memory handlers */ for (i=0x80; i<0xe0; i++) { - /* illegal area */ + /* $800000-$DFFFFF : illegal area by default */ m68k_memory_map[i].read8 = m68k_lockup_r_8; m68k_memory_map[i].read16 = m68k_lockup_r_16; m68k_memory_map[i].write8 = m68k_lockup_w_8; @@ -73,11 +75,7 @@ void gen_init(void) zbank_memory_map[i].write = zbank_lockup_w; } - /* Z80 bus (bank access) */ - zbank_memory_map[0xa0].read = zbank_lockup_r; - zbank_memory_map[0xa0].write = zbank_lockup_w; - - /* I/O & Control registers */ + /* $A10000-$A1FFFF : I/O & Control registers */ m68k_memory_map[0xa1].read8 = ctrl_io_read_byte; m68k_memory_map[0xa1].read16 = ctrl_io_read_word; m68k_memory_map[0xa1].write8 = ctrl_io_write_byte; @@ -85,110 +83,155 @@ void gen_init(void) zbank_memory_map[0xa1].read = zbank_read_ctrl_io; zbank_memory_map[0xa1].write = zbank_write_ctrl_io; - /* VDP (initially locked on models with TMSS) */ - if (!(config.tmss & 1)) + /* $C0xxxx, $C8xxxx, $D0xxxx, $D8xxxx : VDP ports */ + for (i=0xc0; i<0xe0; i+=8) { - for (i=0xc0; i<0xe0; i+=8) - { - m68k_memory_map[i].read8 = vdp_read_byte; - m68k_memory_map[i].read16 = vdp_read_word; - m68k_memory_map[i].write8 = vdp_write_byte; - m68k_memory_map[i].write16 = vdp_write_word; - zbank_memory_map[i].read = zbank_read_vdp; - zbank_memory_map[i].write = zbank_write_vdp; - } + m68k_memory_map[i].read8 = vdp_read_byte; + m68k_memory_map[i].read16 = vdp_read_word; + m68k_memory_map[i].write8 = vdp_write_byte; + m68k_memory_map[i].write16 = vdp_write_word; + zbank_memory_map[i].read = zbank_read_vdp; + zbank_memory_map[i].write = zbank_write_vdp; } - /* SEGA PICO */ - if (system_hw == SYSTEM_PICO) + /* MS COMPATIBILITY mode */ + if (system_hw == SYSTEM_PBC) { - m68k_memory_map[0x80].read8 = pico_read_byte; - m68k_memory_map[0x80].read16 = pico_read_word; - m68k_memory_map[0x80].write8 = m68k_unused_8_w; - m68k_memory_map[0x80].write16 = m68k_unused_16_w; + /* initialize Z80 read handler */ + /* NB: memory map & write handler are defined by cartridge hardware */ + z80_readmem = z80_sms_memory_r; - /* there is no I/O area (Notaz) */ - m68k_memory_map[0xa1].read8 = m68k_read_bus_8; - m68k_memory_map[0xa1].read16 = m68k_read_bus_16; - m68k_memory_map[0xa1].write8 = m68k_unused_8_w; - m68k_memory_map[0xa1].write16 = m68k_unused_16_w; + /* initialize Z80 ports handlers */ + z80_writeport = z80_sms_port_w; + z80_readport = z80_sms_port_r; - /* page registers */ - pico_current = 0x00; - pico_page[0] = 0x00; - pico_page[1] = 0x01; - pico_page[2] = 0x03; - pico_page[3] = 0x07; - pico_page[4] = 0x0F; - pico_page[5] = 0x1F; - pico_page[6] = 0x3F; + /* initialize MS cartridge hardware */ + sms_cart_init(); + } + else + { + /* PICO hardware */ + if (system_hw == SYSTEM_PICO) + { + /* additional registers mapped to $800000-$80FFFF */ + m68k_memory_map[0x80].read8 = pico_read_byte; + m68k_memory_map[0x80].read16 = pico_read_word; + m68k_memory_map[0x80].write8 = m68k_unused_8_w; + m68k_memory_map[0x80].write16 = m68k_unused_16_w; + + /* there is no I/O area (Notaz) */ + m68k_memory_map[0xa1].read8 = m68k_read_bus_8; + m68k_memory_map[0xa1].read16 = m68k_read_bus_16; + m68k_memory_map[0xa1].write8 = m68k_unused_8_w; + m68k_memory_map[0xa1].write16 = m68k_unused_16_w; + + /* page registers */ + pico_current = 0x00; + pico_page[0] = 0x00; + pico_page[1] = 0x01; + pico_page[2] = 0x03; + pico_page[3] = 0x07; + pico_page[4] = 0x0F; + pico_page[5] = 0x1F; + pico_page[6] = 0x3F; + } + + /* initialize Z80 memory map */ + /* $0000-$3FFF is mapped to Z80 RAM (8K mirrored) */ + /* $4000-$FFFF is mapped to hardware but Z80.PC should never point there */ + for (i=0; i<64; i++) + { + z80_readmap[i] = &zram[(i & 7) << 10]; + } + + /* initialize Z80 memory handlers */ + z80_writemem = z80_md_memory_w; + z80_readmem = z80_md_memory_r; + + /* initialize Z80 port handlers */ + z80_writeport = z80_unused_port_w; + z80_readport = z80_unused_port_r; + + /* initialize MD cartridge hardware */ + md_cart_init(); } } -void gen_hardreset(void) +void gen_reset(int hard_reset) { - /* Clear RAM */ - memset (work_ram, 0x00, sizeof (work_ram)); - memset (zram, 0x00, sizeof (zram)); - - /* TMSS + OS ROM support */ - memset(tmss, 0x00, sizeof(tmss)); - if (config.tmss == 3) + /* System Reset */ + if (hard_reset) { - m68k_memory_map[0].base = bios_rom; + /* clear RAM */ + memset (work_ram, 0x00, sizeof (work_ram)); + memset (zram, 0x00, sizeof (zram)); + + /* TMSS & OS ROM support */ + if (config.tmss & 1) + { + /* clear TMSS register */ + memset(tmss, 0x00, sizeof(tmss)); + + /* VDP access is locked by default */ + int i; + for (i=0xc0; i<0xe0; i+=8) + { + m68k_memory_map[i].read8 = m68k_lockup_r_8; + m68k_memory_map[i].read16 = m68k_lockup_r_16; + m68k_memory_map[i].write8 = m68k_lockup_w_8; + m68k_memory_map[i].write16 = m68k_lockup_w_16; + zbank_memory_map[i].read = zbank_lockup_r; + zbank_memory_map[i].write = zbank_lockup_w; + } + + /* OS ROM is mapped at $000000-$0007FF */ + if (config.tmss & 2) + { + m68k_memory_map[0].base = bios_rom; + } + } + } + else + { + /* reset YM2612 (on hard reset, this is done by sound_reset) */ + fm_reset(0); } /* 68k & Z80 could restart anywhere in VDP frame (Bonkers, Eternal Champions, X-Men 2) */ mcycles_68k = mcycles_z80 = (uint32)((MCYCLES_PER_LINE * lines_per_frame) * ((double)rand() / (double)RAND_MAX)); - /* Z80 bus is released & Z80 is stopped */ - zstate = 0; - m68k_memory_map[0xa0].read8 = m68k_read_bus_8; - m68k_memory_map[0xa0].read16 = m68k_read_bus_16; - m68k_memory_map[0xa0].write8 = m68k_unused_8_w; - m68k_memory_map[0xa0].write16 = m68k_unused_16_w; - - /* Assume default bank is $000000-$007FFF */ - zbank = 0; - - /* Reset 68k & Z80 */ - m68k_pulse_reset(); - z80_reset(); -} - -void gen_softreset(int state) -{ - if (state) + if (system_hw == SYSTEM_PBC) { - /* Halt 68k */ + /* reset MS cartridge hardware */ + sms_cart_reset(); + + /* Z80 is running */ + zstate = 1; + + /* 68k is halted */ m68k_pulse_halt(); + } + else + { + /* reset MD cartridge hardware */ + md_cart_reset(hard_reset); /* Z80 bus is released & Z80 is reseted */ - zstate = 0; m68k_memory_map[0xa0].read8 = m68k_read_bus_8; m68k_memory_map[0xa0].read16 = m68k_read_bus_16; m68k_memory_map[0xa0].write8 = m68k_unused_8_w; m68k_memory_map[0xa0].write16 = m68k_unused_16_w; + zstate = 0; - /* Assume default bank is $000000-$007FFF */ + /* assume default bank is $000000-$007FFF */ zbank = 0; - /* Reset YM2612 */ - fm_reset(0); - } - else - { - /* Reset Cartridge Hardware */ - cart_hw_reset(0); - - /* 68k & Z80 could restart anywhere in VDP frame (Bonkers, Eternal Champions, X-Men 2) */ - mcycles_68k = mcycles_z80 = (uint32)((MCYCLES_PER_LINE * lines_per_frame) * ((double)rand() / (double)RAND_MAX)); - - /* Reset 68k, Z80 & YM2612 */ + /* reset 68k */ m68k_pulse_reset(); - z80_reset(); - fm_reset(0); } + + /* reset Z80 */ + z80_reset(); } void gen_shutdown(void) @@ -197,16 +240,16 @@ void gen_shutdown(void) } -/*----------------------------------------------------------------------- - OS ROM / TMSS register control functions - -----------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------*/ +/* OS ROM / TMSS register control functions (Genesis mode) */ +/*-----------------------------------------------------------------------*/ void gen_tmss_w(unsigned int offset, unsigned int data) { /* write TMSS regisiter */ WRITE_WORD(tmss, offset, data); - /* VDP requires "SEGA" value to be written in TMSSS register */ + /* VDP requires "SEGA" value to be written in TMSS register */ int i; if (strncmp((char *)tmss, "SEGA", 4) == 0) { @@ -236,7 +279,7 @@ void gen_tmss_w(unsigned int offset, unsigned int data) void gen_bankswitch_w(unsigned int data) { - /* BIOS has not been loaded yet */ + /* OS ROM has not been loaded yet */ if (!(config.tmss & 2)) { config.tmss |= 2; @@ -262,9 +305,10 @@ unsigned int gen_bankswitch_r(void) } -/*----------------------------------------------------------------------- - Z80 Bus controller chip functions - -----------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------*/ +/* Z80 Bus controller chip functions (Genesis mode) */ +/* ----------------------------------------------------------------------*/ + void gen_zbusreq_w(unsigned int data, unsigned int cycles) { if (data) /* !ZBUSREQ asserted */ @@ -276,11 +320,10 @@ void gen_zbusreq_w(unsigned int data, unsigned int cycles) z80_run(cycles); /* enable 68k access to Z80 bus */ - _m68k_memory_map *base = &m68k_memory_map[0xa0]; - base->read8 = z80_read_byte; - base->read16 = z80_read_word; - base->write8 = z80_write_byte; - base->write16 = z80_write_word; + m68k_memory_map[0xa0].read8 = z80_read_byte; + m68k_memory_map[0xa0].read16 = z80_read_word; + m68k_memory_map[0xa0].write8 = z80_write_byte; + m68k_memory_map[0xa0].write16 = z80_write_word; } /* update Z80 bus status */ @@ -295,12 +338,11 @@ void gen_zbusreq_w(unsigned int data, unsigned int cycles) mcycles_z80 = cycles; /* disable 68k access to Z80 bus */ - _m68k_memory_map *base = &m68k_memory_map[0xa0]; - base->read8 = m68k_read_bus_8; - base->read16 = m68k_read_bus_16; - base->write8 = m68k_unused_8_w; - base->write16 = m68k_unused_16_w; - } + m68k_memory_map[0xa0].read8 = m68k_read_bus_8; + m68k_memory_map[0xa0].read16 = m68k_read_bus_16; + m68k_memory_map[0xa0].write8 = m68k_unused_8_w; + m68k_memory_map[0xa0].write16 = m68k_unused_16_w; + } /* update Z80 bus status */ zstate &= 1; @@ -316,21 +358,20 @@ void gen_zreset_w(unsigned int data, unsigned int cycles) { /* resynchronize with 68k */ mcycles_z80 = cycles; - + /* reset Z80 & YM2612 */ z80_reset(); fm_reset(cycles); } - + /* check if 68k access to Z80 bus is granted */ else if (zstate == 2) { /* enable 68k access to Z80 bus */ - _m68k_memory_map *base = &m68k_memory_map[0xa0]; - base->read8 = z80_read_byte; - base->read16 = z80_read_word; - base->write8 = z80_write_byte; - base->write16 = z80_write_word; + m68k_memory_map[0xa0].read8 = z80_read_byte; + m68k_memory_map[0xa0].read16 = z80_read_word; + m68k_memory_map[0xa0].write8 = z80_write_byte; + m68k_memory_map[0xa0].write16 = z80_write_word; /* reset Z80 & YM2612 */ z80_reset(); @@ -353,11 +394,10 @@ void gen_zreset_w(unsigned int data, unsigned int cycles) else if (zstate == 3) { /* disable 68k access to Z80 bus */ - _m68k_memory_map *base = &m68k_memory_map[0xa0]; - base->read8 = m68k_read_bus_8; - base->read16 = m68k_read_bus_16; - base->write8 = m68k_unused_8_w; - base->write16 = m68k_unused_16_w; + m68k_memory_map[0xa0].read8 = m68k_read_bus_8; + m68k_memory_map[0xa0].read16 = m68k_read_bus_16; + m68k_memory_map[0xa0].write8 = m68k_unused_8_w; + m68k_memory_map[0xa0].write16 = m68k_unused_16_w; } /* stop YM2612 */ @@ -373,6 +413,11 @@ void gen_zbank_w (unsigned int data) zbank = ((zbank >> 1) | ((data & 1) << 23)) & 0xFF8000; } + +/*-----------------------------------------------------------------------*/ +/* Z80 interrupt callback */ +/* ----------------------------------------------------------------------*/ + int z80_irq_callback (int param) { return 0xFF; diff --git a/source/genesis.h b/source/genesis.h index 47af46b..081a80a 100644 --- a/source/genesis.h +++ b/source/genesis.h @@ -1,9 +1,9 @@ /*************************************************************************************** * Genesis Plus - * Genesis internals & Bus controller + * Internal hardware & Bus controllers * * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) - * Eke-Eke (2007,2008,2009), additional code & fixes for the GCN/Wii port + * Eke-Eke (2007-2011), additional code & fixes for the GCN/Wii port * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,8 +36,7 @@ extern uint8 pico_page[7]; /* Function prototypes */ extern void gen_init(void); -extern void gen_hardreset(void); -extern void gen_softreset(int state); +extern void gen_reset(int hard_reset); extern void gen_shutdown(void); extern void gen_tmss_w(unsigned int offset, unsigned int data); extern void gen_bankswitch_w(unsigned int data); diff --git a/source/gx/fileio/file_load.c b/source/gx/fileio/file_load.c index f16d750..f083c1c 100644 --- a/source/gx/fileio/file_load.c +++ b/source/gx/fileio/file_load.c @@ -55,6 +55,9 @@ static char *fileDir; /* current device */ static int deviceType = -1; +/* DVD status flag */ +static u8 dvd_mounted = 0; + /*************************************************************************** * MountDVD * @@ -64,18 +67,31 @@ static int MountDVD(void) { GUI_MsgBoxOpen("Information", "Mounting DVD ...",1); + /* check if DVD is already mounted */ + if (dvd_mounted) + { + /* unmount DVD */ + ISO9660_Unmount("dvd:"); + dvd_mounted = 0; + } + + /* check if disc is found */ if(!dvd->isInserted()) { GUI_WaitPrompt("Error","No Disc inserted !"); return 0; } - if(!ISO9660_Mount()) + /* mount DVD */ + if(!ISO9660_Mount("dvd",dvd)) { GUI_WaitPrompt("Error","Disc can not be read !"); return 0; } - + + /* DVD is mounted */ + dvd_mounted = 1; + GUI_MsgBoxClose(); return 1; } @@ -198,7 +214,7 @@ int ParseDirectory(void) * This functions return the actual size of data copied into the buffer * ****************************************************************************/ -int LoadFile(u8 *buffer, u32 selection) +int LoadFile(u8 *buffer, u32 selection, char *filename) { char fname[MAXPATHLEN]; char *filepath; @@ -264,11 +280,14 @@ int LoadFile(u8 *buffer, u32 selection) fread(buffer + done, length, 1, fd); done += length; GUI_MsgBoxClose(); + + /* update ROM filename (with extension) */ + sprintf(filename, "%s", filelist[selection].filename); } else { /* unzip file */ - done = UnZipBuffer(buffer, fd); + done = UnZipBuffer(buffer, fd, filename); } /* close file */ diff --git a/source/gx/fileio/file_load.h b/source/gx/fileio/file_load.h index 7afd517..655550d 100644 --- a/source/gx/fileio/file_load.h +++ b/source/gx/fileio/file_load.h @@ -39,6 +39,6 @@ extern int OpenDirectory(int device); extern int UpdateDirectory(bool go_up, char *filename); extern int ParseDirectory(void); -extern int LoadFile(u8* buffer,u32 selection); +extern int LoadFile(u8* buffer,u32 selection, char *filename); #endif diff --git a/source/gx/fileio/unzip.c b/source/gx/fileio/unzip.c index 80e0c33..ffc8c5a 100644 --- a/source/gx/fileio/unzip.c +++ b/source/gx/fileio/unzip.c @@ -92,9 +92,8 @@ int IsZipFile (char *buffer) /***************************************************************************** * UnZipBuffer * - * It should be noted that there is a limit of 5MB total size for any ROM ******************************************************************************/ -int UnZipBuffer (unsigned char *outbuffer, FILE *fd) +int UnZipBuffer (unsigned char *outbuffer, FILE *fd, char *filename) { PKZIPHEADER pkzip; int zipoffset = 0; @@ -137,21 +136,27 @@ int UnZipBuffer (unsigned char *outbuffer, FILE *fd) return 0; } + /*** Get file name (first file) ***/ + int size = FLIP16 (pkzip.filenameLength); + if (size > 255) size = 255; + strncpy(filename, &readbuffer[sizeof(PKZIPHEADER)], size); + filename[size] = 0; + /*** Set ZipChunk for first pass ***/ - zipoffset = (sizeof (PKZIPHEADER) + FLIP16 (pkzip.filenameLength) + FLIP16 (pkzip.extraDataLength)); + zipoffset = (sizeof (PKZIPHEADER) + size + FLIP16 (pkzip.extraDataLength)); zipchunk = ZIPCHUNK - zipoffset; /*** Now do it! ***/ do { zs.avail_in = zipchunk; - zs.next_in = (Bytef *) & readbuffer[zipoffset]; + zs.next_in = (Bytef *) &readbuffer[zipoffset]; /*** Now inflate until input buffer is exhausted ***/ do { zs.avail_out = ZIPCHUNK; - zs.next_out = (Bytef *) & out; + zs.next_out = (Bytef *) &out; res = inflate (&zs, Z_NO_FLUSH); if (res == Z_MEM_ERROR) diff --git a/source/gx/fileio/unzip.h b/source/gx/fileio/unzip.h index c2b2fd8..30b77e9 100644 --- a/source/gx/fileio/unzip.h +++ b/source/gx/fileio/unzip.h @@ -30,6 +30,6 @@ #define _UNZIP_H_ extern int IsZipFile (char *buffer); -int UnZipBuffer (unsigned char *outbuffer, FILE *fd); +int UnZipBuffer (unsigned char *outbuffer, FILE *fd, char *filename); #endif diff --git a/source/gx/gui/filesel.c b/source/gx/gui/filesel.c index 5dc3797..53d1016 100644 --- a/source/gx/gui/filesel.c +++ b/source/gx/gui/filesel.c @@ -542,7 +542,7 @@ int FileSelector(void) areplay_shutdown(); /* load ROM file from device */ - size = LoadFile(cart.rom, selection); + size = LoadFile(cart.rom, selection, fname); /* exit menu */ GUI_DeleteMenu(m); @@ -557,7 +557,7 @@ int FileSelector(void) } /* reinitialize emulation */ - reloadrom(size,filelist[selection].filename); + reloadrom(size,fname); } return size; diff --git a/source/gx/gui/menu.c b/source/gx/gui/menu.c index ef6915a..a94d8c3 100644 --- a/source/gx/gui/menu.c +++ b/source/gx/gui/menu.c @@ -32,6 +32,10 @@ #include +#ifdef HW_RVL +#include +#endif + /* Credits */ extern const u8 Bg_credits_png[]; @@ -77,10 +81,16 @@ extern const u8 Button_delete_over_png[]; /* Controller Settings */ extern const u8 Ctrl_4wayplay_png[]; -extern const u8 Ctrl_gamepad_png[]; +extern const u8 Ctrl_gamepad_md_png[]; +extern const u8 Ctrl_gamepad_ms_png[]; extern const u8 Ctrl_justifiers_png[]; extern const u8 Ctrl_menacer_png[]; extern const u8 Ctrl_mouse_png[]; +extern const u8 Ctrl_xe_a1p_png[]; +extern const u8 Ctrl_activator_png[]; +extern const u8 Ctrl_lightphaser_png[]; +extern const u8 Ctrl_paddle_png[]; +extern const u8 Ctrl_sportspad_png[]; extern const u8 Ctrl_none_png[]; extern const u8 Ctrl_teamplayer_png[]; extern const u8 Ctrl_pad3b_png[]; @@ -1011,6 +1021,15 @@ static void soundmenu () * System Settings menu * ****************************************************************************/ +static const uint16 vc_table[4][2] = +{ + /* NTSC, PAL */ + {0xDA , 0xF2}, /* Mode 4 (192 lines) */ + {0xEA , 0x102}, /* Mode 5 (224 lines) */ + {0xDA , 0xF2}, /* Mode 4 (192 lines) */ + {0x106, 0x10A} /* Mode 5 (240 lines) */ +}; + static void systemmenu () { int ret, quit = 0; @@ -1104,11 +1123,7 @@ static void systemmenu () } /* reinitialize VC max value */ - vc_max = 0xEA + 24*vdp_pal; - if (reg[1] & 8) - { - vc_max += (28 - 20*vdp_pal); - } + vc_max = vc_table[(reg[1] >> 2) & 3][vdp_pal]; } break; @@ -1119,7 +1134,10 @@ static void systemmenu () case 2: /*** 68k Address Error ***/ config.addr_error ^= 1; - cart_hw_init (); + if (system_hw != SYSTEM_PBC) + { + md_cart_init (); + } sprintf (items[2].text, "68k Address Error: %s", config.addr_error ? "ON" : "OFF"); break; @@ -1449,6 +1467,9 @@ static void videomenu () sprintf (items[VI_OFFSET+1].text, "Borders: V ONLY"); else sprintf (items[VI_OFFSET+1].text, "Borders: NONE"); + + /* update viewport */ + bitmap.viewport.x = (config.overscan & 2) * 7; break; case VI_OFFSET+2: /*** aspect ratio ***/ @@ -1614,7 +1635,7 @@ static void ctrlmenu_raz(void) { m->buttons[i+2].data = &button_player_data; m->buttons[i+2].state |= BUTTON_ACTIVE; - if (cart.jcart && (i > 4)) + if ((cart.special & HW_J_CART) && (i > 4)) sprintf(m->items[i+2].comment,"Configure Player %d (J-CART) settings", max + 1); else sprintf(m->items[i+2].comment,"Configure Player %d settings", max + 1); @@ -1696,47 +1717,65 @@ static void ctrlmenu(void) gui_item *items = NULL; u8 *special = NULL; u32 exp; + u8 type = 0; /* System devices */ - gui_item items_sys[2][7] = + gui_item items_sys[2][13] = { { {NULL,Ctrl_none_png ,"","Select Port 1 device",110,130,48,72}, - {NULL,Ctrl_gamepad_png ,"","Select Port 1 device", 87,117,96,84}, + {NULL,Ctrl_gamepad_md_png ,"","Select Port 1 device", 85,117,96,84}, {NULL,Ctrl_mouse_png ,"","Select Port 1 device", 97,113,64,88}, {NULL,Ctrl_menacer_png ,"","Select Port 1 device", 94,113,80,88}, {NULL,Ctrl_justifiers_png ,"","Select Port 1 device", 88,117,80,84}, + {NULL,Ctrl_xe_a1p_png ,"","Select Port 1 device", 98,118,72,84}, + {NULL,Ctrl_activator_png ,"","Select Port 1 device", 94,121,72,80}, + {NULL,Ctrl_gamepad_ms_png ,"","Select Port 1 device", 91,125,84,76}, + {NULL,Ctrl_lightphaser_png,"","Select Port 1 device", 89,109,88,92}, + {NULL,Ctrl_paddle_png ,"","Select Port 1 device", 86,117,96,84}, + {NULL,Ctrl_sportspad_png ,"","Select Port 1 device", 95,117,76,84}, {NULL,Ctrl_teamplayer_png ,"","Select Port 1 device", 94,109,80,92}, {NULL,Ctrl_4wayplay_png ,"","Select Port 1 device", 98,110,72,92} }, { {NULL,Ctrl_none_png ,"","Select Port 2 device",110,300,48,72}, - {NULL,Ctrl_gamepad_png ,"","Select Port 2 device", 87,287,96,84}, + {NULL,Ctrl_gamepad_md_png ,"","Select Port 2 device", 85,287,96,84}, {NULL,Ctrl_mouse_png ,"","Select Port 2 device", 97,283,64,88}, {NULL,Ctrl_menacer_png ,"","Select Port 2 device", 94,283,80,88}, {NULL,Ctrl_justifiers_png ,"","Select Port 2 device", 88,287,80,84}, + {NULL,Ctrl_xe_a1p_png ,"","Select Port 2 device", 98,288,72,84}, + {NULL,Ctrl_activator_png ,"","Select Port 2 device", 94,291,72,80}, + {NULL,Ctrl_gamepad_ms_png ,"","Select Port 2 device", 91,295,84,76}, + {NULL,Ctrl_lightphaser_png,"","Select Port 2 device", 89,279,88,92}, + {NULL,Ctrl_paddle_png ,"","Select Port 2 device", 86,287,96,84}, + {NULL,Ctrl_sportspad_png ,"","Select Port 2 device", 95,287,76,84}, {NULL,Ctrl_teamplayer_png ,"","Select Port 2 device", 94,279,80,92}, {NULL,Ctrl_4wayplay_png ,"","Select Port 2 device", 98,280,72,92} } - }; + }; - /* Player Configuration special items */ - gui_item items_special[3][2] = + /* Specific controller options */ + gui_item items_special[4][2] = { { - /* Gamepad options */ + /* Gamepad option */ {NULL,Ctrl_pad3b_png,"Pad\nType","Use 3-buttons Pad",528,180,44,28}, {NULL,Ctrl_pad6b_png,"Pad\nType","Use 6-buttons Pad",528,180,44,28} }, { - /* Mouse options */ - {NULL,ctrl_option_off_png,"Invert\nMouse","Enable/Disable Mouse Y-Axis inversion",534,180,24,24}, - {NULL,ctrl_option_on_png ,"Invert\nMouse","Enable/Disable Mouse Y-Axis inversion",534,180,24,24}, + /* Mouse option */ + {NULL,ctrl_option_off_png,"Invert\nMouse","Enable/Disable Y-Axis inversion",534,180,24,24}, + {NULL,ctrl_option_on_png ,"Invert\nMouse","Enable/Disable Y-Axis inversion",534,180,24,24}, }, { - /* Gun options */ + /* Gun option */ {NULL,ctrl_option_off_png,"Show\nCursor","Enable/Disable Lightgun cursor",534,180,24,24}, {NULL,ctrl_option_on_png ,"Show\nCursor","Enable/Disable Lightgun cursor",534,180,24,24}, + }, + { + /* no option */ + {NULL,NULL,"No Option","",436,180,160,52}, + {NULL,NULL,"","",0,0,0,0}, } }; @@ -1768,13 +1807,10 @@ static void ctrlmenu(void) button_player_none_data.texture[0] = gxTextureOpenPNG(button_player_none_data.image[0],0); /* initialize custom images */ - items_sys[1][0].texture = items_sys[0][0].texture = gxTextureOpenPNG(items_sys[0][0].data,0); - items_sys[1][1].texture = items_sys[0][1].texture = gxTextureOpenPNG(items_sys[0][1].data,0); - items_sys[1][2].texture = items_sys[0][2].texture = gxTextureOpenPNG(items_sys[0][2].data,0); - items_sys[1][3].texture = items_sys[0][3].texture = gxTextureOpenPNG(items_sys[0][3].data,0); - items_sys[1][4].texture = items_sys[0][4].texture = gxTextureOpenPNG(items_sys[0][4].data,0); - items_sys[1][5].texture = items_sys[0][5].texture = gxTextureOpenPNG(items_sys[0][5].data,0); - items_sys[1][6].texture = items_sys[0][6].texture = gxTextureOpenPNG(items_sys[0][6].data,0); + for (i=0; i<13; i++) + { + items_sys[1][i].texture = items_sys[0][i].texture = gxTextureOpenPNG(items_sys[0][i].data,0); + } items_special[0][0].texture = gxTextureOpenPNG(items_special[0][0].data,0); items_special[0][1].texture = gxTextureOpenPNG(items_special[0][1].data,0); items_special[2][0].texture = items_special[1][0].texture = gxTextureOpenPNG(items_special[1][0].data,0); @@ -1811,16 +1847,8 @@ static void ctrlmenu(void) { case 0: /* update port 1 system */ { - if (input.system[0] == SYSTEM_MOUSE) - { - /* lightguns are never used on Port 1 */ - input.system[0] += 3; - } - else - { - /* next connected device */ - input.system[0]++; - } + /* next connected device */ + input.system[0]++; /* allow only one connected mouse */ if ((input.system[0] == SYSTEM_MOUSE) && (input.system[1] == SYSTEM_MOUSE)) @@ -1828,6 +1856,18 @@ static void ctrlmenu(void) input.system[0] += 3; } + /* Menacer & Justifiers on Port B only */ + if (input.system[0] == SYSTEM_MENACER) + { + input.system[0] += 2; + } + + /* allow only one gun type */ + if ((input.system[0] == SYSTEM_LIGHTPHASER) && ((input.system[1] == SYSTEM_MENACER) || (input.system[1] == SYSTEM_JUSTIFIER))) + { + input.system[0] ++; + } + /* 4-wayplay uses both ports */ if (input.system[0] == SYSTEM_WAYPLAY) { @@ -1838,7 +1878,7 @@ static void ctrlmenu(void) if (input.system[0] > SYSTEM_WAYPLAY) { input.system[0] = NO_SYSTEM; - input.system[1] = SYSTEM_GAMEPAD; + input.system[1] = SYSTEM_MD_GAMEPAD; } /* reset I/O ports */ @@ -1886,7 +1926,7 @@ static void ctrlmenu(void) case 1: /* update port 2 system */ { /* J-CART uses fixed configuration */ - if (cart.jcart) break; + if (cart.special & HW_J_CART) break; /* next connected device */ input.system[1] ++; @@ -1897,6 +1937,24 @@ static void ctrlmenu(void) input.system[1] ++; } + /* allow only one gun type */ + if ((input.system[0] == SYSTEM_LIGHTPHASER) && (input.system[1] == SYSTEM_MENACER)) + { + input.system[1] += 3; + } + + /* allow only one gun type */ + if ((input.system[0] == SYSTEM_LIGHTPHASER) && (input.system[1] == SYSTEM_JUSTIFIER)) + { + input.system[1] += 2; + } + + /* XE-1AP on port A only */ + if (input.system[1] == SYSTEM_XE_A1P) + { + input.system[1] ++; + } + /* 4-wayplay uses both ports */ if (input.system[1] == SYSTEM_WAYPLAY) { @@ -1907,7 +1965,7 @@ static void ctrlmenu(void) if (input.system[1] > SYSTEM_WAYPLAY) { input.system[1] = NO_SYSTEM; - input.system[0] = SYSTEM_GAMEPAD; + input.system[0] = SYSTEM_MD_GAMEPAD; } /* reset I/O ports */ @@ -2010,24 +2068,61 @@ static void ctrlmenu(void) m->buttons[9].shift[3] = 1; } + /* emulated device type */ + type = input.dev[m->selected - 2]; + /* retrieve current player informations */ - if (input.dev[m->selected-2] == DEVICE_LIGHTGUN) + switch (type) { - items = items_special[2]; - special = &config.gun_cursor[m->selected & 1]; + case DEVICE_PAD3B: + case DEVICE_PAD6B: + { + items = items_special[0]; + special = &config.input[player].padtype; + break; + } + + case DEVICE_MOUSE: + { + items = items_special[1]; + special = &config.invert_mouse; + break; + } + + case DEVICE_LIGHTGUN: + { + items = items_special[2]; + + if ((input.system[1] == SYSTEM_MENACER) || (input.system[1] == SYSTEM_JUSTIFIER)) + { + /* Menacer & Justifiers affected to devices 4 & 5 */ + special = &config.gun_cursor[m->selected & 1]; + } + else + { + /* Lightphasers affected to devices 0 & 4 */ + special = &config.gun_cursor[m->selected >> 2]; + } + break; + } + + default: + { + items = items_special[3]; + special = NULL; + break; + } } - else if (input.dev[m->selected-2] == DEVICE_MOUSE) + + if (special) { - items = items_special[1]; - special = &config.invert_mouse; + memcpy(&m->items[10],&items[*special],sizeof(gui_item)); } else { - items = items_special[0]; - special = &config.input[player].padtype; + memcpy(&m->items[10],&items[0],sizeof(gui_item)); } - memcpy(&m->items[10],&items[*special],sizeof(gui_item)); memcpy(&m->items[11],&items_device[config.input[player].device + 1],sizeof(gui_item)); /* slide in configuration window */ @@ -2037,8 +2132,18 @@ static void ctrlmenu(void) m->selected = 10; GUI_DrawMenuFX(m, 20, 0); + /* some devices require analog sticks */ + if ((type == DEVICE_XE_A1P) && ((config.input[player].device == -1) || (config.input[player].device == 1))) + { + GUI_WaitPrompt("Warning","One Analog Stick required !"); + } + else if ((type == DEVICE_ACTIVATOR) && ((config.input[player].device != 0) && (config.input[player].device != 3))) + { + GUI_WaitPrompt("Warning","Two Analog Sticks required !"); + } + /* update title */ - if (cart.jcart && (player > 1)) + if ((cart.special & HW_J_CART) && (player > 1)) { sprintf(m->title,"Controller Settings (Player %d) (J-CART)",player+1); } @@ -2051,20 +2156,25 @@ static void ctrlmenu(void) case 10: /* specific option */ { - if (special == &config.input[player].padtype) - { - if (config.input[player].device == 1) break; - config.input[player].padtype ^= 1; - input_init(); - input_reset(); - } - else + if (special) { + /* switch option */ *special ^= 1; - } - /* update menu items */ - memcpy(&m->items[10],&items[*special],sizeof(gui_item)); + /* specific case: controller type */ + if (type < 2) + { + /* re-initialize emulated device */ + input_init(); + input_reset(); + + /* update emulated device type */ + type = *special; + } + + /* update menu items */ + memcpy(&m->items[10],&items[*special],sizeof(gui_item)); + } break; } @@ -2205,8 +2315,11 @@ static void ctrlmenu(void) /* force 3-buttons gamepad when using Wiimote */ if (config.input[player].device == 1) { - config.input[player].padtype = DEVICE_3BUTTON; - memcpy(&m->items[10],&items[*special],sizeof(gui_item)); + config.input[player].padtype = DEVICE_PAD3B; + if (special) + { + memcpy(&m->items[10],&items[*special],sizeof(gui_item)); + } } #endif @@ -2218,30 +2331,12 @@ static void ctrlmenu(void) case 12: /* Controller Keys Configuration */ { - if (config.input[player].device < 0) break; - - GUI_MsgBoxOpen("Keys Configuration", "",0); - - if (config.input[player].padtype == DEVICE_6BUTTON) + if (config.input[player].device >= 0) { - /* 6-buttons gamepad */ - if (config.input[player].device == 0) - { - /* Gamecube PAD: 6-buttons w/o MODE */ - gx_input_Config(config.input[player].port, config.input[player].device, 7); - } - else - { - gx_input_Config(config.input[player].port, config.input[player].device, 8); - } + GUI_MsgBoxOpen("Keys Configuration", "",0); + gx_input_Config(config.input[player].port, config.input[player].device, type); + GUI_MsgBoxClose(); } - else - { - /* 3-Buttons gamepad, mouse, lightgun */ - gx_input_Config(config.input[player].port, config.input[player].device, 4); - } - - GUI_MsgBoxClose(); break; } } @@ -2364,13 +2459,10 @@ static void ctrlmenu(void) gxTextureClose(&button_player_none_data.texture[0]); /* delete custom images */ - gxTextureClose(&items_sys[0][0].texture); - gxTextureClose(&items_sys[0][1].texture); - gxTextureClose(&items_sys[0][2].texture); - gxTextureClose(&items_sys[0][3].texture); - gxTextureClose(&items_sys[0][4].texture); - gxTextureClose(&items_sys[0][5].texture); - gxTextureClose(&items_sys[0][6].texture); + for (i=0; i<13; i++) + { + gxTextureClose(&items_sys[0][i].texture); + } gxTextureClose(&items_special[0][0].texture); gxTextureClose(&items_special[0][1].texture); gxTextureClose(&items_special[1][0].texture); @@ -3284,6 +3376,16 @@ void menu_execute(void) #ifdef HW_RVL while (WPAD_ButtonsHeld(0)) WPAD_ScanPads(); gxTextureClose(&w_pointer); + + /* USB Mouse support */ + if ((input.system[0] == SYSTEM_MOUSE) || (input.system[1] == SYSTEM_MOUSE)) + { + MOUSE_Init(); + } + else + { + MOUSE_Deinit(); + } #endif } diff --git a/source/gx/gx_input.c b/source/gx/gx_input.c index 3759011..f2de552 100644 --- a/source/gx/gx_input.c +++ b/source/gx/gx_input.c @@ -26,6 +26,10 @@ #include "gui.h" #include "cheats.h" +#ifdef HW_RVL +#include +#endif + /* Analog sticks sensitivity */ #define ANALOG_SENSITIVITY 30 @@ -37,27 +41,15 @@ /* lower is the value, faster is the key update */ #define HELD_SPEED 4 -/* Configurable Genesis keys */ +/* Configurable keys */ #define KEY_BUTTONA 0 #define KEY_BUTTONB 1 #define KEY_BUTTONC 2 #define KEY_START 3 -#define KEY_BUTTONX 4 /* 6-buttons only */ -#define KEY_BUTTONY 5 /* 6-buttons only */ -#define KEY_BUTTONZ 6 /* 6-buttons only */ -#define KEY_MODE 7 /* 6-buttons only */ - -static const char *keys_name[MAX_KEYS] = -{ - "Button A", - "Button B", - "Button C", - "START Button", - "Button X", - "Button Y", - "Button Z", - "MODE Button" -}; +#define KEY_BUTTONX 4 +#define KEY_BUTTONY 5 +#define KEY_BUTTONZ 6 +#define KEY_MODE 7 #ifdef HW_RVL @@ -76,15 +68,15 @@ static u32 wpad_dirmap[3][4] = #endif +static char keyname[MAX_KEYS][16]; + static int held_cnt = 0; -static int softreset = 0; /***************************************************************************************/ /* Gamecube PAD support */ /***************************************************************************************/ -static void pad_config(int chan, int max_keys) +static void pad_config(int chan, int first_key, int last_key) { - int i; u16 p,key; char msg[64]; @@ -104,7 +96,7 @@ static void pad_config(int chan, int max_keys) } /* Configure each keys */ - for (i=0; i ANALOG_SENSITIVITY)) input.pad[i] |= INPUT_UP; + else if ((p & PAD_BUTTON_DOWN) || (y < -ANALOG_SENSITIVITY)) input.pad[i] |= INPUT_DOWN; + if ((p & PAD_BUTTON_LEFT) || (x < -ANALOG_SENSITIVITY)) input.pad[i] |= INPUT_LEFT; + else if ((p & PAD_BUTTON_RIGHT) || (x > ANALOG_SENSITIVITY)) input.pad[i] |= INPUT_RIGHT; + + /* Buttons */ + if (p & pad_keymap[KEY_BUTTONA]) input.pad[i] |= INPUT_A; + if (p & pad_keymap[KEY_BUTTONB]) input.pad[i] |= INPUT_B; + if (p & pad_keymap[KEY_BUTTONC]) input.pad[i] |= INPUT_C; + if (p & pad_keymap[KEY_START]) input.pad[i] |= INPUT_START; + + break; + } + + case DEVICE_XE_A1P: + { + /* Left Stick analog position [0-255] */ + input.analog[i][0] = (x + 128); + input.analog[i][1] = y ? (127 - y) : (128 - y); + + /* Right Stick analog position [0-255] */ + x = PAD_SubStickX(chan); + y = PAD_SubStickY(chan); + + /* Emulated stick is unidirectional but can be rotated */ + if (abs(x) > abs(y)) + { + input.analog[i+1][0] = (x + 128); + } + else + { + input.analog[i+1][0] = (y + 128); + } + + /* Buttons */ + if (p & pad_keymap[KEY_BUTTONA]) input.pad[i] |= INPUT_XE_A; + if (p & pad_keymap[KEY_BUTTONB]) input.pad[i] |= INPUT_XE_B; + if (p & pad_keymap[KEY_BUTTONC]) input.pad[i] |= INPUT_XE_C; + if (p & pad_keymap[KEY_START]) input.pad[i] |= INPUT_XE_START; + if (p & pad_keymap[KEY_BUTTONX]) input.pad[i] |= INPUT_XE_D; + if (p & pad_keymap[KEY_BUTTONY]) input.pad[i] |= INPUT_XE_E1; + if (p & pad_keymap[KEY_BUTTONZ]) input.pad[i] |= INPUT_XE_E2; + if (p & pad_keymap[KEY_MODE]) input.pad[i] |= INPUT_XE_SELECT; + + break; + } + + case DEVICE_SPORTSPAD: + { + /* Y analog position [0-255] */ + input.analog[i][1] = y ? (127 - y) : (128 - y); + } + + case DEVICE_PADDLE: + { + /* X analog position [0-255] */ + input.analog[i][0] = (x + 128); + + /* Buttons */ + if (p & pad_keymap[KEY_BUTTONB]) input.pad[i] |= INPUT_BUTTON1; + if (p & pad_keymap[KEY_BUTTONC]) input.pad[i] |= INPUT_BUTTON2; + if (p & pad_keymap[KEY_START]) input.pad[i] |= INPUT_START; + + break; + } + + case DEVICE_PAD2B: + { + /* D-PAD */ + if ((p & PAD_BUTTON_UP) || (y > ANALOG_SENSITIVITY)) input.pad[i] |= INPUT_UP; + else if ((p & PAD_BUTTON_DOWN) || (y < -ANALOG_SENSITIVITY)) input.pad[i] |= INPUT_DOWN; + if ((p & PAD_BUTTON_LEFT) || (x < -ANALOG_SENSITIVITY)) input.pad[i] |= INPUT_LEFT; + else if ((p & PAD_BUTTON_RIGHT) || (x > ANALOG_SENSITIVITY)) input.pad[i] |= INPUT_RIGHT; + + /* Buttons */ + if (p & pad_keymap[KEY_BUTTONB]) input.pad[i] |= INPUT_BUTTON1; + if (p & pad_keymap[KEY_BUTTONC]) input.pad[i] |= INPUT_BUTTON2; + if (p & pad_keymap[KEY_START]) input.pad[i] |= INPUT_START; + + break; + } + case DEVICE_LIGHTGUN: { - /* Lightgun cursor position (x,y) */ - input.analog[i-4][0] += x / ANALOG_SENSITIVITY; - input.analog[i-4][1] -= y / ANALOG_SENSITIVITY; - if (input.analog[i-4][0] < 0) - input.analog[i-4][0] = 0; - else if (input.analog[i-4][0] > bitmap.viewport.w) - input.analog[i-4][0] = bitmap.viewport.w; - if (input.analog[i-4][1] < 0) - input.analog[i-4][1] = 0; - else if (input.analog[i-4][1] > bitmap.viewport.h) - input.analog[i-4][1] = bitmap.viewport.h; + /* Gun screen position (x,y) */ + input.analog[i][0] += x / ANALOG_SENSITIVITY; + input.analog[i][1] -= y / ANALOG_SENSITIVITY; + + /* Limits */ + if (input.analog[i][0] < 0) input.analog[i][0] = 0; + else if (input.analog[i][0] > bitmap.viewport.w) input.analog[i][0] = bitmap.viewport.w; + if (input.analog[i][1] < 0) input.analog[i][1] = 0; + else if (input.analog[i][1] > bitmap.viewport.h) input.analog[i][1] = bitmap.viewport.h; + + /* Buttons */ + if (p & pad_keymap[KEY_BUTTONA]) input.pad[i] |= INPUT_A; + if (p & pad_keymap[KEY_BUTTONB]) input.pad[i] |= INPUT_B; + if (p & pad_keymap[KEY_BUTTONC]) input.pad[i] |= INPUT_C; + if (p & pad_keymap[KEY_START]) input.pad[i] |= INPUT_START; + break; } case DEVICE_MOUSE: { /* Mouse relative movement (-255,255) */ - input.analog[2][0] = (x / ANALOG_SENSITIVITY) * 2; - input.analog[2][1] = (y / ANALOG_SENSITIVITY) * 2; + input.analog[i][0] = (x / ANALOG_SENSITIVITY) * 2; + input.analog[i][1] = (y / ANALOG_SENSITIVITY) * 2; + + /* Y-Axis inversion */ if (config.invert_mouse) - input.analog[2][1] = -input.analog[2][1]; + { + input.analog[i][1] = -input.analog[i][1]; + } + + /* Buttons */ + if (p & pad_keymap[KEY_BUTTONA]) input.pad[i] |= INPUT_MOUSE_CENTER; + if (p & pad_keymap[KEY_BUTTONB]) input.pad[i] |= INPUT_MOUSE_LEFT; + if (p & pad_keymap[KEY_BUTTONC]) input.pad[i] |= INPUT_MOUSE_RIGHT; + if (p & pad_keymap[KEY_START]) input.pad[i] |= INPUT_START; + break; } - default: + case DEVICE_TABLET: { - if (system_hw != SYSTEM_PICO) + /* D-PAD */ + if (p & PAD_BUTTON_UP) input.pad[i] |= INPUT_UP; + else if (p & PAD_BUTTON_DOWN) input.pad[i] |= INPUT_DOWN; + if (p & PAD_BUTTON_LEFT) input.pad[i] |= INPUT_LEFT; + else if (p & PAD_BUTTON_RIGHT) input.pad[i] |= INPUT_RIGHT; + + /* PEN screen position (x,y) */ + input.analog[0][0] += x / ANALOG_SENSITIVITY; + input.analog[0][1] -= y / ANALOG_SENSITIVITY; + + /* Limits */ + if (input.analog[0][0] < 0x17c) input.analog[0][0] = 0x17c; + else if (input.analog[0][0] > 0x3c) input.analog[0][0] = 0x3c; + if (input.analog[0][1] < 0x1fc) input.analog[0][1] = 0x1fc; + else if (input.analog[0][1] > 0x3f3) input.analog[0][1] = 0x3f3; + + /* PEN & RED buttons */ + if (p & pad_keymap[KEY_BUTTONA]) input.pad[i] |= INPUT_A; + if (p & pad_keymap[KEY_BUTTONB]) input.pad[i] |= INPUT_B; + + break; + } + + case DEVICE_ACTIVATOR: + { + /* Left & right analog stick angle [0-360] */ + float ang; + + /* Left stick values */ + if ((abs(x) > ANALOG_SENSITIVITY) || (abs(y) > ANALOG_SENSITIVITY)) { - /* Gamepad */ - if ((p & PAD_BUTTON_UP) || (y > ANALOG_SENSITIVITY)) - input.pad[i] |= INPUT_UP; - else if ((p & PAD_BUTTON_DOWN) || (y < -ANALOG_SENSITIVITY)) - input.pad[i] |= INPUT_DOWN; - if ((p & PAD_BUTTON_LEFT) || (x < -ANALOG_SENSITIVITY)) - input.pad[i] |= INPUT_LEFT; - else if ((p & PAD_BUTTON_RIGHT) || (x > ANALOG_SENSITIVITY)) - input.pad[i] |= INPUT_RIGHT; + /* Calculate angle (in degree) */ + ang = 90.0 - (atan((float)y / (float)x) * 180.0 / M_PI); + if (x < 0) ang += 180.0; + + /* 8 bottom sensors = 8 areas */ + if ((ang > 22.5) && (ang <= 67.5)) input.pad[i] |= INPUT_ACTIVATOR_2L; + else if ((ang > 67.5) && (ang <= 112.5)) input.pad[i] |= INPUT_ACTIVATOR_3L; + else if ((ang > 112.5) && (ang <= 157.5)) input.pad[i] |= INPUT_ACTIVATOR_4L; + else if ((ang > 157.5) && (ang <= 202.5)) input.pad[i] |= INPUT_ACTIVATOR_5L; + else if ((ang > 202.5) && (ang <= 247.5)) input.pad[i] |= INPUT_ACTIVATOR_6L; + else if ((ang > 247.5) && (ang <= 292.5)) input.pad[i] |= INPUT_ACTIVATOR_7L; + else if ((ang > 292.5) && (ang <= 337.5)) input.pad[i] |= INPUT_ACTIVATOR_8L; + else input.pad[i] |= INPUT_ACTIVATOR_1L; } - else if (!i) + + /* Right stick values */ + x = PAD_SubStickX(chan); + y = PAD_SubStickY(chan); + + if ((abs(x) > ANALOG_SENSITIVITY) || (abs(y) > ANALOG_SENSITIVITY)) { - /* PICO PEN tablet position (x,y) */ - input.analog[0][0] += x / ANALOG_SENSITIVITY; - input.analog[0][1] -= y / ANALOG_SENSITIVITY; - if (input.analog[0][0] < 0x17c) - input.analog[0][0] = 0x17c; - else if (input.analog[0][0] > 0x3c) - input.analog[0][0] = 0x3c; - if (input.analog[0][1] < 0x1fc) - input.analog[0][1] = 0x1fc; - else if (input.analog[0][1] > 0x3f3) - input.analog[0][1] = 0x3f3; + /* Calculate angle (in degree) */ + ang = 90.0 - (atan((float)y / (float)x) * 180.0 / M_PI); + if (x < 0) ang += 180.0; + + /* 8 top sensors = 8 areas */ + if ((ang > 22.5) && (ang <= 67.5)) input.pad[i] |= INPUT_ACTIVATOR_2U; + else if ((ang > 67.5) && (ang <= 112.5)) input.pad[i] |= INPUT_ACTIVATOR_3U; + else if ((ang > 112.5) && (ang <= 157.5)) input.pad[i] |= INPUT_ACTIVATOR_4U; + else if ((ang > 157.5) && (ang <= 202.5)) input.pad[i] |= INPUT_ACTIVATOR_5U; + else if ((ang > 202.5) && (ang <= 247.5)) input.pad[i] |= INPUT_ACTIVATOR_6U; + else if ((ang > 247.5) && (ang <= 292.5)) input.pad[i] |= INPUT_ACTIVATOR_7U; + else if ((ang > 292.5) && (ang <= 337.5)) input.pad[i] |= INPUT_ACTIVATOR_8U; + else input.pad[i] |= INPUT_ACTIVATOR_1U; } + break; } } @@ -273,92 +384,94 @@ static void pad_update(s8 chan, u8 i) /***************************************************************************************/ #ifdef HW_RVL -static s8 WPAD_StickX(WPADData *data,u8 right) +static int wpad_StickX(WPADData *data, u8 right) { - float mag = 0.0; - float ang = 0.0; + struct joystick_t* js = NULL; switch (data->exp.type) { case WPAD_EXP_NUNCHUK: - case WPAD_EXP_GUITARHERO3: - if (right == 0) - { - mag = data->exp.nunchuk.js.mag; - ang = data->exp.nunchuk.js.ang; - } + js = right ? NULL : &data->exp.nunchuk.js; break; case WPAD_EXP_CLASSIC: - if (right == 0) - { - mag = data->exp.classic.ljs.mag; - ang = data->exp.classic.ljs.ang; - } - else - { - mag = data->exp.classic.rjs.mag; - ang = data->exp.classic.rjs.ang; - } + js = right ? &data->exp.classic.rjs : &data->exp.classic.ljs; break; default: break; } - /* calculate X value (angle need to be converted into radian) */ - if (mag > 1.0) mag = 1.0; - else if (mag < -1.0) mag = -1.0; - double val = mag * sin(M_PI * ang/180.0f); - - return (s8)(val * 128.0f); + if (js) + { + /* raw X value */ + int x = js->pos.x; + + /* value returned is sometime above calibrated limits */ + if (x > js->max.x) return 127; + if (x < js->min.x) return -128; + + /* adjust against center position */ + x -= js->center.x; + + /* return interpolated range [-128;127] */ + if (x > 0) + { + return (int)(127.0 * ((float)x / (float)(js->max.x - js->center.x))); + } + { + return (int)(128.0 * ((float)x / (float)(js->center.x - js->min.x))); + } + } + + return 0; } - -static s8 WPAD_StickY(WPADData *data, u8 right) +static int wpad_StickY(WPADData *data, u8 right) { - float mag = 0.0; - float ang = 0.0; + struct joystick_t* js = NULL; switch (data->exp.type) { case WPAD_EXP_NUNCHUK: - case WPAD_EXP_GUITARHERO3: - if (right == 0) - { - mag = data->exp.nunchuk.js.mag; - ang = data->exp.nunchuk.js.ang; - } + js = right ? NULL : &data->exp.nunchuk.js; break; case WPAD_EXP_CLASSIC: - if (right == 0) - { - mag = data->exp.classic.ljs.mag; - ang = data->exp.classic.ljs.ang; - } - else - { - mag = data->exp.classic.rjs.mag; - ang = data->exp.classic.rjs.ang; - } + js = right ? &data->exp.classic.rjs : &data->exp.classic.ljs; break; default: break; } - /* calculate X value (angle need to be converted into radian) */ - if (mag > 1.0) mag = 1.0; - else if (mag < -1.0) mag = -1.0; - double val = mag * cos(M_PI * ang/180.0f); - - return (s8)(val * 128.0f); + if (js) + { + /* raw Y value */ + int y = js->pos.y; + + /* value returned is sometime above calibrated limits */ + if (y > js->max.y) return 127; + if (y < js->min.y) return -128; + + /* adjust against center position */ + y -= js->center.y; + + /* return interpolated range [-128;127] */ + if (y > 0) + { + return (int)(127.0 * ((float)y / (float)(js->max.y - js->center.y))); + } + { + return (int)(128.0 * ((float)y / (float)(js->center.y - js->min.y))); + } + } + + return 0; } -static void wpad_config(u8 chan, u8 exp, u8 max_keys) +static void wpad_config(u8 exp, int chan, int first_key, int last_key) { - int i; char msg[64]; u32 key,p = 255; @@ -382,7 +495,7 @@ static void wpad_config(u8 chan, u8 exp, u8 max_keys) } /* loop on each mapped keys */ - for (i=0; ibtns_h; - /* Soft Reset */ - if (((p & WPAD_BUTTON_PLUS) && (p & WPAD_BUTTON_MINUS)) || - ((p & WPAD_CLASSIC_BUTTON_PLUS) && (p & WPAD_CLASSIC_BUTTON_MINUS))) - { - gen_softreset(1); - softreset = 1; - return; - } - else if (softreset & 1) - { - gen_softreset(0); - softreset = 0; - return; - } - - /* Menu Request */ + /* Menu Key */ if ((p & WPAD_BUTTON_HOME) || (p & WPAD_CLASSIC_BUTTON_HOME)) { ConfigRequested = 1; return; } - /* Retrieve current key mapping */ - u32 *wpad_keymap = config.wpad_keymap[exp + (3 * chan)]; - - /* Generic buttons */ - if (p & wpad_keymap[KEY_BUTTONA]) - input.pad[i] |= INPUT_A; - if (p & wpad_keymap[KEY_BUTTONB]) - input.pad[i] |= INPUT_B; - if (p & wpad_keymap[KEY_BUTTONC]) - input.pad[i] |= INPUT_C; - if (p & wpad_keymap[KEY_BUTTONX]) - input.pad[i] |= INPUT_X; - if (p & wpad_keymap[KEY_BUTTONY]) - input.pad[i] |= INPUT_Y; - if (p & wpad_keymap[KEY_BUTTONZ]) - input.pad[i] |= INPUT_Z; - if (p & wpad_keymap[KEY_START]) - input.pad[i] |= INPUT_START; - if (p & wpad_keymap[KEY_MODE]) - input.pad[i] |= INPUT_MODE; - /* Analog sticks */ + s8 x = 0; + s8 y = 0; + if (exp != WPAD_EXP_NONE) { - x = WPAD_StickX(data,0); - y = WPAD_StickY(data,0); + x = wpad_StickX(data,0); + y = wpad_StickY(data,0); } - /* Emulated device specific */ + /* Retrieve current key mapping */ + u32 *wpad_keymap = config.wpad_keymap[exp + (chan * 3)]; + + /* Emulated device */ switch (input.dev[i]) { + case DEVICE_PAD6B: + { + /* Extra buttons */ + if (p & wpad_keymap[KEY_BUTTONX]) input.pad[i] |= INPUT_X; + if (p & wpad_keymap[KEY_BUTTONY]) input.pad[i] |= INPUT_Y; + if (p & wpad_keymap[KEY_BUTTONZ]) input.pad[i] |= INPUT_Z; + if (p & wpad_keymap[KEY_MODE]) input.pad[i] |= INPUT_MODE; + } + + case DEVICE_PAD3B: + { + /* D- PAD */ + if ((p & wpad_dirmap[exp][PAD_UP]) || (y > ANALOG_SENSITIVITY)) input.pad[i] |= INPUT_UP; + else if ((p & wpad_dirmap[exp][PAD_DOWN]) || (y < -ANALOG_SENSITIVITY)) input.pad[i] |= INPUT_DOWN; + if ((p & wpad_dirmap[exp][PAD_LEFT]) || (x < -ANALOG_SENSITIVITY)) input.pad[i] |= INPUT_LEFT; + else if ((p & wpad_dirmap[exp][PAD_RIGHT]) || (x > ANALOG_SENSITIVITY)) input.pad[i] |= INPUT_RIGHT; + + /* Buttons */ + if (p & wpad_keymap[KEY_BUTTONA]) input.pad[i] |= INPUT_A; + if (p & wpad_keymap[KEY_BUTTONB]) input.pad[i] |= INPUT_B; + if (p & wpad_keymap[KEY_BUTTONC]) input.pad[i] |= INPUT_C; + if (p & wpad_keymap[KEY_START]) input.pad[i] |= INPUT_START; + + break; + } + + case DEVICE_XE_A1P: + { + /* Left Stick analog position [0-255] */ + input.analog[i][0] = (x + 128); + input.analog[i][1] = y ? (127 - y) : (128 - y); + + /* Right Stick analog position [0-255] */ + if (exp == WPAD_EXP_CLASSIC) + { + /* Classic Controller right stick */ + x = wpad_StickX(data,1); + y = wpad_StickY(data,1); + + /* Emulated stick is unidirectional but can be rotated */ + if (abs(x) > abs(y)) + { + input.analog[i+1][0] = (x + 128); + } + else + { + input.analog[i+1][0] = (y + 128); + } + } + else + { + /* Wiimote D-PAD */ + if ((p & wpad_dirmap[exp][PAD_DOWN]) || (p & wpad_dirmap[exp][PAD_LEFT])) input.analog[i+1][0]-=2; + else if ((p & wpad_dirmap[exp][PAD_UP]) || (p & wpad_dirmap[exp][PAD_RIGHT])) input.analog[i+1][0]+=2; + + /* Limits */ + if (input.analog[i+1][0] < 0) input.analog[i+1][0] = 0; + else if (input.analog[i+1][0] > 255) input.analog[i+1][0] = 255; + } + + /* Buttons */ + if (p & wpad_keymap[KEY_BUTTONA]) input.pad[i] |= INPUT_XE_A; + if (p & wpad_keymap[KEY_BUTTONB]) input.pad[i] |= INPUT_XE_B; + if (p & wpad_keymap[KEY_BUTTONC]) input.pad[i] |= INPUT_XE_C; + if (p & wpad_keymap[KEY_START]) input.pad[i] |= INPUT_XE_START; + if (p & wpad_keymap[KEY_BUTTONX]) input.pad[i] |= INPUT_XE_D; + if (p & wpad_keymap[KEY_BUTTONY]) input.pad[i] |= INPUT_XE_E1; + if (p & wpad_keymap[KEY_BUTTONZ]) input.pad[i] |= INPUT_XE_E2; + if (p & wpad_keymap[KEY_MODE]) input.pad[i] |= INPUT_XE_SELECT; + + break; + } + + case DEVICE_SPORTSPAD: + { + /* X analog position [0-255] */ + if (p & wpad_dirmap[exp][PAD_LEFT]) input.analog[i][0]-=2; + else if (p & wpad_dirmap[exp][PAD_RIGHT]) input.analog[i][0]+=2; + else input.analog[i][0] = (x + 128); + + /* Y analog position [0-255] */ + if (p & wpad_dirmap[exp][PAD_UP]) input.analog[i][1]-=2; + else if (p & wpad_dirmap[exp][PAD_DOWN]) input.analog[i][1]+=2; + else input.analog[i][1] = y ? (127 - y) : (128 - y); + + /* Limits */ + if (input.analog[i][0] < 0) input.analog[i][0] = 0; + else if (input.analog[i][0] > 255) input.analog[i][0] = 255; + if (input.analog[i][1] < 0) input.analog[i][1] = 0; + else if (input.analog[i][1] > 255) input.analog[i][1] = 255; + + /* Buttons */ + if (p & wpad_keymap[KEY_BUTTONB]) input.pad[i] |= INPUT_BUTTON1; + if (p & wpad_keymap[KEY_BUTTONC]) input.pad[i] |= INPUT_BUTTON2; + if (p & wpad_keymap[KEY_START]) input.pad[i] |= INPUT_START; + + break; + } + + case DEVICE_PADDLE: + { + /* X analog position [0-255] */ + if (exp == WPAD_EXP_NONE) + { + /* Wiimote D-PAD */ + if (p & wpad_dirmap[exp][PAD_LEFT]) input.analog[i][0]-=2; + else if (p & wpad_dirmap[exp][PAD_RIGHT]) input.analog[i][0]+=2; + + /* Limits */ + if (input.analog[i][0] < 0) input.analog[i][0] = 0; + else if (input.analog[i][0] > 255) input.analog[i][0] = 255; + } + else + { + /* Left analog stick */ + input.analog[i][0] = (x + 128); + } + + /* Buttons */ + if (p & wpad_keymap[KEY_BUTTONB]) input.pad[i] |= INPUT_BUTTON1; + if (p & wpad_keymap[KEY_START]) input.pad[i] |= INPUT_START; + + break; + } + + case DEVICE_PAD2B: + { + /* D-PAD */ + if ((p & wpad_dirmap[exp][PAD_UP]) || (y > ANALOG_SENSITIVITY)) input.pad[i] |= INPUT_UP; + else if ((p & wpad_dirmap[exp][PAD_DOWN]) || (y < -ANALOG_SENSITIVITY)) input.pad[i] |= INPUT_DOWN; + if ((p & wpad_dirmap[exp][PAD_LEFT]) || (x < -ANALOG_SENSITIVITY)) input.pad[i] |= INPUT_LEFT; + else if ((p & wpad_dirmap[exp][PAD_RIGHT]) || (x > ANALOG_SENSITIVITY)) input.pad[i] |= INPUT_RIGHT; + + /* Buttons */ + if (p & wpad_keymap[KEY_BUTTONB]) input.pad[i] |= INPUT_BUTTON1; + if (p & wpad_keymap[KEY_BUTTONC]) input.pad[i] |= INPUT_BUTTON2; + if (p & wpad_keymap[KEY_START]) input.pad[i] |= INPUT_START; + + break; + } + case DEVICE_LIGHTGUN: { - /* Lightgun cursor position (x,y) */ - if (x || y) + /* Gun screen position (x,y) */ + if (exp != WPAD_EXP_CLASSIC) { - /* analog stick absolute positions */ - input.analog[i-4][0] += x / ANALOG_SENSITIVITY; - input.analog[i-4][1] -= y / ANALOG_SENSITIVITY; - if (input.analog[i-4][0] < 0) - input.analog[i-4][0] = 0; - else if (input.analog[i-4][0] > bitmap.viewport.w) - input.analog[i-4][0] = bitmap.viewport.w; - if (input.analog[i-4][1] < 0) - input.analog[i-4][1] = 0; - else if (input.analog[i-4][1] > bitmap.viewport.h) - input.analog[i-4][1] = bitmap.viewport.h; + /* Wiimote IR */ + struct ir_t ir; + WPAD_IR(chan, &ir); + + if (ir.valid) + { + /* screen position */ + input.analog[i][0] = (ir.x * bitmap.viewport.w) / 640; + input.analog[i][1] = (ir.y * bitmap.viewport.h) / 480; + } + else + { + /* lightgun should point outside screen area */ + input.analog[i][0] = 512; + input.analog[i][1] = 512; + } } + else + { + /* Classic Controller analog stick */ + input.analog[i][0] += x / ANALOG_SENSITIVITY; + input.analog[i][1] -= y / ANALOG_SENSITIVITY; + + /* Limits */ + if (input.analog[i][0] < 0) input.analog[i][0] = 0; + else if (input.analog[i][0] > bitmap.viewport.w) input.analog[i][0] = bitmap.viewport.w; + if (input.analog[i][1] < 0) input.analog[i][1] = 0; + else if (input.analog[i][1] > bitmap.viewport.h) input.analog[i][1] = bitmap.viewport.h; + } + + /* Buttons */ + if (p & wpad_keymap[KEY_BUTTONA]) input.pad[i] |= INPUT_A; + if (p & wpad_keymap[KEY_BUTTONB]) input.pad[i] |= INPUT_B; + if (p & wpad_keymap[KEY_BUTTONC]) input.pad[i] |= INPUT_C; + if (p & wpad_keymap[KEY_START]) input.pad[i] |= INPUT_START; + + break; + } + + case DEVICE_MOUSE: + { + /* Mouse relative movement (-255,255) */ + input.analog[i][0] = (x / ANALOG_SENSITIVITY) * 2; + input.analog[i][1] = (y / ANALOG_SENSITIVITY) * 2; + + /* Wiimote IR (buggy) */ + if (exp != WPAD_EXP_CLASSIC) + { + struct ir_t ir; + WPAD_IR(chan, &ir); + + /* Only if Wiimote is pointed to screen */ + if(ir.smooth_valid) + { + input.analog[i][0] = (int)((ir.sx - 512) / 2 / ANALOG_SENSITIVITY); + input.analog[i][1] = (int)((ir.sy - 384) * 2 / 3 / ANALOG_SENSITIVITY); + } + } + + /* USB mouse support */ + if (MOUSE_IsConnected()) + { + /* read mouse data */ + mouse_event event; + MOUSE_GetEvent(&event); + MOUSE_FlushEvents(); + + /* mouse position (-127;+127) -> (-255;+255) */ + input.analog[i][0] = event.rx * 2; + input.analog[i][1] = event.ry * 2; + + /* mouse buttons */ + if (event.button & 1) input.pad[i] |= INPUT_MOUSE_RIGHT; + if (event.button & 2) input.pad[i] |= INPUT_MOUSE_CENTER; + if (event.button & 4) input.pad[i] |= INPUT_MOUSE_LEFT; + } + + /* Y-Axis inversion */ + if (config.invert_mouse) + { + input.analog[i][1] = -input.analog[i][1]; + } + + /* Buttons */ + if (p & wpad_keymap[KEY_BUTTONA]) input.pad[i] |= INPUT_MOUSE_CENTER; + if (p & wpad_keymap[KEY_BUTTONB]) input.pad[i] |= INPUT_MOUSE_LEFT; + if (p & wpad_keymap[KEY_BUTTONC]) input.pad[i] |= INPUT_MOUSE_RIGHT; + if (p & wpad_keymap[KEY_START]) input.pad[i] |= INPUT_START; + + break; + } + + case DEVICE_TABLET: + { + /* D-PAD */ + if (p & PAD_BUTTON_UP) input.pad[i] |= INPUT_UP; + else if (p & PAD_BUTTON_DOWN) input.pad[i] |= INPUT_DOWN; + if (p & PAD_BUTTON_LEFT) input.pad[i] |= INPUT_LEFT; + else if (p & PAD_BUTTON_RIGHT) input.pad[i] |= INPUT_RIGHT; + + /* PEN screen position (x,y) */ + input.analog[0][0] += x / ANALOG_SENSITIVITY; + input.analog[0][1] -= y / ANALOG_SENSITIVITY; + + /* Limits */ + if (input.analog[0][0] < 0x17c) input.analog[0][0] = 0x17c; + else if (input.analog[0][0] > 0x3c) input.analog[0][0] = 0x3c; + if (input.analog[0][1] < 0x1fc) input.analog[0][1] = 0x1fc; + else if (input.analog[0][1] > 0x3f3) input.analog[0][1] = 0x3f3; /* Wiimote IR */ if (exp != WPAD_EXP_CLASSIC) @@ -555,123 +885,58 @@ static void wpad_update(s8 chan, u8 i, u32 exp) WPAD_IR(chan, &ir); if (ir.valid) { - input.analog[i-4][0] = (ir.x * bitmap.viewport.w) / 640; - input.analog[i-4][1] = (ir.y * bitmap.viewport.h) / 480; - - /* use default trigger button */ - if (p & WPAD_BUTTON_B) input.pad[i] |= INPUT_B; + input.analog[0][0] = 0x3c + (ir.x * (0x17c - 0x3c + 1)) / 640; + input.analog[0][1] = 0x1fc + (ir.y * (0x3f3 - 0x1fc + 1)) / 480; } } + /* PEN & RED buttons */ + if (p & wpad_keymap[KEY_BUTTONA]) input.pad[i] |= INPUT_A; + if (p & wpad_keymap[KEY_BUTTONB]) input.pad[i] |= INPUT_B; + break; } - case DEVICE_MOUSE: + case DEVICE_ACTIVATOR: { - /* Mouse relative movement (9 bits signed value) */ - input.analog[2][0] = 0; - input.analog[2][1] = 0; - - /* Nunchuk/Classic controller analog stick */ - if (x || y) + /* Classic Controller only */ + if (exp == WPAD_EXP_CLASSIC) { - input.analog[2][0] = (x * 2) / ANALOG_SENSITIVITY; - input.analog[2][1] = -(y * 2) / ANALOG_SENSITIVITY; - } + /* Left stick */ + float mag = data->exp.classic.ljs.mag; + float ang = data->exp.classic.ljs.ang; - /* Wiimote IR */ - if (exp != WPAD_EXP_CLASSIC) - { - struct ir_t ir; - WPAD_IR(chan, &ir); - if(ir.smooth_valid) + if (mag > 0.5) { - input.analog[2][0] = (int)((ir.sx - 512) / 2 / ANALOG_SENSITIVITY); - input.analog[2][1] = (int)((ir.sy - 384) * 2 / 3 / ANALOG_SENSITIVITY); + /* 8 bottom sensors = 8 areas */ + if ((ang > 22.5) && (ang <= 67.5)) input.pad[i] |= INPUT_ACTIVATOR_2L; + else if ((ang > 67.5) && (ang <= 112.5)) input.pad[i] |= INPUT_ACTIVATOR_3L; + else if ((ang > 112.5) && (ang <= 157.5)) input.pad[i] |= INPUT_ACTIVATOR_4L; + else if ((ang > 157.5) && (ang <= 202.5)) input.pad[i] |= INPUT_ACTIVATOR_5L; + else if ((ang > 202.5) && (ang <= 247.5)) input.pad[i] |= INPUT_ACTIVATOR_6L; + else if ((ang > 247.5) && (ang <= 292.5)) input.pad[i] |= INPUT_ACTIVATOR_7L; + else if ((ang > 292.5) && (ang <= 337.5)) input.pad[i] |= INPUT_ACTIVATOR_8L; + else input.pad[i] |= INPUT_ACTIVATOR_1L; + } - /* use default trigger button */ - if (p & WPAD_BUTTON_B) - input.pad[i] |= INPUT_B; + /* Right stick */ + mag = data->exp.classic.rjs.mag; + ang = data->exp.classic.rjs.ang; + + if (mag > 0.5) + { + /* 8 top sensors = 8 areas */ + if ((ang > 22.5) && (ang <= 67.5)) input.pad[i] |= INPUT_ACTIVATOR_2U; + else if ((ang > 67.5) && (ang <= 112.5)) input.pad[i] |= INPUT_ACTIVATOR_3U; + else if ((ang > 112.5) && (ang <= 157.5)) input.pad[i] |= INPUT_ACTIVATOR_4U; + else if ((ang > 157.5) && (ang <= 202.5)) input.pad[i] |= INPUT_ACTIVATOR_5U; + else if ((ang > 202.5) && (ang <= 247.5)) input.pad[i] |= INPUT_ACTIVATOR_6U; + else if ((ang > 247.5) && (ang <= 292.5)) input.pad[i] |= INPUT_ACTIVATOR_7U; + else if ((ang > 292.5) && (ang <= 337.5)) input.pad[i] |= INPUT_ACTIVATOR_8U; + else input.pad[i] |= INPUT_ACTIVATOR_1U; } } -#ifdef USB_MOUSE - /* USB mouse support (FIXME) */ - if (MOUSE_IsConnected()) - { - mouse_event event; - MOUSE_GetEvent(&event); - MOUSE_FlushEvents(); - - /* relative X/Y position: (-128;+127) -> (-255;+255) */ - if (event.rx) - input.analog[2][0] = (event.rx * 2) + 1; - else - input.analog[2][0] = 0; - if (event.ry) - input.analog[2][1] = (event.ry * 2) + 1; - else - input.analog[2][1] = 0; - - /* pressed buttons */ - if (event.button & 1) - input.pad[i] |= INPUT_A; - if (event.button & 2) - input.pad[i] |= INPUT_B; - } -#endif - - /* Invert Y coordinate */ - if (!config.invert_mouse) - input.analog[2][1] = -input.analog[2][1]; - - break; - } - - default: - { - if (system_hw != SYSTEM_PICO) - { - /* gamepad */ - if ((p & wpad_dirmap[exp][PAD_UP]) || (y > ANALOG_SENSITIVITY)) - input.pad[i] |= INPUT_UP; - else if ((p & wpad_dirmap[exp][PAD_DOWN]) || (y < -ANALOG_SENSITIVITY)) - input.pad[i] |= INPUT_DOWN; - if ((p & wpad_dirmap[exp][PAD_LEFT]) || (x < -ANALOG_SENSITIVITY)) - input.pad[i] |= INPUT_LEFT; - else if ((p & wpad_dirmap[exp][PAD_RIGHT]) || (x > ANALOG_SENSITIVITY)) - input.pad[i] |= INPUT_RIGHT; - } - else if (!i) - { - /* PICO PEN tablet position (x,y) */ - if (x || y) - { - /* analog stick relative positions */ - input.analog[0][0] += x / ANALOG_SENSITIVITY; - input.analog[0][1] -= y / ANALOG_SENSITIVITY; - if (input.analog[0][0] < 0x17c) input.analog[0][0] = 0x17c; - else if (input.analog[0][0] > 0x3c) input.analog[0][0] = 0x3c; - if (input.analog[0][1] < 0x1fc) input.analog[0][1] = 0x1fc; - else if (input.analog[0][1] > 0x3f3) input.analog[0][1] = 0x3f3; - } - - /* Wiimote IR */ - if (exp != WPAD_EXP_CLASSIC) - { - struct ir_t ir; - WPAD_IR(chan, &ir); - if (ir.valid) - { - input.analog[0][0] = 0x3c + (ir.x * (0x17c - 0x3c + 1)) / 640; - input.analog[0][1] = 0x1fc + (ir.y * (0x3f3 - 0x1fc + 1)) / 480; - - /* use default trigger button */ - if (p & WPAD_BUTTON_B) - input.pad[i] |= INPUT_B; - } - } - } break; } } @@ -891,23 +1156,135 @@ void gx_input_SetDefault(void) } /* default emulated inputs */ - input.system[0] = SYSTEM_GAMEPAD; - input.system[1] = (config.input[1].device != -1) ? SYSTEM_GAMEPAD : NO_SYSTEM; + input.system[0] = SYSTEM_MD_GAMEPAD; + input.system[1] = (config.input[1].device != -1) ? SYSTEM_MD_GAMEPAD : NO_SYSTEM; } -void gx_input_Config(u8 chan, u8 type, u8 max) +void gx_input_Config(u8 chan, u8 device, u8 type) { + int first_key, last_key; + + /* emulated device */ switch (type) { - case 0: - pad_config(chan, max); + case DEVICE_PADDLE: + case DEVICE_PAD2B: + case DEVICE_SPORTSPAD: + { + first_key = KEY_BUTTONB; + last_key = KEY_START; + sprintf(keyname[KEY_BUTTONB],"Button 1"); + sprintf(keyname[KEY_BUTTONC],"Button 2"); + sprintf(keyname[KEY_START],"PAUSE Button"); break; - + } + + case DEVICE_XE_A1P: + { + first_key = KEY_BUTTONA; + last_key = KEY_MODE; + sprintf(keyname[KEY_BUTTONA],"Button A"); + sprintf(keyname[KEY_BUTTONB],"Button B"); + sprintf(keyname[KEY_BUTTONC],"Button C"); + sprintf(keyname[KEY_START],"START Button"); + sprintf(keyname[KEY_BUTTONX],"Button D"); + sprintf(keyname[KEY_BUTTONY],"Button E1"); + sprintf(keyname[KEY_BUTTONZ],"Button E2"); + sprintf(keyname[KEY_MODE],"SELECT Button"); + break; + } + + case DEVICE_MOUSE: + { + first_key = KEY_BUTTONA; + last_key = KEY_START; + sprintf(keyname[KEY_BUTTONA],"Middle Button"); + sprintf(keyname[KEY_BUTTONB],"Left Button"); + sprintf(keyname[KEY_BUTTONC],"Right Button"); + sprintf(keyname[KEY_START],"START Button"); + break; + } + + case DEVICE_PAD3B: + { + first_key = KEY_BUTTONA; + last_key = KEY_START; + sprintf(keyname[KEY_BUTTONA],"Button A"); + sprintf(keyname[KEY_BUTTONB],"Button B"); + sprintf(keyname[KEY_BUTTONC],"Button C"); + sprintf(keyname[KEY_START],"START Button"); + break; + } + + case DEVICE_PAD6B: + { + first_key = KEY_BUTTONA; + last_key = KEY_MODE; + sprintf(keyname[KEY_BUTTONA],"Button A"); + sprintf(keyname[KEY_BUTTONB],"Button B"); + sprintf(keyname[KEY_BUTTONC],"Button C"); + sprintf(keyname[KEY_START],"START Button"); + sprintf(keyname[KEY_BUTTONX],"Button X"); + sprintf(keyname[KEY_BUTTONY],"Button Y"); + sprintf(keyname[KEY_BUTTONZ],"Button Z"); + sprintf(keyname[KEY_MODE],"MODE Button"); + break; + } + + case DEVICE_LIGHTGUN: + { + if ((input.system[1] == SYSTEM_MENACER) || (input.system[1] == SYSTEM_JUSTIFIER)) + { + first_key = KEY_BUTTONA; + last_key = KEY_START; + sprintf(keyname[KEY_BUTTONA],"Button A"); + sprintf(keyname[KEY_BUTTONB],"Button B"); + sprintf(keyname[KEY_BUTTONC],"Button C"); + sprintf(keyname[KEY_START],"START Button"); + } + else + { + first_key = KEY_BUTTONB; + last_key = KEY_START; + sprintf(keyname[KEY_BUTTONB],"Button 1 (Fire)"); + sprintf(keyname[KEY_BUTTONC],"Button 2"); + sprintf(keyname[KEY_START],"PAUSE Button"); + } + break; + } + + case DEVICE_TABLET: + { + first_key = KEY_BUTTONA; + last_key = KEY_BUTTONB; + sprintf(keyname[KEY_BUTTONA],"PEN Button"); + sprintf(keyname[KEY_BUTTONB],"RED Button"); + break; + } + default: + { + GUI_WaitPrompt("Info","Activator is not configurable !"); + return; + } + } + + /* Input device */ + switch (device) + { + case 0: + { + pad_config(chan, first_key, last_key); + break; + } + + default: + { #ifdef HW_RVL - wpad_config(chan,type-1, max); + wpad_config(device - 1, chan, first_key, last_key); #endif break; + } } } @@ -919,20 +1296,6 @@ void gx_input_UpdateEmu(void) WPAD_ScanPads(); #endif - /* Check RESET button status */ - if (SYS_ResetButtonDown()) - { - gen_softreset(1); - softreset = 2; - return; - } - else if (softreset & 2) - { - gen_softreset(0); - softreset = 0; - return; - } - int i, player = 0; for (i=0; i 0) - wpad_update(config.input[player].port,i, config.input[player].device - 1); + { + wpad_update(config.input[player].port, i, config.input[player].device - 1); + } #endif /* increment player index */ @@ -988,11 +1354,11 @@ void gx_input_UpdateMenu(u32 cnt) u32 pw = data->btns_d; /* WPAD held keys (direction/selection) */ - u32 hw = data->btns_h & (WPAD_BUTTON_UP|WPAD_BUTTON_DOWN|WPAD_BUTTON_LEFT|WPAD_BUTTON_RIGHT|WPAD_BUTTON_A|WPAD_BUTTON_2); + u32 hw = data->btns_h & (WPAD_CLASSIC_BUTTON_UP|WPAD_CLASSIC_BUTTON_DOWN|WPAD_CLASSIC_BUTTON_LEFT|WPAD_CLASSIC_BUTTON_RIGHT|WPAD_BUTTON_UP|WPAD_BUTTON_DOWN|WPAD_BUTTON_LEFT|WPAD_BUTTON_RIGHT|WPAD_BUTTON_A|WPAD_BUTTON_2); /* WPAD analog sticks (handled as PAD held direction keys) */ - x = WPAD_StickX(data, 0); - y = WPAD_StickY(data, 0); + x = wpad_StickX(data, 0); + y = wpad_StickY(data, 0); if (x > ANALOG_SENSITIVITY) hp |= PAD_BUTTON_RIGHT; else if (x < -ANALOG_SENSITIVITY) hp |= PAD_BUTTON_LEFT; else if (y > ANALOG_SENSITIVITY) hp |= PAD_BUTTON_UP; diff --git a/source/gx/gx_input.h b/source/gx/gx_input.h index 7f05b57..1b9f85b 100644 --- a/source/gx/gx_input.h +++ b/source/gx/gx_input.h @@ -31,7 +31,7 @@ #define MAX_INPUTS 8 #endif -/* Genesis controller keys */ +/* Configurable keys */ #define MAX_KEYS 8 /* Key configuration structure */ @@ -46,7 +46,7 @@ typedef struct extern void gx_input_Init(void); extern int gx_input_FindDevices(void); extern void gx_input_SetDefault(void); -extern void gx_input_Config(u8 num, u8 type, u8 max_keys); +extern void gx_input_Config(u8 chan, u8 device, u8 type); extern void gx_input_UpdateEmu(void); extern void gx_input_UpdateMenu(u32 cnt); diff --git a/source/gx/gx_video.c b/source/gx/gx_video.c index 025f07b..9e3ffdc 100644 --- a/source/gx/gx_video.c +++ b/source/gx/gx_video.c @@ -477,7 +477,7 @@ static void gxSetAspectRatio(int *xscale, int *yscale) if (config.overscan & 2) { /* max visible range is ~712 pixels, not 720 */ - *xscale = 356; + *xscale = (reg[12] & 1) ? 356 : 360; } else { @@ -1400,11 +1400,11 @@ void gx_video_Start(void) } /* lightgun textures */ - if (config.gun_cursor[0] && (input.dev[4] == DEVICE_LIGHTGUN)) + if (config.gun_cursor[0] && ((input.system[1] == SYSTEM_MENACER) || (input.system[1] == SYSTEM_JUSTIFIER) || (input.system[0] == SYSTEM_LIGHTPHASER))) { crosshair[0] = gxTextureOpenPNG(Crosshair_p1_png,0); } - if (config.gun_cursor[1] && (input.dev[5] == DEVICE_LIGHTGUN)) + if (config.gun_cursor[1] && ((input.system[1] == SYSTEM_JUSTIFIER) || (input.system[1] == SYSTEM_LIGHTPHASER))) { crosshair[1] = gxTextureOpenPNG(Crosshair_p2_png,0); } @@ -1486,14 +1486,30 @@ void gx_video_Update(void) /* render textured quad */ draw_square(); - /* Lightgun marks */ + /* Lightgun # 1 screen mark */ if (crosshair[0]) { - gxDrawCrosshair(crosshair[0], input.analog[0][0],input.analog[0][1]); + if (input.system[0] == SYSTEM_LIGHTPHASER) + { + gxDrawCrosshair(crosshair[0], input.analog[0][0],input.analog[0][1]); + } + else + { + gxDrawCrosshair(crosshair[0], input.analog[4][0],input.analog[4][1]); + } } + + /* Lightgun # 2 screen mark */ if (crosshair[1]) { - gxDrawCrosshair(crosshair[1], input.analog[1][0],input.analog[1][1]); + if (input.system[1] == SYSTEM_LIGHTPHASER) + { + gxDrawCrosshair(crosshair[1], input.analog[1][0],input.analog[1][1]); + } + else + { + gxDrawCrosshair(crosshair[1], input.analog[5][0],input.analog[5][1]); + } } /* swap XFB */ diff --git a/source/gx/images/Ctrl_activator.png b/source/gx/images/Ctrl_activator.png new file mode 100644 index 0000000..b72cebc Binary files /dev/null and b/source/gx/images/Ctrl_activator.png differ diff --git a/source/gx/images/Ctrl_gamepad_md.png b/source/gx/images/Ctrl_gamepad_md.png new file mode 100644 index 0000000..298b352 Binary files /dev/null and b/source/gx/images/Ctrl_gamepad_md.png differ diff --git a/source/gx/images/Ctrl_gamepad_ms.png b/source/gx/images/Ctrl_gamepad_ms.png new file mode 100644 index 0000000..4428f41 Binary files /dev/null and b/source/gx/images/Ctrl_gamepad_ms.png differ diff --git a/source/gx/images/Ctrl_lightphaser.png b/source/gx/images/Ctrl_lightphaser.png new file mode 100644 index 0000000..ebb223d Binary files /dev/null and b/source/gx/images/Ctrl_lightphaser.png differ diff --git a/source/gx/images/Ctrl_paddle.png b/source/gx/images/Ctrl_paddle.png new file mode 100644 index 0000000..45f5146 Binary files /dev/null and b/source/gx/images/Ctrl_paddle.png differ diff --git a/source/gx/images/Ctrl_sportspad.png b/source/gx/images/Ctrl_sportspad.png new file mode 100644 index 0000000..fa87f31 Binary files /dev/null and b/source/gx/images/Ctrl_sportspad.png differ diff --git a/source/gx/images/Ctrl_xe_a1p.png b/source/gx/images/Ctrl_xe_a1p.png new file mode 100644 index 0000000..d0179fc Binary files /dev/null and b/source/gx/images/Ctrl_xe_a1p.png differ diff --git a/source/gx/main.c b/source/gx/main.c index dafd465..4c666a7 100644 --- a/source/gx/main.c +++ b/source/gx/main.c @@ -4,7 +4,7 @@ * Genesis Plus GX * * Softdev (2006) - * Eke-Eke (2007,2008,2009) + * Eke-Eke (2007-2010) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -53,13 +53,21 @@ u32 ConfigRequested = 1; /**************************************************************************** * Power Button callback ***************************************************************************/ -static void Power_Off(void) +static void PowerOff_cb(void) { Shutdown = 1; ConfigRequested = 1; } #endif +/**************************************************************************** + * Reset Button callback + ***************************************************************************/ +static void Reset_cb(void) +{ + gen_reset(0); +} + /*************************************************************************** * Genesis Plus Virtual Machine * @@ -202,24 +210,43 @@ void reloadrom (int size, char *name) /* hot-swap previous & current cartridge */ bool hotswap = config.hot_swap && cart.romsize; - /* load ROM file */ + /* ROM size */ cart.romsize = size; + + /* load ROM file */ load_rom(name); + /* ROM filename without extension*/ + sprintf(rom_filename,"%s",name); + rom_filename[strlen(rom_filename) - 4] = 0; + if (hotswap) { - cart_hw_init(); - cart_hw_reset(1); + if (system_hw == SYSTEM_PBC) + { + sms_cart_init(); + sms_cart_reset(); + } + else + { + md_cart_init(); + md_cart_reset(1); + } } else { - /* initialize audio back-end */ - /* 60hz video mode requires synchronization with Video Interrupt. */ - /* Framerate is 59.94 fps in interlaced/progressive modes, ~59.825 fps in non-interlaced mode */ + /* initialize audio emulation */ + + /* To prevent any sound skipping, sound chips must run at the exact same speed as the rest of emulation (see sound.c) */ + /* In 60hz video modes with NTSC emulation, we need perfect synchronization with video hardware interrupt (VSYNC) */ + /* Wii & GC framerate has been measured to be exactly 59.94 fps in 240i/480i/480p video modes, ~59.825 fps in 240p */ + /* In other modes, emulation is synchronized with audio hardware instead and we use default framerates (50Hz for PAL, 60Hz for NTSC). */ float framerate = vdp_pal ? 50.0 : ((config.tv_mode == 1) ? 60.0 : (config.render ? 59.94 : (1000000.0/16715.0))); + + /* output samplerate has been measured to be ~48044 samples/sec on GC, 48000 samples/sec on Wii */ audio_init(SAMPLERATE_48KHZ, framerate); - /* System Power ON */ + /* system power ON */ system_init (); system_reset (); } @@ -273,6 +300,8 @@ u32 frameticker = 0; int main (int argc, char *argv[]) { + char pathname[MAXPATHLEN]; + #ifdef HW_RVL /* initialize DI interface */ DI_UseCache(0); @@ -305,7 +334,6 @@ int main (int argc, char *argv[]) if (fatMounted) { /* base directory */ - char pathname[MAXPATHLEN]; sprintf (pathname, DEFAULT_PATH); DIR_ITER *dir = diropen(pathname); if (dir) dirclose(dir); @@ -356,10 +384,10 @@ int main (int argc, char *argv[]) SILENT = 1; if (OpenDirectory(TYPE_RECENT)) { - int size = LoadFile(cart.rom,0); + int size = LoadFile(cart.rom,0,pathname); if (size) { - reloadrom(size,filelist[0].filename); + reloadrom(size,pathname); gx_video_Start(); gx_audio_Start(); frameticker = 1; @@ -371,9 +399,12 @@ int main (int argc, char *argv[]) #ifdef HW_RVL /* power button callback */ - SYS_SetPowerCallback(Power_Off); + SYS_SetPowerCallback(PowerOff_cb); #endif + /* reset button callback */ + SYS_SetResetCallback(Reset_cb); + /* main emulation loop */ run_emulation(); diff --git a/source/gx/osd.h b/source/gx/osd.h index f683c1a..f0913ff 100644 --- a/source/gx/osd.h +++ b/source/gx/osd.h @@ -2,8 +2,6 @@ #ifndef _OSD_H_ #define _OSD_H_ -#define NGC 1 - #include #include #include @@ -33,9 +31,9 @@ #define SK_UPMEM "/genplus/sk2chip.bin" #ifdef HW_RVL -#define VERSION "Genesis Plus GX 1.4.1 (WII)" +#define VERSION "Genesis Plus GX 1.5.0 (WII)" #else -#define VERSION "Genesis Plus GX 1.4.1 (GCN)" +#define VERSION "Genesis Plus GX 1.5.0 (GCN)" #endif #define osd_input_Update() gx_input_UpdateEmu() diff --git a/source/hvc.h b/source/hvc.h index 45758cc..8a8dba1 100644 --- a/source/hvc.h +++ b/source/hvc.h @@ -125,7 +125,7 @@ /***************************************************************/ /* */ -/* H-counter timings in H40 & H32 modes (starts from HINT) */ +/* H-counter timings in H32 & H40 modes (starts from HINT) */ /* */ /* There are exactly 3420 Master Clock counts per raster line. */ /* */ @@ -138,483 +138,480 @@ /* */ /***************************************************************/ -static const uint8 cycle2hc40[3420] = -{ - /* end of active display (16 pixels -> 128 Mcycles) , HINT triggered , Vcounter increment */ - 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, - 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, - 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, - 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, - 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, - 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, - 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, - 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, - - /* right border (14 pixels -> 112 Mcycles) */ - 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, - 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, - 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, - 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, - 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, - 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, - 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, - - /* right blanking (9 pixels -> 72 Mcycles) , VDP status HBLANK flag set */ - 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, - 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, - 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, - 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, - 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, - - /* horizontal sync (32 pixels -> 313 Mcycles) */ - 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, - 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, - 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, - 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, - 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, - 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xeb, - 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, - 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, - 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, - 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee, - 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, - 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, - 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, - 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, - 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, - 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, - 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, - 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, - 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, - 0xf6, - - /* left blanking (32 pixels -> 259 Mcycles) */ - 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, - 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, - 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, - 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, - 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, - 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, - 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - /* Vertical Interrupt triggered */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, - - /* left border (13 pixels -> 104 Mcycles) , VDP status HBLANK flag cleared */ - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - - /* remaining active display (304 pixels -> 2432 Mcycles) */ - 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, - 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, - 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1a, 0x1a, - 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1b, - 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1d, - 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, - 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, - 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, - 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27, 0x27, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2c, - 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, - 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31, - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x32, - 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x33, 0x33, 0x33, 0x33, - 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x34, 0x34, 0x34, 0x34, - 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35, - 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37, 0x37, 0x37, - 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x38, 0x38, 0x38, 0x38, - 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x39, - 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x3a, 0x3a, 0x3a, 0x3a, - 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3c, - 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3d, - 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, 0x3e, - 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x40, 0x40, 0x40, 0x40, - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43, 0x43, 0x43, - 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45, - 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x46, 0x46, 0x46, 0x46, - 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x47, 0x47, 0x47, 0x47, - 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x48, 0x48, 0x48, 0x48, - 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, - 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x4a, 0x4a, 0x4a, 0x4a, - 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4b, - 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c, 0x4c, 0x4c, - 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4d, - 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4e, 0x4e, 0x4e, 0x4e, - 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4f, 0x4f, 0x4f, 0x4f, - 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x50, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51, 0x51, 0x51, - 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x52, - 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x53, 0x53, 0x53, 0x53, - 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54, 0x54, 0x54, - 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x56, 0x56, 0x56, 0x56, - 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x57, 0x57, 0x57, 0x57, - 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x58, 0x58, 0x58, 0x58, - 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x59, - 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5a, - 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5b, - 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, - 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5e, 0x5e, - 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x5f, - 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x60, - 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, - 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x62, - 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x64, 0x64, 0x64, 0x64, - 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x65, 0x65, 0x65, 0x65, - 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x66, 0x66, 0x66, 0x66, - 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x67, - 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x68, - 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, - 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6a, - 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b, - 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6c, 0x6c, 0x6c, 0x6c, - 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, - 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6e, - 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x6f, - 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x70, - 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x71, - 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x72, 0x72, 0x72, 0x72, - 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x73, 0x73, 0x73, 0x73, - 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x74, - 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x75, - 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x76, 0x76, 0x76, 0x76, - 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x77, 0x77, 0x77, 0x77, - 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x78, - 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79, - 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, - 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7b, - 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, - 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7d, - 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7e, - 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, - 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x84, 0x84, 0x84, 0x84, - 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94, - 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x95, 0x95, 0x95, 0x95, - 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x96, 0x96, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, - 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, 0x98, - 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99, - 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a, 0x9a, - 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, - 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, - 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, - 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f, 0x9f, 0x9f, - 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, - 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, - 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, - 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, -}; - -static const uint8 cycle2hc32[3420] = -{ - /* end of active display (16 pixels -> 160 Mcycles) , HINT triggered ? , Vcounter increment */ - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, - 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, - 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, - 0x89, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, - 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, - 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8c, 0x8c, - 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, +static const uint8 cycle2hc32[3420] = +{ + /* end of active display (16 pixels -> 160 Mcycles) , H interrupt triggered, Vcounter increment */ + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, /* right border (14 pixels -> 140 Mcycles) */ - 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, - 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, - 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, - 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, - 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, - 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, - 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, /* right blanking (9 pixels -> 90 Mcycles) , VDP status HBLANK flag set */ - 0xe9, 0xe9, 0xe9, 0xe9, - 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, - 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, - 0xea, 0xea, 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, - 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, - 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, - 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, + 0xe9, 0xe9, 0xe9, 0xe9, + 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, + 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, + 0xea, 0xea, 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, + 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, + 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, + 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, /* horizontal sync (26 pixels -> 260 Mcycles) */ - 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, - 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, - 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, - 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, - 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, - 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, - 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, - 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, - 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, - 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, - 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, - 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, - 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, - 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, - 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, - 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, + 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, + 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, + 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, + 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, + 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, + 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, + 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, /* left blanking (24 pixels -> 240 Mcycles) */ - 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, - 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, - 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, - 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, - 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, - 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, - /* Vertical Interrupt triggered ? */ - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, + /* V interrupt triggered (MD mode) */ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, /* left border (13 pixels -> 130 Mcycles) , VDP status HBLANK flag cleared */ - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, - 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, /* remaining active display (240 pixels -> 2400 Mcycles) */ - 0x0d, 0x0d, 0x0d, 0x0d, - 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, - 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, - 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, - 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, - 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, - 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, - 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, - 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, - 0x1a, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, - 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1d, - 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, - 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, - 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, - 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, - 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, - 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, - 0x26, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, - 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, - 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, - 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, - 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31, - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, - 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, - 0x32, 0x32, 0x32, 0x32, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, - 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, - 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35, - 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, - 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, - 0x36, 0x36, 0x36, 0x36, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, - 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, - 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x39, - 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, - 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, - 0x3a, 0x3a, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, - 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3d, - 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, - 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, - 0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, - 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x41, 0x41, 0x41, 0x41, - 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, - 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x42, 0x42, 0x42, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, - 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45, - 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, - 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, - 0x46, 0x46, 0x46, 0x46, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, - 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, - 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, - 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, - 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, - 0x4a, 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, - 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, - 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4d, - 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, - 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, - 0x4e, 0x4e, 0x4e, 0x4e, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, - 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, - 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51, 0x51, 0x51, - 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, - 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, - 0x52, 0x52, 0x52, 0x52, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, - 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, - 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, - 0x56, 0x56, 0x56, 0x56, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, - 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, - 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x59, - 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, - 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, - 0x5a, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, - 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, - 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, - 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, - 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, - 0x5e, 0x5e, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, - 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, - 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, - 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, - 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, - 0x62, 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, - 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x65, 0x65, 0x65, 0x65, - 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, - 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, - 0x66, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, - 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, - 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, - 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, - 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, - 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, - 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, - 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, - 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, - 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, - 0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, - 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, - 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x71, - 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, - 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, - 0x72, 0x72, 0x72, 0x72, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, - 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, - 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x75, - 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, - 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, - 0x76, 0x76, 0x76, 0x76, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, - 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, - 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79, - 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, - 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, - 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, - 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, - 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7d, - 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, - 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, - 0x7e, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, - 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, - 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35, + 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, + 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, + 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, + 0x3a, 0x3a, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, + 0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, + 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45, + 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, + 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, + 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, + 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, + 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, + 0x4a, 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, + 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, + 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4d, + 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, + 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, + 0x4e, 0x4e, 0x4e, 0x4e, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, + 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, + 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51, 0x51, 0x51, + 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, + 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, + 0x52, 0x52, 0x52, 0x52, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, + 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, + 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, + 0x56, 0x56, 0x56, 0x56, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, + 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x59, + 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, + 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, + 0x5a, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, + 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, + 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, + 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5e, 0x5e, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, + 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, + 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, + 0x62, 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, + 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x65, 0x65, 0x65, 0x65, + 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, + 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, + 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, + 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, + 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, + 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, + 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, + 0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, + 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x71, + 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, + 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, + 0x72, 0x72, 0x72, 0x72, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, + 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, + 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x75, + 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, + 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, + 0x76, 0x76, 0x76, 0x76, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, + 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79, + 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, + 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, + 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, + 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, + 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7d, + 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, + 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, + 0x7e, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, - 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, }; -static const uint8 *hctab; +static const uint8 cycle2hc40[3420] = +{ + /* end of active display (16 pixels -> 128 Mcycles) , HINT triggered , Vcounter increment */ + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + + /* right border (14 pixels -> 112 Mcycles) */ + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + + /* right blanking (9 pixels -> 72 Mcycles) , VDP status HBLANK flag set */ + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, + 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, + 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, + + /* horizontal sync (32 pixels -> 313 Mcycles) */ + 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, + 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, + 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, + 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, + 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xeb, + 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, + 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, + 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, + 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee, + 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, + 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, + 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, + 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, + 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, + 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, + 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, + 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, + 0xf6, + + /* left blanking (32 pixels -> 259 Mcycles) */ + 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, + 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, + 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, + 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, + 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, + 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* Vertical Interrupt triggered */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, + + /* left border (13 pixels -> 104 Mcycles) , VDP status HBLANK flag cleared */ + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + + /* remaining active display (304 pixels -> 2432 Mcycles) */ + 0x0d, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, + 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1a, 0x1a, + 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1c, + 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1d, + 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, + 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x2a, 0x2a, 0x2a, 0x2a, + 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2c, + 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x2f, + 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x34, 0x34, 0x34, 0x34, + 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35, + 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x35, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x37, 0x37, 0x37, + 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x37, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x3a, 0x3a, 0x3a, 0x3a, + 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3c, + 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, 0x3e, + 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43, 0x43, 0x43, + 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45, + 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x45, 0x46, 0x46, 0x46, 0x46, + 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x46, 0x47, 0x47, 0x47, 0x47, + 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x48, 0x48, 0x48, 0x48, + 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, + 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x4a, 0x4a, 0x4a, 0x4a, + 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4b, + 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c, 0x4c, 0x4c, + 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4d, + 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4d, 0x4e, 0x4e, 0x4e, 0x4e, + 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4f, 0x4f, 0x4f, 0x4f, + 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x50, 0x50, 0x50, 0x50, + 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51, 0x51, 0x51, + 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x52, + 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 0x53, 0x53, 0x53, 0x53, + 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54, 0x54, 0x54, + 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x56, 0x56, 0x56, 0x56, + 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x56, 0x57, 0x57, 0x57, 0x57, + 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x57, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x59, + 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5a, + 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5b, + 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d, + 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x5f, + 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, + 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x62, + 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x64, 0x64, 0x64, 0x64, + 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x65, 0x65, 0x65, 0x65, + 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x67, + 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x68, + 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6a, + 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6b, + 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, + 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6e, + 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x6f, + 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x71, + 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x72, 0x72, 0x72, 0x72, + 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x73, 0x73, 0x73, 0x73, + 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x74, + 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x75, + 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x75, 0x76, 0x76, 0x76, 0x76, + 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x77, 0x77, 0x77, 0x77, + 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79, + 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, + 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7b, + 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, + 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7d, + 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7e, + 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4}; #endif /* _HVC_H_ */ diff --git a/source/input_hw/activator.c b/source/input_hw/activator.c new file mode 100644 index 0000000..0ca2206 --- /dev/null +++ b/source/input_hw/activator.c @@ -0,0 +1,121 @@ +/*************************************************************************************** + * Genesis Plus + * Sega Activator support + * + * Copyright Eke-Eke (2007-2011) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************************************/ + +#include "shared.h" + +static struct +{ + uint8 State; + uint8 Counter; +} activator[2]; + +void activator_reset(int index) +{ + + activator[index].State = 0x40; + activator[index].Counter = 0; +} + +static inline unsigned char activator_read(int port) +{ + /* IR sensors 1-16 data (active low) */ + uint16 data = ~input.pad[port]; + + /* Device index */ + port = port >> 2; + + /* D1 = D0 (data is ready) */ + uint8 temp = (activator[port].State & 0x01) << 1; + + switch (activator[port].Counter) + { + case 0: /* x x x x 0 1 0 0 */ + temp |= 0x04; + break; + + case 1: /* x x l1 l2 l3 l4 1 1 */ + temp |= ((data << 2) & 0x3C); + break; + + case 2: /* x x l5 l6 l7 l8 0 0 */ + temp |= ((data >> 2) & 0x3C); + break; + + case 3: /* x x h1 h2 h3 h4 1 1 */ + temp |= ((data >> 6) & 0x3C); + break; + + case 4: /* x x h5 h6 h7 h8 0 0 */ + temp |= ((data >> 10) & 0x3C); + break; + } + + return temp; +} + +static inline void activator_write(int index, unsigned char data, unsigned char mask) +{ + /* update bits set as output only */ + data = (activator[index].State & ~mask) | (data & mask); + + /* TH transitions */ + if ((activator[index].State ^ data) & 0x40) + { + /* reset sequence cycle */ + activator[index].Counter = 0; + } + else + { + /* D0 transitions */ + if ((activator[index].State ^ data) & 0x01) + { + /* increment sequence cycle */ + if (activator[index].Counter < 4) + { + activator[index].Counter++; + } + } + } + + /* update internal state */ + activator[index].State = data; +} + +unsigned char activator_1_read(void) +{ + return activator_read(0); +} + +unsigned char activator_2_read(void) +{ + return activator_read(4); +} + +void activator_1_write(unsigned char data, unsigned char mask) +{ + activator_write(0, data, mask); +} + +void activator_2_write(unsigned char data, unsigned char mask) +{ + activator_write(1, data, mask); +} diff --git a/source/input_hw/activator.h b/source/input_hw/activator.h new file mode 100644 index 0000000..f372439 --- /dev/null +++ b/source/input_hw/activator.h @@ -0,0 +1,33 @@ +/*************************************************************************************** + * Genesis Plus + * Sega Activator support + * + * Copyright Eke-Eke (2007-2011) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************************************/ + +#ifndef _ACTIVATOR_H_ +#define _ACTIVATOR_H_ + +/* Function prototypes */ +extern void activator_reset(int index); +extern unsigned char activator_1_read(void); +extern unsigned char activator_2_read(void); +extern void activator_1_write(unsigned char data, unsigned char mask); +extern void activator_2_write(unsigned char data, unsigned char mask); + +#endif diff --git a/source/input_hw/gamepad.c b/source/input_hw/gamepad.c new file mode 100644 index 0000000..39a4ad9 --- /dev/null +++ b/source/input_hw/gamepad.c @@ -0,0 +1,225 @@ +/*************************************************************************************** + * Genesis Plus + * 3-Buttons & 6-Buttons pad support (incl. 4-WayPlay & J-Cart handlers) + * + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) + * Eke-Eke (2007-2011), additional code & fixes for the GCN/Wii port + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************************************/ + +#include "shared.h" +#include "gamepad.h" + +static struct +{ + uint8 State; + uint8 Counter; + uint8 Timeout; +} gamepad[MAX_DEVICES]; + +static uint8 pad_index; + + +void gamepad_reset(int port) +{ + /* default state (Gouketsuji Ichizoku / Power Instinct, Samurai Spirits / Samurai Shodown) */ + gamepad[port].State = 0x40; + gamepad[port].Counter = 0; + gamepad[port].Timeout = 0; + + /* reset pad index (4-WayPlay) */ + pad_index = 0; +} + +void gamepad_refresh(int port) +{ + /* 6-buttons pad */ + if (gamepad[port].Timeout++ > 25) + { + gamepad[port].Counter = 0; + gamepad[port].Timeout = 0; + } +} + +static inline unsigned char gamepad_read(int port) +{ + /* bit 7 is latched, returns current TH state */ + unsigned int data = (gamepad[port].State & 0x40) | 0x3F; + + /* pad value */ + unsigned int val = input.pad[port]; + + /* get current step (TH state) */ + unsigned int step = (gamepad[port].Counter & 6) | ((data >> 6) & 1); + + switch (step) + { + case 1: /*** First High ***/ + case 3: /*** Second High ***/ + case 5: /*** Third High ***/ + { + /* TH = 1 : ?1CBRLDU */ + data &= ~(val & 0x3F); + break; + } + + case 0: /*** First low ***/ + case 2: /*** Second low ***/ + { + /* TH = 0 : ?0SA00DU */ + data &= ~(val & 0x03); + data &= ~((val >> 2) & 0x30); + data &= ~0x0C; + break; + } + + /* 6buttons specific (taken from gen-hw.txt) */ + /* A 6-button gamepad allows the extra buttons to be read based on how */ + /* many times TH is switched from 1 to 0 (and not 0 to 1). Observe the */ + /* following sequence */ + /* + TH = 1 : ?1CBRLDU 3-button pad return value + 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' + */ + case 4: /*** Third Low ***/ + { + /* TH = 0 : ?0SA0000 D3-0 are forced to '0'*/ + data &= ~((val >> 2) & 0x30); + data &= ~0x0F; + break; + } + + case 6: /*** Fourth Low ***/ + { + /* TH = 0 : ?0SA1111 D3-0 are forced to '1'*/ + data &= ~((val >> 2) & 0x30); + break; + } + + case 7: /*** Fourth High ***/ + { + /* TH = 1 : ?1CBMXYZ Extra buttons returned in D3-0*/ + data &= ~(val & 0x30); + data &= ~((val >> 8) & 0x0F); + break; + } + } + + return data; +} + +static inline void gamepad_write(int port, unsigned char data, unsigned char mask) +{ + /* update bits set as output only */ + data = (gamepad[port].State & ~mask) | (data & mask); + + if (input.dev[port] == DEVICE_PAD6B) + { + /* check TH transitions */ + if ((gamepad[port].State ^ data) & 0x40) + { + gamepad[port].Counter++; + gamepad[port].Timeout = 0; + } + } + + /* update internal state */ + gamepad[port].State = data; +} + + +/*--------------------------------------------------------------------------*/ +/* Default ports handlers */ +/*--------------------------------------------------------------------------*/ + +unsigned char gamepad_1_read(void) +{ + return gamepad_read(0); +} + +unsigned char gamepad_2_read(void) +{ + return gamepad_read(4); +} + +void gamepad_1_write(unsigned char data, unsigned char mask) +{ + gamepad_write(0, data, mask); +} + +void gamepad_2_write(unsigned char data, unsigned char mask) +{ + gamepad_write(4, data, mask); +} + +/*--------------------------------------------------------------------------*/ +/* 4-WayPlay ports handler */ +/*--------------------------------------------------------------------------*/ + +unsigned char wayplay_1_read(void) +{ + if (pad_index < 4) + { + return gamepad_read(pad_index); + } + + /* multitap detection */ + return 0x70; +} + +unsigned char wayplay_2_read(void) +{ + return 0x7F; +} + +void wayplay_1_write(unsigned char data, unsigned char mask) +{ + if (pad_index < 4) + { + gamepad_write(pad_index, data, mask); + } +} + +void wayplay_2_write(unsigned char data, unsigned char mask) +{ + if ((mask & 0x70) == 0x70) + { + pad_index = (data & 0x70) >> 4; + } +} + + +/*--------------------------------------------------------------------------*/ +/* J-Cart memory handlers */ +/*--------------------------------------------------------------------------*/ + +unsigned int jcart_read(unsigned int address) +{ + /* TH2 output read is fixed to zero (fixes Micro Machines 2) */ + return ((gamepad_read(5) & 0x7F) | ((gamepad_read(6) & 0x3F) << 8)); +} + +void jcart_write(unsigned int address, unsigned int data) +{ + gamepad_write(5, (data & 1) << 6, 0x40); + gamepad_write(6, (data & 1) << 6, 0x40); + return; +} diff --git a/source/input_hw/gamepad.h b/source/input_hw/gamepad.h new file mode 100644 index 0000000..3171fe0 --- /dev/null +++ b/source/input_hw/gamepad.h @@ -0,0 +1,41 @@ +/*************************************************************************************** + * Genesis Plus + * 3-Buttons & 6-Buttons pad support (incl. 4-WayPlay & J-Cart handlers) + * + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) + * Eke-Eke (2007-2011), additional code & fixes for the GCN/Wii port + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************************************/ + +#ifndef _GAMEPAD_H_ +#define _GAMEPAD_H_ + +/* Function prototypes */ +extern void gamepad_reset(int port); +extern void gamepad_refresh(int port); +extern unsigned char gamepad_1_read(void); +extern unsigned char gamepad_2_read(void); +extern void gamepad_1_write(unsigned char data, unsigned char mask); +extern void gamepad_2_write(unsigned char data, unsigned char mask); +extern unsigned char wayplay_1_read(void); +extern unsigned char wayplay_2_read(void); +extern void wayplay_1_write(unsigned char data, unsigned char mask); +extern void wayplay_2_write(unsigned char data, unsigned char mask); +extern unsigned int jcart_read(unsigned int address); +extern void jcart_write(unsigned int address, unsigned int data); + +#endif diff --git a/source/input_hw/input.c b/source/input_hw/input.c new file mode 100644 index 0000000..2134eef --- /dev/null +++ b/source/input_hw/input.c @@ -0,0 +1,338 @@ +/*************************************************************************************** + * Genesis Plus + * Input peripherals support + * + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) + * Eke-Eke (2007-2011), additional code & fixes for the GCN/Wii port + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************************************/ + +#include "shared.h" +#include "gamepad.h" +#include "lightgun.h" +#include "mouse.h" +#include "activator.h" +#include "xe_a1p.h" +#include "teamplayer.h" +#include "paddle.h" +#include "sportspad.h" + +t_input input; +int old_system[2] = {-1,-1}; + + +void input_init(void) +{ + int i; + int player = 0; + + for (i=0; i> 2); + break; + } + + case DEVICE_XE_A1P: + { + xe_a1p_reset(); + break; + } + + case DEVICE_PADDLE: + { + paddle_reset(i >> 2); + break; + } + + case DEVICE_SPORTSPAD: + { + sportspad_reset(i >> 2); + break; + } + + default: + { + break; + } + } + } + + /* Team Player */ + for (i=0; i<2; i++) + { + if (input.system[i] == SYSTEM_TEAMPLAYER) + { + teamplayer_reset(i); + } + } +} + +void input_refresh(void) +{ + int i; + for (i=0; i> 1; + input.analog[port][1] = bitmap.viewport.h >> 1; + lightgun.State = 0x40; + lightgun.Port = 4; +} + +void lightgun_refresh(int port) +{ + /* Check that lightgun is enabled */ + if (port == lightgun.Port) + { + /* check if line falls within current gun Y position */ + if ((input.analog[port][1] == v_counter + input.y_offset)) + { + /* HL enabled ? */ + if (io_reg[5] & 0x80) + { + /* External Interrupt ? */ + if (reg[11] & 0x08) + { + m68k_irq_state |= 0x12; + } + + /* HV Counter Latch: + 1) some games does not enable HVC latch but instead use bigger X offset + --> we force the HV counter value read by the gun routine + 2) for games using H40 mode, the gun routine scales up the Hcounter value + --> H-Counter range is approx. 290 dot clocks + */ + hvc_latch = 0x10000 | (v_counter << 8); + if (reg[12] & 1) + { + hvc_latch |= hc_320[((input.analog[port][0] * 290) / (2 * 320) + input.x_offset) % 210]; + } + else + { + hvc_latch |= hc_256[(input.analog[port][0] / 2 + input.x_offset) % 171]; + } + } + } + } +} + + +/*--------------------------------------------------------------------------*/ +/* Sega Phaser */ +/*--------------------------------------------------------------------------*/ + +static inline unsigned char phaser_read(int port) +{ + /* FIRE button status (active low) */ + unsigned char temp = ~(input.pad[port] & 0x10); + + /* Check that TH is set as an input */ + if (io_reg[0] & (0x02 << (port >> 1))) + { + /* Get current X position (phaser is only used in MS compatiblity mode) */ + int hcounter = hctab[(mcycles_z80 + Z80_CYCLE_OFFSET) % MCYCLES_PER_LINE]; + + /* Compare with gun position */ + int dx = input.analog[port][0] - (hcounter << 1); + int dy = input.analog[port][1] - (v_counter); + + /* Check if current pixel is within lightgun spot ? */ + if ((abs(dy) <= 5) && (abs(dx) <= 60)) + { + /* set TH low */ + temp &= ~0x40; + + /* prevents multiple latch at each port read */ + if (lightgun.State) + { + /* latch estimated HC value */ + hvc_latch = 0x10000 | (input.x_offset + (input.analog[port][0] >> 1)); + lightgun.State = 0; + } + else + { + lightgun.State = 1; + } + } + } + + return temp & 0x7F; +} + +unsigned char phaser_1_read(void) +{ + return phaser_read(0); +} + +unsigned char phaser_2_read(void) +{ + return phaser_read(4); +} + + +/*--------------------------------------------------------------------------*/ +/* Sega Menacer */ +/*--------------------------------------------------------------------------*/ + +unsigned char menacer_read(void) +{ + /* Return START,A,B,C buttons status in D0-D3 (active high) */ + /* TL & TR pins always return 0 (normally set as output) */ + return ((input.pad[4] >> 4) & 0x0F); +} + + +/*--------------------------------------------------------------------------*/ +/* Konami Justifiers */ +/*--------------------------------------------------------------------------*/ + +unsigned char justifier_read(void) +{ + /* Gun detection */ + if (lightgun.State & 0x40) + { + return 0x30; + } + + /* Return A & START button status in D0-D1 (active low) */ + /* TL & TR pins should always return 1 (normally set as output) */ + /* LEFT & RIGHT pins should always return 0 */ + return (((~input.pad[lightgun.Port] >> 6) & 0x03) | 0x70); +} + +void justifier_write(unsigned char data, unsigned char mask) +{ + /* update bits set as output only */ + data = (lightgun.State & ~mask) | (data & mask); + + /* gun index */ + lightgun.Port = 4 + ((data >> 5) & 1); + + /* update internal state */ + lightgun.State = data; +} diff --git a/source/input_hw/lightgun.h b/source/input_hw/lightgun.h new file mode 100644 index 0000000..6a9fb08 --- /dev/null +++ b/source/input_hw/lightgun.h @@ -0,0 +1,35 @@ +/*************************************************************************************** + * Genesis Plus + * Sega Light Phaser, Menacer & Konami Justifiers support + * + * Copyright Eke-Eke (2007-2011) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************************************/ + +#ifndef _LIGHTGUN_H_ +#define _LIGHTGUN_H_ + +/* Input devices port handlers */ +extern void lightgun_reset(int index); +extern void lightgun_refresh(int port); +extern unsigned char phaser_1_read(void); +extern unsigned char phaser_2_read(void); +extern unsigned char menacer_read(void); +extern unsigned char justifier_read(void); +extern void justifier_write(unsigned char data, unsigned char mask); + +#endif diff --git a/source/input_hw/mouse.c b/source/input_hw/mouse.c new file mode 100644 index 0000000..804cb6f --- /dev/null +++ b/source/input_hw/mouse.c @@ -0,0 +1,153 @@ +/*************************************************************************************** + * Genesis Plus + * Sega Mouse support + * + * Copyright Eke-Eke (2007-2011) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************************************/ + +#include "shared.h" + +static struct +{ + uint8 State; + uint8 Counter; + uint8 Wait; + uint8 Port; +} mouse; + +void mouse_reset(int port) +{ + input.analog[port][0] = 0; + input.analog[port][1] = 0; + mouse.State = 0x60; + mouse.Counter = 0; + mouse.Wait = 0; + mouse.Port = port; +} + +unsigned char mouse_read() +{ + unsigned int temp = 0x00; + int x = input.analog[mouse.Port][0]; + int y = input.analog[mouse.Port][1]; + + switch (mouse.Counter) + { + case 0: /* initial */ + temp = 0x00; + break; + + case 1: /* xxxx1011 */ + temp = 0x0B; + break; + + case 2: /* xxxx1111 */ + temp = 0x0F; + break; + + case 3: /* xxxx1111 */ + temp = 0x0F; + break; + + case 4: /* Axis sign & overflow (not emulated) bits */ + temp |= (x < 0); + temp |= (y < 0) << 1; + /* + temp |= (abs(x) > 255) << 2; + temp |= (abs(y) > 255) << 3; + */ + break; + + case 5: /* START, A, B, C buttons state (active high) */ + temp = (input.pad[mouse.Port] >> 4) & 0x0F; + break; + + case 6: /* X Axis MSB */ + temp = (x >> 4) & 0x0F; + break; + + case 7: /* X Axis LSB */ + temp = (x & 0x0F); + break; + + case 8: /* Y Axis MSB */ + temp = (y >> 4) & 0x0F; + break; + + case 9: /* Y Axis LSB */ + temp = (y & 0x0F); + break; + } + + /* TL = busy status */ + if (mouse.Wait) + { + /* wait before ACK, fix some buggy mouse routine (Shangai 2, Wack World,...) */ + mouse.Wait = 0; + + /* TL = !TR */ + temp |= (~mouse.State & 0x20) >> 1; + } + else + { + /* TL = TR (data is ready) */ + temp |= (mouse.State & 0x20) >> 1; + } + + return temp; +} + +void mouse_write(unsigned char data, unsigned char mask) +{ + /* update bits set as output only */ + data = (mouse.State & ~mask) | (data & mask); + + if (mouse.Counter == 0) + { + /* wait for TH 1->0 transition */ + if ((mouse.State & 0x40) && !(data & 0x40)) + { + /* start acquisition */ + mouse.Counter = 1; + } + } + else + { + /* TR handshake */ + if ((mouse.State ^ data) & 0x20) + { + /* increment phase */ + if (mouse.Counter < 10) + { + mouse.Counter++; + } + + /* input latency */ + mouse.Wait = 1; + } + } + + /* end of acquisition (TH=1) */ + if (data & 0x40) + { + mouse.Counter = 0; + } + + /* update internal state */ + mouse.State = data; +} diff --git a/source/input_hw/mouse.h b/source/input_hw/mouse.h new file mode 100644 index 0000000..145d60f --- /dev/null +++ b/source/input_hw/mouse.h @@ -0,0 +1,31 @@ +/*************************************************************************************** + * Genesis Plus + * Sega Mouse support + * + * Copyright Eke-Eke (2007-2011) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************************************/ + +#ifndef _MOUSE_H_ +#define _MOUSE_H_ + +/* Function prototypes */ +extern void mouse_reset(int port); +extern unsigned char mouse_read(void); +extern void mouse_write(unsigned char data, unsigned char mask); + +#endif diff --git a/source/input_hw/paddle.c b/source/input_hw/paddle.c new file mode 100644 index 0000000..53a9edb --- /dev/null +++ b/source/input_hw/paddle.c @@ -0,0 +1,95 @@ +/*************************************************************************************** + * Genesis Plus + * Sega Paddle Control support + * + * Copyright Eke-Eke (2007-2011) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************************************/ + +#include "shared.h" + +static struct +{ + uint8 State; +} paddle[2]; + +void paddle_reset(int index) +{ + input.analog[index << 2][0] = 128; + paddle[index].State = 0x40; +} + +static inline unsigned char paddle_read(int port) +{ + /* FIRE button status (active low) */ + unsigned char temp = ~(input.pad[port] & 0x10); + + /* Pad index */ + int index = port >> 2; + + /* Clear low bits */ + temp &= 0x70; + + /* Japanese model: automatic flip-flop */ + if (region_code < REGION_USA) + { + paddle[index].State ^= 0x40; + } + + if (paddle[index].State & 0x40) + { + /* return higher bits */ + temp |= (input.analog[port][0] >> 4) & 0x0F; + } + else + { + /* return lower bits */ + temp |= input.analog[port][0] & 0x0F; + + /* set TR low */ + temp &= ~0x20; + } + + return temp; +} + +static inline void paddle_write(int index, unsigned char data, unsigned char mask) +{ + /* update bits set as output only */ + paddle[index].State = (paddle[index].State & ~mask) | (data & mask); +} + + +unsigned char paddle_1_read(void) +{ + return paddle_read(0); +} + +unsigned char paddle_2_read(void) +{ + return paddle_read(4); +} + +void paddle_1_write(unsigned char data, unsigned char mask) +{ + paddle_write(0, data, mask); +} + +void paddle_2_write(unsigned char data, unsigned char mask) +{ + paddle_write(1, data, mask); +} diff --git a/source/input_hw/paddle.h b/source/input_hw/paddle.h new file mode 100644 index 0000000..8f8978b --- /dev/null +++ b/source/input_hw/paddle.h @@ -0,0 +1,33 @@ +/*************************************************************************************** + * Genesis Plus + * Sega Paddle Control support + * + * Copyright Eke-Eke (2007-2011) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************************************/ + +#ifndef _PADDLE_H_ +#define _PADDLE_H_ + +/* Function prototypes */ +extern void paddle_reset(int index); +extern unsigned char paddle_1_read(void); +extern unsigned char paddle_2_read(void); +extern void paddle_1_write(unsigned char data, unsigned char mask); +extern void paddle_2_write(unsigned char data, unsigned char mask); + +#endif diff --git a/source/input_hw/sportspad.c b/source/input_hw/sportspad.c new file mode 100644 index 0000000..2006508 --- /dev/null +++ b/source/input_hw/sportspad.c @@ -0,0 +1,118 @@ +/*************************************************************************************** + * Genesis Plus + * Sega Sports Pad support + * + * Copyright Eke-Eke (2007-2011) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************************************/ + +#include "shared.h" + +static struct +{ + uint8 State; + uint8 Counter; +} sportspad[2]; + +void sportspad_reset(int index) +{ + input.analog[index << 2][0] = 128; + input.analog[index << 2][1] = 128; + sportspad[index].State = 0x40; + sportspad[index].Counter = 0; +} + +static inline unsigned char sportspad_read(int port) +{ + /* Buttons 1(B) & 2(C) status (active low) */ + unsigned char temp = ~(input.pad[port] & 0x30); + + /* Pad index */ + int index = port >> 2; + + /* Clear low bits */ + temp &= 0x70; + + /* Detect current state */ + switch (sportspad[index].Counter & 3) + { + case 1: + { + /* X position high bits */ + temp |= (input.analog[port][0] >> 4) & 0x0F; + break; + } + + case 2: + { + /* X position low bits */ + temp |= input.analog[port][0] & 0x0F; + break; + } + + case 3: + { + /* Y position high bits */ + temp |= (input.analog[port][1] >> 4) & 0x0F; + break; + } + + default: + { + /* Y position low bits */ + temp |= input.analog[port][1] & 0x0F; + break; + } + } + + return temp; +} + +static inline void sportspad_write(int index, unsigned char data, unsigned char mask) +{ + /* update bits set as output only */ + data = (sportspad[index].State & ~mask) | (data & mask); + + /* check TH transitions */ + if ((data ^ sportspad[index].State) & 0x40) + { + sportspad[index].Counter++; + } + + /* update internal state */ + sportspad[index].State = data; +} + +unsigned char sportspad_1_read(void) +{ + return sportspad_read(0); +} + +unsigned char sportspad_2_read(void) +{ + return sportspad_read(4); +} + +void sportspad_1_write(unsigned char data, unsigned char mask) +{ + sportspad_write(0, data, mask); +} + +void sportspad_2_write(unsigned char data, unsigned char mask) +{ + sportspad_write(1, data, mask); +} diff --git a/source/input_hw/sportspad.h b/source/input_hw/sportspad.h new file mode 100644 index 0000000..55aaa08 --- /dev/null +++ b/source/input_hw/sportspad.h @@ -0,0 +1,33 @@ +/*************************************************************************************** + * Genesis Plus + * Sega Sports Pad support + * + * Copyright Eke-Eke (2007-2011) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************************************/ + +#ifndef _SPORTSPAD_H_ +#define _SPORTSPAD_H_ + +/* Function prototypes */ +extern void sportspad_reset(int index); +extern unsigned char sportspad_1_read(void); +extern unsigned char sportspad_2_read(void); +extern void sportspad_1_write(unsigned char data, unsigned char mask); +extern void sportspad_2_write(unsigned char data, unsigned char mask); + +#endif diff --git a/source/input_hw/teamplayer.c b/source/input_hw/teamplayer.c new file mode 100644 index 0000000..e1fd927 --- /dev/null +++ b/source/input_hw/teamplayer.c @@ -0,0 +1,160 @@ +/*************************************************************************************** + * Genesis Plus + * Team Player support + * + * Copyright Eke-Eke (2007-2011) +* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************************************/ + +#include "shared.h" + +static struct +{ + uint8 State; + uint8 Counter; + uint8 Table[12]; +} teamplayer[2]; + + +void teamplayer_init(int port) +{ + int i,padnum; + int index = 0; + + /* this table determines which gamepad input should be returned during acquisition sequence + index = teamplayer read table index: 0=1st read, 1=2nd read, ... + table = high bits are pad index, low bits are pad input shift: 0=RLDU, 4=SABC, 8=MXYZ + */ + for (i=0; i<4; i++) + { + padnum = (4 * port) + i; + if (input.dev[padnum] == DEVICE_PAD3B) + { + padnum = padnum << 4; + teamplayer[port].Table[index++] = padnum; + teamplayer[port].Table[index++] = padnum | 4; + } + else + { + padnum = padnum << 4; + teamplayer[port].Table[index++] = padnum; + teamplayer[port].Table[index++] = padnum | 4; + teamplayer[port].Table[index++] = padnum | 8; + } + } +} + +void teamplayer_reset(int port) +{ + teamplayer[port].State = 0x60; /* TH = 1, TR = 1 */ + teamplayer[port].Counter = 0; +} + +static inline unsigned int teamplayer_read(int port) +{ + unsigned int counter = teamplayer[port].Counter; + + /* acquisition sequence */ + switch (counter) + { + case 0: /* initial state: TH = 1, TR = 1 -> RLDU = 0011 */ + { + return 0x73; + } + + case 1: /* start request: TH = 0, TR = 1 -> RLDU = 1111 */ + { + return 0x3F; + } + + case 2: + case 3: /* ack request: TH=0, TR=0/1 -> RLDU = 0000 */ + { + /* TL should match TR */ + return ((teamplayer[port].State & 0x20) >> 1); + } + + case 4: + case 5: + case 6: + case 7: /* PAD type */ + { + unsigned int retval = input.dev[(port << 2) + (counter - 4)]; + + /* TL should match TR */ + return (((teamplayer[port].State & 0x20) >> 1) | retval); + } + + default: /* PAD status */ + { + unsigned int retval = 0x0F; + + /* SEGA teamplayer returns successively PAD1 -> PAD2 -> PAD3 -> PAD4 inputs */ + unsigned int padnum = teamplayer[port].Table[counter - 8] >> 4; + + /* Each PAD inputs is obtained through 2 or 3 sequential reads: RLDU -> SACB -> MXYZ */ + retval &= ~(input.pad[padnum] >> (teamplayer[port].Table[counter - 8] & 0x0F)); + + /* TL should match TR */ + return (((teamplayer[port].State & 0x20) >> 1) | retval); + } + } +} + +static inline void teamplayer_write(int port, unsigned char data, unsigned char mask) +{ + /* update bits set as output only */ + unsigned int state = (teamplayer[port].State & ~mask) | (data & mask); + + /* TH & TR handshaking */ + if ((teamplayer[port].State ^ state) & 0x60) + { + if (state & 0x40) + { + /* TH high -> reset counter */ + teamplayer[port].Counter = 0; + } + else + { + /* increment counter */ + teamplayer[port].Counter++; + } + + /* update internal state */ + teamplayer[port].State = state; + } +} + +unsigned char teamplayer_1_read(void) +{ + return teamplayer_read(0); +} + +unsigned char teamplayer_2_read(void) +{ + return teamplayer_read(1); +} + +void teamplayer_1_write(unsigned char data, unsigned char mask) +{ + teamplayer_write(0, data, mask); +} + +void teamplayer_2_write(unsigned char data, unsigned char mask) +{ + teamplayer_write(1, data, mask); +} diff --git a/source/input_hw/teamplayer.h b/source/input_hw/teamplayer.h new file mode 100644 index 0000000..cb0c181 --- /dev/null +++ b/source/input_hw/teamplayer.h @@ -0,0 +1,34 @@ +/*************************************************************************************** + * Genesis Plus + * Team Player support + * + * Copyright Eke-Eke (2007-2011) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************************************/ + +#ifndef _TEAMPLAYER_H_ +#define _TEAMPLAYER_H_ + +/* Function prototypes */ +extern void teamplayer_init(int port); +extern void teamplayer_reset(int port); +extern unsigned char teamplayer_1_read(void); +extern unsigned char teamplayer_2_read(void); +extern void teamplayer_1_write(unsigned char data, unsigned char mask); +extern void teamplayer_2_write(unsigned char data, unsigned char mask); + +#endif diff --git a/source/input_hw/xe_a1p.c b/source/input_hw/xe_a1p.c new file mode 100644 index 0000000..3c7d437 --- /dev/null +++ b/source/input_hw/xe_a1p.c @@ -0,0 +1,144 @@ +/*************************************************************************************** + * Genesis Plus + * XE-A1P analog controller support + * + * Copyright Eke-Eke (2007-2011) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************************************/ + +#include "shared.h" + +static struct +{ + uint8 State; + uint8 Counter; + uint8 Latency; +} xe_a1p; + +void xe_a1p_reset(void) +{ + input.analog[0][0] = 128; + input.analog[0][1] = 128; + input.analog[1][0] = 128; + xe_a1p.State = 0x40; + xe_a1p.Counter = 0; + xe_a1p.Latency = 0; +} + +unsigned char xe_a1p_read() +{ + unsigned int temp = 0x40; + + /* Left Stick X & Y analog values (bidirectional) */ + int x = input.analog[0][0]; + int y = input.analog[0][1]; + + /* Right Stick X or Y value (unidirectional) */ + int z = input.analog[1][0]; + + /* Buttons status (active low) */ + uint16 pad = ~input.pad[0]; + + /* Current 4-bit data cycle */ + /* There are eight internal data cycle for each 5 acquisition sequence */ + /* First 4 return the same 4-bit data, next 4 return next 4-bit data */ + switch (xe_a1p.Counter >> 2) + { + case 0: + temp |= ((pad >> 8) & 0x0F); /* E1 E2 Start Select */ + break; + case 1: + temp |= ((pad >> 4) & 0x0F); /* A B C D */ + break; + case 2: + temp |= ((x >> 4) & 0x0F); + break; + case 3: + temp |= ((y >> 4) & 0x0F); + break; + case 4: + break; + case 5: + temp |= ((z >> 4) & 0x0F); + break; + case 6: + temp |= (x & 0x0F); + break; + case 7: + temp |= (y & 0x0F); + break; + case 8: + break; + case 9: + temp |= (z & 0x0F); + break; + } + + /* Get current internal cycle (0-7) */ + unsigned int cycle = xe_a1p.Counter & 7; + + /* TL indicates which part of data is returned (0=1st part, 1=2nd part) */ + temp |= ((cycle & 4) << 2); + + /* TR indicates if data is ready (0=ready, 1=not ready) */ + /* Fastest One input routine actually expects this bit to switch between 0 & 1 */ + /* so we make the first read of a data cycle return 1 then 0 for remaining reads */ + temp |= (!(cycle & 3) << 5); + + /* Automatically increment data cycle on each read (within current acquisition sequence) */ + cycle = (cycle + 1) & 7; + + /* Update internal cycle counter */ + xe_a1p.Counter = (xe_a1p.Counter & ~7) | cycle; + + /* Update internal latency on each read */ + xe_a1p.Latency++; + + return temp; +} + +void xe_a1p_write(unsigned char data, unsigned char mask) +{ + /* update bits set as output only */ + data = (xe_a1p.State & ~mask) | (data & mask); + + /* look for TH 1->0 transitions */ + if (!(data & 0x40) && (xe_a1p.State & 0x40)) + { + /* reset acquisition cycle */ + xe_a1p.Latency = xe_a1p.Counter = 0; + } + else + { + /* some games immediately write new data to TH */ + /* so we make sure first sequence has actually been handled */ + if (xe_a1p.Latency > 2) + { + /* next acquisition sequence */ + xe_a1p.Counter = (xe_a1p.Counter & ~7) + 8; + + /* 5 sequence max with 8 cycles each */ + if (xe_a1p.Counter > 32) + { + xe_a1p.Counter = 32; + } + } + } + + /* update internal state */ + xe_a1p.State = data; +} diff --git a/source/input_hw/xe_a1p.h b/source/input_hw/xe_a1p.h new file mode 100644 index 0000000..189223b --- /dev/null +++ b/source/input_hw/xe_a1p.h @@ -0,0 +1,31 @@ +/*************************************************************************************** + * Genesis Plus + * XE-A1P analog controller support + * + * Copyright Eke-Eke (2007-2011) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************************************/ + +#ifndef _XE_A1PH_ +#define _XE_A1PH_ + +/* Function prototypes */ +extern void xe_a1p_reset(void); +extern unsigned char xe_a1p_read(void); +extern void xe_a1p_write(unsigned char data, unsigned char mask); + +#endif diff --git a/source/io_ctrl.c b/source/io_ctrl.c new file mode 100644 index 0000000..29f92cb --- /dev/null +++ b/source/io_ctrl.c @@ -0,0 +1,450 @@ +/*************************************************************************************** + * Genesis Plus + * I/O controller (MD & MS compatibility modes) + * + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) + * Eke-Eke (2007-2011), additional code & fixes for the GCN/Wii port + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************************************/ + +#include "shared.h" +#include "gamepad.h" +#include "lightgun.h" +#include "mouse.h" +#include "activator.h" +#include "xe_a1p.h" +#include "teamplayer.h" +#include "paddle.h" +#include "sportspad.h" + +uint8 io_reg[0x10]; + +uint8 region_code = REGION_USA; + +static struct port_t +{ + void (*data_w)(unsigned char data, unsigned char mask); + unsigned char (*data_r)(void); +} port[3]; + +static void dummy_write(unsigned char data, unsigned char mask) +{ +} + +static unsigned char dummy_read(void) +{ + return 0x7F; +} + +/***************************************************************************** + * I/O chip functions * + * * + *****************************************************************************/ +void io_init(void) +{ + /* Initialize connected peripherals */ + input_init(); + + /* Initialize IO Ports handlers & connected peripherals */ + switch (input.system[0]) + { + case SYSTEM_MS_GAMEPAD: + { + port[0].data_w = dummy_write; + port[0].data_r = gamepad_1_read; + break; + } + + case SYSTEM_MD_GAMEPAD: + { + port[0].data_w = gamepad_1_write; + port[0].data_r = gamepad_1_read; + break; + } + + case SYSTEM_MOUSE: + { + port[0].data_w = mouse_write; + port[0].data_r = mouse_read; + break; + } + + case SYSTEM_ACTIVATOR: + { + port[0].data_w = activator_1_write; + port[0].data_r = activator_1_read; + break; + } + + case SYSTEM_XE_A1P: + { + port[0].data_w = xe_a1p_write; + port[0].data_r = xe_a1p_read; + break; + } + + case SYSTEM_WAYPLAY: + { + port[0].data_w = wayplay_1_write; + port[0].data_r = wayplay_1_read; + break; + } + + case SYSTEM_TEAMPLAYER: + { + port[0].data_w = teamplayer_1_write; + port[0].data_r = teamplayer_1_read; + break; + } + + case SYSTEM_LIGHTPHASER: + { + port[0].data_w = dummy_write; + port[0].data_r = phaser_1_read; + break; + } + + case SYSTEM_PADDLE: + { + port[0].data_w = paddle_1_write; + port[0].data_r = paddle_1_read; + break; + } + + case SYSTEM_SPORTSPAD: + { + port[0].data_w = sportspad_1_write; + port[0].data_r = sportspad_1_read; + break; + } + + default: + { + port[0].data_w = dummy_write; + port[0].data_r = dummy_read; + break; + } + } + + switch (input.system[1]) + { + case SYSTEM_MS_GAMEPAD: + { + port[1].data_w = dummy_write; + port[1].data_r = gamepad_2_read; + break; + } + + case SYSTEM_MD_GAMEPAD: + { + port[1].data_w = gamepad_2_write; + port[1].data_r = gamepad_2_read; + break; + } + + case SYSTEM_MOUSE: + { + port[1].data_w = mouse_write; + port[1].data_r = mouse_read; + break; + } + + case SYSTEM_ACTIVATOR: + { + port[1].data_w = activator_2_write; + port[1].data_r = activator_2_read; + break; + } + + case SYSTEM_MENACER: + { + port[1].data_w = dummy_write; + port[1].data_r = menacer_read; + break; + } + + case SYSTEM_JUSTIFIER: + { + port[1].data_w = justifier_write; + port[1].data_r = justifier_read; + break; + } + + case SYSTEM_WAYPLAY: + { + port[1].data_w = wayplay_2_write; + port[1].data_r = wayplay_2_read; + break; + } + + case SYSTEM_TEAMPLAYER: + { + port[1].data_w = teamplayer_2_write; + port[1].data_r = teamplayer_2_read; + break; + } + + case SYSTEM_LIGHTPHASER: + { + port[1].data_w = dummy_write; + port[1].data_r = phaser_2_read; + break; + } + + case SYSTEM_PADDLE: + { + port[1].data_w = paddle_2_write; + port[1].data_r = paddle_2_read; + break; + } + + case SYSTEM_SPORTSPAD: + { + port[1].data_w = sportspad_2_write; + port[1].data_r = sportspad_2_read; + break; + } + + default: + { + port[1].data_w = dummy_write; + port[1].data_r = dummy_read; + break; + } + } + + /* External Port (unconnected) */ + port[2].data_w = dummy_write; + port[2].data_r = dummy_read; +} + + +void io_reset(void) +{ + /* Reset I/O registers */ + if (system_hw == SYSTEM_PBC) + { + /* SMS compatibility mode control register */ + io_reg[0x00] = 0xFF; + } + else + { + /* Genesis mode registers */ + io_reg[0x00] = region_code | 0x20 | (config.tmss & 1); + io_reg[0x01] = 0x00; + io_reg[0x02] = 0x00; + io_reg[0x03] = 0x00; + io_reg[0x04] = 0x00; + io_reg[0x05] = 0x00; + io_reg[0x06] = 0x00; + io_reg[0x07] = 0xFF; + io_reg[0x08] = 0x00; + io_reg[0x09] = 0x00; + io_reg[0x0A] = 0xFF; + io_reg[0x0B] = 0x00; + io_reg[0x0C] = 0x00; + io_reg[0x0D] = 0xFB; + io_reg[0x0E] = 0x00; + io_reg[0x0F] = 0x00; + } + + /* Reset connected peripherals */ + input_reset(); +} + +void io_68k_write(unsigned int offset, unsigned int data) +{ + switch (offset) + { + case 0x01: /* Port A Data */ + case 0x02: /* Port B Data */ + case 0x03: /* Port C Data */ + { + io_reg[offset] = data; + port[offset-1].data_w(data, io_reg[offset + 3]); + return; + } + + case 0x04: /* Port A Ctrl */ + case 0x05: /* Port B Ctrl */ + case 0x06: /* Port C Ctrl */ + { + if (data != io_reg[offset]) + { + io_reg[offset] = data; + port[offset-4].data_w(io_reg[offset-3], data); + } + return; + } + + case 0x07: /* Port A TxData */ + case 0x0A: /* Port B TxData */ + case 0x0D: /* Port C TxData */ + { + io_reg[offset] = data; + return; + } + + case 0x09: /* Port A S-Ctrl */ + case 0x0C: /* Port B S-Ctrl */ + case 0x0F: /* Port C S-Ctrl */ + { + io_reg[offset] = data & 0xF8; + return; + } + + default: /* Read-only ports */ + { + return; + } + } +} + +unsigned int io_68k_read(unsigned int offset) +{ + switch(offset) + { + case 0x01: /* Port A Data */ + case 0x02: /* Port B Data */ + case 0x03: /* Port C Data */ + { + unsigned int mask = 0x80 | io_reg[offset + 3]; + unsigned int data = port[offset-1].data_r(); + return (io_reg[offset] & mask) | (data & ~mask); + } + + default: /* return register value */ + { + return io_reg[offset]; + } + } +} + +void io_z80_write(unsigned int data) +{ +/* pins can't be configured as output on japanese models */ + if (region_code & REGION_USA) + { + /* + Bit Function + -------------- + D7 : Port B TH pin output level (1=high, 0=low) + D6 : Port B TR pin output level (1=high, 0=low) + D5 : Port A TH pin output level (1=high, 0=low) + D4 : Port A TR pin output level (1=high, 0=low) + D3 : Port B TH pin direction (1=input, 0=output) + D2 : Port B TR pin direction (1=input, 0=output) + D1 : Port A TH pin direction (1=input, 0=output) + D0 : Port A TR pin direction (1=input, 0=output) + */ + + /* Send TR/TH state to connected peripherals */ + port[0].data_w((data << 1) & 0x60, (~io_reg[0] << 5) & 0x60); + port[1].data_w((data >> 1) & 0x60, (~io_reg[0] << 3) & 0x60); + + + /* Check for TH low-to-high transitions on both ports */ + if ((!(io_reg[0] & 0x80) && (data & 0x80)) || + (!(io_reg[0] & 0x20) && (data & 0x20))) + { + /* Latch new HVC */ + hvc_latch = hctab[(mcycles_z80 + Z80_CYCLE_OFFSET) % MCYCLES_PER_LINE] | 0x10000; + } + } + else + { + /* outputs return fixed value */ + data &= 0x0F; + } + + /* Update control register */ + io_reg[0] = data; +} + +unsigned int io_z80_read(unsigned int offset) +{ + /* Read port A & port B input data */ + unsigned int data = (port[0].data_r()) | (port[1].data_r() << 8); + + /* Read control register value */ + unsigned int ctrl = io_reg[0]; + + /* I/O ports */ + if (offset) + { + /* + Bit Function + -------------- + D7 : Port B TH pin input + D6 : Port A TH pin input + D5 : Unused (0 on Mega Drive, 1 otherwise) + D4 : RESET button (always 1 on Mega Drive) + D3 : Port B TR pin input + D2 : Port B TL pin input + D1 : Port B Right pin input + D0 : Port B Left pin input + */ + data = ((data >> 10) & 0x0F) | (data & 0x40) | ((data >> 7) & 0x80) | 0x10; + + /* Adjust port B TH state if configured as output */ + if (!(ctrl & 0x08)) + { + data &= ~0x80; + data |= (ctrl & 0x80); + } + + /* Adjust port A TH state if configured as output */ + if (!(ctrl & 0x02)) + { + data &= ~0x40; + data |= ((ctrl & 0x20) << 1); + } + + /* Adjust port B TR state if configured as output */ + if (!(ctrl & 0x04)) + { + data &= ~0x08; + data |= ((ctrl & 0x40) >> 3); + } + } + else + { + /* + Bit Function + -------------- + D7 : Port B Down pin input + D6 : Port B Up pin input + D5 : Port A TR pin input + D4 : Port A TL pin input + D3 : Port A Right pin input + D2 : Port A Left pin input + D1 : Port A Down pin input + D0 : Port A Up pin input + */ + data = (data & 0x3F) | ((data >> 2) & 0xC0); + + /* Adjust port A TR state if configured as output */ + if (!(ctrl & 0x01)) + { + data &= ~0x20; + data |= ((ctrl & 0x10) << 1); + } + } + + return data; +} + diff --git a/source/io_ctrl.h b/source/io_ctrl.h new file mode 100644 index 0000000..853524f --- /dev/null +++ b/source/io_ctrl.h @@ -0,0 +1,45 @@ +/*************************************************************************************** + * Genesis Plus + * I/O controller (MD & MS compatibility modes) + * + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) + * Eke-Eke (2007-2011), additional code & fixes for the GCN/Wii port + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************************************/ + +#ifndef _GEN_IO_H_ +#define _GEN_IO_H_ + +#define REGION_JAPAN_NTSC 0x00 +#define REGION_JAPAN_PAL 0x40 +#define REGION_USA 0x80 +#define REGION_EUROPE 0xC0 + +/* Global variables */ +extern uint8 io_reg[0x10]; +extern uint8 region_code; + +/* Function prototypes */ +extern void io_init(void); +extern void io_reset(void); +extern void io_68k_write(unsigned int offset, unsigned int data); +extern unsigned int io_68k_read(unsigned int offset); +extern void io_z80_write(unsigned int data); +extern unsigned int io_z80_read(unsigned int offset); + +#endif /* _IO_H_ */ + diff --git a/source/loadrom.c b/source/loadrom.c index 4e035af..403f81d 100644 --- a/source/loadrom.c +++ b/source/loadrom.c @@ -60,7 +60,6 @@ #define MAXCOMPANY 64 #define MAXPERIPHERALS 14 - typedef struct { char companyid[6]; @@ -84,7 +83,8 @@ char rom_filename[256]; * Based on the document provided at * http://www.zophar.net/tech/files/Genesis_ROM_Format.txt **************************************************************************/ -static COMPANYINFO companyinfo[MAXCOMPANY] = { +static const COMPANYINFO companyinfo[MAXCOMPANY] = +{ {"ACLD", "Ballistic"}, {"RSI", "Razorsoft"}, {"SEGA", "SEGA"}, @@ -157,7 +157,8 @@ static COMPANYINFO companyinfo[MAXCOMPANY] = { * Based on the document provided at * http://www.zophar.net/tech/files/Genesis_ROM_Format.txt ***************************************************************************/ -static PERIPHERALINFO peripheralinfo[MAXPERIPHERALS] = { +static const PERIPHERALINFO peripheralinfo[MAXPERIPHERALS] = +{ {"J", "3B Joypad"}, {"6", "6B Joypad"}, {"K", "Keyboard"}, @@ -175,11 +176,10 @@ static PERIPHERALINFO peripheralinfo[MAXPERIPHERALS] = { }; /*************************************************************************** - * GetRealChecksum * - * Compute ROM checksum. + * Compute ROM real checksum. ***************************************************************************/ -static uint16 GetRealChecksum (uint8 *rom, int length) +static uint16 getchecksum(uint8 *rom, int length) { int i; uint16 checksum = 0; @@ -193,60 +193,157 @@ static uint16 GetRealChecksum (uint8 *rom, int length) } /*************************************************************************** - * getrominfo * * Pass a pointer to the ROM base address. ***************************************************************************/ -static void getrominfo (char *romheader) +static void getrominfo(char *romheader) { - int i,j; + uint16 offset = 0; + /* Clear ROM info structure */ memset (&rominfo, 0, sizeof (ROMINFO)); - memcpy (&rominfo.consoletype, romheader + ROMCONSOLE, 16); - memcpy (&rominfo.copyright, romheader + ROMCOPYRIGHT, 16); - - rominfo.domestic[0] = romheader[ROMDOMESTIC]; - j=1; - for (i=1; i<48; i++) + /* Look for Master System ROM header */ + if (!memcmp (&cart.rom[0x1ff0], "TMR SEGA", 8)) { - if ((rominfo.domestic[j-1] != 32) || (romheader[ROMDOMESTIC + i] != 32)) + offset = 0x1ff0; + } + else if (!memcmp (&cart.rom[0x3ff0], "TMR SEGA", 8)) + { + offset = 0x3ff0; + } + else if (!memcmp (&cart.rom[0x7ff0], "TMR SEGA", 8)) + { + offset = 0x7ff0; + } + + /* If found, assume this is a SMS game */ + if (offset) + { + /* force SMS compatibilty mode */ + system_hw = SYSTEM_PBC; + + /* checksum */ + rominfo.checksum = cart.rom[offset + 0x0a] | (cart.rom[offset + 0x0b] << 8); + + /* product code & version */ + sprintf(&rominfo.product[0], "%02d", cart.rom[offset + 0x0e] >> 4); + sprintf(&rominfo.product[2], "%02x", cart.rom[offset + 0x0d]); + sprintf(&rominfo.product[4], "%02x", cart.rom[offset + 0x0c]); + sprintf(&rominfo.product[6], "-%d", cart.rom[offset + 0x0e] & 0x0F); + + /* region code */ + switch (cart.rom[offset + 0x0f] >> 4) { - rominfo.domestic[j] = romheader[ROMDOMESTIC + i]; - j++; + case 3: + strcpy(rominfo.country,"SMS Japan"); + break; + case 4: + strcpy(rominfo.country,"SMS Export"); + break; + case 5: + strcpy(rominfo.country,"GG Japan"); + break; + case 6: + strcpy(rominfo.country,"GG Export"); + break; + case 7: + strcpy(rominfo.country,"GG International"); + break; + default: + sprintf(rominfo.country,"Unknown (%d)", cart.rom[offset + 0x0f] >> 4); + break; + } + + /* ROM size */ + rominfo.romstart = 0; + switch (cart.rom[offset + 0x0f] & 0x0F) + { + case 0x00: + rominfo.romend = 0x3FFFF; + break; + case 0x01: + rominfo.romend = 0x7FFFF; + break; + case 0x02: + rominfo.romend = 0xFFFFF; + break; + case 0x0a: + rominfo.romend = 0x1FFF; + break; + case 0x0b: + rominfo.romend = 0x3FFF; + break; + case 0x0c: + rominfo.romend = 0x7FFF; + break; + case 0x0d: + rominfo.romend = 0xBFFF; + break; + case 0x0e: + rominfo.romend = 0xFFFF; + break; + case 0x0f: + rominfo.romend = 0x1FFFF; + break; } } - rominfo.domestic[j] = 0; - - rominfo.international[0] = romheader[ROMWORLD]; - j=1; - for (i=1; i<48; i++) + else { - if ((rominfo.international[j-1] != 32) || (romheader[ROMWORLD + i] != 32)) + /* Some SMS games don't have any header */ + if (system_hw == SYSTEM_PBC) return; + + /* Genesis ROM header support */ + memcpy (&rominfo.consoletype, romheader + ROMCONSOLE, 16); + memcpy (&rominfo.copyright, romheader + ROMCOPYRIGHT, 16); + + /* Domestic (japanese) name */ + rominfo.domestic[0] = romheader[ROMDOMESTIC]; + int i, j = 1; + for (i=1; i<48; i++) { - rominfo.international[j] = romheader[ROMWORLD + i]; - j++; + if ((rominfo.domestic[j-1] != 32) || (romheader[ROMDOMESTIC + i] != 32)) + { + rominfo.domestic[j] = romheader[ROMDOMESTIC + i]; + j++; + } } - } - rominfo.international[j] = 0; + rominfo.domestic[j] = 0; - memcpy (&rominfo.ROMType, romheader + ROMTYPE, 2); - memcpy (&rominfo.product, romheader + ROMPRODUCT, 12); - memcpy (&rominfo.checksum, romheader + ROMCHECKSUM, 2); - memcpy (&rominfo.romstart, romheader + ROMROMSTART, 4); - memcpy (&rominfo.romend, romheader + ROMROMEND, 4); - memcpy (&rominfo.country, romheader + ROMCOUNTRY, 16); + /* International name */ + rominfo.international[0] = romheader[ROMWORLD]; + j=1; + for (i=1; i<48; i++) + { + if ((rominfo.international[j-1] != 32) || (romheader[ROMWORLD + i] != 32)) + { + rominfo.international[j] = romheader[ROMWORLD + i]; + j++; + } + } + rominfo.international[j] = 0; + /* ROM informations */ + memcpy (&rominfo.ROMType, romheader + ROMTYPE, 2); + memcpy (&rominfo.product, romheader + ROMPRODUCT, 12); + memcpy (&rominfo.checksum, romheader + ROMCHECKSUM, 2); + memcpy (&rominfo.romstart, romheader + ROMROMSTART, 4); + memcpy (&rominfo.romend, romheader + ROMROMEND, 4); + memcpy (&rominfo.country, romheader + ROMCOUNTRY, 16); + + /* Checksums */ #ifdef LSB_FIRST - rominfo.checksum = (rominfo.checksum >> 8) | ((rominfo.checksum & 0xff) << 8); + rominfo.checksum = (rominfo.checksum >> 8) | ((rominfo.checksum & 0xff) << 8); #endif - rominfo.realchecksum = GetRealChecksum (((uint8 *) cart.rom) + 0x200, cart.romsize - 0x200); + rominfo.realchecksum = getchecksum(((uint8 *) cart.rom) + 0x200, cart.romsize - 0x200); - rominfo.peripherals = 0; - for (i = 0; i < 14; i++) - for (j=0; j < 14; j++) - if (romheader[ROMIOSUPPORT+i] == peripheralinfo[j].pID[0]) - rominfo.peripherals |= (1 << j); + /* Supported peripherals */ + rominfo.peripherals = 0; + for (i = 0; i < 14; i++) + for (j=0; j < 14; j++) + if (romheader[ROMIOSUPPORT+i] == peripheralinfo[j].pID[0]) + rominfo.peripherals |= (1 << j); + } } /*************************************************************************** @@ -273,61 +370,71 @@ static void deinterleave_block(uint8 * src) ***************************************************************************/ int load_rom(char *filename) { - int i, size, offset = 0; + int i, size; #ifdef NGC size = cart.romsize; - sprintf(rom_filename,"%s",filename); - rom_filename[strlen(rom_filename) - 4] = 0; #else uint8 *ptr; ptr = load_archive(filename, &size); if(!ptr) return (0); - memcpy(cart.rom, ptr + offset, size); + memcpy(cart.rom, ptr, size); free(ptr); #endif - /* detect interleaved format (.SMD) */ + /* Minimal ROM size */ + if (size < 0x4000) + { + memset(cart.rom + size, 0xFF, 0x4000 - size); + size = 0x4000; + } + + /* Get file extension */ + if (!strnicmp(".sms", &filename[strlen(filename) - 4], 4)) + { + /* Force SMS compatibility mode */ + system_hw = SYSTEM_PBC; + } + else + { + /* Assume Genesis mode */ + system_hw = SYSTEM_GENESIS; + } + + /* Take care of 512 byte header, if present */ if (strncmp((char *)(cart.rom + 0x100),"SEGA", 4) && ((size / 512) & 1)) { size -= 512; - offset += 512; - for (i = 0; i < (size / 0x4000); i += 1) - deinterleave_block (cart.rom + offset + (i * 0x4000)); - memcpy(cart.rom, cart.rom + offset, size); + memcpy (cart.rom, cart.rom + 512, size); + + /* interleaved ROM format (.smd) */ + if (system_hw != SYSTEM_PBC) + { + for (i = 0; i < (size / 0x4000); i++) + { + deinterleave_block (cart.rom + (i * 0x4000)); + } + } } /* max. 10 MBytes supported */ if (size > MAXROMSIZE) size = MAXROMSIZE; cart.romsize = size; - + /* clear unused ROM space */ memset(cart.rom + size, 0xff, MAXROMSIZE - size); /* get infos from ROM header */ getrominfo((char *)cart.rom); - /* get specific input devices */ - input_autodetect(); - - /* get default region */ + /* detect console region */ region_autodetect(); + /* Genesis ROM specific */ + if (system_hw != SYSTEM_PBC) + { #ifdef LSB_FIRST - /* Byteswap ROM */ - uint8 temp; - for(i = 0; i < size; i += 2) - { - temp = cart.rom[i]; - cart.rom[i] = cart.rom[i+1]; - cart.rom[i+1] = temp; - } -#endif - - /* byteswapped RADICA dumps (from Haze) */ - if (((strstr(rominfo.product,"-K0101") != NULL) && (rominfo.checksum == 0xf424)) || - ((strstr(rominfo.product,"-K0109") != NULL) && (rominfo.checksum == 0x4f10))) - { + /* Byteswap ROM */ uint8 temp; for(i = 0; i < size; i += 2) { @@ -335,15 +442,27 @@ int load_rom(char *filename) cart.rom[i] = cart.rom[i+1]; cart.rom[i+1] = temp; } - } +#endif - /* console hardware */ - if (strstr(rominfo.consoletype, "SEGA PICO") != NULL) - system_hw = SYSTEM_PICO; - else if (strstr(rominfo.consoletype, "SEGA MEGADRIVE") != NULL) - system_hw = SYSTEM_MEGADRIVE; - else - system_hw = SYSTEM_GENESIS; + /* byteswapped RADICA dumps (from Haze) */ + if (((strstr(rominfo.product,"-K0101") != NULL) && (rominfo.checksum == 0xf424)) || + ((strstr(rominfo.product,"-K0109") != NULL) && (rominfo.checksum == 0x4f10))) + { + uint8 temp; + for(i = 0; i < size; i += 2) + { + temp = cart.rom[i]; + cart.rom[i] = cart.rom[i+1]; + cart.rom[i+1] = temp; + } + } + + /* PICO hardware */ + if (strstr(rominfo.consoletype, "SEGA PICO") != NULL) + { + system_hw = SYSTEM_PICO; + } + } return(1); } @@ -356,83 +475,79 @@ int load_rom(char *filename) ****************************************************************************/ void region_autodetect(void) { - /* country codes used to differentiate region */ - /* 0001 = japan ntsc (1) */ - /* 0010 = japan pal (2) */ - /* 0100 = usa (4) */ - /* 1000 = europe (8) */ - - int country = 0; - int i = 0; - char c; - - /* reading header to find the country */ - if (!strnicmp(rominfo.country, "eur", 3)) country |= 8; - else if (!strnicmp(rominfo.country, "usa", 3)) country |= 4; - else if (!strnicmp(rominfo.country, "jap", 3)) country |= 1; - - else for(i = 0; i < 4; i++) + if (system_hw == SYSTEM_PBC) { - c = toupper((int)rominfo.country[i]); - if (c == 'U') country |= 4; - else if (c == 'J') country |= 1; - else if (c == 'E') country |= 8; - else if (c == 'K') country |= 1; - else if (c < 16) country |= c; - else if ((c >= '0') && (c <= '9')) country |= c - '0'; - else if ((c >= 'A') && (c <= 'F')) country |= c - 'A' + 10; + region_code = sms_cart_region_detect(); + } + else + { + /* country codes used to differentiate region */ + /* 0001 = japan ntsc (1) */ + /* 0010 = japan pal (2) */ + /* 0100 = usa (4) */ + /* 1000 = europe (8) */ + int country = 0; + int i = 0; + char c; + + /* from Gens */ + if (!strnicmp(rominfo.country, "eur", 3)) country |= 8; + else if (!strnicmp(rominfo.country, "usa", 3)) country |= 4; + else if (!strnicmp(rominfo.country, "jap", 3)) country |= 1; + else + { + /* look for each characters */ + for(i = 0; i < 4; i++) + { + c = toupper((int)rominfo.country[i]); + + if (c == 'U') country |= 4; + else if (c == 'J') country |= 1; + else if (c == 'E') country |= 8; + else if (c == 'K') country |= 1; + else if (c < 16) country |= c; + else if ((c >= '0') && (c <= '9')) country |= c - '0'; + else if ((c >= 'A') && (c <= 'F')) country |= c - 'A' + 10; + } + } + + /* set default console region (USA > JAPAN > EUROPE) */ + if (country & 4) region_code = REGION_USA; + else if (country & 1) region_code = REGION_JAPAN_NTSC; + else if (country & 8) region_code = REGION_EUROPE; + else if (country & 2) region_code = REGION_JAPAN_PAL; + else region_code = REGION_USA; + + /* some games need specific REGION setting */ + if (((strstr(rominfo.product,"T-45033") != NULL) && (rominfo.checksum == 0x0F81)) || /* Alisia Dragon (Europe) */ + (strstr(rominfo.product,"T-69046-50") != NULL) || /* Back to the Future III (Europe) */ + (strstr(rominfo.product,"T-120106-00") != NULL) || /* Brian Lara Cricket (Europe) */ + (strstr(rominfo.product,"T-70096 -00") != NULL)) /* Muhammad Ali Heavyweight Boxing (Europe) */ + { + /* need PAL settings */ + region_code = REGION_EUROPE; + } + + if ((rominfo.realchecksum == 0x532e) && (strstr(rominfo.product,"1011-00") != NULL)) + { + /* On Dal Jang Goon (Korea) needs JAPAN region code */ + region_code = REGION_JAPAN_NTSC; + } } - /* automatic detection */ - /* setting region */ - /* this is used by IO register */ - if (country & 4) region_code = REGION_USA; - else if (country & 1) region_code = REGION_JAPAN_NTSC; - else if (country & 8) region_code = REGION_EUROPE; - else if (country & 2) region_code = REGION_JAPAN_PAL; - else region_code = REGION_USA; + /* forced console region */ + if (config.region_detect == 1) region_code = REGION_USA; + else if (config.region_detect == 2) region_code = REGION_EUROPE; + else if (config.region_detect == 3) region_code = REGION_JAPAN_NTSC; + else if (config.region_detect == 4) region_code = REGION_JAPAN_PAL; - /* some games need specific REGION setting */ - if (((strstr(rominfo.product,"T-45033") != NULL) && (rominfo.checksum == 0x0F81)) || /* Alisia Dragon (PAL) */ - (strstr(rominfo.product,"T-69046-50") != NULL) || /* Back to the Future III (PAL) */ - (strstr(rominfo.product,"T-120106-00") != NULL) || /* Brian Lara Cricket (PAL) */ - (strstr(rominfo.product,"T-70096 -00") != NULL)) /* Muhammad Ali Heavyweight Boxing (PAL) */ - { - /* need PAL settings */ - region_code = REGION_EUROPE; - } - else if ((rominfo.realchecksum == 0x532e) && (strstr(rominfo.product,"1011-00") != NULL)) - { - /* On Dal Jang Goon (Korea) needs JAP region code */ - region_code = REGION_JAPAN_NTSC; - } - - /* Force region setting */ - if (config.region_detect == 1) - region_code = REGION_USA; - else if (config.region_detect == 2) - region_code = REGION_EUROPE; - else if (config.region_detect == 3) - region_code = REGION_JAPAN_NTSC; - else if (config.region_detect == 4) - region_code = REGION_JAPAN_PAL; - - /* Set VDP default mode */ - switch (region_code) - { - case REGION_EUROPE: - case REGION_JAPAN_PAL: - vdp_pal = 1; - break; - - default: - vdp_pal = 0; - break; - } + /* PAL/NTSC timings */ + vdp_pal = (region_code & REGION_JAPAN_PAL) >> 6; } + /**************************************************************************** - * get_company + * get_company (Softdev - 2006) * * Try to determine which company made this rom * @@ -446,8 +561,10 @@ char *get_company(void) int i; char company[10]; - for (i = 3; i < 8; i++) + for (i = 3; i < 8; i++) + { company[i - 3] = rominfo.copyright[i]; + } company[5] = 0; /** OK, first look for a hyphen @@ -466,19 +583,19 @@ char *get_company(void) company[i] = 0; if (strlen (company) == 0) - return companyinfo[MAXCOMPANY - 1].company; + return (char *)companyinfo[MAXCOMPANY - 1].company; for (i = 0; i < MAXCOMPANY - 1; i++) { if (!(strncmp (company, companyinfo[i].companyid, strlen (company)))) - return companyinfo[i].company; + return (char *)companyinfo[i].company; } - return companyinfo[MAXCOMPANY - 1].company; + return (char *)companyinfo[MAXCOMPANY - 1].company; } /**************************************************************************** - * get_peripheral + * get_peripheral (Softdev - 2006) * * Return peripheral name based on header code * @@ -486,7 +603,7 @@ char *get_company(void) char *get_peripheral(int index) { if (index < MAXPERIPHERALS) - return peripheralinfo[index].pName; - return companyinfo[MAXCOMPANY - 1].company; + return (char *)peripheralinfo[index].pName; + return (char *)companyinfo[MAXCOMPANY - 1].company; } diff --git a/source/m68k/m68k.h b/source/m68k/m68k.h index 6d0075c..5c8571a 100644 --- a/source/m68k/m68k.h +++ b/source/m68k/m68k.h @@ -381,7 +381,7 @@ unsigned int m68k_disassemble(char* str_buff, unsigned int pc, unsigned int cpu_ unsigned int m68k_disassemble_raw(char* str_buff, unsigned int pc, const unsigned char* opdata, const unsigned char* argdata, unsigned int cpu_type); /*** Not really required, but makes for clean compile under DevkitPPC ***/ -extern int vdp_int_ack_callback(int int_level); +extern int vdp_68k_irq_ack(int int_level); /* ======================================================================== */ /* ============================== MAME STUFF ============================== */ diff --git a/source/m68k/m68kconf.h b/source/m68k/m68kconf.h index 55d53ab..cbd3cf6 100644 --- a/source/m68k/m68kconf.h +++ b/source/m68k/m68kconf.h @@ -82,7 +82,7 @@ * auto-clear when the interrupt is serviced. */ #define M68K_EMULATE_INT_ACK OPT_SPECIFY_HANDLER -#define M68K_INT_ACK_CALLBACK(A) vdp_int_ack_callback(A) +#define M68K_INT_ACK_CALLBACK(A) vdp_68k_irq_ack(A) /* If ON, CPU will call the breakpoint acknowledge callback when it encounters diff --git a/source/m68k/m68kcpu.c b/source/m68k/m68kcpu.c index 2e1b3d3..4b992e4 100644 --- a/source/m68k/m68kcpu.c +++ b/source/m68k/m68kcpu.c @@ -846,7 +846,7 @@ void m68k_set_irq(unsigned int int_level) extern uint16 v_counter; extern void error(char *format, ...); #endif -extern uint8 irq_status; +extern uint8 m68k_irq_state; void m68k_run (unsigned int cycles) { @@ -856,13 +856,13 @@ void m68k_run (unsigned int cycles) while (mcycles_68k < cycles) { /* check IRQ triggering */ - if (irq_status & 0x10) + if (m68k_irq_state & 0x10) { - irq_status &= ~0x10; - CPU_INT_LEVEL = (irq_status & 6) << 8; + m68k_irq_state &= ~0x10; + CPU_INT_LEVEL = (m68k_irq_state & 6) << 8; /* IRQ was triggered during previous instruction */ - if (irq_status & 0x20) + if (m68k_irq_state & 0x20) { /* one instruction latency */ REG_IR = m68ki_read_imm_16(); diff --git a/source/m68k/m68kcpu.h b/source/m68k/m68kcpu.h index 06b3b7d..5b1df54 100644 --- a/source/m68k/m68kcpu.h +++ b/source/m68k/m68kcpu.h @@ -1451,9 +1451,13 @@ INLINE void m68ki_jump(uint new_pc) INLINE void m68ki_jump_vector(uint vector) { +#if M68K_EMULATE_040 || M68K_EMULATE_020 || M68K_EMULATE_EC020 || M68K_EMULATE_010 REG_PC = (vector<<2) + REG_VBR; REG_PC = m68ki_read_data_32(REG_PC); +#else + REG_PC = m68ki_read_data_32(vector<<2); m68ki_pc_changed(REG_PC); +#endif } @@ -1993,10 +1997,6 @@ INLINE void m68ki_exception_address_error(void) /* Service an interrupt request and start exception processing */ INLINE void m68ki_exception_interrupt(uint int_level) { - uint vector; - uint sr; - uint new_pc; - #if M68K_EMULATE_ADDRESS_ERROR == OPT_ON if(CPU_TYPE_IS_000(CPU_TYPE)) { @@ -2012,30 +2012,23 @@ INLINE void m68ki_exception_interrupt(uint int_level) return; /* Acknowledge the interrupt */ - vector = m68ki_int_ack(int_level); + m68ki_int_ack(int_level); - /* Get the interrupt vector */ - if(vector == M68K_INT_ACK_AUTOVECTOR) - /* Use the autovectors. This is the most commonly used implementation */ - vector = EXCEPTION_INTERRUPT_AUTOVECTOR+int_level; - else if(vector == M68K_INT_ACK_SPURIOUS) - /* Called if no devices respond to the interrupt acknowledge */ - vector = EXCEPTION_SPURIOUS_INTERRUPT; - else if(vector > 255) - { - M68K_DO_LOG_EMU((M68K_LOG_FILEHANDLE "%s at %08x: Interrupt acknowledge returned invalid vector $%x\n", - m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PC), vector)); - return; - } + /* Always use the autovectors. */ + uint vector = EXCEPTION_INTERRUPT_AUTOVECTOR+int_level; /* Start exception processing */ - sr = m68ki_init_exception(); + uint sr = m68ki_init_exception(); /* Set the interrupt mask to the level of the one being serviced */ FLAG_INT_MASK = int_level<<8; /* Get the new PC */ - new_pc = m68ki_read_data_32((vector<<2) + REG_VBR); +#if M68K_EMULATE_040 || M68K_EMULATE_020 || M68K_EMULATE_EC020 || M68K_EMULATE_010 + uint new_pc = m68ki_read_data_32((vector<<2) + REG_VBR); +#else + uint new_pc = m68ki_read_data_32(vector<<2); +#endif /* If vector is uninitialized, call the uninitialized interrupt vector */ if(new_pc == 0) @@ -2043,6 +2036,7 @@ INLINE void m68ki_exception_interrupt(uint int_level) /* Generate a stack frame */ m68ki_stack_frame_0000(REG_PC, sr, vector); +#if M68K_EMULATE_040 || M68K_EMULATE_020 || M68K_EMULATE_EC020 || M68K_EMULATE_010 if(FLAG_M && CPU_TYPE_IS_EC020_PLUS(CPU_TYPE)) { /* Create throwaway frame */ @@ -2050,6 +2044,7 @@ INLINE void m68ki_exception_interrupt(uint int_level) sr |= 0x2000; /* Same as SR in master stack frame except S is forced high */ m68ki_stack_frame_0001(REG_PC, sr, vector); } +#endif m68ki_jump(new_pc); diff --git a/source/mem68k.c b/source/mem68k.c index 39de9ea..5274d08 100644 --- a/source/mem68k.c +++ b/source/mem68k.c @@ -1,6 +1,6 @@ /*************************************************************************************** * Genesis Plus - * 68k bus address decoding + * 68k bus controller * * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) * Eke-Eke (2007,2008,2009), additional code & fixes for the GCN/Wii port @@ -124,7 +124,7 @@ unsigned int eeprom_read_byte(unsigned int address) unsigned int eeprom_read_word(unsigned int address) { - if (address == (eeprom.type.sda_out_adr & 0xfffffe)) + if (address == (eeprom.type.sda_out_adr & 0xFFFFFE)) { return eeprom_read(1); } @@ -143,7 +143,7 @@ void eeprom_write_byte(unsigned int address, unsigned int data) void eeprom_write_word(unsigned int address, unsigned int data) { - if ((address == (eeprom.type.sda_in_adr & 0xfffffe)) || (address == (eeprom.type.scl_adr & 0xfffffe))) + if ((address == (eeprom.type.sda_in_adr & 0xFFFFFE)) || (address == (eeprom.type.scl_adr & 0xFFFFFE))) { eeprom_write(address, data, 1); return; @@ -166,17 +166,17 @@ unsigned int z80_read_byte(unsigned int address) case 3: /* Misc */ { - if ((address & 0xff00) == 0x7f00) + if ((address & 0xFF00) == 0x7F00) { /* VDP (through 68k bus) */ return m68k_lockup_r_8(address); } - return (m68k_read_bus_8(address) | 0xff); + return (m68k_read_bus_8(address) | 0xFF); } default: /* ZRAM */ { - return zram[address & 0x1fff]; + return zram[address & 0x1FFF]; } } } @@ -199,7 +199,7 @@ void z80_write_byte(unsigned int address, unsigned int data) case 3: { - switch ((address >> 8) & 0x7f) + switch ((address >> 8) & 0x7F) { case 0x60: /* Bank register */ { @@ -207,7 +207,7 @@ void z80_write_byte(unsigned int address, unsigned int data) return; } - case 0x7f: /* VDP */ + case 0x7F: /* VDP */ { m68k_lockup_w_8(address, data); return; @@ -223,7 +223,7 @@ void z80_write_byte(unsigned int address, unsigned int data) default: /* ZRAM */ { - zram[address & 0x1fff] = data; + zram[address & 0x1FFF] = data; mcycles_68k += 8; /* ZRAM access latency (fixes Pacman 2: New Adventures) */ return; } @@ -241,13 +241,13 @@ void z80_write_word(unsigned int address, unsigned int data) /*--------------------------------------------------------------------------*/ unsigned int ctrl_io_read_byte(unsigned int address) { - switch ((address >> 8) & 0xff) + switch ((address >> 8) & 0xFF) { case 0x00: /* I/O chip */ { - if (!(address & 0xe0)) + if (!(address & 0xE0)) { - return io_read((address >> 1) & 0x0f); + return io_68k_read((address >> 1) & 0x0F); } return m68k_read_bus_8(address); } @@ -256,7 +256,7 @@ unsigned int ctrl_io_read_byte(unsigned int address) { if (!(address & 1)) { - unsigned int data = m68k_read_pcrelative_8(REG_PC) & 0xfe; + unsigned int data = m68k_read_pcrelative_8(REG_PC) & 0xFE; if (zstate == 3) { return data; @@ -273,7 +273,7 @@ unsigned int ctrl_io_read_byte(unsigned int address) unsigned int data = cart.hw.time_r(address); if (address & 1) { - return (data & 0xff); + return (data & 0xFF); } return (data >> 8); } @@ -284,7 +284,7 @@ unsigned int ctrl_io_read_byte(unsigned int address) { if (address & 1) { - unsigned int data = m68k_read_pcrelative_8(REG_PC) & 0xfe; + unsigned int data = m68k_read_pcrelative_8(REG_PC) & 0xFE; return (gen_bankswitch_r() | data); } return m68k_read_bus_8(address); @@ -309,13 +309,13 @@ unsigned int ctrl_io_read_byte(unsigned int address) unsigned int ctrl_io_read_word(unsigned int address) { - switch ((address >> 8) & 0xff) + switch ((address >> 8) & 0xFF) { case 0x00: /* I/O chip */ { - if (!(address & 0xe0)) + if (!(address & 0xE0)) { - unsigned int data = io_read((address >> 1) & 0x0f); + unsigned int data = io_68k_read((address >> 1) & 0x0F); return (data << 8 | data); } return m68k_read_bus_16(address); @@ -323,7 +323,7 @@ unsigned int ctrl_io_read_word(unsigned int address) case 0x11: /* BUSACK */ { - unsigned int data = m68k_read_pcrelative_16(REG_PC) & 0xfeff; + unsigned int data = m68k_read_pcrelative_16(REG_PC) & 0xFEFF; if (zstate == 3) { return data; @@ -342,12 +342,12 @@ unsigned int ctrl_io_read_word(unsigned int address) case 0x50: /* SVP */ { - if ((address & 0xfd) == 0) + if ((address & 0xFD) == 0) { return svp->ssp1601.gr[SSP_XST].h; } - if ((address & 0xff) == 4) + if ((address & 0xFF) == 4) { unsigned int data = svp->ssp1601.gr[SSP_PM0].h; svp->ssp1601.gr[SSP_PM0].h &= ~1; @@ -376,14 +376,14 @@ unsigned int ctrl_io_read_word(unsigned int address) void ctrl_io_write_byte(unsigned int address, unsigned int data) { - switch ((address >> 8) & 0xff) + switch ((address >> 8) & 0xFF) { case 0x00: /* I/O chip */ { - if ((address & 0xe1) == 0x01) + if ((address & 0xE1) == 0x01) { /* get /LWR only */ - io_write((address >> 1) & 0x0f, data); + io_68k_write((address >> 1) & 0x0F, data); return; } m68k_unused_8_w(address, data); @@ -449,13 +449,13 @@ void ctrl_io_write_byte(unsigned int address, unsigned int data) void ctrl_io_write_word(unsigned int address, unsigned int data) { - switch ((address >> 8) & 0xff) + switch ((address >> 8) & 0xFF) { case 0x00: /* I/O chip */ { - if (!(address & 0xe0)) + if (!(address & 0xE0)) { - io_write((address >> 1) & 0x0f, data & 0xff); + io_68k_write((address >> 1) & 0x0F, data & 0xFF); return; } m68k_unused_16_w(address, data); @@ -493,7 +493,7 @@ void ctrl_io_write_word(unsigned int address, unsigned int data) case 0x50: /* SVP REGISTERS */ { - if (!(address & 0xfd)) + if (!(address & 0xFD)) { svp->ssp1601.gr[SSP_XST].h = data; svp->ssp1601.gr[SSP_PM0].h |= 2; @@ -527,44 +527,44 @@ void ctrl_io_write_word(unsigned int address, unsigned int data) /*--------------------------------------------------------------------------*/ unsigned int vdp_read_byte(unsigned int address) { - switch (address & 0xfd) + switch (address & 0xFD) { case 0x00: /* DATA */ { - return (vdp_data_r() >> 8); + return (vdp_68k_data_r() >> 8); } case 0x01: /* DATA */ { - return (vdp_data_r() & 0xff); + return (vdp_68k_data_r() & 0xFF); } case 0x04: /* CTRL */ { - return (((vdp_ctrl_r(mcycles_68k) >> 8) & 3) | (m68k_read_pcrelative_8(REG_PC) & 0xfc)); + return (((vdp_ctrl_r(mcycles_68k) >> 8) & 3) | (m68k_read_pcrelative_8(REG_PC) & 0xFC)); } case 0x05: /* CTRL */ { - return (vdp_ctrl_r(mcycles_68k) & 0xff); + return (vdp_ctrl_r(mcycles_68k) & 0xFF); } case 0x08: /* HVC */ - case 0x0c: + case 0x0C: { return (vdp_hvc_r(mcycles_68k) >> 8); } case 0x09: /* HVC */ - case 0x0d: + case 0x0D: { - return (vdp_hvc_r(mcycles_68k) & 0xff); + return (vdp_hvc_r(mcycles_68k) & 0xFF); } case 0x18: /* Unused */ case 0x19: - case 0x1c: - case 0x1d: + case 0x1C: + case 0x1D: { return m68k_read_bus_8(address); } @@ -578,11 +578,11 @@ unsigned int vdp_read_byte(unsigned int address) unsigned int vdp_read_word(unsigned int address) { - switch (address & 0xfc) + switch (address & 0xFC) { case 0x00: /* DATA */ { - return vdp_data_r(); + return vdp_68k_data_r(); } case 0x04: /* CTRL */ @@ -591,13 +591,13 @@ unsigned int vdp_read_word(unsigned int address) } case 0x08: /* HVC */ - case 0x0c: + case 0x0C: { return vdp_hvc_r(mcycles_68k); } case 0x18: /* Unused */ - case 0x1c: + case 0x1C: { return m68k_read_bus_16(address); } @@ -611,17 +611,17 @@ unsigned int vdp_read_word(unsigned int address) void vdp_write_byte(unsigned int address, unsigned int data) { - switch (address & 0xfc) + switch (address & 0xFC) { case 0x00: /* Data port */ { - vdp_data_w(data << 8 | data); + vdp_68k_data_w(data << 8 | data); return; } case 0x04: /* Control port */ { - vdp_ctrl_w(data << 8 | data); + vdp_68k_ctrl_w(data << 8 | data); return; } @@ -643,7 +643,7 @@ void vdp_write_byte(unsigned int address, unsigned int data) return; } - case 0x1c: /* TEST register */ + case 0x1C: /* TEST register */ { vdp_test_w(data << 8 | data); return; @@ -659,24 +659,24 @@ void vdp_write_byte(unsigned int address, unsigned int data) void vdp_write_word(unsigned int address, unsigned int data) { - switch (address & 0xfc) + switch (address & 0xFC) { case 0x00: /* DATA */ { - vdp_data_w(data); + vdp_68k_data_w(data); return; } case 0x04: /* CTRL */ { - vdp_ctrl_w(data); + vdp_68k_ctrl_w(data); return; } case 0x10: /* PSG */ case 0x14: { - psg_write(mcycles_68k, data & 0xff); + psg_write(mcycles_68k, data & 0xFF); return; } @@ -686,7 +686,7 @@ void vdp_write_word(unsigned int address, unsigned int data) return; } - case 0x1c: /* Test register */ + case 0x1C: /* Test register */ { vdp_test_w(data); return; @@ -706,7 +706,7 @@ void vdp_write_word(unsigned int address, unsigned int data) unsigned int pico_read_byte(unsigned int address) { /* PICO */ - switch (address & 0xff) + switch (address & 0xFF) { case 0x01: /* VERSION register */ { @@ -715,7 +715,7 @@ unsigned int pico_read_byte(unsigned int address) case 0x03: /* IO register */ { - unsigned int retval = 0xff; + unsigned int retval = 0xFF; if (input.pad[0] & INPUT_B) retval &= ~0x10; if (input.pad[0] & INPUT_A) retval &= ~0x80; if (input.pad[0] & INPUT_UP) retval &= ~0x01; @@ -734,7 +734,7 @@ unsigned int pico_read_byte(unsigned int address) case 0x07: /* LSB PEN X coordinate */ { - return (input.analog[0][0] & 0xff); + return (input.analog[0][0] & 0xFF); } case 0x09: /* MSB PEN Y coordinate */ @@ -742,12 +742,12 @@ unsigned int pico_read_byte(unsigned int address) return (input.analog[0][1] >> 8); } - case 0x0b: /* LSB PEN Y coordinate */ + case 0x0B: /* LSB PEN Y coordinate */ { - return (input.analog[0][1] & 0xff); + return (input.analog[0][1] & 0xFF); } - case 0x0d: /* PAGE register (TODO) */ + case 0x0D: /* PAGE register (TODO) */ { return pico_page[pico_current]; } diff --git a/source/mem68k.h b/source/mem68k.h index 282666f..329f620 100644 --- a/source/mem68k.h +++ b/source/mem68k.h @@ -1,6 +1,6 @@ /*************************************************************************************** * Genesis Plus - * 68k bus arbitration + * 68k bus controller * * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) * Eke-Eke (2007,2008,2009), additional code & fixes for the GCN/Wii port diff --git a/source/membnk.c b/source/membnk.c index 0a87a65..952548f 100644 --- a/source/membnk.c +++ b/source/membnk.c @@ -30,9 +30,9 @@ unsigned int zbank_unused_r(unsigned int address) { #ifdef LOGERROR - error("Z80 bank unused read %06X\n", address); + error("Z80 bank unused read %06X (%x)\n", address, Z80.pc.d); #endif - return (address & 1) ? 0x00 : 0xff; + return (address & 1) ? 0x00 : 0xFF; } void zbank_unused_w(unsigned int address, unsigned int data) @@ -45,24 +45,24 @@ void zbank_unused_w(unsigned int address, unsigned int data) unsigned int zbank_lockup_r(unsigned int address) { #ifdef LOGERROR - error("Z80 bank lockup read %06X\n", address); + error("Z80 bank lockup read %06X (%x)\n", address, Z80.pc.d); #endif if (!config.force_dtack) { - mcycles_z80 = 0xffffffff; + mcycles_z80 = 0xFFFFFFFF; zstate = 0; } - return 0xff; + return 0xFF; } void zbank_lockup_w(unsigned int address, unsigned int data) { #ifdef LOGERROR - error("Z80 bank lockup write %06X = %02X\n", address, data); + error("Z80 bank lockup write %06X = %02X (%x)\n", address, data, Z80.pc.d); #endif if (!config.force_dtack) { - mcycles_z80 = 0xffffffff; + mcycles_z80 = 0xFFFFFFFF; zstate = 0; } } @@ -70,13 +70,13 @@ void zbank_lockup_w(unsigned int address, unsigned int data) /* I/O & Control registers */ unsigned int zbank_read_ctrl_io(unsigned int address) { - switch ((address >> 8) & 0xff) + switch ((address >> 8) & 0xFF) { case 0x00: /* I/O chip */ { - if (!(address & 0xe0)) + if (!(address & 0xE0)) { - return (io_read((address >> 1) & 0x0f)); + return (io_68k_read((address >> 1) & 0x0F)); } return zbank_unused_r(address); } @@ -87,7 +87,7 @@ unsigned int zbank_read_ctrl_io(unsigned int address) { return zbank_unused_r(address); } - return 0xff; + return 0xFF; } case 0x30: /* TIME */ @@ -97,7 +97,7 @@ unsigned int zbank_read_ctrl_io(unsigned int address) unsigned int data = cart.hw.time_r(address); if (address & 1) { - return (data & 0xff); + return (data & 0xFF); } return (data >> 8); } @@ -108,7 +108,7 @@ unsigned int zbank_read_ctrl_io(unsigned int address) { if (address & 1) { - return (gen_bankswitch_r() | 0xfe); + return (gen_bankswitch_r() | 0xFE); } return zbank_unused_r(address); } @@ -132,14 +132,14 @@ unsigned int zbank_read_ctrl_io(unsigned int address) void zbank_write_ctrl_io(unsigned int address, unsigned int data) { - switch ((address >> 8) & 0xff) + switch ((address >> 8) & 0xFF) { case 0x00: /* I/O chip */ { /* get /LWR only */ - if ((address & 0xe1) == 0x01) + if ((address & 0xE1) == 0x01) { - io_write((address >> 1) & 0x0f, data); + io_68k_write((address >> 1) & 0x0F, data); return; } zbank_unused_w(address, data); @@ -207,44 +207,44 @@ void zbank_write_ctrl_io(unsigned int address, unsigned int data) /* VDP */ unsigned int zbank_read_vdp(unsigned int address) { - switch (address & 0xfd) + switch (address & 0xFD) { case 0x00: /* DATA */ { - return (vdp_data_r() >> 8); + return (vdp_68k_data_r() >> 8); } case 0x01: /* DATA */ { - return (vdp_data_r() & 0xff); + return (vdp_68k_data_r() & 0xFF); } case 0x04: /* CTRL */ { - return (((vdp_ctrl_r(mcycles_z80) >> 8) & 3) | 0xfc); + return (((vdp_ctrl_r(mcycles_z80) >> 8) & 3) | 0xFC); } case 0x05: /* CTRL */ { - return (vdp_ctrl_r(mcycles_z80) & 0xff); + return (vdp_ctrl_r(mcycles_z80) & 0xFF); } case 0x08: /* HVC */ - case 0x0c: + case 0x0C: { return (vdp_hvc_r(mcycles_z80) >> 8); } case 0x09: /* HVC */ - case 0x0d: + case 0x0D: { - return (vdp_hvc_r(mcycles_z80) & 0xff); + return (vdp_hvc_r(mcycles_z80) & 0xFF); } case 0x18: /* Unused */ case 0x19: - case 0x1c: - case 0x1d: + case 0x1C: + case 0x1D: { return zbank_unused_r(address); } @@ -258,17 +258,17 @@ unsigned int zbank_read_vdp(unsigned int address) void zbank_write_vdp(unsigned int address, unsigned int data) { - switch (address & 0xfc) + switch (address & 0xFC) { case 0x00: /* Data port */ { - vdp_data_w(data << 8 | data); + vdp_68k_data_w(data << 8 | data); return; } case 0x04: /* Control port */ { - vdp_ctrl_w(data << 8 | data); + vdp_68k_ctrl_w(data << 8 | data); return; } @@ -290,7 +290,7 @@ void zbank_write_vdp(unsigned int address, unsigned int data) return; } - case 0x1c: /* TEST register */ + case 0x1C: /* TEST register */ { vdp_test_w(data << 8 | data); return; diff --git a/source/membnk.h b/source/membnk.h index d6cb6b8..af46fc9 100644 --- a/source/membnk.h +++ b/source/membnk.h @@ -1,6 +1,6 @@ /*************************************************************************************** * Genesis Plus - * 68k bus banked access from Z80 + * Z80 bank access to 68k bus * * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) * Eke-Eke (2007,2008,2009), additional code & fixes for the GCN/Wii port diff --git a/source/memz80.c b/source/memz80.c index 5a9fa8c..85d834a 100644 --- a/source/memz80.c +++ b/source/memz80.c @@ -1,9 +1,9 @@ /*************************************************************************************** * Genesis Plus - * Z80 bus address decoding (Genesis mode) + * Z80 bus controller (MD & MS compatibility modes) * * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) - * Eke-Eke (2007,2008,2009), additional code & fixes for the GCN/Wii port + * Eke-Eke (2007-2011), additional code & fixes for the GCN/Wii port * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,104 +22,106 @@ ****************************************************************************************/ #include "shared.h" -/* - Handlers for access to unused addresses and those which make the - machine lock up. -*/ -static inline void z80_unused_w(unsigned int address, unsigned int data) +/*--------------------------------------------------------------------------*/ +/* Handlers for access to unused addresses and those which make the */ +/* machine lock up. */ +/*--------------------------------------------------------------------------*/ + +static inline void z80_unused_w(unsigned int address, unsigned char data) { #ifdef LOGERROR - error("Z80 unused write %04X = %02X\n", address, data); + error("Z80 unused write %04X = %02X (%x)\n", address, data, Z80.pc.w.l); #endif } -static inline unsigned int z80_unused_r(unsigned int address) +static inline unsigned char z80_unused_r(unsigned int address) { #ifdef LOGERROR - error("Z80 unused read %04X\n", address); + error("Z80 unused read %04X (%x)\n", address, Z80.pc.w.l); #endif - return 0xff; + return 0xFF; } -static inline void z80_lockup_w(unsigned int address, unsigned int data) +static inline void z80_lockup_w(unsigned int address, unsigned char data) { #ifdef LOGERROR - error("Z80 lockup write %04X = %02X\n", address, data); + error("Z80 lockup write %04X = %02X (%x)\n", address, data, Z80.pc.w.l); #endif if (!config.force_dtack) { - mcycles_z80 = 0xffffffff; + mcycles_z80 = 0xFFFFFFFF; zstate = 0; } } -static inline unsigned int z80_lockup_r(unsigned int address) +static inline unsigned char z80_lockup_r(unsigned int address) { #ifdef LOGERROR - error("Z80 lockup read %04X\n", address); + error("Z80 lockup read %04X (%x)\n", address, Z80.pc); #endif if (!config.force_dtack) { - mcycles_z80 = 0xffffffff; + mcycles_z80 = 0xFFFFFFFF; zstate = 0; } - return 0xff; + return 0xFF; } -/* - Z80 memory handlers -*/ -unsigned int cpu_readmem16(unsigned int address) +/*--------------------------------------------------------------------------*/ +/* Z80 Memory handlers (Genesis mode) */ +/*--------------------------------------------------------------------------*/ + +unsigned char z80_md_memory_r(unsigned int address) { switch((address >> 13) & 7) { - case 0: /* Work RAM */ + case 0: /* $0000-$3FFF: Z80 RAM (8K mirrored) */ case 1: { - return zram[address & 0x1fff]; + return zram[address & 0x1FFF]; } - case 2: /* YM2612 */ + case 2: /* $4000-$5FFF: YM2612 */ { - return fm_read(mcycles_68k, address & 3); + return fm_read(mcycles_z80, address & 3); } - case 3: /* VDP */ + case 3: /* $7F00-$7FFF: VDP */ { - if ((address >> 8) == 0x7f) + if ((address >> 8) == 0x7F) { return (*zbank_memory_map[0xc0].read)(address); } return z80_unused_r(address); } - default: /* V-bus bank */ + default: /* $8000-$FFFF: 68k bank (32K) */ { - address = zbank | (address & 0x7fff); + address = zbank | (address & 0x7FFF); unsigned int slot = address >> 16; if (zbank_memory_map[slot].read) { return (*zbank_memory_map[slot].read)(address); } - return READ_BYTE(m68k_memory_map[slot].base, address & 0xffff); + return READ_BYTE(m68k_memory_map[slot].base, address & 0xFFFF); } } } -void cpu_writemem16(unsigned int address, unsigned int data) +void z80_md_memory_w(unsigned int address, unsigned char data) { switch((address >> 13) & 7) { - case 0: /* Work RAM */ + case 0: /* $0000-$3FFF: Z80 RAM (8K mirrored) */ case 1: { - zram[address & 0x1fff] = data; + zram[address & 0x1FFF] = data; return; } - case 2: /* YM2612 */ + case 2: /* $4000-$5FFF: YM2612 */ { fm_write(mcycles_z80, address & 3, data); return; @@ -129,13 +131,13 @@ void cpu_writemem16(unsigned int address, unsigned int data) { switch(address >> 8) { - case 0x60: + case 0x60: /* $6000-$60FF: Bank register */ { gen_zbank_w(data & 1); return; } - case 0x7f: + case 0x7F: /* $7F00-$7FFF: VDP */ { (*zbank_memory_map[0xc0].write)(address, data); return; @@ -149,39 +151,146 @@ void cpu_writemem16(unsigned int address, unsigned int data) } } - default: /* V-bus bank */ + default: /* $8000-$FFFF: 68k bank (32K) */ { - address = zbank | (address & 0x7fff); + address = zbank | (address & 0x7FFF); unsigned int slot = address >> 16; if (zbank_memory_map[slot].write) { (*zbank_memory_map[slot].write)(address, data); return; } - WRITE_BYTE(m68k_memory_map[slot].base, address & 0xffff, data); + WRITE_BYTE(m68k_memory_map[slot].base, address & 0xFFFF, data); return; } } } -/* - Port handlers. Ports are unused when not in Mark III compatability mode. - Games that access ports anyway: +/*--------------------------------------------------------------------------*/ +/* Z80 Memory handlers (Master System mode) */ +/*--------------------------------------------------------------------------*/ + +unsigned char z80_sms_memory_r(unsigned int address) +{ + return z80_readmap[(address) >> 10][(address) & 0x03FF]; +} + +/*--------------------------------------------------------------------------*/ +/* Z80 Port handlers */ +/*--------------------------------------------------------------------------*/ +/* + Ports are unused when not in Mark III compatibility mode. + + Genesis games that access ports anyway: Thunder Force IV reads port $BF in it's interrupt handler. */ -unsigned int cpu_readport16(unsigned int port) +unsigned char z80_unused_port_r(unsigned int port) { #if LOGERROR - error("Z80 read port %04X\n", port); + error("Z80 unused read from port %04X (%x)\n", port, Z80.pc.w.l); #endif - return 0xff; + return 0xFF; } -void cpu_writeport16(unsigned int port, unsigned int data) +void z80_unused_port_w(unsigned int port, unsigned char data) { #if LOGERROR - error("Z80 write %02X to port %04X\n", data, port); + error("Z80 unused write to port %04X = %02X (%x)\n", port, data, Z80.pc.w.l); #endif } + +void z80_sms_port_w(unsigned int port, unsigned char data) +{ + switch (port & 0xC1) + { + case 0x01: + { + io_z80_write(data); + return; + } + + case 0x40: + case 0x41: + { + psg_write(mcycles_z80, data); + return; + } + + case 0x80: + { + vdp_z80_data_w(data); + return; + } + + case 0x81: + { + vdp_z80_ctrl_w(data); + return; + } + + default: + { + if ((port & 0xFF) == 0x3E) + { + /* Memory Control Register */ + /* NB: this register does not exist on MD hardware but is partially emulated to support BIOS ROM image files */ + if (data & 0x40) + { + /* Assume only BIOS would disable Cartridge ROM */ + if (data & 0x08) + { + /* BIOS ROM disabled */ + sms_cart_switch(0); + } + else + { + /* BIOS ROM enabled */ + sms_cart_switch(1); + } + } + return; + } + z80_unused_port_w(port, data); + return; + } + } +} + +unsigned char z80_sms_port_r(unsigned int port) +{ + switch (port & 0xC1) + { + case 0x40: + { + return ((vdp_hvc_r(mcycles_z80) >> 8) & 0xFF); + } + + case 0x41: + { + return (vdp_hvc_r(mcycles_z80) & 0xFF); + } + + case 0x80: + { + return vdp_z80_data_r(); + } + + case 0x81: + { + return (vdp_ctrl_r(mcycles_z80) & 0xFF); + } + + default: + { + port &= 0xFF; + if ((port == 0xC0) || (port == 0xC1) || (port == 0xDC) || (port == 0xDD) || (port == 0xDE) || (port == 0xDF)) + { + return io_z80_read(port & 1); + } + + return z80_unused_port_r(port); + } + } +} diff --git a/source/memz80.h b/source/memz80.h index 1223e08..2abab66 100644 --- a/source/memz80.h +++ b/source/memz80.h @@ -1,9 +1,9 @@ /*************************************************************************************** * Genesis Plus - * Z80 bus arbitration (Genesis mode) + * Z80 bus controller (MD & MS compatibility mode) * * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) - * Eke-Eke (2007,2008,2009), additional code & fixes for the GCN/Wii port + * Eke-Eke (2007-2011), additional code & fixes for the GCN/Wii port * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,9 +23,13 @@ #ifndef _MEMZ80_H_ #define _MEMZ80_H_ -extern unsigned int cpu_readmem16(unsigned int address); -extern void cpu_writemem16(unsigned int address, unsigned int data); -extern unsigned int cpu_readport16(unsigned int port); -extern void cpu_writeport16(unsigned int port, unsigned int data); +extern unsigned char z80_md_memory_r(unsigned int address); +extern void z80_md_memory_w(unsigned int address, unsigned char data); +extern unsigned char z80_sms_memory_r(unsigned int address); + +extern unsigned char z80_unused_port_r(unsigned int port); +extern void z80_unused_port_w(unsigned int port, unsigned char data); +extern unsigned char z80_sms_port_r(unsigned int port); +extern void z80_sms_port_w(unsigned int port, unsigned char data); #endif /* _MEMZ80_H_ */ diff --git a/source/ntsc/md_ntsc.c b/source/ntsc/md_ntsc.c index 43a8cfc..e705ca3 100644 --- a/source/ntsc/md_ntsc.c +++ b/source/ntsc/md_ntsc.c @@ -1,163 +1,164 @@ -/* md_ntsc 0.1.2. http://www.slack.net/~ant/ */ - -/* Added a custom blitter to double the height md_ntsc_blit_y2 -- AamirM */ - -#include "shared.h" -#include "md_ntsc.h" - -/* Copyright (C) 2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -md_ntsc_setup_t const md_ntsc_monochrome = { 0,-1, 0, 0,.2, 0, 0,-.2,-.2,-1, 0, 0 }; -md_ntsc_setup_t const md_ntsc_composite = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -md_ntsc_setup_t const md_ntsc_svideo = { 0, 0, 0, 0, 0, 0,.2, -1, -1, 0, 0, 0 }; -md_ntsc_setup_t const md_ntsc_rgb = { 0, 0, 0, 0,.2, 0,.7, -1, -1,-1, 0, 0 }; - -#define alignment_count 2 -#define burst_count 1 -#define rescale_in 1 -#define rescale_out 1 - -#define artifacts_mid 0.40f -#define fringing_mid 0.30f -#define std_decoder_hue 0 - -#define gamma_size 8 -#define artifacts_max 1.00f -#define LUMA_CUTOFF 0.1974 - -#include "md_ntsc_impl.h" - -/* 2 input pixels -> 4 composite samples */ -pixel_info_t const md_ntsc_pixels [alignment_count] = { - { PIXEL_OFFSET( -4, -9 ), { 0.1f, 0.9f, 0.9f, 0.1f } }, - { PIXEL_OFFSET( -2, -7 ), { 0.1f, 0.9f, 0.9f, 0.1f } }, -}; - -static void correct_errors( md_ntsc_rgb_t color, md_ntsc_rgb_t* out ) -{ - unsigned i; - for ( i = 0; i < rgb_kernel_size / 4; i++ ) - { - md_ntsc_rgb_t error = color - - out [i ] - out [i + 2 +16] - out [i + 4 ] - out [i + 6 +16] - - out [i + 8] - out [(i+10)%16+16] - out [(i+12)%16] - out [(i+14)%16+16]; - CORRECT_ERROR( i + 6 + 16 ); - /*DISTRIBUTE_ERROR( 2+16, 4, 6+16 );*/ - } -} - -void md_ntsc_init( md_ntsc_t* ntsc, md_ntsc_setup_t const* setup ) -{ - int entry; - init_t impl; - if ( !setup ) - setup = &md_ntsc_composite; - init( &impl, setup ); - - for ( entry = 0; entry < md_ntsc_palette_size; entry++ ) - { - float bb = impl.to_float [entry >> 6 & 7]; - float gg = impl.to_float [entry >> 3 & 7]; - float rr = impl.to_float [entry & 7]; - - float y, i, q = RGB_TO_YIQ( rr, gg, bb, y, i ); - - int r, g, b = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, r, g ); - md_ntsc_rgb_t rgb = PACK_RGB( r, g, b ); - - if ( setup->palette_out ) - RGB_PALETTE_OUT( rgb, &setup->palette_out [entry * 3] ); - - if ( ntsc ) - { - gen_kernel( &impl, y, i, q, ntsc->table [entry] ); - correct_errors( rgb, ntsc->table [entry] ); - } - } -} - -#ifndef MD_NTSC_NO_BLITTERS -/* modified blitters to work on a line basis with genesis plus renderer*/ -void md_ntsc_blit( md_ntsc_t const* ntsc, MD_NTSC_IN_T const* table, unsigned char* input, - int in_width, int vline) -{ - int const chunk_count = in_width / md_ntsc_in_chunk - 1; - MD_NTSC_IN_T border = table[0]; - - MD_NTSC_BEGIN_ROW( ntsc, border, - MD_NTSC_ADJ_IN( table[*input++] ), - MD_NTSC_ADJ_IN( table[*input++] ), - MD_NTSC_ADJ_IN( table[*input++] ) ); - -#ifdef NGC - /* directly fill the RGB565 texture */ - /* one tile is 32 byte = 4x4 pixels */ - /* tiles are stored continuously in texture memory */ - in_width = MD_NTSC_OUT_WIDTH(in_width) >> 2; - int offset = ((in_width << 5) * (vline >> 2)) + ((vline & 3) * 8); - md_ntsc_out_t* restrict line_out = (md_ntsc_out_t*)(texturemem + offset); -#else - md_ntsc_out_t* restrict line_out = (md_ntsc_out_t*)(&bitmap.data[(vline * bitmap.pitch)]); -#endif - - int n; - - for ( n = chunk_count; n; --n ) - { - /* order of input and output pixels must not be altered */ - MD_NTSC_COLOR_IN( 0, ntsc, MD_NTSC_ADJ_IN( table[*input++] ) ); - MD_NTSC_RGB_OUT( 0, *line_out++, MD_NTSC_OUT_DEPTH ); - MD_NTSC_RGB_OUT( 1, *line_out++, MD_NTSC_OUT_DEPTH ); - - MD_NTSC_COLOR_IN( 1, ntsc, MD_NTSC_ADJ_IN( table[*input++] ) ); - MD_NTSC_RGB_OUT( 2, *line_out++, MD_NTSC_OUT_DEPTH ); - MD_NTSC_RGB_OUT( 3, *line_out++, MD_NTSC_OUT_DEPTH ); - -#ifdef NGC - line_out += 12; -#endif - - MD_NTSC_COLOR_IN( 2, ntsc, MD_NTSC_ADJ_IN( table[*input++] ) ); - MD_NTSC_RGB_OUT( 4, *line_out++, MD_NTSC_OUT_DEPTH ); - MD_NTSC_RGB_OUT( 5, *line_out++, MD_NTSC_OUT_DEPTH ); - - MD_NTSC_COLOR_IN( 3, ntsc, MD_NTSC_ADJ_IN( table[*input++] ) ); - MD_NTSC_RGB_OUT( 6, *line_out++, MD_NTSC_OUT_DEPTH ); - MD_NTSC_RGB_OUT( 7, *line_out++, MD_NTSC_OUT_DEPTH ); - - #ifdef NGC - line_out += 12; - #endif -} - - /* finish final pixels */ - MD_NTSC_COLOR_IN( 0, ntsc, MD_NTSC_ADJ_IN( table[*input++] ) ); - MD_NTSC_RGB_OUT( 0, *line_out++, MD_NTSC_OUT_DEPTH ); - MD_NTSC_RGB_OUT( 1, *line_out++, MD_NTSC_OUT_DEPTH ); - - MD_NTSC_COLOR_IN( 1, ntsc, border ); - MD_NTSC_RGB_OUT( 2, *line_out++, MD_NTSC_OUT_DEPTH ); - MD_NTSC_RGB_OUT( 3, *line_out++, MD_NTSC_OUT_DEPTH ); - -#ifdef NGC - line_out += 12; -#endif - - MD_NTSC_COLOR_IN( 2, ntsc, border ); - MD_NTSC_RGB_OUT( 4, *line_out++, MD_NTSC_OUT_DEPTH ); - MD_NTSC_RGB_OUT( 5, *line_out++, MD_NTSC_OUT_DEPTH ); - - MD_NTSC_COLOR_IN( 3, ntsc, border ); - MD_NTSC_RGB_OUT( 6, *line_out++, MD_NTSC_OUT_DEPTH ); - MD_NTSC_RGB_OUT( 7, *line_out++, MD_NTSC_OUT_DEPTH ); -} -#endif +/* md_ntsc 0.1.2. http://www.slack.net/~ant/ */ + +/* Added a custom blitter to double the height md_ntsc_blit_y2 -- AamirM */ +/* Added a custom blitter to work with Genesis Plus GX -- EkeEke*/ + +#include "shared.h" +#include "md_ntsc.h" + +/* Copyright (C) 2006 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +md_ntsc_setup_t const md_ntsc_monochrome = { 0,-1, 0, 0,.2, 0, 0,-.2,-.2,-1, 0, 0 }; +md_ntsc_setup_t const md_ntsc_composite = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +md_ntsc_setup_t const md_ntsc_svideo = { 0, 0, 0, 0, 0, 0,.2, -1, -1, 0, 0, 0 }; +md_ntsc_setup_t const md_ntsc_rgb = { 0, 0, 0, 0,.2, 0,.7, -1, -1,-1, 0, 0 }; + +#define alignment_count 2 +#define burst_count 1 +#define rescale_in 1 +#define rescale_out 1 + +#define artifacts_mid 0.40f +#define fringing_mid 0.30f +#define std_decoder_hue 0 + +#define gamma_size 8 +#define artifacts_max 1.00f +#define LUMA_CUTOFF 0.1974 + +#include "md_ntsc_impl.h" + +/* 2 input pixels -> 4 composite samples */ +pixel_info_t const md_ntsc_pixels [alignment_count] = { + { PIXEL_OFFSET( -4, -9 ), { 0.1f, 0.9f, 0.9f, 0.1f } }, + { PIXEL_OFFSET( -2, -7 ), { 0.1f, 0.9f, 0.9f, 0.1f } }, +}; + +static void correct_errors( md_ntsc_rgb_t color, md_ntsc_rgb_t* out ) +{ + unsigned i; + for ( i = 0; i < rgb_kernel_size / 4; i++ ) + { + md_ntsc_rgb_t error = color - + out [i ] - out [i + 2 +16] - out [i + 4 ] - out [i + 6 +16] - + out [i + 8] - out [(i+10)%16+16] - out [(i+12)%16] - out [(i+14)%16+16]; + CORRECT_ERROR( i + 6 + 16 ); + /*DISTRIBUTE_ERROR( 2+16, 4, 6+16 );*/ + } +} + +void md_ntsc_init( md_ntsc_t* ntsc, md_ntsc_setup_t const* setup ) +{ + int entry; + init_t impl; + if ( !setup ) + setup = &md_ntsc_composite; + init( &impl, setup ); + + for ( entry = 0; entry < md_ntsc_palette_size; entry++ ) + { + float bb = impl.to_float [entry >> 6 & 7]; + float gg = impl.to_float [entry >> 3 & 7]; + float rr = impl.to_float [entry & 7]; + + float y, i, q = RGB_TO_YIQ( rr, gg, bb, y, i ); + + int r, g, b = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, r, g ); + md_ntsc_rgb_t rgb = PACK_RGB( r, g, b ); + + if ( setup->palette_out ) + RGB_PALETTE_OUT( rgb, &setup->palette_out [entry * 3] ); + + if ( ntsc ) + { + gen_kernel( &impl, y, i, q, ntsc->table [entry] ); + correct_errors( rgb, ntsc->table [entry] ); + } + } +} + +#ifndef MD_NTSC_NO_BLITTERS +/* modified blitters to work on a line basis with genesis plus renderer*/ +void md_ntsc_blit( md_ntsc_t const* ntsc, MD_NTSC_IN_T const* table, unsigned char* input, + int in_width, int vline) +{ + int const chunk_count = in_width / md_ntsc_in_chunk - 1; + MD_NTSC_IN_T border = table[0]; + + MD_NTSC_BEGIN_ROW( ntsc, border, + MD_NTSC_ADJ_IN( table[*input++] ), + MD_NTSC_ADJ_IN( table[*input++] ), + MD_NTSC_ADJ_IN( table[*input++] ) ); + +#ifdef NGC + /* directly fill the RGB565 texture */ + /* one tile is 32 byte = 4x4 pixels */ + /* tiles are stored continuously in texture memory */ + in_width = MD_NTSC_OUT_WIDTH(in_width) >> 2; + int offset = ((in_width << 5) * (vline >> 2)) + ((vline & 3) * 8); + md_ntsc_out_t* restrict line_out = (md_ntsc_out_t*)(texturemem + offset); +#else + md_ntsc_out_t* restrict line_out = (md_ntsc_out_t*)(&bitmap.data[(vline * bitmap.pitch)]); +#endif + + int n; + + for ( n = chunk_count; n; --n ) + { + /* order of input and output pixels must not be altered */ + MD_NTSC_COLOR_IN( 0, ntsc, MD_NTSC_ADJ_IN( table[*input++] ) ); + MD_NTSC_RGB_OUT( 0, *line_out++, MD_NTSC_OUT_DEPTH ); + MD_NTSC_RGB_OUT( 1, *line_out++, MD_NTSC_OUT_DEPTH ); + + MD_NTSC_COLOR_IN( 1, ntsc, MD_NTSC_ADJ_IN( table[*input++] ) ); + MD_NTSC_RGB_OUT( 2, *line_out++, MD_NTSC_OUT_DEPTH ); + MD_NTSC_RGB_OUT( 3, *line_out++, MD_NTSC_OUT_DEPTH ); + +#ifdef NGC + line_out += 12; +#endif + + MD_NTSC_COLOR_IN( 2, ntsc, MD_NTSC_ADJ_IN( table[*input++] ) ); + MD_NTSC_RGB_OUT( 4, *line_out++, MD_NTSC_OUT_DEPTH ); + MD_NTSC_RGB_OUT( 5, *line_out++, MD_NTSC_OUT_DEPTH ); + + MD_NTSC_COLOR_IN( 3, ntsc, MD_NTSC_ADJ_IN( table[*input++] ) ); + MD_NTSC_RGB_OUT( 6, *line_out++, MD_NTSC_OUT_DEPTH ); + MD_NTSC_RGB_OUT( 7, *line_out++, MD_NTSC_OUT_DEPTH ); + + #ifdef NGC + line_out += 12; + #endif +} + + /* finish final pixels */ + MD_NTSC_COLOR_IN( 0, ntsc, MD_NTSC_ADJ_IN( table[*input++] ) ); + MD_NTSC_RGB_OUT( 0, *line_out++, MD_NTSC_OUT_DEPTH ); + MD_NTSC_RGB_OUT( 1, *line_out++, MD_NTSC_OUT_DEPTH ); + + MD_NTSC_COLOR_IN( 1, ntsc, border ); + MD_NTSC_RGB_OUT( 2, *line_out++, MD_NTSC_OUT_DEPTH ); + MD_NTSC_RGB_OUT( 3, *line_out++, MD_NTSC_OUT_DEPTH ); + +#ifdef NGC + line_out += 12; +#endif + + MD_NTSC_COLOR_IN( 2, ntsc, border ); + MD_NTSC_RGB_OUT( 4, *line_out++, MD_NTSC_OUT_DEPTH ); + MD_NTSC_RGB_OUT( 5, *line_out++, MD_NTSC_OUT_DEPTH ); + + MD_NTSC_COLOR_IN( 3, ntsc, border ); + MD_NTSC_RGB_OUT( 6, *line_out++, MD_NTSC_OUT_DEPTH ); + MD_NTSC_RGB_OUT( 7, *line_out++, MD_NTSC_OUT_DEPTH ); +} +#endif diff --git a/source/ntsc/md_ntsc.h b/source/ntsc/md_ntsc.h index dd2b09b..6d756b1 100644 --- a/source/ntsc/md_ntsc.h +++ b/source/ntsc/md_ntsc.h @@ -1,148 +1,148 @@ -/* Sega Genesis/Mega Drive NTSC video filter */ - -/* md_ntsc 0.1.2 */ -#ifndef MD_NTSC_H -#define MD_NTSC_H - -#include "md_ntsc_config.h" - -#ifdef __cplusplus - extern "C" { -#endif - -/* Image parameters, ranging from -1.0 to 1.0. Actual internal values shown -in parenthesis and should remain fairly stable in future versions. */ -typedef struct md_ntsc_setup_t -{ - /* Basic parameters */ - double hue; /* -1 = -180 degrees +1 = +180 degrees */ - double saturation; /* -1 = grayscale (0.0) +1 = oversaturated colors (2.0) */ - double contrast; /* -1 = dark (0.5) +1 = light (1.5) */ - double brightness; /* -1 = dark (0.5) +1 = light (1.5) */ - double sharpness; /* edge contrast enhancement/blurring */ - - /* Advanced parameters */ - double gamma; /* -1 = dark (1.5) +1 = light (0.5) */ - double resolution; /* image resolution */ - double artifacts; /* artifacts caused by color changes */ - double fringing; /* color artifacts caused by brightness changes */ - double bleed; /* color bleed (color resolution reduction) */ - float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */ - - unsigned char* palette_out; /* optional RGB palette out, 3 bytes per color */ -} md_ntsc_setup_t; - -/* Video format presets */ -extern md_ntsc_setup_t const md_ntsc_composite; /* color bleeding + artifacts */ -extern md_ntsc_setup_t const md_ntsc_svideo; /* color bleeding only */ -extern md_ntsc_setup_t const md_ntsc_rgb; /* crisp image */ -extern md_ntsc_setup_t const md_ntsc_monochrome;/* desaturated + artifacts */ - -enum { md_ntsc_palette_size = 512 }; - -/* Initializes and adjusts parameters. Can be called multiple times on the same -md_ntsc_t object. Can pass NULL for either parameter. */ -typedef struct md_ntsc_t md_ntsc_t; -void md_ntsc_init( md_ntsc_t* ntsc, md_ntsc_setup_t const* setup ); - -/* Filters one or more rows of pixels. Input pixel format is set by MD_NTSC_IN_FORMAT -and output RGB depth is set by MD_NTSC_OUT_DEPTH. Both default to 16-bit RGB. -In_row_width is the number of pixels to get to the next input row. Out_pitch -is the number of *bytes* to get to the next output row. */ -void md_ntsc_blit( md_ntsc_t const* ntsc, MD_NTSC_IN_T const* table, unsigned char* input, - int in_width, int vline); - -/* Number of output pixels written by blitter for given input width. */ -#define MD_NTSC_OUT_WIDTH( in_width ) \ - (((in_width) - 3) / md_ntsc_in_chunk * md_ntsc_out_chunk + md_ntsc_out_chunk) - -/* Number of input pixels that will fit within given output width. Might be -rounded down slightly; use MD_NTSC_OUT_WIDTH() on result to find rounded -value. */ -#define MD_NTSC_IN_WIDTH( out_width ) \ - ((out_width) / md_ntsc_out_chunk * md_ntsc_in_chunk - md_ntsc_in_chunk + 3) - - -/* Interface for user-defined custom blitters */ - -enum { md_ntsc_in_chunk = 4 }; /* number of input pixels read per chunk */ -enum { md_ntsc_out_chunk = 8 }; /* number of output pixels generated per chunk */ -enum { md_ntsc_black = 0 }; /* palette index for black */ - -/* Begin outputting row and start three pixels. First pixel will be cut off a bit. -Use md_ntsc_black for unused pixels. Declares variables, so must be before first -statement in a block (unless you're using C++). */ -#define MD_NTSC_BEGIN_ROW( ntsc, pixel0, pixel1, pixel2, pixel3 ) \ - unsigned const md_pixel0_ = (pixel0);\ - md_ntsc_rgb_t const* kernel0 = MD_NTSC_IN_FORMAT( ntsc, md_pixel0_ );\ - unsigned const md_pixel1_ = (pixel1);\ - md_ntsc_rgb_t const* kernel1 = MD_NTSC_IN_FORMAT( ntsc, md_pixel1_ );\ - unsigned const md_pixel2_ = (pixel2);\ - md_ntsc_rgb_t const* kernel2 = MD_NTSC_IN_FORMAT( ntsc, md_pixel2_ );\ - unsigned const md_pixel3_ = (pixel3);\ - md_ntsc_rgb_t const* kernel3 = MD_NTSC_IN_FORMAT( ntsc, md_pixel3_ );\ - md_ntsc_rgb_t const* kernelx0;\ - md_ntsc_rgb_t const* kernelx1 = kernel0;\ - md_ntsc_rgb_t const* kernelx2 = kernel0;\ - md_ntsc_rgb_t const* kernelx3 = kernel0 - -/* Begin input pixel */ -#define MD_NTSC_COLOR_IN( index, ntsc, color ) \ - MD_NTSC_COLOR_IN_( index, color, MD_NTSC_IN_FORMAT, ntsc ) - -/* Generate output pixel. Bits can be 24, 16, 15, 32 (treated as 24), or 0: -24: RRRRRRRR GGGGGGGG BBBBBBBB -16: RRRRRGGG GGGBBBBB -15: RRRRRGG GGGBBBBB - 0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format; x = junk bits) */ -#define MD_NTSC_RGB_OUT( x, rgb_out, bits ) {\ - md_ntsc_rgb_t raw_ =\ - kernel0 [x+ 0] + kernel1 [(x+6)%8+16] + kernel2 [(x+4)%8 ] + kernel3 [(x+2)%8+16] +\ - kernelx0 [x+ 8] + kernelx1 [(x+6)%8+24] + kernelx2 [(x+4)%8+8] + kernelx3 [(x+2)%8+24];\ - MD_NTSC_CLAMP_( raw_, 0 );\ - MD_NTSC_RGB_OUT_( rgb_out, bits, 0 );\ -} - - -/* private */ -enum { md_ntsc_entry_size = 2 * 16 }; -typedef unsigned long md_ntsc_rgb_t; -struct md_ntsc_t { - md_ntsc_rgb_t table [md_ntsc_palette_size] [md_ntsc_entry_size]; -}; - -#define MD_NTSC_BGR9( ntsc, n ) (ntsc)->table [n & 0x1FF] - -#define MD_NTSC_RGB16( ntsc, n ) \ - (md_ntsc_rgb_t*) ((char*) (ntsc)->table +\ - ((n << 9 & 0x3800) | (n & 0x0700) | (n >> 8 & 0x00E0)) *\ - (md_ntsc_entry_size * sizeof (md_ntsc_rgb_t) / 32)) - -/* common ntsc macros */ -#define md_ntsc_rgb_builder ((1L << 21) | (1 << 11) | (1 << 1)) -#define md_ntsc_clamp_mask (md_ntsc_rgb_builder * 3 / 2) -#define md_ntsc_clamp_add (md_ntsc_rgb_builder * 0x101) -#define MD_NTSC_CLAMP_( io, shift ) {\ - md_ntsc_rgb_t sub = (io) >> (9-(shift)) & md_ntsc_clamp_mask;\ - md_ntsc_rgb_t clamp = md_ntsc_clamp_add - sub;\ - io |= clamp;\ - clamp -= sub;\ - io &= clamp;\ -} - -#define MD_NTSC_COLOR_IN_( index, color, ENTRY, table ) {\ - unsigned color_;\ - kernelx##index = kernel##index;\ - kernel##index = (color_ = (color), ENTRY( table, color_ ));\ -} - -/* x is always zero except in snes_ntsc library */ -#define MD_NTSC_RGB_OUT_( rgb_out, bits, x ) {\ - rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\ - } - -#ifdef __cplusplus -} -#endif - -#endif +/* Sega Genesis/Mega Drive NTSC video filter */ + +/* md_ntsc 0.1.2 */ +#ifndef MD_NTSC_H +#define MD_NTSC_H + +#include "md_ntsc_config.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/* Image parameters, ranging from -1.0 to 1.0. Actual internal values shown +in parenthesis and should remain fairly stable in future versions. */ +typedef struct md_ntsc_setup_t +{ + /* Basic parameters */ + double hue; /* -1 = -180 degrees +1 = +180 degrees */ + double saturation; /* -1 = grayscale (0.0) +1 = oversaturated colors (2.0) */ + double contrast; /* -1 = dark (0.5) +1 = light (1.5) */ + double brightness; /* -1 = dark (0.5) +1 = light (1.5) */ + double sharpness; /* edge contrast enhancement/blurring */ + + /* Advanced parameters */ + double gamma; /* -1 = dark (1.5) +1 = light (0.5) */ + double resolution; /* image resolution */ + double artifacts; /* artifacts caused by color changes */ + double fringing; /* color artifacts caused by brightness changes */ + double bleed; /* color bleed (color resolution reduction) */ + float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */ + + unsigned char* palette_out; /* optional RGB palette out, 3 bytes per color */ +} md_ntsc_setup_t; + +/* Video format presets */ +extern md_ntsc_setup_t const md_ntsc_composite; /* color bleeding + artifacts */ +extern md_ntsc_setup_t const md_ntsc_svideo; /* color bleeding only */ +extern md_ntsc_setup_t const md_ntsc_rgb; /* crisp image */ +extern md_ntsc_setup_t const md_ntsc_monochrome;/* desaturated + artifacts */ + +enum { md_ntsc_palette_size = 512 }; + +/* Initializes and adjusts parameters. Can be called multiple times on the same +md_ntsc_t object. Can pass NULL for either parameter. */ +typedef struct md_ntsc_t md_ntsc_t; +void md_ntsc_init( md_ntsc_t* ntsc, md_ntsc_setup_t const* setup ); + +/* Filters one or more rows of pixels. Input pixel format is set by MD_NTSC_IN_FORMAT +and output RGB depth is set by MD_NTSC_OUT_DEPTH. Both default to 16-bit RGB. +In_row_width is the number of pixels to get to the next input row. Out_pitch +is the number of *bytes* to get to the next output row. */ +void md_ntsc_blit( md_ntsc_t const* ntsc, MD_NTSC_IN_T const* table, unsigned char* input, + int in_width, int vline); + +/* Number of output pixels written by blitter for given input width. */ +#define MD_NTSC_OUT_WIDTH( in_width ) \ + (((in_width) - 3) / md_ntsc_in_chunk * md_ntsc_out_chunk + md_ntsc_out_chunk) + +/* Number of input pixels that will fit within given output width. Might be +rounded down slightly; use MD_NTSC_OUT_WIDTH() on result to find rounded +value. */ +#define MD_NTSC_IN_WIDTH( out_width ) \ + ((out_width) / md_ntsc_out_chunk * md_ntsc_in_chunk - md_ntsc_in_chunk + 3) + + +/* Interface for user-defined custom blitters */ + +enum { md_ntsc_in_chunk = 4 }; /* number of input pixels read per chunk */ +enum { md_ntsc_out_chunk = 8 }; /* number of output pixels generated per chunk */ +enum { md_ntsc_black = 0 }; /* palette index for black */ + +/* Begin outputting row and start three pixels. First pixel will be cut off a bit. +Use md_ntsc_black for unused pixels. Declares variables, so must be before first +statement in a block (unless you're using C++). */ +#define MD_NTSC_BEGIN_ROW( ntsc, pixel0, pixel1, pixel2, pixel3 ) \ + unsigned const md_pixel0_ = (pixel0);\ + md_ntsc_rgb_t const* kernel0 = MD_NTSC_IN_FORMAT( ntsc, md_pixel0_ );\ + unsigned const md_pixel1_ = (pixel1);\ + md_ntsc_rgb_t const* kernel1 = MD_NTSC_IN_FORMAT( ntsc, md_pixel1_ );\ + unsigned const md_pixel2_ = (pixel2);\ + md_ntsc_rgb_t const* kernel2 = MD_NTSC_IN_FORMAT( ntsc, md_pixel2_ );\ + unsigned const md_pixel3_ = (pixel3);\ + md_ntsc_rgb_t const* kernel3 = MD_NTSC_IN_FORMAT( ntsc, md_pixel3_ );\ + md_ntsc_rgb_t const* kernelx0;\ + md_ntsc_rgb_t const* kernelx1 = kernel0;\ + md_ntsc_rgb_t const* kernelx2 = kernel0;\ + md_ntsc_rgb_t const* kernelx3 = kernel0 + +/* Begin input pixel */ +#define MD_NTSC_COLOR_IN( index, ntsc, color ) \ + MD_NTSC_COLOR_IN_( index, color, MD_NTSC_IN_FORMAT, ntsc ) + +/* Generate output pixel. Bits can be 24, 16, 15, 32 (treated as 24), or 0: +24: RRRRRRRR GGGGGGGG BBBBBBBB +16: RRRRRGGG GGGBBBBB +15: RRRRRGG GGGBBBBB + 0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format; x = junk bits) */ +#define MD_NTSC_RGB_OUT( x, rgb_out, bits ) {\ + md_ntsc_rgb_t raw_ =\ + kernel0 [x+ 0] + kernel1 [(x+6)%8+16] + kernel2 [(x+4)%8 ] + kernel3 [(x+2)%8+16] +\ + kernelx0 [x+ 8] + kernelx1 [(x+6)%8+24] + kernelx2 [(x+4)%8+8] + kernelx3 [(x+2)%8+24];\ + MD_NTSC_CLAMP_( raw_, 0 );\ + MD_NTSC_RGB_OUT_( rgb_out, bits, 0 );\ +} + + +/* private */ +enum { md_ntsc_entry_size = 2 * 16 }; +typedef unsigned long md_ntsc_rgb_t; +struct md_ntsc_t { + md_ntsc_rgb_t table [md_ntsc_palette_size] [md_ntsc_entry_size]; +}; + +#define MD_NTSC_BGR9( ntsc, n ) (ntsc)->table [n & 0x1FF] + +#define MD_NTSC_RGB16( ntsc, n ) \ + (md_ntsc_rgb_t*) ((char*) (ntsc)->table +\ + ((n << 9 & 0x3800) | (n & 0x0700) | (n >> 8 & 0x00E0)) *\ + (md_ntsc_entry_size * sizeof (md_ntsc_rgb_t) / 32)) + +/* common ntsc macros */ +#define md_ntsc_rgb_builder ((1L << 21) | (1 << 11) | (1 << 1)) +#define md_ntsc_clamp_mask (md_ntsc_rgb_builder * 3 / 2) +#define md_ntsc_clamp_add (md_ntsc_rgb_builder * 0x101) +#define MD_NTSC_CLAMP_( io, shift ) {\ + md_ntsc_rgb_t sub = (io) >> (9-(shift)) & md_ntsc_clamp_mask;\ + md_ntsc_rgb_t clamp = md_ntsc_clamp_add - sub;\ + io |= clamp;\ + clamp -= sub;\ + io &= clamp;\ +} + +#define MD_NTSC_COLOR_IN_( index, color, ENTRY, table ) {\ + unsigned color_;\ + kernelx##index = kernel##index;\ + kernel##index = (color_ = (color), ENTRY( table, color_ ));\ +} + +/* x is always zero except in snes_ntsc library */ +#define MD_NTSC_RGB_OUT_( rgb_out, bits, x ) {\ + rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\ + } + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/ntsc/md_ntsc_config.h b/source/ntsc/md_ntsc_config.h index 7306692..85d002d 100644 --- a/source/ntsc/md_ntsc_config.h +++ b/source/ntsc/md_ntsc_config.h @@ -1,26 +1,26 @@ -/* Configure library by modifying this file */ - -#ifndef MD_NTSC_CONFIG_H -#define MD_NTSC_CONFIG_H - -/* Format of source pixels */ -#define MD_NTSC_IN_FORMAT MD_NTSC_RGB16 -/* #define MD_NTSC_IN_FORMAT MD_NTSC_BGR9 */ - -/* The following affect the built-in blitter only; a custom blitter can -handle things however it wants. */ - -/* Bits per pixel of output. Can be 15, 16, 32, or 24 (same as 32). */ -#define MD_NTSC_OUT_DEPTH 16 - -/* Type of input pixel values */ -#define MD_NTSC_IN_T unsigned short - -/* Each raw pixel input value is passed through this. You might want to mask -the pixel index if you use the high bits as flags, etc. */ -#define MD_NTSC_ADJ_IN( in ) in - -/* For each pixel, this is the basic operation: -output_color = MD_NTSC_ADJ_IN( MD_NTSC_IN_T ) */ - -#endif +/* Configure library by modifying this file */ + +#ifndef MD_NTSC_CONFIG_H +#define MD_NTSC_CONFIG_H + +/* Format of source pixels */ +#define MD_NTSC_IN_FORMAT MD_NTSC_RGB16 +/* #define MD_NTSC_IN_FORMAT MD_NTSC_BGR9 */ + +/* The following affect the built-in blitter only; a custom blitter can +handle things however it wants. */ + +/* Bits per pixel of output. Can be 15, 16, 32, or 24 (same as 32). */ +#define MD_NTSC_OUT_DEPTH 16 + +/* Type of input pixel values */ +#define MD_NTSC_IN_T unsigned short + +/* Each raw pixel input value is passed through this. You might want to mask +the pixel index if you use the high bits as flags, etc. */ +#define MD_NTSC_ADJ_IN( in ) in + +/* For each pixel, this is the basic operation: +output_color = MD_NTSC_ADJ_IN( MD_NTSC_IN_T ) */ + +#endif diff --git a/source/ntsc/md_ntsc_impl.h b/source/ntsc/md_ntsc_impl.h index 76f05a2..274fb81 100644 --- a/source/ntsc/md_ntsc_impl.h +++ b/source/ntsc/md_ntsc_impl.h @@ -1,439 +1,439 @@ -/* md_ntsc 0.1.2. http://www.slack.net/~ant/ */ - -/* Common implementation of NTSC filters */ - -#include -#include - -/* Copyright (C) 2006-2007 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#define DISABLE_CORRECTION 0 - -#undef PI -#define PI 3.14159265358979323846f - -#ifndef LUMA_CUTOFF - #define LUMA_CUTOFF 0.20 -#endif -#ifndef gamma_size - #define gamma_size 1 -#endif -#ifndef rgb_bits - #define rgb_bits 8 -#endif -#ifndef artifacts_max - #define artifacts_max (artifacts_mid * 1.5f) -#endif -#ifndef fringing_max - #define fringing_max (fringing_mid * 2) -#endif -#ifndef STD_HUE_CONDITION - #define STD_HUE_CONDITION( setup ) 1 -#endif - -#define ext_decoder_hue (std_decoder_hue + 15) -#define rgb_unit (1 << rgb_bits) -#define rgb_offset (rgb_unit * 2 + 0.5f) - -enum { burst_size = md_ntsc_entry_size / burst_count }; -enum { kernel_half = 16 }; -enum { kernel_size = kernel_half * 2 + 1 }; - -typedef struct init_t -{ - float to_rgb [burst_count * 6]; - float to_float [gamma_size]; - float contrast; - float brightness; - float artifacts; - float fringing; - float kernel [rescale_out * kernel_size * 2]; -} init_t; - -#define ROTATE_IQ( i, q, sin_b, cos_b ) {\ - float t;\ - t = i * cos_b - q * sin_b;\ - q = i * sin_b + q * cos_b;\ - i = t;\ -} - -static void init_filters( init_t* impl, md_ntsc_setup_t const* setup ) -{ -#if rescale_out > 1 - float kernels [kernel_size * 2]; -#else - float* const kernels = impl->kernel; -#endif - - /* generate luma (y) filter using sinc kernel */ - { - /* sinc with rolloff (dsf) */ - float const rolloff = 1 + (float) setup->sharpness * (float) 0.032; - float const maxh = 32; - float const pow_a_n = (float) pow( rolloff, maxh ); - float sum; - int i; - /* quadratic mapping to reduce negative (blurring) range */ - float to_angle = (float) setup->resolution + 1; - to_angle = PI / maxh * (float) LUMA_CUTOFF * (to_angle * to_angle + 1); - - kernels [kernel_size * 3 / 2] = maxh; /* default center value */ - for ( i = 0; i < kernel_half * 2 + 1; i++ ) - { - int x = i - kernel_half; - float angle = x * to_angle; - /* instability occurs at center point with rolloff very close to 1.0 */ - if ( x || pow_a_n > (float) 1.056 || pow_a_n < (float) 0.981 ) - { - float rolloff_cos_a = rolloff * (float) cos( angle ); - float num = 1 - rolloff_cos_a - - pow_a_n * (float) cos( maxh * angle ) + - pow_a_n * rolloff * (float) cos( (maxh - 1) * angle ); - float den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff; - float dsf = num / den; - kernels [kernel_size * 3 / 2 - kernel_half + i] = dsf - (float) 0.5; - } - } - - /* apply blackman window and find sum */ - sum = 0; - for ( i = 0; i < kernel_half * 2 + 1; i++ ) - { - float x = PI * 2 / (kernel_half * 2) * i; - float blackman = 0.42f - 0.5f * (float) cos( x ) + 0.08f * (float) cos( x * 2 ); - sum += (kernels [kernel_size * 3 / 2 - kernel_half + i] *= blackman); - } - - /* normalize kernel */ - sum = 1.0f / sum; - for ( i = 0; i < kernel_half * 2 + 1; i++ ) - { - int x = kernel_size * 3 / 2 - kernel_half + i; - kernels [x] *= sum; - assert( kernels [x] == kernels [x] ); /* catch numerical instability */ - } - } - - /* generate chroma (iq) filter using gaussian kernel */ - { - float const cutoff_factor = -0.03125f; - float cutoff = (float) setup->bleed; - int i; - - if ( cutoff < 0 ) - { - /* keep extreme value accessible only near upper end of scale (1.0) */ - cutoff *= cutoff; - cutoff *= cutoff; - cutoff *= cutoff; - cutoff *= -30.0f / 0.65f; - } - cutoff = cutoff_factor - 0.65f * cutoff_factor * cutoff; - - for ( i = -kernel_half; i <= kernel_half; i++ ) - kernels [kernel_size / 2 + i] = (float) exp( i * i * cutoff ); - - /* normalize even and odd phases separately */ - for ( i = 0; i < 2; i++ ) - { - float sum = 0; - int x; - for ( x = i; x < kernel_size; x += 2 ) - sum += kernels [x]; - - sum = 1.0f / sum; - for ( x = i; x < kernel_size; x += 2 ) - { - kernels [x] *= sum; - assert( kernels [x] == kernels [x] ); /* catch numerical instability */ - } - } - } - - /* - printf( "luma:\n" ); - for ( i = kernel_size; i < kernel_size * 2; i++ ) - printf( "%f\n", kernels [i] ); - printf( "chroma:\n" ); - for ( i = 0; i < kernel_size; i++ ) - printf( "%f\n", kernels [i] ); - */ - - /* generate linear rescale kernels */ - #if rescale_out > 1 - { - float weight = 1.0f; - float* out = impl->kernel; - int n = rescale_out; - do - { - float remain = 0; - int i; - weight -= 1.0f / rescale_in; - for ( i = 0; i < kernel_size * 2; i++ ) - { - float cur = kernels [i]; - float m = cur * weight; - *out++ = m + remain; - remain = cur - m; - } - } - while ( --n ); - } - #endif -} - -static float const default_decoder [6] = - { 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f }; - -static void init( init_t* impl, md_ntsc_setup_t const* setup ) -{ - impl->brightness = (float) setup->brightness * (0.5f * rgb_unit) + rgb_offset; - impl->contrast = (float) setup->contrast * (0.5f * rgb_unit) + rgb_unit; - #ifdef default_palette_contrast - if ( !setup->palette ) - impl->contrast *= default_palette_contrast; - #endif - - impl->artifacts = (float) setup->artifacts; - if ( impl->artifacts > 0 ) - impl->artifacts *= artifacts_max - artifacts_mid; - impl->artifacts = impl->artifacts * artifacts_mid + artifacts_mid; - - impl->fringing = (float) setup->fringing; - if ( impl->fringing > 0 ) - impl->fringing *= fringing_max - fringing_mid; - impl->fringing = impl->fringing * fringing_mid + fringing_mid; - - init_filters( impl, setup ); - - /* generate gamma table */ - if ( gamma_size > 1 ) - { - float const to_float = 1.0f / (gamma_size - (gamma_size > 1)); - float const gamma = 1.1333f - (float) setup->gamma * 0.5f; - /* match common PC's 2.2 gamma to TV's 2.65 gamma */ - int i; - for ( i = 0; i < gamma_size; i++ ) - impl->to_float [i] = - (float) pow( i * to_float, gamma ) * impl->contrast + impl->brightness; - } - - /* setup decoder matricies */ - { - float hue = (float) setup->hue * PI + PI / 180 * ext_decoder_hue; - float sat = (float) setup->saturation + 1; - float const* decoder = setup->decoder_matrix; - if ( !decoder ) - { - decoder = default_decoder; - if ( STD_HUE_CONDITION( setup ) ) - hue += PI / 180 * (std_decoder_hue - ext_decoder_hue); - } - - { - float s = (float) sin( hue ) * sat; - float c = (float) cos( hue ) * sat; - float* out = impl->to_rgb; - int n; - - n = burst_count; - do - { - float const* in = decoder; - int n = 3; - do - { - float i = *in++; - float q = *in++; - *out++ = i * c - q * s; - *out++ = i * s + q * c; - } - while ( --n ); - if ( burst_count <= 1 ) - break; - ROTATE_IQ( s, c, 0.866025f, -0.5f ); /* +120 degrees */ - } - while ( --n ); - } - } -} - -/* kernel generation */ - -#define RGB_TO_YIQ( r, g, b, y, i ) (\ - (y = (r) * 0.299f + (g) * 0.587f + (b) * 0.114f),\ - (i = (r) * 0.596f - (g) * 0.275f - (b) * 0.321f),\ - ((r) * 0.212f - (g) * 0.523f + (b) * 0.311f)\ -) - -#define YIQ_TO_RGB( y, i, q, to_rgb, type, r, g ) (\ - r = (type) (y + to_rgb [0] * i + to_rgb [1] * q),\ - g = (type) (y + to_rgb [2] * i + to_rgb [3] * q),\ - (type) (y + to_rgb [4] * i + to_rgb [5] * q)\ -) - -#define PACK_RGB( r, g, b ) ((r) << 21 | (g) << 11 | (b) << 1) - -enum { rgb_kernel_size = burst_size / alignment_count }; -enum { rgb_bias = rgb_unit * 2 * md_ntsc_rgb_builder }; - -typedef struct pixel_info_t -{ - int offset; - float negate; - float kernel [4]; -} pixel_info_t; - -#if rescale_in > 1 - #define PIXEL_OFFSET_( ntsc, scaled ) \ - (kernel_size / 2 + ntsc + (scaled != 0) + (rescale_out - scaled) % rescale_out + \ - (kernel_size * 2 * scaled)) - - #define PIXEL_OFFSET( ntsc, scaled ) \ - PIXEL_OFFSET_( ((ntsc) - (scaled) / rescale_out * rescale_in),\ - (((scaled) + rescale_out * 10) % rescale_out) ),\ - (1.0f - (((ntsc) + 100) & 2)) -#else - #define PIXEL_OFFSET( ntsc, scaled ) \ - (kernel_size / 2 + (ntsc) - (scaled)),\ - (1.0f - (((ntsc) + 100) & 2)) -#endif - -extern pixel_info_t const md_ntsc_pixels [alignment_count]; - -/* Generate pixel at all burst phases and column alignments */ -static void gen_kernel( init_t* impl, float y, float i, float q, md_ntsc_rgb_t* out ) -{ - /* generate for each scanline burst phase */ - float const* to_rgb = impl->to_rgb; - int burst_remain = burst_count; - y -= rgb_offset; - do - { - /* Encode yiq into *two* composite signals (to allow control over artifacting). - Convolve these with kernels which: filter respective components, apply - sharpening, and rescale horizontally. Convert resulting yiq to rgb and pack - into integer. Based on algorithm by NewRisingSun. */ - pixel_info_t const* pixel = md_ntsc_pixels; - int alignment_remain = alignment_count; - do - { - /* negate is -1 when composite starts at odd multiple of 2 */ - float const yy = y * impl->fringing * pixel->negate; - float const ic0 = (i + yy) * pixel->kernel [0]; - float const qc1 = (q + yy) * pixel->kernel [1]; - float const ic2 = (i - yy) * pixel->kernel [2]; - float const qc3 = (q - yy) * pixel->kernel [3]; - - float const factor = impl->artifacts * pixel->negate; - float const ii = i * factor; - float const yc0 = (y + ii) * pixel->kernel [0]; - float const yc2 = (y - ii) * pixel->kernel [2]; - - float const qq = q * factor; - float const yc1 = (y + qq) * pixel->kernel [1]; - float const yc3 = (y - qq) * pixel->kernel [3]; - - float const* k = &impl->kernel [pixel->offset]; - int n; - ++pixel; - for ( n = rgb_kernel_size; n; --n ) - { - float i = k[0]*ic0 + k[2]*ic2; - float q = k[1]*qc1 + k[3]*qc3; - float y = k[kernel_size+0]*yc0 + k[kernel_size+1]*yc1 + - k[kernel_size+2]*yc2 + k[kernel_size+3]*yc3 + rgb_offset; - if ( rescale_out <= 1 ) - k--; - else if ( k < &impl->kernel [kernel_size * 2 * (rescale_out - 1)] ) - k += kernel_size * 2 - 1; - else - k -= kernel_size * 2 * (rescale_out - 1) + 2; - { - int r, g, b = YIQ_TO_RGB( y, i, q, to_rgb, int, r, g ); - *out++ = PACK_RGB( r, g, b ) - rgb_bias; - } - } - } - while ( alignment_count > 1 && --alignment_remain ); - - if ( burst_count <= 1 ) - break; - - to_rgb += 6; - - ROTATE_IQ( i, q, -0.866025f, -0.5f ); /* -120 degrees */ - } - while ( --burst_remain ); -} - -static void correct_errors( md_ntsc_rgb_t color, md_ntsc_rgb_t* out ); - -#if DISABLE_CORRECTION - #define CORRECT_ERROR( a ) { out [i] += rgb_bias; } - #define DISTRIBUTE_ERROR( a, b, c ) { out [i] += rgb_bias; } -#else - #define CORRECT_ERROR( a ) { out [a] += error; } - #define DISTRIBUTE_ERROR( a, b, c ) {\ - md_ntsc_rgb_t fourth = (error + 2 * md_ntsc_rgb_builder) >> 2;\ - fourth &= (rgb_bias >> 1) - md_ntsc_rgb_builder;\ - fourth -= rgb_bias >> 2;\ - out [a] += fourth;\ - out [b] += fourth;\ - out [c] += fourth;\ - out [i] += error - (fourth * 3);\ - } -#endif - -#define RGB_PALETTE_OUT( rgb, out_ )\ -{\ - unsigned char* out = (out_);\ - md_ntsc_rgb_t clamped = (rgb);\ - MD_NTSC_CLAMP_( clamped, (8 - rgb_bits) );\ - out [0] = (unsigned char) (clamped >> 21);\ - out [1] = (unsigned char) (clamped >> 11);\ - out [2] = (unsigned char) (clamped >> 1);\ -} - -/* blitter related */ - -#ifndef restrict - #if defined (__GNUC__) - #define restrict __restrict__ - #elif defined (_MSC_VER) && _MSC_VER > 1300 - #define restrict - #else - /* no support for restricted pointers */ - #define restrict - #endif -#endif - -#include - -#if MD_NTSC_OUT_DEPTH <= 16 - #if USHRT_MAX == 0xFFFF - typedef unsigned short md_ntsc_out_t; - #else - #error "Need 16-bit int type" - #endif - -#else - #if UINT_MAX == 0xFFFFFFFF - typedef unsigned int md_ntsc_out_t; - #elif ULONG_MAX == 0xFFFFFFFF - typedef unsigned long md_ntsc_out_t; - #else - #error "Need 32-bit int type" - #endif - -#endif +/* md_ntsc 0.1.2. http://www.slack.net/~ant/ */ + +/* Common implementation of NTSC filters */ + +#include +#include + +/* Copyright (C) 2006-2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#define DISABLE_CORRECTION 0 + +#undef PI +#define PI 3.14159265358979323846f + +#ifndef LUMA_CUTOFF + #define LUMA_CUTOFF 0.20 +#endif +#ifndef gamma_size + #define gamma_size 1 +#endif +#ifndef rgb_bits + #define rgb_bits 8 +#endif +#ifndef artifacts_max + #define artifacts_max (artifacts_mid * 1.5f) +#endif +#ifndef fringing_max + #define fringing_max (fringing_mid * 2) +#endif +#ifndef STD_HUE_CONDITION + #define STD_HUE_CONDITION( setup ) 1 +#endif + +#define ext_decoder_hue (std_decoder_hue + 15) +#define rgb_unit (1 << rgb_bits) +#define rgb_offset (rgb_unit * 2 + 0.5f) + +enum { burst_size = md_ntsc_entry_size / burst_count }; +enum { kernel_half = 16 }; +enum { kernel_size = kernel_half * 2 + 1 }; + +typedef struct init_t +{ + float to_rgb [burst_count * 6]; + float to_float [gamma_size]; + float contrast; + float brightness; + float artifacts; + float fringing; + float kernel [rescale_out * kernel_size * 2]; +} init_t; + +#define ROTATE_IQ( i, q, sin_b, cos_b ) {\ + float t;\ + t = i * cos_b - q * sin_b;\ + q = i * sin_b + q * cos_b;\ + i = t;\ +} + +static void init_filters( init_t* impl, md_ntsc_setup_t const* setup ) +{ +#if rescale_out > 1 + float kernels [kernel_size * 2]; +#else + float* const kernels = impl->kernel; +#endif + + /* generate luma (y) filter using sinc kernel */ + { + /* sinc with rolloff (dsf) */ + float const rolloff = 1 + (float) setup->sharpness * (float) 0.032; + float const maxh = 32; + float const pow_a_n = (float) pow( rolloff, maxh ); + float sum; + int i; + /* quadratic mapping to reduce negative (blurring) range */ + float to_angle = (float) setup->resolution + 1; + to_angle = PI / maxh * (float) LUMA_CUTOFF * (to_angle * to_angle + 1); + + kernels [kernel_size * 3 / 2] = maxh; /* default center value */ + for ( i = 0; i < kernel_half * 2 + 1; i++ ) + { + int x = i - kernel_half; + float angle = x * to_angle; + /* instability occurs at center point with rolloff very close to 1.0 */ + if ( x || pow_a_n > (float) 1.056 || pow_a_n < (float) 0.981 ) + { + float rolloff_cos_a = rolloff * (float) cos( angle ); + float num = 1 - rolloff_cos_a - + pow_a_n * (float) cos( maxh * angle ) + + pow_a_n * rolloff * (float) cos( (maxh - 1) * angle ); + float den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff; + float dsf = num / den; + kernels [kernel_size * 3 / 2 - kernel_half + i] = dsf - (float) 0.5; + } + } + + /* apply blackman window and find sum */ + sum = 0; + for ( i = 0; i < kernel_half * 2 + 1; i++ ) + { + float x = PI * 2 / (kernel_half * 2) * i; + float blackman = 0.42f - 0.5f * (float) cos( x ) + 0.08f * (float) cos( x * 2 ); + sum += (kernels [kernel_size * 3 / 2 - kernel_half + i] *= blackman); + } + + /* normalize kernel */ + sum = 1.0f / sum; + for ( i = 0; i < kernel_half * 2 + 1; i++ ) + { + int x = kernel_size * 3 / 2 - kernel_half + i; + kernels [x] *= sum; + assert( kernels [x] == kernels [x] ); /* catch numerical instability */ + } + } + + /* generate chroma (iq) filter using gaussian kernel */ + { + float const cutoff_factor = -0.03125f; + float cutoff = (float) setup->bleed; + int i; + + if ( cutoff < 0 ) + { + /* keep extreme value accessible only near upper end of scale (1.0) */ + cutoff *= cutoff; + cutoff *= cutoff; + cutoff *= cutoff; + cutoff *= -30.0f / 0.65f; + } + cutoff = cutoff_factor - 0.65f * cutoff_factor * cutoff; + + for ( i = -kernel_half; i <= kernel_half; i++ ) + kernels [kernel_size / 2 + i] = (float) exp( i * i * cutoff ); + + /* normalize even and odd phases separately */ + for ( i = 0; i < 2; i++ ) + { + float sum = 0; + int x; + for ( x = i; x < kernel_size; x += 2 ) + sum += kernels [x]; + + sum = 1.0f / sum; + for ( x = i; x < kernel_size; x += 2 ) + { + kernels [x] *= sum; + assert( kernels [x] == kernels [x] ); /* catch numerical instability */ + } + } + } + + /* + printf( "luma:\n" ); + for ( i = kernel_size; i < kernel_size * 2; i++ ) + printf( "%f\n", kernels [i] ); + printf( "chroma:\n" ); + for ( i = 0; i < kernel_size; i++ ) + printf( "%f\n", kernels [i] ); + */ + + /* generate linear rescale kernels */ + #if rescale_out > 1 + { + float weight = 1.0f; + float* out = impl->kernel; + int n = rescale_out; + do + { + float remain = 0; + int i; + weight -= 1.0f / rescale_in; + for ( i = 0; i < kernel_size * 2; i++ ) + { + float cur = kernels [i]; + float m = cur * weight; + *out++ = m + remain; + remain = cur - m; + } + } + while ( --n ); + } + #endif +} + +static float const default_decoder [6] = + { 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f }; + +static void init( init_t* impl, md_ntsc_setup_t const* setup ) +{ + impl->brightness = (float) setup->brightness * (0.5f * rgb_unit) + rgb_offset; + impl->contrast = (float) setup->contrast * (0.5f * rgb_unit) + rgb_unit; + #ifdef default_palette_contrast + if ( !setup->palette ) + impl->contrast *= default_palette_contrast; + #endif + + impl->artifacts = (float) setup->artifacts; + if ( impl->artifacts > 0 ) + impl->artifacts *= artifacts_max - artifacts_mid; + impl->artifacts = impl->artifacts * artifacts_mid + artifacts_mid; + + impl->fringing = (float) setup->fringing; + if ( impl->fringing > 0 ) + impl->fringing *= fringing_max - fringing_mid; + impl->fringing = impl->fringing * fringing_mid + fringing_mid; + + init_filters( impl, setup ); + + /* generate gamma table */ + if ( gamma_size > 1 ) + { + float const to_float = 1.0f / (gamma_size - (gamma_size > 1)); + float const gamma = 1.1333f - (float) setup->gamma * 0.5f; + /* match common PC's 2.2 gamma to TV's 2.65 gamma */ + int i; + for ( i = 0; i < gamma_size; i++ ) + impl->to_float [i] = + (float) pow( i * to_float, gamma ) * impl->contrast + impl->brightness; + } + + /* setup decoder matricies */ + { + float hue = (float) setup->hue * PI + PI / 180 * ext_decoder_hue; + float sat = (float) setup->saturation + 1; + float const* decoder = setup->decoder_matrix; + if ( !decoder ) + { + decoder = default_decoder; + if ( STD_HUE_CONDITION( setup ) ) + hue += PI / 180 * (std_decoder_hue - ext_decoder_hue); + } + + { + float s = (float) sin( hue ) * sat; + float c = (float) cos( hue ) * sat; + float* out = impl->to_rgb; + int n; + + n = burst_count; + do + { + float const* in = decoder; + int n = 3; + do + { + float i = *in++; + float q = *in++; + *out++ = i * c - q * s; + *out++ = i * s + q * c; + } + while ( --n ); + if ( burst_count <= 1 ) + break; + ROTATE_IQ( s, c, 0.866025f, -0.5f ); /* +120 degrees */ + } + while ( --n ); + } + } +} + +/* kernel generation */ + +#define RGB_TO_YIQ( r, g, b, y, i ) (\ + (y = (r) * 0.299f + (g) * 0.587f + (b) * 0.114f),\ + (i = (r) * 0.596f - (g) * 0.275f - (b) * 0.321f),\ + ((r) * 0.212f - (g) * 0.523f + (b) * 0.311f)\ +) + +#define YIQ_TO_RGB( y, i, q, to_rgb, type, r, g ) (\ + r = (type) (y + to_rgb [0] * i + to_rgb [1] * q),\ + g = (type) (y + to_rgb [2] * i + to_rgb [3] * q),\ + (type) (y + to_rgb [4] * i + to_rgb [5] * q)\ +) + +#define PACK_RGB( r, g, b ) ((r) << 21 | (g) << 11 | (b) << 1) + +enum { rgb_kernel_size = burst_size / alignment_count }; +enum { rgb_bias = rgb_unit * 2 * md_ntsc_rgb_builder }; + +typedef struct pixel_info_t +{ + int offset; + float negate; + float kernel [4]; +} pixel_info_t; + +#if rescale_in > 1 + #define PIXEL_OFFSET_( ntsc, scaled ) \ + (kernel_size / 2 + ntsc + (scaled != 0) + (rescale_out - scaled) % rescale_out + \ + (kernel_size * 2 * scaled)) + + #define PIXEL_OFFSET( ntsc, scaled ) \ + PIXEL_OFFSET_( ((ntsc) - (scaled) / rescale_out * rescale_in),\ + (((scaled) + rescale_out * 10) % rescale_out) ),\ + (1.0f - (((ntsc) + 100) & 2)) +#else + #define PIXEL_OFFSET( ntsc, scaled ) \ + (kernel_size / 2 + (ntsc) - (scaled)),\ + (1.0f - (((ntsc) + 100) & 2)) +#endif + +extern pixel_info_t const md_ntsc_pixels [alignment_count]; + +/* Generate pixel at all burst phases and column alignments */ +static void gen_kernel( init_t* impl, float y, float i, float q, md_ntsc_rgb_t* out ) +{ + /* generate for each scanline burst phase */ + float const* to_rgb = impl->to_rgb; + int burst_remain = burst_count; + y -= rgb_offset; + do + { + /* Encode yiq into *two* composite signals (to allow control over artifacting). + Convolve these with kernels which: filter respective components, apply + sharpening, and rescale horizontally. Convert resulting yiq to rgb and pack + into integer. Based on algorithm by NewRisingSun. */ + pixel_info_t const* pixel = md_ntsc_pixels; + int alignment_remain = alignment_count; + do + { + /* negate is -1 when composite starts at odd multiple of 2 */ + float const yy = y * impl->fringing * pixel->negate; + float const ic0 = (i + yy) * pixel->kernel [0]; + float const qc1 = (q + yy) * pixel->kernel [1]; + float const ic2 = (i - yy) * pixel->kernel [2]; + float const qc3 = (q - yy) * pixel->kernel [3]; + + float const factor = impl->artifacts * pixel->negate; + float const ii = i * factor; + float const yc0 = (y + ii) * pixel->kernel [0]; + float const yc2 = (y - ii) * pixel->kernel [2]; + + float const qq = q * factor; + float const yc1 = (y + qq) * pixel->kernel [1]; + float const yc3 = (y - qq) * pixel->kernel [3]; + + float const* k = &impl->kernel [pixel->offset]; + int n; + ++pixel; + for ( n = rgb_kernel_size; n; --n ) + { + float i = k[0]*ic0 + k[2]*ic2; + float q = k[1]*qc1 + k[3]*qc3; + float y = k[kernel_size+0]*yc0 + k[kernel_size+1]*yc1 + + k[kernel_size+2]*yc2 + k[kernel_size+3]*yc3 + rgb_offset; + if ( rescale_out <= 1 ) + k--; + else if ( k < &impl->kernel [kernel_size * 2 * (rescale_out - 1)] ) + k += kernel_size * 2 - 1; + else + k -= kernel_size * 2 * (rescale_out - 1) + 2; + { + int r, g, b = YIQ_TO_RGB( y, i, q, to_rgb, int, r, g ); + *out++ = PACK_RGB( r, g, b ) - rgb_bias; + } + } + } + while ( alignment_count > 1 && --alignment_remain ); + + if ( burst_count <= 1 ) + break; + + to_rgb += 6; + + ROTATE_IQ( i, q, -0.866025f, -0.5f ); /* -120 degrees */ + } + while ( --burst_remain ); +} + +static void correct_errors( md_ntsc_rgb_t color, md_ntsc_rgb_t* out ); + +#if DISABLE_CORRECTION + #define CORRECT_ERROR( a ) { out [i] += rgb_bias; } + #define DISTRIBUTE_ERROR( a, b, c ) { out [i] += rgb_bias; } +#else + #define CORRECT_ERROR( a ) { out [a] += error; } + #define DISTRIBUTE_ERROR( a, b, c ) {\ + md_ntsc_rgb_t fourth = (error + 2 * md_ntsc_rgb_builder) >> 2;\ + fourth &= (rgb_bias >> 1) - md_ntsc_rgb_builder;\ + fourth -= rgb_bias >> 2;\ + out [a] += fourth;\ + out [b] += fourth;\ + out [c] += fourth;\ + out [i] += error - (fourth * 3);\ + } +#endif + +#define RGB_PALETTE_OUT( rgb, out_ )\ +{\ + unsigned char* out = (out_);\ + md_ntsc_rgb_t clamped = (rgb);\ + MD_NTSC_CLAMP_( clamped, (8 - rgb_bits) );\ + out [0] = (unsigned char) (clamped >> 21);\ + out [1] = (unsigned char) (clamped >> 11);\ + out [2] = (unsigned char) (clamped >> 1);\ +} + +/* blitter related */ + +#ifndef restrict + #if defined (__GNUC__) + #define restrict __restrict__ + #elif defined (_MSC_VER) && _MSC_VER > 1300 + #define restrict + #else + /* no support for restricted pointers */ + #define restrict + #endif +#endif + +#include + +#if MD_NTSC_OUT_DEPTH <= 16 + #if USHRT_MAX == 0xFFFF + typedef unsigned short md_ntsc_out_t; + #else + #error "Need 16-bit int type" + #endif + +#else + #if UINT_MAX == 0xFFFFFFFF + typedef unsigned int md_ntsc_out_t; + #elif ULONG_MAX == 0xFFFFFFFF + typedef unsigned long md_ntsc_out_t; + #else + #error "Need 32-bit int type" + #endif + +#endif diff --git a/source/ntsc/sms_ntsc.c b/source/ntsc/sms_ntsc.c index 48a9abc..0c6dd7d 100644 --- a/source/ntsc/sms_ntsc.c +++ b/source/ntsc/sms_ntsc.c @@ -1,195 +1,197 @@ -/* sms_ntsc 0.2.3. http://www.slack.net/~ant/ */ - -#include "shared.h" -#include "sms_ntsc.h" - -/* Copyright (C) 2006-2007 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -sms_ntsc_setup_t const sms_ntsc_monochrome = { 0,-1, 0, 0,.2, 0, .2,-.2,-.2,-1, 0, 0 }; -sms_ntsc_setup_t const sms_ntsc_composite = { 0, 0, 0, 0, 0, 0,.25, 0, 0, 0, 0, 0 }; -sms_ntsc_setup_t const sms_ntsc_svideo = { 0, 0, 0, 0, 0, 0,.25, -1, -1, 0, 0, 0 }; -sms_ntsc_setup_t const sms_ntsc_rgb = { 0, 0, 0, 0,.2, 0,.70, -1, -1,-1, 0, 0 }; - -#define alignment_count 3 -#define burst_count 1 -#define rescale_in 8 -#define rescale_out 7 - -#define artifacts_mid 0.4f -#define artifacts_max 1.2f -#define fringing_mid 0.8f -#define std_decoder_hue 0 - -#define gamma_size 16 - -#include "sms_ntsc_impl.h" - -/* 3 input pixels -> 8 composite samples */ -pixel_info_t const sms_ntsc_pixels [alignment_count] = { - { PIXEL_OFFSET( -4, -9 ), { 1, 1, .6667f, 0 } }, - { PIXEL_OFFSET( -2, -7 ), { .3333f, 1, 1, .3333f } }, - { PIXEL_OFFSET( 0, -5 ), { 0, .6667f, 1, 1 } }, -}; - -static void correct_errors( sms_ntsc_rgb_t color, sms_ntsc_rgb_t* out ) -{ - unsigned i; - for ( i = 0; i < rgb_kernel_size / 2; i++ ) - { - sms_ntsc_rgb_t error = color - - out [i ] - out [(i+12)%14+14] - out [(i+10)%14+28] - - out [i + 7] - out [i + 5 +14] - out [i + 3 +28]; - CORRECT_ERROR( i + 3 + 28 ); - } -} - -void sms_ntsc_init( sms_ntsc_t* ntsc, sms_ntsc_setup_t const* setup ) -{ - int entry; - init_t impl; - if ( !setup ) - setup = &sms_ntsc_composite; - init( &impl, setup ); - - for ( entry = 0; entry < sms_ntsc_palette_size; entry++ ) - { - float bb = impl.to_float [entry >> 8 & 0x0F]; - float gg = impl.to_float [entry >> 4 & 0x0F]; - float rr = impl.to_float [entry & 0x0F]; - - float y, i, q = RGB_TO_YIQ( rr, gg, bb, y, i ); - - int r, g, b = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, r, g ); - sms_ntsc_rgb_t rgb = PACK_RGB( r, g, b ); - - if ( setup->palette_out ) - RGB_PALETTE_OUT( rgb, &setup->palette_out [entry * 3] ); - - if ( ntsc ) - { - gen_kernel( &impl, y, i, q, ntsc->table [entry] ); - correct_errors( rgb, ntsc->table [entry] ); - } - } -} - -#ifndef SMS_NTSC_NO_BLITTERS - -/* modified blitters to work on a line basis with genesis plus renderer*/ -void sms_ntsc_blit( sms_ntsc_t const* ntsc, SMS_NTSC_IN_T const* table, unsigned char* input, - int in_width, int vline) -{ - int const chunk_count = in_width / sms_ntsc_in_chunk; - - /* handle extra 0, 1, or 2 pixels by placing them at beginning of row */ - int const in_extra = in_width - chunk_count * sms_ntsc_in_chunk; - unsigned const extra2 = (unsigned) -(in_extra >> 1 & 1); /* (unsigned) -1 = ~0 */ - unsigned const extra1 = (unsigned) -(in_extra & 1) | extra2; - - SMS_NTSC_IN_T border = table[0]; - - SMS_NTSC_BEGIN_ROW( ntsc, border, - (SMS_NTSC_ADJ_IN( table[input[0]] )) & extra2, - (SMS_NTSC_ADJ_IN( table[input[extra2 & 1]] )) & extra1 ); - -#ifdef NGC - /* directly fill the RGB565 texture */ - /* one tile is 32 byte = 4x4 pixels */ - /* tiles are stored continuously in texture memory */ - in_width = SMS_NTSC_OUT_WIDTH(in_width) / 4; - int offset = ((in_width * 32) * (vline / 4)) + ((vline & 3) * 8); - sms_ntsc_out_t* restrict line_out = (sms_ntsc_out_t*)(texturemem + offset); - offset = 0; -#else - sms_ntsc_out_t* restrict line_out = (sms_ntsc_out_t*)(&bitmap.data[(vline * bitmap.pitch)]); -#endif - int n; - input += in_extra; - - for ( n = chunk_count; n; --n ) - { - /* order of input and output pixels must not be altered */ - SMS_NTSC_COLOR_IN( 0, ntsc, SMS_NTSC_ADJ_IN( table[*input++] ) ); -#ifdef NGC - SMS_NTSC_RGB_OUT( 0, line_out[offset++], SMS_NTSC_OUT_DEPTH ); - if ((offset % 4) == 0) offset += 12; - SMS_NTSC_RGB_OUT( 1, line_out[offset++], SMS_NTSC_OUT_DEPTH ); - if ((offset % 4) == 0) offset += 12; -#else - SMS_NTSC_RGB_OUT( 0, *line_out++, SMS_NTSC_OUT_DEPTH ); - SMS_NTSC_RGB_OUT( 1, *line_out++, SMS_NTSC_OUT_DEPTH ); -#endif - - SMS_NTSC_COLOR_IN( 1, ntsc, SMS_NTSC_ADJ_IN( table[*input++] ) ); -#ifdef NGC - SMS_NTSC_RGB_OUT( 2, line_out[offset++], SMS_NTSC_OUT_DEPTH ); - if ((offset % 4) == 0) offset += 12; - SMS_NTSC_RGB_OUT( 3, line_out[offset++], SMS_NTSC_OUT_DEPTH ); - if ((offset % 4) == 0) offset += 12; -#else - SMS_NTSC_RGB_OUT( 2, *line_out++, SMS_NTSC_OUT_DEPTH ); - SMS_NTSC_RGB_OUT( 3, *line_out++, SMS_NTSC_OUT_DEPTH ); -#endif - - SMS_NTSC_COLOR_IN( 2, ntsc, SMS_NTSC_ADJ_IN( table[*input++] ) ); -#ifdef NGC - SMS_NTSC_RGB_OUT( 4, line_out[offset++], SMS_NTSC_OUT_DEPTH ); - if ((offset % 4) == 0) offset += 12; - SMS_NTSC_RGB_OUT( 5, line_out[offset++], SMS_NTSC_OUT_DEPTH ); - if ((offset % 4) == 0) offset += 12; - SMS_NTSC_RGB_OUT( 6, line_out[offset++], SMS_NTSC_OUT_DEPTH ); - if ((offset % 4) == 0) offset += 12; -#else - SMS_NTSC_RGB_OUT( 4, *line_out++, SMS_NTSC_OUT_DEPTH ); - SMS_NTSC_RGB_OUT( 5, *line_out++, SMS_NTSC_OUT_DEPTH ); - SMS_NTSC_RGB_OUT( 6, *line_out++, SMS_NTSC_OUT_DEPTH ); -#endif - } - - /* finish final pixels */ - SMS_NTSC_COLOR_IN( 0, ntsc, border ); -#ifdef NGC - SMS_NTSC_RGB_OUT( 0, line_out[offset++], SMS_NTSC_OUT_DEPTH ); - if ((offset % 4) == 0) offset += 12; - SMS_NTSC_RGB_OUT( 1, line_out[offset++], SMS_NTSC_OUT_DEPTH ); - if ((offset % 4) == 0) offset += 12; -#else - SMS_NTSC_RGB_OUT( 0, *line_out++, SMS_NTSC_OUT_DEPTH ); - SMS_NTSC_RGB_OUT( 1, *line_out++, SMS_NTSC_OUT_DEPTH ); -#endif - - SMS_NTSC_COLOR_IN( 1, ntsc, border ); -#ifdef NGC - SMS_NTSC_RGB_OUT( 2, line_out[offset++], SMS_NTSC_OUT_DEPTH ); - if ((offset % 4) == 0) offset += 12; - SMS_NTSC_RGB_OUT( 3, line_out[offset++], SMS_NTSC_OUT_DEPTH ); - if ((offset % 4) == 0) offset += 12; -#else - SMS_NTSC_RGB_OUT( 2, *line_out++, SMS_NTSC_OUT_DEPTH ); - SMS_NTSC_RGB_OUT( 3, *line_out++, SMS_NTSC_OUT_DEPTH ); -#endif - - SMS_NTSC_COLOR_IN( 2, ntsc, border ); -#ifdef NGC - SMS_NTSC_RGB_OUT( 4, line_out[offset++], SMS_NTSC_OUT_DEPTH ); - if ((offset % 4) == 0) offset += 12; - SMS_NTSC_RGB_OUT( 5, line_out[offset++], SMS_NTSC_OUT_DEPTH ); - if ((offset % 4) == 0) offset += 12; - SMS_NTSC_RGB_OUT( 6, line_out[offset++], SMS_NTSC_OUT_DEPTH ); - if ((offset % 4) == 0) offset += 12; -#else - SMS_NTSC_RGB_OUT( 4, *line_out++, SMS_NTSC_OUT_DEPTH ); - SMS_NTSC_RGB_OUT( 5, *line_out++, SMS_NTSC_OUT_DEPTH ); - SMS_NTSC_RGB_OUT( 6, *line_out++, SMS_NTSC_OUT_DEPTH ); -#endif -} -#endif +/* sms_ntsc 0.2.3. http://www.slack.net/~ant/ */ + +#include "shared.h" +#include "sms_ntsc.h" + +/* Copyright (C) 2006-2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* Added a custom blitter to work with Genesis Plus GX -- EkeEke*/ + +sms_ntsc_setup_t const sms_ntsc_monochrome = { 0,-1, 0, 0,.2, 0, .2,-.2,-.2,-1, 0, 0 }; +sms_ntsc_setup_t const sms_ntsc_composite = { 0, 0, 0, 0, 0, 0,.25, 0, 0, 0, 0, 0 }; +sms_ntsc_setup_t const sms_ntsc_svideo = { 0, 0, 0, 0, 0, 0,.25, -1, -1, 0, 0, 0 }; +sms_ntsc_setup_t const sms_ntsc_rgb = { 0, 0, 0, 0,.2, 0,.70, -1, -1,-1, 0, 0 }; + +#define alignment_count 3 +#define burst_count 1 +#define rescale_in 8 +#define rescale_out 7 + +#define artifacts_mid 0.4f +#define artifacts_max 1.2f +#define fringing_mid 0.8f +#define std_decoder_hue 0 + +#define gamma_size 16 + +#include "sms_ntsc_impl.h" + +/* 3 input pixels -> 8 composite samples */ +pixel_info_t const sms_ntsc_pixels [alignment_count] = { + { PIXEL_OFFSET( -4, -9 ), { 1, 1, .6667f, 0 } }, + { PIXEL_OFFSET( -2, -7 ), { .3333f, 1, 1, .3333f } }, + { PIXEL_OFFSET( 0, -5 ), { 0, .6667f, 1, 1 } }, +}; + +static void correct_errors( sms_ntsc_rgb_t color, sms_ntsc_rgb_t* out ) +{ + unsigned i; + for ( i = 0; i < rgb_kernel_size / 2; i++ ) + { + sms_ntsc_rgb_t error = color - + out [i ] - out [(i+12)%14+14] - out [(i+10)%14+28] - + out [i + 7] - out [i + 5 +14] - out [i + 3 +28]; + CORRECT_ERROR( i + 3 + 28 ); + } +} + +void sms_ntsc_init( sms_ntsc_t* ntsc, sms_ntsc_setup_t const* setup ) +{ + int entry; + init_t impl; + if ( !setup ) + setup = &sms_ntsc_composite; + init( &impl, setup ); + + for ( entry = 0; entry < sms_ntsc_palette_size; entry++ ) + { + float bb = impl.to_float [entry >> 8 & 0x0F]; + float gg = impl.to_float [entry >> 4 & 0x0F]; + float rr = impl.to_float [entry & 0x0F]; + + float y, i, q = RGB_TO_YIQ( rr, gg, bb, y, i ); + + int r, g, b = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, r, g ); + sms_ntsc_rgb_t rgb = PACK_RGB( r, g, b ); + + if ( setup->palette_out ) + RGB_PALETTE_OUT( rgb, &setup->palette_out [entry * 3] ); + + if ( ntsc ) + { + gen_kernel( &impl, y, i, q, ntsc->table [entry] ); + correct_errors( rgb, ntsc->table [entry] ); + } + } +} + +#ifndef SMS_NTSC_NO_BLITTERS + +/* modified blitters to work on a line basis with genesis plus renderer*/ +void sms_ntsc_blit( sms_ntsc_t const* ntsc, SMS_NTSC_IN_T const* table, unsigned char* input, + int in_width, int vline) +{ + int const chunk_count = in_width / sms_ntsc_in_chunk; + + /* handle extra 0, 1, or 2 pixels by placing them at beginning of row */ + int const in_extra = in_width - chunk_count * sms_ntsc_in_chunk; + unsigned const extra2 = (unsigned) -(in_extra >> 1 & 1); /* (unsigned) -1 = ~0 */ + unsigned const extra1 = (unsigned) -(in_extra & 1) | extra2; + + SMS_NTSC_IN_T border = table[0]; + + SMS_NTSC_BEGIN_ROW( ntsc, border, + (SMS_NTSC_ADJ_IN( table[input[0]] )) & extra2, + (SMS_NTSC_ADJ_IN( table[input[extra2 & 1]] )) & extra1 ); + +#ifdef NGC + /* directly fill the RGB565 texture */ + /* one tile is 32 byte = 4x4 pixels */ + /* tiles are stored continuously in texture memory */ + in_width = SMS_NTSC_OUT_WIDTH(in_width) / 4; + int offset = ((in_width * 32) * (vline / 4)) + ((vline & 3) * 8); + sms_ntsc_out_t* restrict line_out = (sms_ntsc_out_t*)(texturemem + offset); + offset = 0; +#else + sms_ntsc_out_t* restrict line_out = (sms_ntsc_out_t*)(&bitmap.data[(vline * bitmap.pitch)]); +#endif + int n; + input += in_extra; + + for ( n = chunk_count; n; --n ) + { + /* order of input and output pixels must not be altered */ + SMS_NTSC_COLOR_IN( 0, ntsc, SMS_NTSC_ADJ_IN( table[*input++] ) ); +#ifdef NGC + SMS_NTSC_RGB_OUT( 0, line_out[offset++], SMS_NTSC_OUT_DEPTH ); + if ((offset % 4) == 0) offset += 12; + SMS_NTSC_RGB_OUT( 1, line_out[offset++], SMS_NTSC_OUT_DEPTH ); + if ((offset % 4) == 0) offset += 12; +#else + SMS_NTSC_RGB_OUT( 0, *line_out++, SMS_NTSC_OUT_DEPTH ); + SMS_NTSC_RGB_OUT( 1, *line_out++, SMS_NTSC_OUT_DEPTH ); +#endif + + SMS_NTSC_COLOR_IN( 1, ntsc, SMS_NTSC_ADJ_IN( table[*input++] ) ); +#ifdef NGC + SMS_NTSC_RGB_OUT( 2, line_out[offset++], SMS_NTSC_OUT_DEPTH ); + if ((offset % 4) == 0) offset += 12; + SMS_NTSC_RGB_OUT( 3, line_out[offset++], SMS_NTSC_OUT_DEPTH ); + if ((offset % 4) == 0) offset += 12; +#else + SMS_NTSC_RGB_OUT( 2, *line_out++, SMS_NTSC_OUT_DEPTH ); + SMS_NTSC_RGB_OUT( 3, *line_out++, SMS_NTSC_OUT_DEPTH ); +#endif + + SMS_NTSC_COLOR_IN( 2, ntsc, SMS_NTSC_ADJ_IN( table[*input++] ) ); +#ifdef NGC + SMS_NTSC_RGB_OUT( 4, line_out[offset++], SMS_NTSC_OUT_DEPTH ); + if ((offset % 4) == 0) offset += 12; + SMS_NTSC_RGB_OUT( 5, line_out[offset++], SMS_NTSC_OUT_DEPTH ); + if ((offset % 4) == 0) offset += 12; + SMS_NTSC_RGB_OUT( 6, line_out[offset++], SMS_NTSC_OUT_DEPTH ); + if ((offset % 4) == 0) offset += 12; +#else + SMS_NTSC_RGB_OUT( 4, *line_out++, SMS_NTSC_OUT_DEPTH ); + SMS_NTSC_RGB_OUT( 5, *line_out++, SMS_NTSC_OUT_DEPTH ); + SMS_NTSC_RGB_OUT( 6, *line_out++, SMS_NTSC_OUT_DEPTH ); +#endif + } + + /* finish final pixels */ + SMS_NTSC_COLOR_IN( 0, ntsc, border ); +#ifdef NGC + SMS_NTSC_RGB_OUT( 0, line_out[offset++], SMS_NTSC_OUT_DEPTH ); + if ((offset % 4) == 0) offset += 12; + SMS_NTSC_RGB_OUT( 1, line_out[offset++], SMS_NTSC_OUT_DEPTH ); + if ((offset % 4) == 0) offset += 12; +#else + SMS_NTSC_RGB_OUT( 0, *line_out++, SMS_NTSC_OUT_DEPTH ); + SMS_NTSC_RGB_OUT( 1, *line_out++, SMS_NTSC_OUT_DEPTH ); +#endif + + SMS_NTSC_COLOR_IN( 1, ntsc, border ); +#ifdef NGC + SMS_NTSC_RGB_OUT( 2, line_out[offset++], SMS_NTSC_OUT_DEPTH ); + if ((offset % 4) == 0) offset += 12; + SMS_NTSC_RGB_OUT( 3, line_out[offset++], SMS_NTSC_OUT_DEPTH ); + if ((offset % 4) == 0) offset += 12; +#else + SMS_NTSC_RGB_OUT( 2, *line_out++, SMS_NTSC_OUT_DEPTH ); + SMS_NTSC_RGB_OUT( 3, *line_out++, SMS_NTSC_OUT_DEPTH ); +#endif + + SMS_NTSC_COLOR_IN( 2, ntsc, border ); +#ifdef NGC + SMS_NTSC_RGB_OUT( 4, line_out[offset++], SMS_NTSC_OUT_DEPTH ); + if ((offset % 4) == 0) offset += 12; + SMS_NTSC_RGB_OUT( 5, line_out[offset++], SMS_NTSC_OUT_DEPTH ); + if ((offset % 4) == 0) offset += 12; + SMS_NTSC_RGB_OUT( 6, line_out[offset++], SMS_NTSC_OUT_DEPTH ); + if ((offset % 4) == 0) offset += 12; +#else + SMS_NTSC_RGB_OUT( 4, *line_out++, SMS_NTSC_OUT_DEPTH ); + SMS_NTSC_RGB_OUT( 5, *line_out++, SMS_NTSC_OUT_DEPTH ); + SMS_NTSC_RGB_OUT( 6, *line_out++, SMS_NTSC_OUT_DEPTH ); +#endif +} +#endif diff --git a/source/ntsc/sms_ntsc.h b/source/ntsc/sms_ntsc.h index a3b543c..f124bdf 100644 --- a/source/ntsc/sms_ntsc.h +++ b/source/ntsc/sms_ntsc.h @@ -1,157 +1,157 @@ -/* Sega Master System/Game Gear/TI 99/4A NTSC video filter */ - -/* sms_ntsc 0.2.3 */ -#ifndef SMS_NTSC_H -#define SMS_NTSC_H - -#include "sms_ntsc_config.h" - -#ifdef __cplusplus - extern "C" { -#endif - -/* Image parameters, ranging from -1.0 to 1.0. Actual internal values shown -in parenthesis and should remain fairly stable in future versions. */ -typedef struct sms_ntsc_setup_t -{ - /* Basic parameters */ - double hue; /* -1 = -180 degrees +1 = +180 degrees */ - double saturation; /* -1 = grayscale (0.0) +1 = oversaturated colors (2.0) */ - double contrast; /* -1 = dark (0.5) +1 = light (1.5) */ - double brightness; /* -1 = dark (0.5) +1 = light (1.5) */ - double sharpness; /* edge contrast enhancement/blurring */ - - /* Advanced parameters */ - double gamma; /* -1 = dark (1.5) +1 = light (0.5) */ - double resolution; /* image resolution */ - double artifacts; /* artifacts caused by color changes */ - double fringing; /* color artifacts caused by brightness changes */ - double bleed; /* color bleed (color resolution reduction) */ - float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */ - - unsigned char* palette_out; /* optional RGB palette out, 3 bytes per color */ -} sms_ntsc_setup_t; - -/* Video format presets */ -extern sms_ntsc_setup_t const sms_ntsc_composite; /* color bleeding + artifacts */ -extern sms_ntsc_setup_t const sms_ntsc_svideo; /* color bleeding only */ -extern sms_ntsc_setup_t const sms_ntsc_rgb; /* crisp image */ -extern sms_ntsc_setup_t const sms_ntsc_monochrome;/* desaturated + artifacts */ - -enum { sms_ntsc_palette_size = 4096 }; - -/* Initializes and adjusts parameters. Can be called multiple times on the same -sms_ntsc_t object. Can pass NULL for either parameter. */ -typedef struct sms_ntsc_t sms_ntsc_t; -void sms_ntsc_init( sms_ntsc_t* ntsc, sms_ntsc_setup_t const* setup ); - -/* Filters one or more rows of pixels. Input pixel format is set by SMS_NTSC_IN_FORMAT -and output RGB depth is set by SMS_NTSC_OUT_DEPTH. Both default to 16-bit RGB. -In_row_width is the number of pixels to get to the next input row. Out_pitch -is the number of *bytes* to get to the next output row. */ -void sms_ntsc_blit( sms_ntsc_t const* ntsc, SMS_NTSC_IN_T const* table, unsigned char* input, - int in_width, int vline); - -/* Number of output pixels written by blitter for given input width. */ -#define SMS_NTSC_OUT_WIDTH( in_width ) \ - (((in_width) / sms_ntsc_in_chunk + 1) * sms_ntsc_out_chunk) - -/* Number of input pixels that will fit within given output width. Might be -rounded down slightly; use SMS_NTSC_OUT_WIDTH() on result to find rounded -value. */ -#define SMS_NTSC_IN_WIDTH( out_width ) \ - (((out_width) / sms_ntsc_out_chunk - 1) * sms_ntsc_in_chunk + 2) - - -/* Interface for user-defined custom blitters */ - -enum { sms_ntsc_in_chunk = 3 }; /* number of input pixels read per chunk */ -enum { sms_ntsc_out_chunk = 7 }; /* number of output pixels generated per chunk */ -enum { sms_ntsc_black = 0 }; /* palette index for black */ - -/* Begins outputting row and starts three pixels. First pixel will be cut off a bit. -Use sms_ntsc_black for unused pixels. Declares variables, so must be before first -statement in a block (unless you're using C++). */ -#define SMS_NTSC_BEGIN_ROW( ntsc, pixel0, pixel1, pixel2 ) \ - SMS_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, SMS_NTSC_IN_FORMAT, ntsc ) - -/* Begins input pixel */ -#define SMS_NTSC_COLOR_IN( in_index, ntsc, color_in ) \ - SMS_NTSC_COLOR_IN_( in_index, color_in, SMS_NTSC_IN_FORMAT, ntsc ) - -/* Generates output pixel. Bits can be 24, 16, 15, 32 (treated as 24), or 0: -24: RRRRRRRR GGGGGGGG BBBBBBBB (8-8-8 RGB) -16: RRRRRGGG GGGBBBBB (5-6-5 RGB) -15: RRRRRGG GGGBBBBB (5-5-5 RGB) - 0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format; x = junk bits) */ -#define SMS_NTSC_RGB_OUT( index, rgb_out, bits ) \ - SMS_NTSC_RGB_OUT_14_( index, rgb_out, bits, 0 ) - - -/* private */ -enum { sms_ntsc_entry_size = 3 * 14 }; -typedef unsigned long sms_ntsc_rgb_t; -struct sms_ntsc_t { - sms_ntsc_rgb_t table [sms_ntsc_palette_size] [sms_ntsc_entry_size]; -}; - -#define SMS_NTSC_BGR12( ntsc, n ) (ntsc)->table [n & 0xFFF] - -#define SMS_NTSC_RGB16( ntsc, n ) \ - (sms_ntsc_rgb_t const*) ((char const*) (ntsc)->table +\ - ((n << 10 & 0x7800) | (n & 0x0780) | (n >> 9 & 0x0078)) *\ - (sms_ntsc_entry_size * sizeof (sms_ntsc_rgb_t) / 8)) - -#define SMS_NTSC_RGB15( ntsc, n ) \ - (sms_ntsc_rgb_t const*) ((char const*) (ntsc)->table +\ - ((n << 9 & 0x3C00) | (n & 0x03C0) | (n >> 9 & 0x003C)) *\ - (sms_ntsc_entry_size * sizeof (sms_ntsc_rgb_t) / 4)) - -/* common 3->7 ntsc macros */ -#define SMS_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, ENTRY, table ) \ - unsigned const sms_ntsc_pixel0_ = (pixel0);\ - sms_ntsc_rgb_t const* kernel0 = ENTRY( table, sms_ntsc_pixel0_ );\ - unsigned const sms_ntsc_pixel1_ = (pixel1);\ - sms_ntsc_rgb_t const* kernel1 = ENTRY( table, sms_ntsc_pixel1_ );\ - unsigned const sms_ntsc_pixel2_ = (pixel2);\ - sms_ntsc_rgb_t const* kernel2 = ENTRY( table, sms_ntsc_pixel2_ );\ - sms_ntsc_rgb_t const* kernelx0;\ - sms_ntsc_rgb_t const* kernelx1 = kernel0;\ - sms_ntsc_rgb_t const* kernelx2 = kernel0 - -#define SMS_NTSC_RGB_OUT_14_( x, rgb_out, bits, shift ) {\ - sms_ntsc_rgb_t raw_ =\ - kernel0 [x ] + kernel1 [(x+12)%7+14] + kernel2 [(x+10)%7+28] +\ - kernelx0 [(x+7)%14] + kernelx1 [(x+ 5)%7+21] + kernelx2 [(x+ 3)%7+35];\ - SMS_NTSC_CLAMP_( raw_, shift );\ - SMS_NTSC_RGB_OUT_( rgb_out, bits, shift );\ -} - -/* common ntsc macros */ -#define sms_ntsc_rgb_builder ((1L << 21) | (1 << 11) | (1 << 1)) -#define sms_ntsc_clamp_mask (sms_ntsc_rgb_builder * 3 / 2) -#define sms_ntsc_clamp_add (sms_ntsc_rgb_builder * 0x101) -#define SMS_NTSC_CLAMP_( io, shift ) {\ - sms_ntsc_rgb_t sub = (io) >> (9-(shift)) & sms_ntsc_clamp_mask;\ - sms_ntsc_rgb_t clamp = sms_ntsc_clamp_add - sub;\ - io |= clamp;\ - clamp -= sub;\ - io &= clamp;\ -} - -#define SMS_NTSC_COLOR_IN_( index, color, ENTRY, table ) {\ - unsigned color_;\ - kernelx##index = kernel##index;\ - kernel##index = (color_ = (color), ENTRY( table, color_ ));\ -} - -/* x is always zero except in snes_ntsc library */ -#define SMS_NTSC_RGB_OUT_( rgb_out, bits, x ) {\ - rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\ - } - -#ifdef __cplusplus - } -#endif - -#endif +/* Sega Master System/Game Gear/TI 99/4A NTSC video filter */ + +/* sms_ntsc 0.2.3 */ +#ifndef SMS_NTSC_H +#define SMS_NTSC_H + +#include "sms_ntsc_config.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/* Image parameters, ranging from -1.0 to 1.0. Actual internal values shown +in parenthesis and should remain fairly stable in future versions. */ +typedef struct sms_ntsc_setup_t +{ + /* Basic parameters */ + double hue; /* -1 = -180 degrees +1 = +180 degrees */ + double saturation; /* -1 = grayscale (0.0) +1 = oversaturated colors (2.0) */ + double contrast; /* -1 = dark (0.5) +1 = light (1.5) */ + double brightness; /* -1 = dark (0.5) +1 = light (1.5) */ + double sharpness; /* edge contrast enhancement/blurring */ + + /* Advanced parameters */ + double gamma; /* -1 = dark (1.5) +1 = light (0.5) */ + double resolution; /* image resolution */ + double artifacts; /* artifacts caused by color changes */ + double fringing; /* color artifacts caused by brightness changes */ + double bleed; /* color bleed (color resolution reduction) */ + float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */ + + unsigned char* palette_out; /* optional RGB palette out, 3 bytes per color */ +} sms_ntsc_setup_t; + +/* Video format presets */ +extern sms_ntsc_setup_t const sms_ntsc_composite; /* color bleeding + artifacts */ +extern sms_ntsc_setup_t const sms_ntsc_svideo; /* color bleeding only */ +extern sms_ntsc_setup_t const sms_ntsc_rgb; /* crisp image */ +extern sms_ntsc_setup_t const sms_ntsc_monochrome;/* desaturated + artifacts */ + +enum { sms_ntsc_palette_size = 4096 }; + +/* Initializes and adjusts parameters. Can be called multiple times on the same +sms_ntsc_t object. Can pass NULL for either parameter. */ +typedef struct sms_ntsc_t sms_ntsc_t; +void sms_ntsc_init( sms_ntsc_t* ntsc, sms_ntsc_setup_t const* setup ); + +/* Filters one or more rows of pixels. Input pixel format is set by SMS_NTSC_IN_FORMAT +and output RGB depth is set by SMS_NTSC_OUT_DEPTH. Both default to 16-bit RGB. +In_row_width is the number of pixels to get to the next input row. Out_pitch +is the number of *bytes* to get to the next output row. */ +void sms_ntsc_blit( sms_ntsc_t const* ntsc, SMS_NTSC_IN_T const* table, unsigned char* input, + int in_width, int vline); + +/* Number of output pixels written by blitter for given input width. */ +#define SMS_NTSC_OUT_WIDTH( in_width ) \ + (((in_width) / sms_ntsc_in_chunk + 1) * sms_ntsc_out_chunk) + +/* Number of input pixels that will fit within given output width. Might be +rounded down slightly; use SMS_NTSC_OUT_WIDTH() on result to find rounded +value. */ +#define SMS_NTSC_IN_WIDTH( out_width ) \ + (((out_width) / sms_ntsc_out_chunk - 1) * sms_ntsc_in_chunk + 2) + + +/* Interface for user-defined custom blitters */ + +enum { sms_ntsc_in_chunk = 3 }; /* number of input pixels read per chunk */ +enum { sms_ntsc_out_chunk = 7 }; /* number of output pixels generated per chunk */ +enum { sms_ntsc_black = 0 }; /* palette index for black */ + +/* Begins outputting row and starts three pixels. First pixel will be cut off a bit. +Use sms_ntsc_black for unused pixels. Declares variables, so must be before first +statement in a block (unless you're using C++). */ +#define SMS_NTSC_BEGIN_ROW( ntsc, pixel0, pixel1, pixel2 ) \ + SMS_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, SMS_NTSC_IN_FORMAT, ntsc ) + +/* Begins input pixel */ +#define SMS_NTSC_COLOR_IN( in_index, ntsc, color_in ) \ + SMS_NTSC_COLOR_IN_( in_index, color_in, SMS_NTSC_IN_FORMAT, ntsc ) + +/* Generates output pixel. Bits can be 24, 16, 15, 32 (treated as 24), or 0: +24: RRRRRRRR GGGGGGGG BBBBBBBB (8-8-8 RGB) +16: RRRRRGGG GGGBBBBB (5-6-5 RGB) +15: RRRRRGG GGGBBBBB (5-5-5 RGB) + 0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format; x = junk bits) */ +#define SMS_NTSC_RGB_OUT( index, rgb_out, bits ) \ + SMS_NTSC_RGB_OUT_14_( index, rgb_out, bits, 0 ) + + +/* private */ +enum { sms_ntsc_entry_size = 3 * 14 }; +typedef unsigned long sms_ntsc_rgb_t; +struct sms_ntsc_t { + sms_ntsc_rgb_t table [sms_ntsc_palette_size] [sms_ntsc_entry_size]; +}; + +#define SMS_NTSC_BGR12( ntsc, n ) (ntsc)->table [n & 0xFFF] + +#define SMS_NTSC_RGB16( ntsc, n ) \ + (sms_ntsc_rgb_t const*) ((char const*) (ntsc)->table +\ + ((n << 10 & 0x7800) | (n & 0x0780) | (n >> 9 & 0x0078)) *\ + (sms_ntsc_entry_size * sizeof (sms_ntsc_rgb_t) / 8)) + +#define SMS_NTSC_RGB15( ntsc, n ) \ + (sms_ntsc_rgb_t const*) ((char const*) (ntsc)->table +\ + ((n << 9 & 0x3C00) | (n & 0x03C0) | (n >> 9 & 0x003C)) *\ + (sms_ntsc_entry_size * sizeof (sms_ntsc_rgb_t) / 4)) + +/* common 3->7 ntsc macros */ +#define SMS_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, ENTRY, table ) \ + unsigned const sms_ntsc_pixel0_ = (pixel0);\ + sms_ntsc_rgb_t const* kernel0 = ENTRY( table, sms_ntsc_pixel0_ );\ + unsigned const sms_ntsc_pixel1_ = (pixel1);\ + sms_ntsc_rgb_t const* kernel1 = ENTRY( table, sms_ntsc_pixel1_ );\ + unsigned const sms_ntsc_pixel2_ = (pixel2);\ + sms_ntsc_rgb_t const* kernel2 = ENTRY( table, sms_ntsc_pixel2_ );\ + sms_ntsc_rgb_t const* kernelx0;\ + sms_ntsc_rgb_t const* kernelx1 = kernel0;\ + sms_ntsc_rgb_t const* kernelx2 = kernel0 + +#define SMS_NTSC_RGB_OUT_14_( x, rgb_out, bits, shift ) {\ + sms_ntsc_rgb_t raw_ =\ + kernel0 [x ] + kernel1 [(x+12)%7+14] + kernel2 [(x+10)%7+28] +\ + kernelx0 [(x+7)%14] + kernelx1 [(x+ 5)%7+21] + kernelx2 [(x+ 3)%7+35];\ + SMS_NTSC_CLAMP_( raw_, shift );\ + SMS_NTSC_RGB_OUT_( rgb_out, bits, shift );\ +} + +/* common ntsc macros */ +#define sms_ntsc_rgb_builder ((1L << 21) | (1 << 11) | (1 << 1)) +#define sms_ntsc_clamp_mask (sms_ntsc_rgb_builder * 3 / 2) +#define sms_ntsc_clamp_add (sms_ntsc_rgb_builder * 0x101) +#define SMS_NTSC_CLAMP_( io, shift ) {\ + sms_ntsc_rgb_t sub = (io) >> (9-(shift)) & sms_ntsc_clamp_mask;\ + sms_ntsc_rgb_t clamp = sms_ntsc_clamp_add - sub;\ + io |= clamp;\ + clamp -= sub;\ + io &= clamp;\ +} + +#define SMS_NTSC_COLOR_IN_( index, color, ENTRY, table ) {\ + unsigned color_;\ + kernelx##index = kernel##index;\ + kernel##index = (color_ = (color), ENTRY( table, color_ ));\ +} + +/* x is always zero except in snes_ntsc library */ +#define SMS_NTSC_RGB_OUT_( rgb_out, bits, x ) {\ + rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\ + } + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/source/ntsc/sms_ntsc_config.h b/source/ntsc/sms_ntsc_config.h index 3ae5af9..3c933d0 100644 --- a/source/ntsc/sms_ntsc_config.h +++ b/source/ntsc/sms_ntsc_config.h @@ -1,27 +1,27 @@ -/* Configure library by modifying this file */ - -#ifndef SMS_NTSC_CONFIG_H -#define SMS_NTSC_CONFIG_H - -/* Format of source pixels */ -#define SMS_NTSC_IN_FORMAT SMS_NTSC_RGB16 -/* #define SMS_NTSC_IN_FORMAT SMS_NTSC_RGB15 */ -/* #define SMS_NTSC_IN_FORMAT SMS_NTSC_BGR12 */ - -/* The following affect the built-in blitter only; a custom blitter can -handle things however it wants. */ - -/* Bits per pixel of output. Can be 15, 16, 32, or 24 (same as 32). */ -#define SMS_NTSC_OUT_DEPTH 16 - -/* Type of input pixel values */ -#define SMS_NTSC_IN_T unsigned short - -/* Each raw pixel input value is passed through this. You might want to mask -the pixel index if you use the high bits as flags, etc. */ -#define SMS_NTSC_ADJ_IN( in ) in - -/* For each pixel, this is the basic operation: -output_color = SMS_NTSC_ADJ_IN( SMS_NTSC_IN_T ) */ - -#endif +/* Configure library by modifying this file */ + +#ifndef SMS_NTSC_CONFIG_H +#define SMS_NTSC_CONFIG_H + +/* Format of source pixels */ +#define SMS_NTSC_IN_FORMAT SMS_NTSC_RGB16 +/* #define SMS_NTSC_IN_FORMAT SMS_NTSC_RGB15 */ +/* #define SMS_NTSC_IN_FORMAT SMS_NTSC_BGR12 */ + +/* The following affect the built-in blitter only; a custom blitter can +handle things however it wants. */ + +/* Bits per pixel of output. Can be 15, 16, 32, or 24 (same as 32). */ +#define SMS_NTSC_OUT_DEPTH 16 + +/* Type of input pixel values */ +#define SMS_NTSC_IN_T unsigned short + +/* Each raw pixel input value is passed through this. You might want to mask +the pixel index if you use the high bits as flags, etc. */ +#define SMS_NTSC_ADJ_IN( in ) in + +/* For each pixel, this is the basic operation: +output_color = SMS_NTSC_ADJ_IN( SMS_NTSC_IN_T ) */ + +#endif diff --git a/source/ntsc/sms_ntsc_impl.h b/source/ntsc/sms_ntsc_impl.h index c2f3415..cf86a55 100644 --- a/source/ntsc/sms_ntsc_impl.h +++ b/source/ntsc/sms_ntsc_impl.h @@ -1,439 +1,439 @@ -/* sms_ntsc 0.2.3. http://www.slack.net/~ant/ */ - -/* Common implementation of NTSC filters */ - -#include -#include - -/* Copyright (C) 2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#define DISABLE_CORRECTION 0 - -#undef PI -#define PI 3.14159265358979323846f - -#ifndef LUMA_CUTOFF - #define LUMA_CUTOFF 0.20 -#endif -#ifndef gamma_size - #define gamma_size 1 -#endif -#ifndef rgb_bits - #define rgb_bits 8 -#endif -#ifndef artifacts_max - #define artifacts_max (artifacts_mid * 1.5f) -#endif -#ifndef fringing_max - #define fringing_max (fringing_mid * 2) -#endif -#ifndef STD_HUE_CONDITION - #define STD_HUE_CONDITION( setup ) 1 -#endif - -#define ext_decoder_hue (std_decoder_hue + 15) -#define rgb_unit (1 << rgb_bits) -#define rgb_offset (rgb_unit * 2 + 0.5f) - -enum { burst_size = sms_ntsc_entry_size / burst_count }; -enum { kernel_half = 16 }; -enum { kernel_size = kernel_half * 2 + 1 }; - -typedef struct init_t -{ - float to_rgb [burst_count * 6]; - float to_float [gamma_size]; - float contrast; - float brightness; - float artifacts; - float fringing; - float kernel [rescale_out * kernel_size * 2]; -} init_t; - -#define ROTATE_IQ( i, q, sin_b, cos_b ) {\ - float t;\ - t = i * cos_b - q * sin_b;\ - q = i * sin_b + q * cos_b;\ - i = t;\ -} - -static void init_filters( init_t* impl, sms_ntsc_setup_t const* setup ) -{ -#if rescale_out > 1 - float kernels [kernel_size * 2]; -#else - float* const kernels = impl->kernel; -#endif - - /* generate luma (y) filter using sinc kernel */ - { - /* sinc with rolloff (dsf) */ - float const rolloff = 1 + (float) setup->sharpness * (float) 0.032; - float const maxh = 32; - float const pow_a_n = (float) pow( rolloff, maxh ); - float sum; - int i; - /* quadratic mapping to reduce negative (blurring) range */ - float to_angle = (float) setup->resolution + 1; - to_angle = PI / maxh * (float) LUMA_CUTOFF * (to_angle * to_angle + 1); - - kernels [kernel_size * 3 / 2] = maxh; /* default center value */ - for ( i = 0; i < kernel_half * 2 + 1; i++ ) - { - int x = i - kernel_half; - float angle = x * to_angle; - /* instability occurs at center point with rolloff very close to 1.0 */ - if ( x || pow_a_n > (float) 1.056 || pow_a_n < (float) 0.981 ) - { - float rolloff_cos_a = rolloff * (float) cos( angle ); - float num = 1 - rolloff_cos_a - - pow_a_n * (float) cos( maxh * angle ) + - pow_a_n * rolloff * (float) cos( (maxh - 1) * angle ); - float den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff; - float dsf = num / den; - kernels [kernel_size * 3 / 2 - kernel_half + i] = dsf - (float) 0.5; - } - } - - /* apply blackman window and find sum */ - sum = 0; - for ( i = 0; i < kernel_half * 2 + 1; i++ ) - { - float x = PI * 2 / (kernel_half * 2) * i; - float blackman = 0.42f - 0.5f * (float) cos( x ) + 0.08f * (float) cos( x * 2 ); - sum += (kernels [kernel_size * 3 / 2 - kernel_half + i] *= blackman); - } - - /* normalize kernel */ - sum = 1.0f / sum; - for ( i = 0; i < kernel_half * 2 + 1; i++ ) - { - int x = kernel_size * 3 / 2 - kernel_half + i; - kernels [x] *= sum; - assert( kernels [x] == kernels [x] ); /* catch numerical instability */ - } - } - - /* generate chroma (iq) filter using gaussian kernel */ - { - float const cutoff_factor = -0.03125f; - float cutoff = (float) setup->bleed; - int i; - - if ( cutoff < 0 ) - { - /* keep extreme value accessible only near upper end of scale (1.0) */ - cutoff *= cutoff; - cutoff *= cutoff; - cutoff *= cutoff; - cutoff *= -30.0f / 0.65f; - } - cutoff = cutoff_factor - 0.65f * cutoff_factor * cutoff; - - for ( i = -kernel_half; i <= kernel_half; i++ ) - kernels [kernel_size / 2 + i] = (float) exp( i * i * cutoff ); - - /* normalize even and odd phases separately */ - for ( i = 0; i < 2; i++ ) - { - float sum = 0; - int x; - for ( x = i; x < kernel_size; x += 2 ) - sum += kernels [x]; - - sum = 1.0f / sum; - for ( x = i; x < kernel_size; x += 2 ) - { - kernels [x] *= sum; - assert( kernels [x] == kernels [x] ); /* catch numerical instability */ - } - } - } - - /* - printf( "luma:\n" ); - for ( i = kernel_size; i < kernel_size * 2; i++ ) - printf( "%f\n", kernels [i] ); - printf( "chroma:\n" ); - for ( i = 0; i < kernel_size; i++ ) - printf( "%f\n", kernels [i] ); - */ - - /* generate linear rescale kernels */ - #if rescale_out > 1 - { - float weight = 1.0f; - float* out = impl->kernel; - int n = rescale_out; - do - { - float remain = 0; - int i; - weight -= 1.0f / rescale_in; - for ( i = 0; i < kernel_size * 2; i++ ) - { - float cur = kernels [i]; - float m = cur * weight; - *out++ = m + remain; - remain = cur - m; - } - } - while ( --n ); - } - #endif -} - -static float const default_decoder [6] = - { 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f }; - -static void init( init_t* impl, sms_ntsc_setup_t const* setup ) -{ - impl->brightness = (float) setup->brightness * (0.5f * rgb_unit) + rgb_offset; - impl->contrast = (float) setup->contrast * (0.5f * rgb_unit) + rgb_unit; - #ifdef default_palette_contrast - if ( !setup->palette ) - impl->contrast *= default_palette_contrast; - #endif - - impl->artifacts = (float) setup->artifacts; - if ( impl->artifacts > 0 ) - impl->artifacts *= artifacts_max - artifacts_mid; - impl->artifacts = impl->artifacts * artifacts_mid + artifacts_mid; - - impl->fringing = (float) setup->fringing; - if ( impl->fringing > 0 ) - impl->fringing *= fringing_max - fringing_mid; - impl->fringing = impl->fringing * fringing_mid + fringing_mid; - - init_filters( impl, setup ); - - /* generate gamma table */ - if ( gamma_size > 1 ) - { - float const to_float = 1.0f / (gamma_size - (gamma_size > 1)); - float const gamma = 1.1333f - (float) setup->gamma * 0.5f; - /* match common PC's 2.2 gamma to TV's 2.65 gamma */ - int i; - for ( i = 0; i < gamma_size; i++ ) - impl->to_float [i] = - (float) pow( i * to_float, gamma ) * impl->contrast + impl->brightness; - } - - /* setup decoder matricies */ - { - float hue = (float) setup->hue * PI + PI / 180 * ext_decoder_hue; - float sat = (float) setup->saturation + 1; - float const* decoder = setup->decoder_matrix; - if ( !decoder ) - { - decoder = default_decoder; - if ( STD_HUE_CONDITION( setup ) ) - hue += PI / 180 * (std_decoder_hue - ext_decoder_hue); - } - - { - float s = (float) sin( hue ) * sat; - float c = (float) cos( hue ) * sat; - float* out = impl->to_rgb; - int n; - - n = burst_count; - do - { - float const* in = decoder; - int n = 3; - do - { - float i = *in++; - float q = *in++; - *out++ = i * c - q * s; - *out++ = i * s + q * c; - } - while ( --n ); - if ( burst_count <= 1 ) - break; - ROTATE_IQ( s, c, 0.866025f, -0.5f ); /* +120 degrees */ - } - while ( --n ); - } - } -} - -/* kernel generation */ - -#define RGB_TO_YIQ( r, g, b, y, i ) (\ - (y = (r) * 0.299f + (g) * 0.587f + (b) * 0.114f),\ - (i = (r) * 0.596f - (g) * 0.275f - (b) * 0.321f),\ - ((r) * 0.212f - (g) * 0.523f + (b) * 0.311f)\ -) - -#define YIQ_TO_RGB( y, i, q, to_rgb, type, r, g ) (\ - r = (type) (y + to_rgb [0] * i + to_rgb [1] * q),\ - g = (type) (y + to_rgb [2] * i + to_rgb [3] * q),\ - (type) (y + to_rgb [4] * i + to_rgb [5] * q)\ -) - -#define PACK_RGB( r, g, b ) ((r) << 21 | (g) << 11 | (b) << 1) - -enum { rgb_kernel_size = burst_size / alignment_count }; -enum { rgb_bias = rgb_unit * 2 * sms_ntsc_rgb_builder }; - -typedef struct pixel_info_t -{ - int offset; - float negate; - float kernel [4]; -} pixel_info_t; - -#if rescale_in > 1 - #define PIXEL_OFFSET_( ntsc, scaled ) \ - (kernel_size / 2 + ntsc + (scaled != 0) + (rescale_out - scaled) % rescale_out + \ - (kernel_size * 2 * scaled)) - - #define PIXEL_OFFSET( ntsc, scaled ) \ - PIXEL_OFFSET_( ((ntsc) - (scaled) / rescale_out * rescale_in),\ - (((scaled) + rescale_out * 10) % rescale_out) ),\ - (1.0f - (((ntsc) + 100) & 2)) -#else - #define PIXEL_OFFSET( ntsc, scaled ) \ - (kernel_size / 2 + (ntsc) - (scaled)),\ - (1.0f - (((ntsc) + 100) & 2)) -#endif - -extern pixel_info_t const sms_ntsc_pixels [alignment_count]; - -/* Generate pixel at all burst phases and column alignments */ -static void gen_kernel( init_t* impl, float y, float i, float q, sms_ntsc_rgb_t* out ) -{ - /* generate for each scanline burst phase */ - float const* to_rgb = impl->to_rgb; - int burst_remain = burst_count; - y -= rgb_offset; - do - { - /* Encode yiq into *two* composite signals (to allow control over artifacting). - Convolve these with kernels which: filter respective components, apply - sharpening, and rescale horizontally. Convert resulting yiq to rgb and pack - into integer. Based on algorithm by NewRisingSun. */ - pixel_info_t const* pixel = sms_ntsc_pixels; - int alignment_remain = alignment_count; - do - { - /* negate is -1 when composite starts at odd multiple of 2 */ - float const yy = y * impl->fringing * pixel->negate; - float const ic0 = (i + yy) * pixel->kernel [0]; - float const qc1 = (q + yy) * pixel->kernel [1]; - float const ic2 = (i - yy) * pixel->kernel [2]; - float const qc3 = (q - yy) * pixel->kernel [3]; - - float const factor = impl->artifacts * pixel->negate; - float const ii = i * factor; - float const yc0 = (y + ii) * pixel->kernel [0]; - float const yc2 = (y - ii) * pixel->kernel [2]; - - float const qq = q * factor; - float const yc1 = (y + qq) * pixel->kernel [1]; - float const yc3 = (y - qq) * pixel->kernel [3]; - - float const* k = &impl->kernel [pixel->offset]; - int n; - ++pixel; - for ( n = rgb_kernel_size; n; --n ) - { - float i = k[0]*ic0 + k[2]*ic2; - float q = k[1]*qc1 + k[3]*qc3; - float y = k[kernel_size+0]*yc0 + k[kernel_size+1]*yc1 + - k[kernel_size+2]*yc2 + k[kernel_size+3]*yc3 + rgb_offset; - if ( rescale_out <= 1 ) - k--; - else if ( k < &impl->kernel [kernel_size * 2 * (rescale_out - 1)] ) - k += kernel_size * 2 - 1; - else - k -= kernel_size * 2 * (rescale_out - 1) + 2; - { - int r, g, b = YIQ_TO_RGB( y, i, q, to_rgb, int, r, g ); - *out++ = PACK_RGB( r, g, b ) - rgb_bias; - } - } - } - while ( alignment_count > 1 && --alignment_remain ); - - if ( burst_count <= 1 ) - break; - - to_rgb += 6; - - ROTATE_IQ( i, q, -0.866025f, -0.5f ); /* -120 degrees */ - } - while ( --burst_remain ); -} - -static void correct_errors( sms_ntsc_rgb_t color, sms_ntsc_rgb_t* out ); - -#if DISABLE_CORRECTION - #define CORRECT_ERROR( a ) { out [i] += rgb_bias; } - #define DISTRIBUTE_ERROR( a, b, c ) { out [i] += rgb_bias; } -#else - #define CORRECT_ERROR( a ) { out [a] += error; } - #define DISTRIBUTE_ERROR( a, b, c ) {\ - sms_ntsc_rgb_t fourth = (error + 2 * sms_ntsc_rgb_builder) >> 2;\ - fourth &= (rgb_bias >> 1) - sms_ntsc_rgb_builder;\ - fourth -= rgb_bias >> 2;\ - out [a] += fourth;\ - out [b] += fourth;\ - out [c] += fourth;\ - out [i] += error - (fourth * 3);\ - } -#endif - -#define RGB_PALETTE_OUT( rgb, out_ )\ -{\ - unsigned char* out = (out_);\ - sms_ntsc_rgb_t clamped = (rgb);\ - SMS_NTSC_CLAMP_( clamped, (8 - rgb_bits) );\ - out [0] = (unsigned char) (clamped >> 21);\ - out [1] = (unsigned char) (clamped >> 11);\ - out [2] = (unsigned char) (clamped >> 1);\ -} - -/* blitter related */ - -#ifndef restrict - #if defined (__GNUC__) - #define restrict __restrict__ - #elif defined (_MSC_VER) && _MSC_VER > 1300 - #define restrict __restrict - #else - /* no support for restricted pointers */ - #define restrict - #endif -#endif - -#include - -#if SMS_NTSC_OUT_DEPTH <= 16 - #if USHRT_MAX == 0xFFFF - typedef unsigned short sms_ntsc_out_t; - #else - #error "Need 16-bit int type" - #endif - -#else - #if UINT_MAX == 0xFFFFFFFF - typedef unsigned int sms_ntsc_out_t; - #elif ULONG_MAX == 0xFFFFFFFF - typedef unsigned long sms_ntsc_out_t; - #else - #error "Need 32-bit int type" - #endif - -#endif +/* sms_ntsc 0.2.3. http://www.slack.net/~ant/ */ + +/* Common implementation of NTSC filters */ + +#include +#include + +/* Copyright (C) 2006 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#define DISABLE_CORRECTION 0 + +#undef PI +#define PI 3.14159265358979323846f + +#ifndef LUMA_CUTOFF + #define LUMA_CUTOFF 0.20 +#endif +#ifndef gamma_size + #define gamma_size 1 +#endif +#ifndef rgb_bits + #define rgb_bits 8 +#endif +#ifndef artifacts_max + #define artifacts_max (artifacts_mid * 1.5f) +#endif +#ifndef fringing_max + #define fringing_max (fringing_mid * 2) +#endif +#ifndef STD_HUE_CONDITION + #define STD_HUE_CONDITION( setup ) 1 +#endif + +#define ext_decoder_hue (std_decoder_hue + 15) +#define rgb_unit (1 << rgb_bits) +#define rgb_offset (rgb_unit * 2 + 0.5f) + +enum { burst_size = sms_ntsc_entry_size / burst_count }; +enum { kernel_half = 16 }; +enum { kernel_size = kernel_half * 2 + 1 }; + +typedef struct init_t +{ + float to_rgb [burst_count * 6]; + float to_float [gamma_size]; + float contrast; + float brightness; + float artifacts; + float fringing; + float kernel [rescale_out * kernel_size * 2]; +} init_t; + +#define ROTATE_IQ( i, q, sin_b, cos_b ) {\ + float t;\ + t = i * cos_b - q * sin_b;\ + q = i * sin_b + q * cos_b;\ + i = t;\ +} + +static void init_filters( init_t* impl, sms_ntsc_setup_t const* setup ) +{ +#if rescale_out > 1 + float kernels [kernel_size * 2]; +#else + float* const kernels = impl->kernel; +#endif + + /* generate luma (y) filter using sinc kernel */ + { + /* sinc with rolloff (dsf) */ + float const rolloff = 1 + (float) setup->sharpness * (float) 0.032; + float const maxh = 32; + float const pow_a_n = (float) pow( rolloff, maxh ); + float sum; + int i; + /* quadratic mapping to reduce negative (blurring) range */ + float to_angle = (float) setup->resolution + 1; + to_angle = PI / maxh * (float) LUMA_CUTOFF * (to_angle * to_angle + 1); + + kernels [kernel_size * 3 / 2] = maxh; /* default center value */ + for ( i = 0; i < kernel_half * 2 + 1; i++ ) + { + int x = i - kernel_half; + float angle = x * to_angle; + /* instability occurs at center point with rolloff very close to 1.0 */ + if ( x || pow_a_n > (float) 1.056 || pow_a_n < (float) 0.981 ) + { + float rolloff_cos_a = rolloff * (float) cos( angle ); + float num = 1 - rolloff_cos_a - + pow_a_n * (float) cos( maxh * angle ) + + pow_a_n * rolloff * (float) cos( (maxh - 1) * angle ); + float den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff; + float dsf = num / den; + kernels [kernel_size * 3 / 2 - kernel_half + i] = dsf - (float) 0.5; + } + } + + /* apply blackman window and find sum */ + sum = 0; + for ( i = 0; i < kernel_half * 2 + 1; i++ ) + { + float x = PI * 2 / (kernel_half * 2) * i; + float blackman = 0.42f - 0.5f * (float) cos( x ) + 0.08f * (float) cos( x * 2 ); + sum += (kernels [kernel_size * 3 / 2 - kernel_half + i] *= blackman); + } + + /* normalize kernel */ + sum = 1.0f / sum; + for ( i = 0; i < kernel_half * 2 + 1; i++ ) + { + int x = kernel_size * 3 / 2 - kernel_half + i; + kernels [x] *= sum; + assert( kernels [x] == kernels [x] ); /* catch numerical instability */ + } + } + + /* generate chroma (iq) filter using gaussian kernel */ + { + float const cutoff_factor = -0.03125f; + float cutoff = (float) setup->bleed; + int i; + + if ( cutoff < 0 ) + { + /* keep extreme value accessible only near upper end of scale (1.0) */ + cutoff *= cutoff; + cutoff *= cutoff; + cutoff *= cutoff; + cutoff *= -30.0f / 0.65f; + } + cutoff = cutoff_factor - 0.65f * cutoff_factor * cutoff; + + for ( i = -kernel_half; i <= kernel_half; i++ ) + kernels [kernel_size / 2 + i] = (float) exp( i * i * cutoff ); + + /* normalize even and odd phases separately */ + for ( i = 0; i < 2; i++ ) + { + float sum = 0; + int x; + for ( x = i; x < kernel_size; x += 2 ) + sum += kernels [x]; + + sum = 1.0f / sum; + for ( x = i; x < kernel_size; x += 2 ) + { + kernels [x] *= sum; + assert( kernels [x] == kernels [x] ); /* catch numerical instability */ + } + } + } + + /* + printf( "luma:\n" ); + for ( i = kernel_size; i < kernel_size * 2; i++ ) + printf( "%f\n", kernels [i] ); + printf( "chroma:\n" ); + for ( i = 0; i < kernel_size; i++ ) + printf( "%f\n", kernels [i] ); + */ + + /* generate linear rescale kernels */ + #if rescale_out > 1 + { + float weight = 1.0f; + float* out = impl->kernel; + int n = rescale_out; + do + { + float remain = 0; + int i; + weight -= 1.0f / rescale_in; + for ( i = 0; i < kernel_size * 2; i++ ) + { + float cur = kernels [i]; + float m = cur * weight; + *out++ = m + remain; + remain = cur - m; + } + } + while ( --n ); + } + #endif +} + +static float const default_decoder [6] = + { 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f }; + +static void init( init_t* impl, sms_ntsc_setup_t const* setup ) +{ + impl->brightness = (float) setup->brightness * (0.5f * rgb_unit) + rgb_offset; + impl->contrast = (float) setup->contrast * (0.5f * rgb_unit) + rgb_unit; + #ifdef default_palette_contrast + if ( !setup->palette ) + impl->contrast *= default_palette_contrast; + #endif + + impl->artifacts = (float) setup->artifacts; + if ( impl->artifacts > 0 ) + impl->artifacts *= artifacts_max - artifacts_mid; + impl->artifacts = impl->artifacts * artifacts_mid + artifacts_mid; + + impl->fringing = (float) setup->fringing; + if ( impl->fringing > 0 ) + impl->fringing *= fringing_max - fringing_mid; + impl->fringing = impl->fringing * fringing_mid + fringing_mid; + + init_filters( impl, setup ); + + /* generate gamma table */ + if ( gamma_size > 1 ) + { + float const to_float = 1.0f / (gamma_size - (gamma_size > 1)); + float const gamma = 1.1333f - (float) setup->gamma * 0.5f; + /* match common PC's 2.2 gamma to TV's 2.65 gamma */ + int i; + for ( i = 0; i < gamma_size; i++ ) + impl->to_float [i] = + (float) pow( i * to_float, gamma ) * impl->contrast + impl->brightness; + } + + /* setup decoder matricies */ + { + float hue = (float) setup->hue * PI + PI / 180 * ext_decoder_hue; + float sat = (float) setup->saturation + 1; + float const* decoder = setup->decoder_matrix; + if ( !decoder ) + { + decoder = default_decoder; + if ( STD_HUE_CONDITION( setup ) ) + hue += PI / 180 * (std_decoder_hue - ext_decoder_hue); + } + + { + float s = (float) sin( hue ) * sat; + float c = (float) cos( hue ) * sat; + float* out = impl->to_rgb; + int n; + + n = burst_count; + do + { + float const* in = decoder; + int n = 3; + do + { + float i = *in++; + float q = *in++; + *out++ = i * c - q * s; + *out++ = i * s + q * c; + } + while ( --n ); + if ( burst_count <= 1 ) + break; + ROTATE_IQ( s, c, 0.866025f, -0.5f ); /* +120 degrees */ + } + while ( --n ); + } + } +} + +/* kernel generation */ + +#define RGB_TO_YIQ( r, g, b, y, i ) (\ + (y = (r) * 0.299f + (g) * 0.587f + (b) * 0.114f),\ + (i = (r) * 0.596f - (g) * 0.275f - (b) * 0.321f),\ + ((r) * 0.212f - (g) * 0.523f + (b) * 0.311f)\ +) + +#define YIQ_TO_RGB( y, i, q, to_rgb, type, r, g ) (\ + r = (type) (y + to_rgb [0] * i + to_rgb [1] * q),\ + g = (type) (y + to_rgb [2] * i + to_rgb [3] * q),\ + (type) (y + to_rgb [4] * i + to_rgb [5] * q)\ +) + +#define PACK_RGB( r, g, b ) ((r) << 21 | (g) << 11 | (b) << 1) + +enum { rgb_kernel_size = burst_size / alignment_count }; +enum { rgb_bias = rgb_unit * 2 * sms_ntsc_rgb_builder }; + +typedef struct pixel_info_t +{ + int offset; + float negate; + float kernel [4]; +} pixel_info_t; + +#if rescale_in > 1 + #define PIXEL_OFFSET_( ntsc, scaled ) \ + (kernel_size / 2 + ntsc + (scaled != 0) + (rescale_out - scaled) % rescale_out + \ + (kernel_size * 2 * scaled)) + + #define PIXEL_OFFSET( ntsc, scaled ) \ + PIXEL_OFFSET_( ((ntsc) - (scaled) / rescale_out * rescale_in),\ + (((scaled) + rescale_out * 10) % rescale_out) ),\ + (1.0f - (((ntsc) + 100) & 2)) +#else + #define PIXEL_OFFSET( ntsc, scaled ) \ + (kernel_size / 2 + (ntsc) - (scaled)),\ + (1.0f - (((ntsc) + 100) & 2)) +#endif + +extern pixel_info_t const sms_ntsc_pixels [alignment_count]; + +/* Generate pixel at all burst phases and column alignments */ +static void gen_kernel( init_t* impl, float y, float i, float q, sms_ntsc_rgb_t* out ) +{ + /* generate for each scanline burst phase */ + float const* to_rgb = impl->to_rgb; + int burst_remain = burst_count; + y -= rgb_offset; + do + { + /* Encode yiq into *two* composite signals (to allow control over artifacting). + Convolve these with kernels which: filter respective components, apply + sharpening, and rescale horizontally. Convert resulting yiq to rgb and pack + into integer. Based on algorithm by NewRisingSun. */ + pixel_info_t const* pixel = sms_ntsc_pixels; + int alignment_remain = alignment_count; + do + { + /* negate is -1 when composite starts at odd multiple of 2 */ + float const yy = y * impl->fringing * pixel->negate; + float const ic0 = (i + yy) * pixel->kernel [0]; + float const qc1 = (q + yy) * pixel->kernel [1]; + float const ic2 = (i - yy) * pixel->kernel [2]; + float const qc3 = (q - yy) * pixel->kernel [3]; + + float const factor = impl->artifacts * pixel->negate; + float const ii = i * factor; + float const yc0 = (y + ii) * pixel->kernel [0]; + float const yc2 = (y - ii) * pixel->kernel [2]; + + float const qq = q * factor; + float const yc1 = (y + qq) * pixel->kernel [1]; + float const yc3 = (y - qq) * pixel->kernel [3]; + + float const* k = &impl->kernel [pixel->offset]; + int n; + ++pixel; + for ( n = rgb_kernel_size; n; --n ) + { + float i = k[0]*ic0 + k[2]*ic2; + float q = k[1]*qc1 + k[3]*qc3; + float y = k[kernel_size+0]*yc0 + k[kernel_size+1]*yc1 + + k[kernel_size+2]*yc2 + k[kernel_size+3]*yc3 + rgb_offset; + if ( rescale_out <= 1 ) + k--; + else if ( k < &impl->kernel [kernel_size * 2 * (rescale_out - 1)] ) + k += kernel_size * 2 - 1; + else + k -= kernel_size * 2 * (rescale_out - 1) + 2; + { + int r, g, b = YIQ_TO_RGB( y, i, q, to_rgb, int, r, g ); + *out++ = PACK_RGB( r, g, b ) - rgb_bias; + } + } + } + while ( alignment_count > 1 && --alignment_remain ); + + if ( burst_count <= 1 ) + break; + + to_rgb += 6; + + ROTATE_IQ( i, q, -0.866025f, -0.5f ); /* -120 degrees */ + } + while ( --burst_remain ); +} + +static void correct_errors( sms_ntsc_rgb_t color, sms_ntsc_rgb_t* out ); + +#if DISABLE_CORRECTION + #define CORRECT_ERROR( a ) { out [i] += rgb_bias; } + #define DISTRIBUTE_ERROR( a, b, c ) { out [i] += rgb_bias; } +#else + #define CORRECT_ERROR( a ) { out [a] += error; } + #define DISTRIBUTE_ERROR( a, b, c ) {\ + sms_ntsc_rgb_t fourth = (error + 2 * sms_ntsc_rgb_builder) >> 2;\ + fourth &= (rgb_bias >> 1) - sms_ntsc_rgb_builder;\ + fourth -= rgb_bias >> 2;\ + out [a] += fourth;\ + out [b] += fourth;\ + out [c] += fourth;\ + out [i] += error - (fourth * 3);\ + } +#endif + +#define RGB_PALETTE_OUT( rgb, out_ )\ +{\ + unsigned char* out = (out_);\ + sms_ntsc_rgb_t clamped = (rgb);\ + SMS_NTSC_CLAMP_( clamped, (8 - rgb_bits) );\ + out [0] = (unsigned char) (clamped >> 21);\ + out [1] = (unsigned char) (clamped >> 11);\ + out [2] = (unsigned char) (clamped >> 1);\ +} + +/* blitter related */ + +#ifndef restrict + #if defined (__GNUC__) + #define restrict __restrict__ + #elif defined (_MSC_VER) && _MSC_VER > 1300 + #define restrict __restrict + #else + /* no support for restricted pointers */ + #define restrict + #endif +#endif + +#include + +#if SMS_NTSC_OUT_DEPTH <= 16 + #if USHRT_MAX == 0xFFFF + typedef unsigned short sms_ntsc_out_t; + #else + #error "Need 16-bit int type" + #endif + +#else + #if UINT_MAX == 0xFFFFFFFF + typedef unsigned int sms_ntsc_out_t; + #elif ULONG_MAX == 0xFFFFFFFF + typedef unsigned long sms_ntsc_out_t; + #else + #error "Need 32-bit int type" + #endif + +#endif diff --git a/source/shared.h b/source/shared.h index fa39bd0..36cb336 100644 --- a/source/shared.h +++ b/source/shared.h @@ -11,19 +11,20 @@ #include "z80.h" #include "system.h" #include "genesis.h" -#include "vdp.h" -#include "render.h" +#include "vdp_ctrl.h" +#include "vdp_render.h" #include "mem68k.h" #include "memz80.h" #include "membnk.h" -#include "gen_io.h" -#include "gen_input.h" +#include "io_ctrl.h" +#include "input.h" #include "state.h" #include "sound.h" #include "sn76489.h" #include "ym2612.h" #include "loadrom.h" -#include "cart_hw.h" +#include "sms_cart.h" +#include "md_cart.h" #include "eeprom.h" #include "sram.h" #include "ggenie.h" diff --git a/source/sound/Fir_Resampler.c b/source/sound/Fir_Resampler.c index 0192712..85aef32 100644 --- a/source/sound/Fir_Resampler.c +++ b/source/sound/Fir_Resampler.c @@ -1,351 +1,351 @@ -/* Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */ -/* Copyright (C) 2004-2006 Shay Green. */ -/* C Conversion by Eke-Eke for use in Genesis Plus (2009). */ - -#include "Fir_Resampler.h" -#include "shared.h" - -#include -#include -#include -#include - -/* sound buffer */ -static sample_t *buffer = NULL; -static int buffer_size = 0; - -static sample_t impulses[MAX_RES][WIDTH]; -static sample_t* write_pos = NULL; -static int res = 1; -static int imp_phase = 0; -static unsigned long skip_bits = 0; -static int step = STEREO; -static int input_per_cycle; -static double ratio = 1.0; - -static void gen_sinc(double rolloff, int width, double offset, double spacing, double scale, int count, sample_t *out ) -{ - double w, rolloff_cos_a, num, den, sinc; - double const maxh = 256; - double const fstep = M_PI / maxh * spacing; - double const to_w = maxh * 2 / width; - double const pow_a_n = pow( rolloff, maxh ); - scale /= maxh * 2; - - double angle = (count / 2 - 1 + offset) * -fstep; - - while ( count-- ) - { - *out++ = 0; - w = angle * to_w; - if ( fabs( w ) < M_PI ) - { - rolloff_cos_a = rolloff * cos( angle ); - num = 1 - rolloff_cos_a - - pow_a_n * cos( maxh * angle ) + - pow_a_n * rolloff * cos( (maxh - 1) * angle ); - den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff; - sinc = scale * num / den - scale; - - out [-1] = (short) (cos( w ) * sinc + sinc); - } - angle += fstep; - } -} - -/*static int available( long input_count ) -{ - int cycle_count = input_count / input_per_cycle; - int output_count = cycle_count * res * STEREO; - input_count -= cycle_count * input_per_cycle; - - unsigned long skip = skip_bits >> imp_phase; - int remain = res - imp_phase; - while ( input_count >= 0 ) - { - input_count -= step + (skip & 1) * STEREO; - skip >>= 1; - if ( !--remain ) - { - skip = skip_bits; - remain = res; - } - output_count += 2; - } - return output_count; -} -*/ -int Fir_Resampler_avail() -{ - long count = 0; - sample_t* in = buffer; - sample_t* end_pos = write_pos; - unsigned long skip = skip_bits >> imp_phase; - int remain = res - imp_phase; - if ( end_pos - in >= WIDTH * STEREO ) - { - end_pos -= WIDTH * STEREO; - do - { - count++; - remain--; - in += (skip * STEREO) & STEREO; - skip >>= 1; - in += step; - - if ( !remain ) - { - skip = skip_bits; - remain = res; - } - } - while ( in <= end_pos ); - } - - return count; -} - -int Fir_Resampler_initialize( int new_size ) -{ - res = 1; - skip_bits = 0; - imp_phase = 0; - step = STEREO; - ratio = 1.0; - buffer = (sample_t *) realloc( buffer, (new_size + WRITE_OFFSET) * sizeof (sample_t) ); - write_pos = 0; - if ( !buffer ) return 0; - buffer_size = new_size + WRITE_OFFSET; - Fir_Resampler_clear(); - return 1; -} - -void Fir_Resampler_shutdown( void ) -{ - if (buffer) free(buffer); - buffer = 0; - buffer_size = 0; - write_pos = 0; -} - -void Fir_Resampler_clear() -{ - imp_phase = 0; - if ( buffer_size ) - { - write_pos = &buffer [WRITE_OFFSET]; - memset( buffer, 0, buffer_size * sizeof (sample_t) ); - } -} - -double Fir_Resampler_time_ratio( double new_factor, double rolloff ) -{ - ratio = new_factor; - - int i, r; - double nearest, error; - double fstep = 0.0; - double least_error = 2; - double pos = 0.0; - res = -1; - -#ifdef NGC - u32 level = IRQ_Disable(); -#endif - - for ( r = 1; r <= MAX_RES; r++ ) - { - pos += ratio; - nearest = floor( pos + 0.5 ); - error = fabs( pos - nearest ); - if ( error < least_error ) - { - res = r; - fstep = nearest / res; - least_error = error; - } - } - - skip_bits = 0; - - step = STEREO * (int) floor( fstep ); - - ratio = fstep; - fstep = fmod( fstep, 1.0 ); - - double filter = (ratio < 1.0) ? 1.0 : 1.0 / ratio; - pos = 0.0; - input_per_cycle = 0; - - memset(impulses, 0, MAX_RES*WIDTH*sizeof(sample_t)); - - for ( i = 0; i < res; i++ ) - { - gen_sinc( rolloff, (int) (WIDTH * filter + 1) & ~1, pos, filter, - (double) (0x7FFF * GAIN * filter), - (int) WIDTH, impulses[i] ); - - pos += fstep; - input_per_cycle += step; - if ( pos >= 0.9999999 ) - { - pos -= 1.0; - skip_bits |= 1 << i; - input_per_cycle++; - } - } - -#ifdef NGC - IRQ_Restore(level); -#endif - - Fir_Resampler_clear(); - - return ratio; -} - -/* Current ratio */ -double Fir_Resampler_ratio( void ) -{ - return ratio; -} - -/* Number of input samples that can be written */ -int Fir_Resampler_max_write( void ) -{ - return buffer + buffer_size - write_pos; -} - -/* Pointer to place to write input samples */ -sample_t* Fir_Resampler_buffer( void ) -{ - return write_pos; -} - -/* Number of input samples in buffer */ -int Fir_Resampler_written( void ) -{ - return write_pos - &buffer [WRITE_OFFSET]; -} - -/* Number of output samples available */ -/*int Fir_Resampler_avail( void ) -{ - return available( write_pos - &buffer [WIDTH * STEREO] ); -}*/ - -void Fir_Resampler_write( long count ) -{ - write_pos += count; -} - -int Fir_Resampler_read( sample_t* out, long count ) -{ - sample_t* out_ = out; - sample_t* in = buffer; - sample_t* end_pos = write_pos; - unsigned long skip = skip_bits >> imp_phase; - sample_t const* imp = impulses [imp_phase]; - int remain = res - imp_phase; - int n; - int pt0,pt1; - sample_t* i; - long l,r; - - if ( end_pos - in >= WIDTH * STEREO ) - { - end_pos -= WIDTH * STEREO; - do - { - count--; - - if ( count < 0 ) - break; - - /* accumulate in extended precision */ - l = 0; - r = 0; - - i = in; - - for ( n = WIDTH / 2; n; --n ) - { - pt0 = imp [0]; - l += pt0 * i [0]; - r += pt0 * i [1]; - pt1 = imp [1]; - imp += 2; - l += pt1 * i [2]; - r += pt1 * i [3]; - i += 4; - } - - remain--; - - l >>= 15; - r >>= 15; - - in += (skip * STEREO) & STEREO; - skip >>= 1; - in += step; - - if ( !remain ) - { - imp = impulses [0]; - skip = skip_bits; - remain = res; - } - - *out++ = (sample_t) l; - *out++ = (sample_t) r; - } - while ( in <= end_pos ); - } - - imp_phase = res - remain; - - int left = write_pos - in; - write_pos = &buffer [left]; - memmove( buffer, in, left * sizeof *in ); - - return out - out_; -} - - /* fixed (Eke_Eke) */ -int Fir_Resampler_input_needed( long output_count ) -{ - long input_count = 0; - - unsigned long skip = skip_bits >> imp_phase; - int remain = res - imp_phase; - while ( (output_count) > 0 ) - { - input_count += step + (skip & 1) * STEREO; - skip >>= 1; - if ( !--remain ) - { - skip = skip_bits; - remain = res; - } - output_count --; - } - - long input_extra = input_count - (write_pos - &buffer [WRITE_OFFSET]); - if ( input_extra < 0 ) - input_extra = 0; - return (input_extra >> 1); -} - -int Fir_Resampler_skip_input( long count ) -{ - int remain = write_pos - buffer; - int max_count = remain - WIDTH * STEREO; - if ( count > max_count ) - count = max_count; - - remain -= count; - write_pos = &buffer [remain]; - memmove( buffer, &buffer [count], remain * sizeof buffer [0] ); - - return count; -} +/* Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ */ +/* Copyright (C) 2004-2006 Shay Green. */ +/* C Conversion by Eke-Eke for use in Genesis Plus (2009). */ + +#include "Fir_Resampler.h" +#include "shared.h" + +#include +#include +#include +#include + +/* sound buffer */ +static sample_t *buffer = NULL; +static int buffer_size = 0; + +static sample_t impulses[MAX_RES][WIDTH]; +static sample_t* write_pos = NULL; +static int res = 1; +static int imp_phase = 0; +static unsigned long skip_bits = 0; +static int step = STEREO; +static int input_per_cycle; +static double ratio = 1.0; + +static void gen_sinc(double rolloff, int width, double offset, double spacing, double scale, int count, sample_t *out ) +{ + double w, rolloff_cos_a, num, den, sinc; + double const maxh = 256; + double const fstep = M_PI / maxh * spacing; + double const to_w = maxh * 2 / width; + double const pow_a_n = pow( rolloff, maxh ); + scale /= maxh * 2; + + double angle = (count / 2 - 1 + offset) * -fstep; + + while ( count-- ) + { + *out++ = 0; + w = angle * to_w; + if ( fabs( w ) < M_PI ) + { + rolloff_cos_a = rolloff * cos( angle ); + num = 1 - rolloff_cos_a - + pow_a_n * cos( maxh * angle ) + + pow_a_n * rolloff * cos( (maxh - 1) * angle ); + den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff; + sinc = scale * num / den - scale; + + out [-1] = (short) (cos( w ) * sinc + sinc); + } + angle += fstep; + } +} + +/*static int available( long input_count ) +{ + int cycle_count = input_count / input_per_cycle; + int output_count = cycle_count * res * STEREO; + input_count -= cycle_count * input_per_cycle; + + unsigned long skip = skip_bits >> imp_phase; + int remain = res - imp_phase; + while ( input_count >= 0 ) + { + input_count -= step + (skip & 1) * STEREO; + skip >>= 1; + if ( !--remain ) + { + skip = skip_bits; + remain = res; + } + output_count += 2; + } + return output_count; +} +*/ +int Fir_Resampler_avail() +{ + long count = 0; + sample_t* in = buffer; + sample_t* end_pos = write_pos; + unsigned long skip = skip_bits >> imp_phase; + int remain = res - imp_phase; + if ( end_pos - in >= WIDTH * STEREO ) + { + end_pos -= WIDTH * STEREO; + do + { + count++; + remain--; + in += (skip * STEREO) & STEREO; + skip >>= 1; + in += step; + + if ( !remain ) + { + skip = skip_bits; + remain = res; + } + } + while ( in <= end_pos ); + } + + return count; +} + +int Fir_Resampler_initialize( int new_size ) +{ + res = 1; + skip_bits = 0; + imp_phase = 0; + step = STEREO; + ratio = 1.0; + buffer = (sample_t *) realloc( buffer, (new_size + WRITE_OFFSET) * sizeof (sample_t) ); + write_pos = 0; + if ( !buffer ) return 0; + buffer_size = new_size + WRITE_OFFSET; + Fir_Resampler_clear(); + return 1; +} + +void Fir_Resampler_shutdown( void ) +{ + if (buffer) free(buffer); + buffer = 0; + buffer_size = 0; + write_pos = 0; +} + +void Fir_Resampler_clear() +{ + imp_phase = 0; + if ( buffer_size ) + { + write_pos = &buffer [WRITE_OFFSET]; + memset( buffer, 0, buffer_size * sizeof (sample_t) ); + } +} + +double Fir_Resampler_time_ratio( double new_factor, double rolloff ) +{ + ratio = new_factor; + + int i, r; + double nearest, error; + double fstep = 0.0; + double least_error = 2; + double pos = 0.0; + res = -1; + +#ifdef NGC + u32 level = IRQ_Disable(); +#endif + + for ( r = 1; r <= MAX_RES; r++ ) + { + pos += ratio; + nearest = floor( pos + 0.5 ); + error = fabs( pos - nearest ); + if ( error < least_error ) + { + res = r; + fstep = nearest / res; + least_error = error; + } + } + + skip_bits = 0; + + step = STEREO * (int) floor( fstep ); + + ratio = fstep; + fstep = fmod( fstep, 1.0 ); + + double filter = (ratio < 1.0) ? 1.0 : 1.0 / ratio; + pos = 0.0; + input_per_cycle = 0; + + memset(impulses, 0, MAX_RES*WIDTH*sizeof(sample_t)); + + for ( i = 0; i < res; i++ ) + { + gen_sinc( rolloff, (int) (WIDTH * filter + 1) & ~1, pos, filter, + (double) (0x7FFF * GAIN * filter), + (int) WIDTH, impulses[i] ); + + pos += fstep; + input_per_cycle += step; + if ( pos >= 0.9999999 ) + { + pos -= 1.0; + skip_bits |= 1 << i; + input_per_cycle++; + } + } + +#ifdef NGC + IRQ_Restore(level); +#endif + + Fir_Resampler_clear(); + + return ratio; +} + +/* Current ratio */ +double Fir_Resampler_ratio( void ) +{ + return ratio; +} + +/* Number of input samples that can be written */ +int Fir_Resampler_max_write( void ) +{ + return buffer + buffer_size - write_pos; +} + +/* Pointer to place to write input samples */ +sample_t* Fir_Resampler_buffer( void ) +{ + return write_pos; +} + +/* Number of input samples in buffer */ +int Fir_Resampler_written( void ) +{ + return write_pos - &buffer [WRITE_OFFSET]; +} + +/* Number of output samples available */ +/*int Fir_Resampler_avail( void ) +{ + return available( write_pos - &buffer [WIDTH * STEREO] ); +}*/ + +void Fir_Resampler_write( long count ) +{ + write_pos += count; +} + +int Fir_Resampler_read( sample_t* out, long count ) +{ + sample_t* out_ = out; + sample_t* in = buffer; + sample_t* end_pos = write_pos; + unsigned long skip = skip_bits >> imp_phase; + sample_t const* imp = impulses [imp_phase]; + int remain = res - imp_phase; + int n; + int pt0,pt1; + sample_t* i; + long l,r; + + if ( end_pos - in >= WIDTH * STEREO ) + { + end_pos -= WIDTH * STEREO; + do + { + count--; + + if ( count < 0 ) + break; + + /* accumulate in extended precision */ + l = 0; + r = 0; + + i = in; + + for ( n = WIDTH / 2; n; --n ) + { + pt0 = imp [0]; + l += pt0 * i [0]; + r += pt0 * i [1]; + pt1 = imp [1]; + imp += 2; + l += pt1 * i [2]; + r += pt1 * i [3]; + i += 4; + } + + remain--; + + l >>= 15; + r >>= 15; + + in += (skip * STEREO) & STEREO; + skip >>= 1; + in += step; + + if ( !remain ) + { + imp = impulses [0]; + skip = skip_bits; + remain = res; + } + + *out++ = (sample_t) l; + *out++ = (sample_t) r; + } + while ( in <= end_pos ); + } + + imp_phase = res - remain; + + int left = write_pos - in; + write_pos = &buffer [left]; + memmove( buffer, in, left * sizeof *in ); + + return out - out_; +} + + /* fixed (Eke_Eke) */ +int Fir_Resampler_input_needed( long output_count ) +{ + long input_count = 0; + + unsigned long skip = skip_bits >> imp_phase; + int remain = res - imp_phase; + while ( (output_count) > 0 ) + { + input_count += step + (skip & 1) * STEREO; + skip >>= 1; + if ( !--remain ) + { + skip = skip_bits; + remain = res; + } + output_count --; + } + + long input_extra = input_count - (write_pos - &buffer [WRITE_OFFSET]); + if ( input_extra < 0 ) + input_extra = 0; + return (input_extra >> 1); +} + +int Fir_Resampler_skip_input( long count ) +{ + int remain = write_pos - buffer; + int max_count = remain - WIDTH * STEREO; + if ( count > max_count ) + count = max_count; + + remain -= count; + write_pos = &buffer [remain]; + memmove( buffer, &buffer [count], remain * sizeof buffer [0] ); + + return count; +} diff --git a/source/sound/Fir_Resampler.h b/source/sound/Fir_Resampler.h index 12f6f85..a4f0c58 100644 --- a/source/sound/Fir_Resampler.h +++ b/source/sound/Fir_Resampler.h @@ -1,29 +1,29 @@ -/* Finite impulse response (FIR) resampler with adjustable FIR size */ - -/* Game_Music_Emu 0.5.2 */ -#ifndef FIR_RESAMPLER_H -#define FIR_RESAMPLER_H - -#define STEREO 2 -#define MAX_RES 32 -#define WIDTH 16 -#define WRITE_OFFSET (WIDTH * STEREO) - STEREO -#define GAIN 1.0 - -typedef signed long int sample_t; - -extern int Fir_Resampler_initialize( int new_size ); -extern void Fir_Resampler_shutdown( void ); -extern void Fir_Resampler_clear( void ); -extern double Fir_Resampler_time_ratio( double new_factor, double rolloff ); -extern double Fir_Resampler_ratio( void ); -extern int Fir_Resampler_max_write( void ); -extern sample_t* Fir_Resampler_buffer( void ); -extern int Fir_Resampler_written( void ); -extern int Fir_Resampler_avail( void ); -extern void Fir_Resampler_write( long count ); -extern int Fir_Resampler_read( sample_t* out, long count ); -extern int Fir_Resampler_input_needed( long output_count ); -extern int Fir_Resampler_skip_input( long count ); - -#endif +/* Finite impulse response (FIR) resampler with adjustable FIR size */ + +/* Game_Music_Emu 0.5.2 */ +#ifndef FIR_RESAMPLER_H +#define FIR_RESAMPLER_H + +#define STEREO 2 +#define MAX_RES 32 +#define WIDTH 16 +#define WRITE_OFFSET (WIDTH * STEREO) - STEREO +#define GAIN 1.0 + +typedef signed long int sample_t; + +extern int Fir_Resampler_initialize( int new_size ); +extern void Fir_Resampler_shutdown( void ); +extern void Fir_Resampler_clear( void ); +extern double Fir_Resampler_time_ratio( double new_factor, double rolloff ); +extern double Fir_Resampler_ratio( void ); +extern int Fir_Resampler_max_write( void ); +extern sample_t* Fir_Resampler_buffer( void ); +extern int Fir_Resampler_written( void ); +extern int Fir_Resampler_avail( void ); +extern void Fir_Resampler_write( long count ); +extern int Fir_Resampler_read( sample_t* out, long count ); +extern int Fir_Resampler_input_needed( long output_count ); +extern int Fir_Resampler_skip_input( long count ); + +#endif diff --git a/source/sound/blip.c b/source/sound/blip.c index 032ac1b..38fea15 100644 --- a/source/sound/blip.c +++ b/source/sound/blip.c @@ -1,163 +1,155 @@ -/* http://www.slack.net/~ant/ */ - -#include "blip.h" - -//#include -#include -#include -#include - -/* Copyright (C) 2003-2008 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -enum { buf_extra = 2 }; /* extra samples to save past end */ -enum { time_bits = 16 }; /* bits in fraction of fixed-point sample counts */ -enum { time_unit = 1 << time_bits }; -enum { phase_bits = 15 }; /* bits in fraction of deltas in buffer */ -enum { phase_count = 1 << phase_bits }; -enum { phase_shift = time_bits - phase_bits }; - -typedef int buf_t; /* type of element in delta buffer */ - -struct blip_buffer_t -{ - int factor; /* clocks to samples conversion factor */ - int offset; /* fractional position of clock 0 in delta buffer */ - int amp; /* current output amplitude (sum of all deltas up to now) */ - int size; /* size of delta buffer */ - buf_t buf [65536]; /* delta buffer, only size elements actually allocated */ -}; - -blip_buffer_t* blip_alloc( double clock_rate, double sample_rate, int size ) -{ - /* Allocate space for structure and delta buffer */ - blip_buffer_t* s = (blip_buffer_t*) malloc( - offsetof (blip_buffer_t, buf) + (size + buf_extra) * sizeof (buf_t) ); - if ( s != NULL ) - { - /* Calculate output:input ratio and convert to fixed-point */ - double ratio = sample_rate / clock_rate; - s->factor = (int) (ratio * time_unit + 0.5); - - s->size = size; - blip_clear( s ); - } - return s; -} - -void blip_free( blip_buffer_t* s ) -{ - free( s ); -} - -void blip_clear( blip_buffer_t* s ) -{ - s->offset = 0; - s->amp = 0; - memset( s->buf, 0, (s->size + buf_extra) * sizeof (buf_t) ); -} - -void blip_add( blip_buffer_t* s, int clocks, int delta ) -{ - /* Convert to fixed-point time in terms of output samples */ - int fixed_time = clocks * s->factor + s->offset; - - /* Extract whole and fractional parts */ - int index = fixed_time >> time_bits; /* whole */ - int phase = fixed_time >> phase_shift & (phase_count - 1); /* fraction */ - - /* Split delta between first and second samples */ - int second = delta * phase; - int first = delta * phase_count - second; - - /* Be sure index is within buffer */ - //assert( index >= 0 && index+1 < s->size + buf_extra ); - - /* Add deltas to buffer */ - s->buf [index ] += first; - s->buf [index+1] += second; -} - -int blip_clocks_needed( const blip_buffer_t* s, int samples ) -{ - int fixed_needed; - if ( samples > s->size ) - samples = s->size; - - /* Fixed-point number of samples needed in addition to those in buffer */ - fixed_needed = samples * time_unit - s->offset; - - /* If more are needed, convert to clocks and round up */ - return (fixed_needed <= 0) ? 0 : (fixed_needed - 1) / s->factor + 1; -} - -void blip_end_frame( blip_buffer_t* s, int clocks ) -{ - s->offset += clocks * s->factor; - - /* Ensure time wasn't past end of buffer */ - //assert( blip_samples_avail( s ) <= s->size ); -} - -int blip_samples_avail( const blip_buffer_t* s ) -{ - return s->offset >> time_bits; -} - -/* Removes n samples from buffer */ -static void remove_samples( blip_buffer_t* s, int n ) -{ - int remain = blip_samples_avail( s ) + buf_extra - n; - - s->offset -= n * time_unit; - //assert( s->offset >= 0 ); - - /* Copy remaining samples to beginning of buffer and clear the rest */ - memmove( s->buf, &s->buf [n], remain * sizeof (buf_t) ); - memset( &s->buf [remain], 0, n * sizeof (buf_t) ); -} - -int blip_read_samples( blip_buffer_t* s, short out [], int count, int stereo ) -{ - /* can't read more than available */ - int avail = blip_samples_avail( s ); - if ( count > avail ) - count = avail; - - if ( count ) - { - /* Sum deltas and write out */ - int i; - for ( i = 0; i < count; ++i ) - { - int sample; - - /* Apply slight high-pass filter */ - s->amp -= s->amp >> 9; - - /* Add next delta */ - s->amp += s->buf [i]; - - /* Calculate output sample */ - sample = s->amp >> phase_bits; - - /* Keep within 16-bit sample range */ - if ( sample < -32768 ) sample = -32768; - if ( sample > +32767 ) sample = +32767; - - out [i << stereo] = sample; - } - - remove_samples( s, count ); - } - - return count; -} +/* http://www.slack.net/~ant/ */ + +#include "blip.h" + +#include +#include +#include + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +enum { buf_extra = 2 }; /* extra samples to save past end */ +enum { time_bits = 16 }; /* bits in fraction of fixed-point sample counts */ +enum { time_unit = 1 << time_bits }; +enum { phase_bits = 15 }; /* bits in fraction of deltas in buffer */ +enum { phase_count = 1 << phase_bits }; +enum { phase_shift = time_bits - phase_bits }; + +typedef int buf_t; /* type of element in delta buffer */ + +struct blip_buffer_t +{ + int factor; /* clocks to samples conversion factor */ + int offset; /* fractional position of clock 0 in delta buffer */ + int amp; /* current output amplitude (sum of all deltas up to now) */ + int size; /* size of delta buffer */ + buf_t buf [65536]; /* delta buffer, only size elements actually allocated */ +}; + +blip_buffer_t* blip_alloc( double clock_rate, double sample_rate, int size ) +{ + /* Allocate space for structure and delta buffer */ + blip_buffer_t* s = (blip_buffer_t*) malloc( + offsetof (blip_buffer_t, buf) + (size + buf_extra) * sizeof (buf_t) ); + if ( s != NULL ) + { + /* Calculate output:input ratio and convert to fixed-point */ + double ratio = sample_rate / clock_rate; + s->factor = (int) (ratio * time_unit + 0.5); + + s->size = size; + blip_clear( s ); + } + return s; +} + +void blip_free( blip_buffer_t* s ) +{ + free( s ); +} + +void blip_clear( blip_buffer_t* s ) +{ + s->offset = 0; + s->amp = 0; + memset( s->buf, 0, (s->size + buf_extra) * sizeof (buf_t) ); +} + +void blip_add( blip_buffer_t* s, int clocks, int delta ) +{ + /* Convert to fixed-point time in terms of output samples */ + int fixed_time = clocks * s->factor + s->offset; + + /* Extract whole and fractional parts */ + int index = fixed_time >> time_bits; /* whole */ + int phase = fixed_time >> phase_shift & (phase_count - 1); /* fraction */ + + /* Split delta between first and second samples */ + int second = delta * phase; + int first = delta * phase_count - second; + + /* Add deltas to buffer */ + s->buf [index ] += first; + s->buf [index+1] += second; +} + +int blip_clocks_needed( const blip_buffer_t* s, int samples ) +{ + int fixed_needed; + if ( samples > s->size ) + samples = s->size; + + /* Fixed-point number of samples needed in addition to those in buffer */ + fixed_needed = samples * time_unit - s->offset; + + /* If more are needed, convert to clocks and round up */ + return (fixed_needed <= 0) ? 0 : (fixed_needed - 1) / s->factor + 1; +} + +void blip_end_frame( blip_buffer_t* s, int clocks ) +{ + s->offset += clocks * s->factor; +} + +int blip_samples_avail( const blip_buffer_t* s ) +{ + return s->offset >> time_bits; +} + +/* Removes n samples from buffer */ +static void remove_samples( blip_buffer_t* s, int n ) +{ + int remain = blip_samples_avail( s ) + buf_extra - n; + + s->offset -= n * time_unit; + + /* Copy remaining samples to beginning of buffer and clear the rest */ + memmove( s->buf, &s->buf [n], remain * sizeof (buf_t) ); + memset( &s->buf [remain], 0, n * sizeof (buf_t) ); +} + +int blip_read_samples( blip_buffer_t* s, short out [], int count, int stereo ) +{ + /* can't read more than available */ + int avail = blip_samples_avail( s ); + if ( count > avail ) + count = avail; + + if ( count ) + { + /* Sum deltas and write out */ + int i; + for ( i = 0; i < count; ++i ) + { + int sample; + + /* Apply slight high-pass filter */ + s->amp -= s->amp >> 9; + + /* Add next delta */ + s->amp += s->buf [i]; + + /* Calculate output sample */ + sample = s->amp >> phase_bits; + + /* Keep within 16-bit sample range */ + if ( sample < -32768 ) sample = -32768; + if ( sample > +32767 ) sample = +32767; + + out [i << stereo] = sample; + } + + remove_samples( s, count ); + } + + return count; +} diff --git a/source/sound/blip.h b/source/sound/blip.h index 865733c..98daa55 100644 --- a/source/sound/blip.h +++ b/source/sound/blip.h @@ -1,51 +1,51 @@ -/* Fast sound synthesis buffer for use in real-time emulators of electronic -sound generator chips like those in early video game consoles. Uses linear -interpolation. Higher-quality versions are available that use sinc-based -band-limited synthesis. */ - -#ifndef BLIP_H -#define BLIP_H - -#ifdef __cplusplus - extern "C" { -#endif - -/* Creates a new blip_buffer with specified input clock rate, output -sample rate, and size (in samples), or returns NULL if out of memory. */ -typedef struct blip_buffer_t blip_buffer_t; -blip_buffer_t* blip_alloc( double clock_rate, double sample_rate, int size ); - -/* Frees memory used by a blip_buffer. No effect if NULL is passed. */ -void blip_free( blip_buffer_t* ); - -/* Removes all samples and clears buffer. */ -void blip_clear( blip_buffer_t* ); - -/* Adds an amplitude transition of delta at specified time in source clocks. -Delta can be negative. */ -void blip_add( blip_buffer_t*, int time, int delta ); - -/* Number of additional clocks needed until n samples will be available. -If buffer cannot even hold n samples, returns number of clocks until buffer -becomes full. */ -int blip_clocks_needed( const blip_buffer_t*, int samples_needed ); - -/* Ends current time frame of specified duration and make its samples available -(along with any still-unread samples) for reading with read_samples(), then -begins a new time frame at the end of the current frame. */ -void blip_end_frame( blip_buffer_t*, int duration ); - -/* Number of samples available for reading with read(). */ -int blip_samples_avail( const blip_buffer_t* ); - -/* Reads at most n samples out of buffer into out, removing them from from -the buffer. Returns number of samples actually read and removed. If stereo is -true, increments 'out' one extra time after writing each sample, to allow -easy interleving of two channels into a stereo output buffer. */ -int blip_read_samples( blip_buffer_t*, short out [], int n, int stereo ); - -#ifdef __cplusplus - } -#endif - -#endif +/* Fast sound synthesis buffer for use in real-time emulators of electronic +sound generator chips like those in early video game consoles. Uses linear +interpolation. Higher-quality versions are available that use sinc-based +band-limited synthesis. */ + +#ifndef BLIP_H +#define BLIP_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Creates a new blip_buffer with specified input clock rate, output +sample rate, and size (in samples), or returns NULL if out of memory. */ +typedef struct blip_buffer_t blip_buffer_t; +blip_buffer_t* blip_alloc( double clock_rate, double sample_rate, int size ); + +/* Frees memory used by a blip_buffer. No effect if NULL is passed. */ +void blip_free( blip_buffer_t* ); + +/* Removes all samples and clears buffer. */ +void blip_clear( blip_buffer_t* ); + +/* Adds an amplitude transition of delta at specified time in source clocks. +Delta can be negative. */ +void blip_add( blip_buffer_t*, int time, int delta ); + +/* Number of additional clocks needed until n samples will be available. +If buffer cannot even hold n samples, returns number of clocks until buffer +becomes full. */ +int blip_clocks_needed( const blip_buffer_t*, int samples_needed ); + +/* Ends current time frame of specified duration and make its samples available +(along with any still-unread samples) for reading with read_samples(), then +begins a new time frame at the end of the current frame. */ +void blip_end_frame( blip_buffer_t*, int duration ); + +/* Number of samples available for reading with read(). */ +int blip_samples_avail( const blip_buffer_t* ); + +/* Reads at most n samples out of buffer into out, removing them from from +the buffer. Returns number of samples actually read and removed. If stereo is +true, increments 'out' one extra time after writing each sample, to allow +easy interleving of two channels into a stereo output buffer. */ +int blip_read_samples( blip_buffer_t*, short out [], int n, int stereo ); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/source/sound/eq.c b/source/sound/eq.c index 58835f1..327cb63 100644 --- a/source/sound/eq.c +++ b/source/sound/eq.c @@ -1,131 +1,131 @@ -//---------------------------------------------------------------------------- -// -// 3 Band EQ :) -// -// EQ.C - Main Source file for 3 band EQ -// -// (c) Neil C / Etanza Systems / 2K6 -// -// Shouts / Loves / Moans = etanza at lycos dot co dot uk -// -// This work is hereby placed in the public domain for all purposes, including -// use in commercial applications. -// -// The author assumes NO RESPONSIBILITY for any problems caused by the use of -// this software. -// -//---------------------------------------------------------------------------- - -// NOTES : -// -// - Original filter code by Paul Kellet (musicdsp.pdf) -// -// - Uses 4 first order filters in series, should give 24dB per octave -// -// - Now with P4 Denormal fix :) - - -//---------------------------------------------------------------------------- - -// ---------- -//| Includes | -// ---------- -#include -#include -#include -#include "eq.h" - - -// ----------- -//| Constants | -// ----------- - -static double vsa = (1.0 / 4294967295.0); // Very small amount (Denormal Fix) - - -// --------------- -//| Initialise EQ | -// --------------- - -// Recommended frequencies are ... -// -// lowfreq = 880 Hz -// highfreq = 5000 Hz -// -// Set mixfreq to whatever rate your system is using (eg 48Khz) - -void init_3band_state(EQSTATE * es, int lowfreq, int highfreq, int mixfreq) -{ - // Clear state - - memset(es, 0, sizeof(EQSTATE)); - - // Set Low/Mid/High gains to unity - - es->lg = 1.0; - es->mg = 1.0; - es->hg = 1.0; - - // Calculate filter cutoff frequencies - - es->lf = 2 * sin(M_PI * ((double) lowfreq / (double) mixfreq)); - es->hf = 2 * sin(M_PI * ((double) highfreq / (double) mixfreq)); -} - - -// --------------- -//| EQ one sample | -// --------------- - -// - sample can be any range you like :) -// -// Note that the output will depend on the gain settings for each band -// (especially the bass) so may require clipping before output, but you -// knew that anyway :) - -double do_3band(EQSTATE * es, int sample) -{ - // Locals - - double l, m, h; // Low / Mid / High - Sample Values - - // Filter #1 (lowpass) - - es->f1p0 += (es->lf * ((double) sample - es->f1p0)) + vsa; - es->f1p1 += (es->lf * (es->f1p0 - es->f1p1)); - es->f1p2 += (es->lf * (es->f1p1 - es->f1p2)); - es->f1p3 += (es->lf * (es->f1p2 - es->f1p3)); - - l = es->f1p3; - - // Filter #2 (highpass) - - es->f2p0 += (es->hf * ((double) sample - es->f2p0)) + vsa; - es->f2p1 += (es->hf * (es->f2p0 - es->f2p1)); - es->f2p2 += (es->hf * (es->f2p1 - es->f2p2)); - es->f2p3 += (es->hf * (es->f2p2 - es->f2p3)); - - h = es->sdm3 - es->f2p3; - - // Calculate midrange (signal - (low + high)) - - //m = es->sdm3 - (h + l); - // fix from http://www.musicdsp.org/showArchiveComment.php?ArchiveID=236 ? - m = sample - (h + l); - - // Scale, Combine and store - - l *= es->lg; - m *= es->mg; - h *= es->hg; - - // Shuffle history buffer - - es->sdm3 = es->sdm2; - es->sdm2 = es->sdm1; - es->sdm1 = sample; - - // Return result - - return (int) (l + m + h); -} +//---------------------------------------------------------------------------- +// +// 3 Band EQ :) +// +// EQ.C - Main Source file for 3 band EQ +// +// (c) Neil C / Etanza Systems / 2K6 +// +// Shouts / Loves / Moans = etanza at lycos dot co dot uk +// +// This work is hereby placed in the public domain for all purposes, including +// use in commercial applications. +// +// The author assumes NO RESPONSIBILITY for any problems caused by the use of +// this software. +// +//---------------------------------------------------------------------------- + +// NOTES : +// +// - Original filter code by Paul Kellet (musicdsp.pdf) +// +// - Uses 4 first order filters in series, should give 24dB per octave +// +// - Now with P4 Denormal fix :) + + +//---------------------------------------------------------------------------- + +// ---------- +//| Includes | +// ---------- +#include +#include +#include +#include "eq.h" + + +// ----------- +//| Constants | +// ----------- + +static double vsa = (1.0 / 4294967295.0); // Very small amount (Denormal Fix) + + +// --------------- +//| Initialise EQ | +// --------------- + +// Recommended frequencies are ... +// +// lowfreq = 880 Hz +// highfreq = 5000 Hz +// +// Set mixfreq to whatever rate your system is using (eg 48Khz) + +void init_3band_state(EQSTATE * es, int lowfreq, int highfreq, int mixfreq) +{ + // Clear state + + memset(es, 0, sizeof(EQSTATE)); + + // Set Low/Mid/High gains to unity + + es->lg = 1.0; + es->mg = 1.0; + es->hg = 1.0; + + // Calculate filter cutoff frequencies + + es->lf = 2 * sin(M_PI * ((double) lowfreq / (double) mixfreq)); + es->hf = 2 * sin(M_PI * ((double) highfreq / (double) mixfreq)); +} + + +// --------------- +//| EQ one sample | +// --------------- + +// - sample can be any range you like :) +// +// Note that the output will depend on the gain settings for each band +// (especially the bass) so may require clipping before output, but you +// knew that anyway :) + +double do_3band(EQSTATE * es, int sample) +{ + // Locals + + double l, m, h; // Low / Mid / High - Sample Values + + // Filter #1 (lowpass) + + es->f1p0 += (es->lf * ((double) sample - es->f1p0)) + vsa; + es->f1p1 += (es->lf * (es->f1p0 - es->f1p1)); + es->f1p2 += (es->lf * (es->f1p1 - es->f1p2)); + es->f1p3 += (es->lf * (es->f1p2 - es->f1p3)); + + l = es->f1p3; + + // Filter #2 (highpass) + + es->f2p0 += (es->hf * ((double) sample - es->f2p0)) + vsa; + es->f2p1 += (es->hf * (es->f2p0 - es->f2p1)); + es->f2p2 += (es->hf * (es->f2p1 - es->f2p2)); + es->f2p3 += (es->hf * (es->f2p2 - es->f2p3)); + + h = es->sdm3 - es->f2p3; + + // Calculate midrange (signal - (low + high)) + + //m = es->sdm3 - (h + l); + // fix from http://www.musicdsp.org/showArchiveComment.php?ArchiveID=236 ? + m = sample - (h + l); + + // Scale, Combine and store + + l *= es->lg; + m *= es->mg; + h *= es->hg; + + // Shuffle history buffer + + es->sdm3 = es->sdm2; + es->sdm2 = es->sdm1; + es->sdm1 = sample; + + // Return result + + return (int) (l + m + h); +} diff --git a/source/sound/eq.h b/source/sound/eq.h index 08d738e..4eaef4d 100644 --- a/source/sound/eq.h +++ b/source/sound/eq.h @@ -1,67 +1,67 @@ -//--------------------------------------------------------------------------- -// -// 3 Band EQ :) -// -// EQ.H - Header file for 3 band EQ -// -// (c) Neil C / Etanza Systems / 2K6 -// -// Shouts / Loves / Moans = etanza at lycos dot co dot uk -// -// This work is hereby placed in the public domain for all purposes, including -// use in commercial applications. -// -// The author assumes NO RESPONSIBILITY for any problems caused by the use of -// this software. -// -//---------------------------------------------------------------------------- - -#ifndef __EQ3BAND__ -#define __EQ3BAND__ - -// ------------ -//| Structures | -// ------------ - -typedef struct { - // Filter #1 (Low band) - - double lf; // Frequency - double f1p0; // Poles ... - double f1p1; - double f1p2; - double f1p3; - - // Filter #2 (High band) - - double hf; // Frequency - double f2p0; // Poles ... - double f2p1; - double f2p2; - double f2p3; - - // Sample history buffer - - double sdm1; // Sample data minus 1 - double sdm2; // 2 - double sdm3; // 3 - - // Gain Controls - - double lg; // low gain - double mg; // mid gain - double hg; // high gain - -} EQSTATE; - - -// --------- -//| Exports | -// --------- - -extern void init_3band_state(EQSTATE * es, int lowfreq, int highfreq, - int mixfreq); -extern double do_3band(EQSTATE * es, int sample); - - -#endif // #ifndef __EQ3BAND__ +//--------------------------------------------------------------------------- +// +// 3 Band EQ :) +// +// EQ.H - Header file for 3 band EQ +// +// (c) Neil C / Etanza Systems / 2K6 +// +// Shouts / Loves / Moans = etanza at lycos dot co dot uk +// +// This work is hereby placed in the public domain for all purposes, including +// use in commercial applications. +// +// The author assumes NO RESPONSIBILITY for any problems caused by the use of +// this software. +// +//---------------------------------------------------------------------------- + +#ifndef __EQ3BAND__ +#define __EQ3BAND__ + +// ------------ +//| Structures | +// ------------ + +typedef struct { + // Filter #1 (Low band) + + double lf; // Frequency + double f1p0; // Poles ... + double f1p1; + double f1p2; + double f1p3; + + // Filter #2 (High band) + + double hf; // Frequency + double f2p0; // Poles ... + double f2p1; + double f2p2; + double f2p3; + + // Sample history buffer + + double sdm1; // Sample data minus 1 + double sdm2; // 2 + double sdm3; // 3 + + // Gain Controls + + double lg; // low gain + double mg; // mid gain + double hg; // high gain + +} EQSTATE; + + +// --------- +//| Exports | +// --------- + +extern void init_3band_state(EQSTATE * es, int lowfreq, int highfreq, + int mixfreq); +extern double do_3band(EQSTATE * es, int sample); + + +#endif // #ifndef __EQ3BAND__ diff --git a/source/sound/sn76489.c b/source/sound/sn76489.c index 383a33a..1176062 100644 --- a/source/sound/sn76489.c +++ b/source/sound/sn76489.c @@ -1,4 +1,4 @@ -/* +/* SN76489 emulation by Maxim in 2001 and 2002 converted from my original Delphi implementation @@ -47,7 +47,7 @@ /* SN76489 clone in Sega's VDP chips (315-5124, 315-5246, 315-5313, Game Gear) */ #define FB_SEGAVDP 0x0009 #define SRW_SEGAVDP 16 - + typedef struct { /* Configuration */ @@ -81,19 +81,19 @@ static const int PSGVolumeValues[16] = { static struct blip_buffer_t* blip; /* delta resampler */ static SN76489_Context SN76489; - + void SN76489_Init(double PSGClockValue, int SamplingRate) -{ +{ SN76489_Shutdown(); /* SamplingRate*16 instead of PSGClockValue/16 since division would lose some precision. blip_alloc doesn't care about the absolute sampling rate, just the ratio to clock rate. */ blip = blip_alloc(PSGClockValue, SamplingRate * 16.0, SamplingRate / 4); -} - +} + void SN76489_Reset() -{ +{ SN76489_Context *chip = &SN76489; int i; @@ -102,35 +102,35 @@ void SN76489_Reset() /* Initialise PSG state */ chip->Registers[2*i] = 1; /* tone freq=1 */ chip->Registers[2*i+1] = 0xf; /* vol=off */ - + /* Set counters to 0 */ chip->ToneFreqVals[i] = 0; - + /* Set flip-flops to 1 */ chip->ToneFreqPos[i] = 1; - + /* Clear channels output */ chip->Channels[i] = 0; - + /* Clear current amplitudes in delta buffer */ chip->chan_amp[i] = 0; } - + chip->LatchedRegister=0; - + /* Initialise noise generator */ chip->NoiseShiftRegister=NoiseInitialState; chip->NoiseFreq = 0x10; - + /* Clear Blip delta buffer */ if (blip) blip_clear(blip); -} +} void SN76489_Shutdown(void) -{ +{ if (blip) blip_free(blip); blip = NULL; -} +} void SN76489_BoostNoise(int boost) { @@ -139,7 +139,7 @@ void SN76489_BoostNoise(int boost) } void SN76489_SetContext(uint8 *data) -{ +{ memcpy(&SN76489, data, sizeof(SN76489_Context)); } @@ -335,4 +335,4 @@ void SN76489_Update(INT16 *buffer, int length) /* Read samples into output buffer */ blip_end_frame(blip, clock_length); blip_read_samples(blip, buffer, length, 0); -} +} diff --git a/source/sound/sn76489.h b/source/sound/sn76489.h index fec46d9..0c981bd 100644 --- a/source/sound/sn76489.h +++ b/source/sound/sn76489.h @@ -1,21 +1,13 @@ /* SN76489 emulation by Maxim in 2001 and 2002 - converted from my original Delphi implementation - - I'm a C newbie so I'm sure there are loads of stupid things - in here which I'll come back to some day and redo - - Includes: - - Super-high quality tone channel "oversampling" by calculating fractional positions on transitions - - Noise output pattern reverse engineered from actual SMS output - - Volume levels taken from actual SMS output */ #ifndef _SN76489_H_ #define _SN76489_H_ /* Function prototypes */ + extern void SN76489_Init(double PSGClockValue, int SamplingRate); extern void SN76489_Reset(void); extern void SN76489_Shutdown(void); @@ -27,5 +19,5 @@ extern void SN76489_Write(int data); extern void SN76489_Update(INT16 *buffer, int length); extern void SN76489_BoostNoise(int boost); -#endif /* _SN76489_H_ */ - +#endif /* _SN76489_H_ */ + diff --git a/source/sound/sound.c b/source/sound/sound.c index f60131c..a26770e 100644 --- a/source/sound/sound.c +++ b/source/sound/sound.c @@ -172,7 +172,8 @@ int sound_context_load(uint8 *state, char *version) int bufferptr = YM2612LoadContext(state, version); load_param(SN76489_GetContextPtr(),SN76489_GetContextSize()); - if (version[15] > 0x30) + /* extended state (from 1.4.1 and above) */ + if ((version[11] > 0x31) || (version[13] > 0x34) || (version[15] > 0x30)) { load_param(&fm_cycles_count,sizeof(fm_cycles_count)); load_param(&psg_cycles_count,sizeof(psg_cycles_count)); diff --git a/source/sound/ym2612.c b/source/sound/ym2612.c index 95b2725..30496a1 100644 --- a/source/sound/ym2612.c +++ b/source/sound/ym2612.c @@ -1,28 +1,28 @@ /* ** -** software implementation of Yamaha YM2612 FM sound generator (taken from M.A.M.E fm.c) +** software implementation of Yamaha FM sound generator (YM2612/YM3438) +** +** Original code (MAME fm.c) ** ** Copyright (C) 2001, 2002, 2003 Jarek Burczynski (bujar at mame dot net) ** Copyright (C) 1998 Tatsuyuki Satoh , MultiArcadeMachineEmulator development ** ** Version 1.4 (final beta) ** -*/ - -/* -** History: +** Additional code & fixes by Eke-Eke for Genesis Plus GX ** -** 2006~2009 Eke-Eke (Genesis Plus GX): -** Huge thanks to Nemesis, lot of those fixes came from his tests on Sega Genesis hardware +** Huge thanks to Nemesis, most of those fixes came from his tests on Sega Genesis hardware ** More informations at http://gendev.spritesmind.net/forum/viewtopic.php?t=386 ** ** TODO: -** -** - core documentation -** - BUSY flag support -** +** - better documentation +** - BUSY flag emulation +*/ + +/* ** CHANGELOG: ** +** 2006~2011 Eke-Eke (Genesis Plus GX): ** - removed unused multichip support ** - added YM2612 Context external access functions ** - fixed LFO implementation: @@ -40,6 +40,7 @@ ** - implemented accurate CSM mode emulation ** - implemented accurate SSG-EG emulation (Asterix, Beavis&Butthead, Bubba'n Stix & many other games) ** - implemented accurate address/data ports behavior +** - added preliminar support for DAC precision ** ** ** 03-08-2003 Jarek Burczynski: @@ -2213,8 +2214,8 @@ int YM2612LoadContext(unsigned char *state, char *version) /* restore YM2612 context */ YM2612Restore(state); - /* extended state */ - if (version[15] > 0x31) + /* extended state (from 1.5.0 and above) */ + if ((version[11] > 0x31) || (version[13] > 0x34)) { int c,s; uint8 index; diff --git a/source/sound/ym2612.h b/source/sound/ym2612.h index c544539..976c2c0 100644 --- a/source/sound/ym2612.h +++ b/source/sound/ym2612.h @@ -1,12 +1,15 @@ /* ** -** File: ym2612.h -- header for ym2612.c -** software implementation of Yamaha FM sound generator +** software implementation of Yamaha FM sound generator (YM2612/YM3438) +** +** Original code (MAME fm.c) ** ** Copyright (C) 2001, 2002, 2003 Jarek Burczynski (bujar at mame dot net) ** Copyright (C) 1998 Tatsuyuki Satoh , MultiArcadeMachineEmulator development ** -** Version 1.4 (final beta) +** Version 1.4 (final beta) +** +** Additional code & fixes by Eke-Eke for Genesis Plus GX ** */ @@ -18,7 +21,6 @@ #define INLINE static __inline__ #endif - extern int YM2612Init(double clock, int rate); extern int YM2612ResetChip(void); extern void YM2612Update(long int *buffer, int length); @@ -30,5 +32,4 @@ extern void YM2612Restore(unsigned char *buffer); extern int YM2612LoadContext(unsigned char *state, char *version); extern int YM2612SaveContext(unsigned char *state); - #endif /* _YM2612_ */ diff --git a/source/state.c b/source/state.c index 60debf6..45fd1b8 100644 --- a/source/state.c +++ b/source/state.c @@ -2,7 +2,7 @@ * Genesis Plus * Savestate support * - * Copyright (C) 2007, 2008, 2009 Eke-Eke (GCN/Wii port) + * Copyright (C) 2007-2011 Eke-Eke (GCN/Wii port) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,11 +37,18 @@ int state_load(unsigned char *buffer) outbytes = STATE_SIZE; uncompress ((Bytef *)state, &outbytes, (Bytef *)(buffer + 4), inbytes); - /* version check */ + /* signature check (GENPLUS-GX x.x.x) */ char version[17]; load_param(version,16); version[16] = 0; - if (strncmp(version,STATE_VERSION,15)) + if (strncmp(version,STATE_VERSION,11)) + { + free(state); + return -1; + } + + /* version check (1.4.0 and above) */ + if ((version[11] < 0x31) || ((version[11] == 0x31) && (version[13] < 0x34))) { free(state); return -1; @@ -51,26 +58,34 @@ int state_load(unsigned char *buffer) system_reset(); // GENESIS - load_param(work_ram, sizeof(work_ram)); - load_param(zram, sizeof(zram)); - load_param(&zstate, sizeof(zstate)); - load_param(&zbank, sizeof(zbank)); - if (zstate == 3) + if (system_hw == SYSTEM_PBC) { - m68k_memory_map[0xa0].read8 = z80_read_byte; - m68k_memory_map[0xa0].read16 = z80_read_word; - m68k_memory_map[0xa0].write8 = z80_write_byte; - m68k_memory_map[0xa0].write16 = z80_write_word; + load_param(work_ram, 0x2000); } else { - m68k_memory_map[0xa0].read8 = m68k_read_bus_8; - m68k_memory_map[0xa0].read16 = m68k_read_bus_16; - m68k_memory_map[0xa0].write8 = m68k_unused_8_w; - m68k_memory_map[0xa0].write16 = m68k_unused_16_w; + load_param(work_ram, sizeof(work_ram)); + load_param(zram, sizeof(zram)); + load_param(&zstate, sizeof(zstate)); + load_param(&zbank, sizeof(zbank)); + if (zstate == 3) + { + m68k_memory_map[0xa0].read8 = z80_read_byte; + m68k_memory_map[0xa0].read16 = z80_read_word; + m68k_memory_map[0xa0].write8 = z80_write_byte; + m68k_memory_map[0xa0].write16 = z80_write_word; + } + else + { + m68k_memory_map[0xa0].read8 = m68k_read_bus_8; + m68k_memory_map[0xa0].read16 = m68k_read_bus_16; + m68k_memory_map[0xa0].write8 = m68k_unused_8_w; + m68k_memory_map[0xa0].write16 = m68k_unused_16_w; + } } - if (version[15] > 0x30) + /* 1.4.1 and above */ + if ((version[11] > 0x31) || (version[13] > 0x34) || (version[15] > 0x30)) { /* extended state */ load_param(&mcycles_68k, sizeof(mcycles_68k)); @@ -78,8 +93,15 @@ int state_load(unsigned char *buffer) } // IO - load_param(io_reg, sizeof(io_reg)); - io_reg[0] = region_code | 0x20 | (config.tmss & 1); + if (system_hw == SYSTEM_PBC) + { + load_param(&io_reg[0], 1); + } + else + { + load_param(io_reg, sizeof(io_reg)); + io_reg[0] = region_code | 0x20 | (config.tmss & 1); + } // VDP bufferptr += vdp_context_load(&state[bufferptr], version); @@ -88,34 +110,44 @@ int state_load(unsigned char *buffer) bufferptr += sound_context_load(&state[bufferptr], version); // 68000 - uint16 tmp16; - uint32 tmp32; - load_param(&tmp32, 4); m68k_set_reg(M68K_REG_D0, tmp32); - load_param(&tmp32, 4); m68k_set_reg(M68K_REG_D1, tmp32); - load_param(&tmp32, 4); m68k_set_reg(M68K_REG_D2, tmp32); - load_param(&tmp32, 4); m68k_set_reg(M68K_REG_D3, tmp32); - load_param(&tmp32, 4); m68k_set_reg(M68K_REG_D4, tmp32); - load_param(&tmp32, 4); m68k_set_reg(M68K_REG_D5, tmp32); - load_param(&tmp32, 4); m68k_set_reg(M68K_REG_D6, tmp32); - load_param(&tmp32, 4); m68k_set_reg(M68K_REG_D7, tmp32); - load_param(&tmp32, 4); m68k_set_reg(M68K_REG_A0, tmp32); - load_param(&tmp32, 4); m68k_set_reg(M68K_REG_A1, tmp32); - load_param(&tmp32, 4); m68k_set_reg(M68K_REG_A2, tmp32); - load_param(&tmp32, 4); m68k_set_reg(M68K_REG_A3, tmp32); - load_param(&tmp32, 4); m68k_set_reg(M68K_REG_A4, tmp32); - load_param(&tmp32, 4); m68k_set_reg(M68K_REG_A5, tmp32); - load_param(&tmp32, 4); m68k_set_reg(M68K_REG_A6, tmp32); - load_param(&tmp32, 4); m68k_set_reg(M68K_REG_A7, tmp32); - load_param(&tmp32, 4); m68k_set_reg(M68K_REG_PC, tmp32); - load_param(&tmp16, 2); m68k_set_reg(M68K_REG_SR, tmp16); - load_param(&tmp32, 4); m68k_set_reg(M68K_REG_USP,tmp32); + if (system_hw != SYSTEM_PBC) + { + uint16 tmp16; + uint32 tmp32; + load_param(&tmp32, 4); m68k_set_reg(M68K_REG_D0, tmp32); + load_param(&tmp32, 4); m68k_set_reg(M68K_REG_D1, tmp32); + load_param(&tmp32, 4); m68k_set_reg(M68K_REG_D2, tmp32); + load_param(&tmp32, 4); m68k_set_reg(M68K_REG_D3, tmp32); + load_param(&tmp32, 4); m68k_set_reg(M68K_REG_D4, tmp32); + load_param(&tmp32, 4); m68k_set_reg(M68K_REG_D5, tmp32); + load_param(&tmp32, 4); m68k_set_reg(M68K_REG_D6, tmp32); + load_param(&tmp32, 4); m68k_set_reg(M68K_REG_D7, tmp32); + load_param(&tmp32, 4); m68k_set_reg(M68K_REG_A0, tmp32); + load_param(&tmp32, 4); m68k_set_reg(M68K_REG_A1, tmp32); + load_param(&tmp32, 4); m68k_set_reg(M68K_REG_A2, tmp32); + load_param(&tmp32, 4); m68k_set_reg(M68K_REG_A3, tmp32); + load_param(&tmp32, 4); m68k_set_reg(M68K_REG_A4, tmp32); + load_param(&tmp32, 4); m68k_set_reg(M68K_REG_A5, tmp32); + load_param(&tmp32, 4); m68k_set_reg(M68K_REG_A6, tmp32); + load_param(&tmp32, 4); m68k_set_reg(M68K_REG_A7, tmp32); + load_param(&tmp32, 4); m68k_set_reg(M68K_REG_PC, tmp32); + load_param(&tmp16, 2); m68k_set_reg(M68K_REG_SR, tmp16); + load_param(&tmp32, 4); m68k_set_reg(M68K_REG_USP,tmp32); + } // Z80 load_param(&Z80, sizeof(Z80_Regs)); Z80.irq_callback = z80_irq_callback; // Cartridge HW - bufferptr += cart_hw_context_load(&state[bufferptr], version); + if (system_hw == SYSTEM_PBC) + { + bufferptr += sms_cart_context_load(&state[bufferptr], version); + } + else + { + bufferptr += md_cart_context_load(&state[bufferptr], version); + } free(state); return 1; @@ -136,15 +168,29 @@ int state_save(unsigned char *buffer) save_param(version, 16); // GENESIS - save_param(work_ram, sizeof(work_ram)); - save_param(zram, sizeof(zram)); - save_param(&zstate, sizeof(zstate)); - save_param(&zbank, sizeof(zbank)); + if (system_hw == SYSTEM_PBC) + { + save_param(work_ram, 0x2000); + } + else + { + save_param(work_ram, sizeof(work_ram)); + save_param(zram, sizeof(zram)); + save_param(&zstate, sizeof(zstate)); + save_param(&zbank, sizeof(zbank)); + } save_param(&mcycles_68k, sizeof(mcycles_68k)); save_param(&mcycles_z80, sizeof(mcycles_z80)); // IO - save_param(io_reg, sizeof(io_reg)); + if (system_hw == SYSTEM_PBC) + { + save_param(&io_reg[0], 1); + } + else + { + save_param(io_reg, sizeof(io_reg)); + } // VDP bufferptr += vdp_context_save(&state[bufferptr]); @@ -153,33 +199,43 @@ int state_save(unsigned char *buffer) bufferptr += sound_context_save(&state[bufferptr]); // 68000 - uint16 tmp16; - uint32 tmp32; - tmp32 = m68k_get_reg(NULL, M68K_REG_D0); save_param(&tmp32, 4); - tmp32 = m68k_get_reg(NULL, M68K_REG_D1); save_param(&tmp32, 4); - tmp32 = m68k_get_reg(NULL, M68K_REG_D2); save_param(&tmp32, 4); - tmp32 = m68k_get_reg(NULL, M68K_REG_D3); save_param(&tmp32, 4); - tmp32 = m68k_get_reg(NULL, M68K_REG_D4); save_param(&tmp32, 4); - tmp32 = m68k_get_reg(NULL, M68K_REG_D5); save_param(&tmp32, 4); - tmp32 = m68k_get_reg(NULL, M68K_REG_D6); save_param(&tmp32, 4); - tmp32 = m68k_get_reg(NULL, M68K_REG_D7); save_param(&tmp32, 4); - tmp32 = m68k_get_reg(NULL, M68K_REG_A0); save_param(&tmp32, 4); - tmp32 = m68k_get_reg(NULL, M68K_REG_A1); save_param(&tmp32, 4); - tmp32 = m68k_get_reg(NULL, M68K_REG_A2); save_param(&tmp32, 4); - tmp32 = m68k_get_reg(NULL, M68K_REG_A3); save_param(&tmp32, 4); - tmp32 = m68k_get_reg(NULL, M68K_REG_A4); save_param(&tmp32, 4); - tmp32 = m68k_get_reg(NULL, M68K_REG_A5); save_param(&tmp32, 4); - tmp32 = m68k_get_reg(NULL, M68K_REG_A6); save_param(&tmp32, 4); - tmp32 = m68k_get_reg(NULL, M68K_REG_A7); save_param(&tmp32, 4); - tmp32 = m68k_get_reg(NULL, M68K_REG_PC); save_param(&tmp32, 4); - tmp16 = m68k_get_reg(NULL, M68K_REG_SR); save_param(&tmp16, 2); - tmp32 = m68k_get_reg(NULL, M68K_REG_USP); save_param(&tmp32, 4); + if (system_hw != SYSTEM_PBC) + { + uint16 tmp16; + uint32 tmp32; + tmp32 = m68k_get_reg(NULL, M68K_REG_D0); save_param(&tmp32, 4); + tmp32 = m68k_get_reg(NULL, M68K_REG_D1); save_param(&tmp32, 4); + tmp32 = m68k_get_reg(NULL, M68K_REG_D2); save_param(&tmp32, 4); + tmp32 = m68k_get_reg(NULL, M68K_REG_D3); save_param(&tmp32, 4); + tmp32 = m68k_get_reg(NULL, M68K_REG_D4); save_param(&tmp32, 4); + tmp32 = m68k_get_reg(NULL, M68K_REG_D5); save_param(&tmp32, 4); + tmp32 = m68k_get_reg(NULL, M68K_REG_D6); save_param(&tmp32, 4); + tmp32 = m68k_get_reg(NULL, M68K_REG_D7); save_param(&tmp32, 4); + tmp32 = m68k_get_reg(NULL, M68K_REG_A0); save_param(&tmp32, 4); + tmp32 = m68k_get_reg(NULL, M68K_REG_A1); save_param(&tmp32, 4); + tmp32 = m68k_get_reg(NULL, M68K_REG_A2); save_param(&tmp32, 4); + tmp32 = m68k_get_reg(NULL, M68K_REG_A3); save_param(&tmp32, 4); + tmp32 = m68k_get_reg(NULL, M68K_REG_A4); save_param(&tmp32, 4); + tmp32 = m68k_get_reg(NULL, M68K_REG_A5); save_param(&tmp32, 4); + tmp32 = m68k_get_reg(NULL, M68K_REG_A6); save_param(&tmp32, 4); + tmp32 = m68k_get_reg(NULL, M68K_REG_A7); save_param(&tmp32, 4); + tmp32 = m68k_get_reg(NULL, M68K_REG_PC); save_param(&tmp32, 4); + tmp16 = m68k_get_reg(NULL, M68K_REG_SR); save_param(&tmp16, 2); + tmp32 = m68k_get_reg(NULL, M68K_REG_USP); save_param(&tmp32, 4); + } // Z80 save_param(&Z80, sizeof(Z80_Regs)); // Cartridge HW - bufferptr += cart_hw_context_save(&state[bufferptr]); + if (system_hw == SYSTEM_PBC) + { + bufferptr += sms_cart_context_save(&state[bufferptr]); + } + else + { + bufferptr += md_cart_context_save(&state[bufferptr]); + } /* compress state file */ unsigned long inbytes = bufferptr; diff --git a/source/state.h b/source/state.h index 9561619..35e6d3f 100644 --- a/source/state.h +++ b/source/state.h @@ -2,7 +2,7 @@ * Genesis Plus * Savestate support * - * Copyright (C) 2007, 2008, 2009 Eke-Eke (GCN/Wii port) + * Copyright (C) 2007-2011 Eke-Eke (GCN/Wii port) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,7 +24,7 @@ #define _STATE_H_ #define STATE_SIZE 0x48100 -#define STATE_VERSION "GENPLUS-GX 1.4.2" +#define STATE_VERSION "GENPLUS-GX 1.5.0" #define load_param(param, size) \ memcpy(param, &state[bufferptr], size); \ diff --git a/source/system.c b/source/system.c index eddaaed..a36f406 100644 --- a/source/system.c +++ b/source/system.c @@ -1,9 +1,9 @@ /*************************************************************************************** * Genesis Plus - * Main Emulation + * Virtual System emulation * * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) - * Eke-Eke (2007,2008,2009), additional code & fixes for the GCN/Wii port + * Eke-Eke (2007-2011), additional code & fixes for the GCN/Wii port * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,11 +32,82 @@ uint32 mcycles_vdp; uint32 mcycles_z80; uint32 mcycles_68k; uint8 system_hw; +void (*system_frame)(int do_skip); + +static void system_frame_md(int do_skip); +static void system_frame_sms(int do_skip); +static int pause_b; +static EQSTATE eq; +static int32 llp,rrp; /**************************************************************** - * AUDIO equalizer + * Audio subsystem ****************************************************************/ -static EQSTATE eq; + +int audio_init (int samplerate, float framerate) +{ + /* Shutdown first */ + audio_shutdown(); + + /* Clear the sound data context */ + memset(&snd, 0, sizeof (snd)); + + /* Default settings */ + snd.sample_rate = samplerate; + snd.frame_rate = framerate; + + /* Calculate the sound buffer size (for one frame) */ + snd.buffer_size = (int)(samplerate / framerate) + 32; + + /* SN76489 stream buffers */ + snd.psg.buffer = (int16 *) malloc(snd.buffer_size * sizeof(int16)); + if (!snd.psg.buffer) return (-1); + + /* YM2612 stream buffers */ + snd.fm.buffer = (int32 *) malloc(snd.buffer_size * sizeof(int32) * 2); + if (!snd.fm.buffer) return (-1); + +#ifndef NGC + /* Output buffers */ + snd.buffer[0] = (int16 *) malloc(snd.buffer_size * sizeof(int16)); + snd.buffer[1] = (int16 *) malloc(snd.buffer_size * sizeof(int16)); + if (!snd.buffer[0] || !snd.buffer[1]) return (-1); +#endif + + /* Resampling buffer */ + if (config.hq_fm && !Fir_Resampler_initialize(4096)) return (-1); + + /* Set audio enable flag */ + snd.enabled = 1; + + /* Reset audio */ + audio_reset(); + + return (0); +} + +void audio_reset(void) +{ + /* Low-Pass filter */ + llp = 0; + rrp = 0; + + /* 3 band EQ */ + audio_set_equalizer(); + + /* Resampling buffer */ + Fir_Resampler_clear(); + + /* Audio buffers */ + snd.psg.pos = snd.psg.buffer; + snd.fm.pos = snd.fm.buffer; + if (snd.psg.buffer) memset (snd.psg.buffer, 0, snd.buffer_size * sizeof(int16)); + if (snd.fm.buffer) memset (snd.fm.buffer, 0, snd.buffer_size * sizeof(int32) * 2); +#ifndef NGC + if (snd.buffer[0]) memset (snd.buffer[0], 0, snd.buffer_size * sizeof(int16)); + if (snd.buffer[1]) memset (snd.buffer[1], 0, snd.buffer_size * sizeof(int16)); +#endif +} void audio_set_equalizer(void) { @@ -46,10 +117,19 @@ void audio_set_equalizer(void) eq.hg = (double)(config.hg) / 100.0; } -/**************************************************************** - * AUDIO stream update & mixing - ****************************************************************/ -static int32 llp,rrp; +void audio_shutdown(void) +{ + /* Sound buffers */ + if (snd.fm.buffer) free(snd.fm.buffer); + if (snd.psg.buffer) free(snd.psg.buffer); +#ifndef NGC + if (snd.buffer[0]) free(snd.buffer[0]); + if (snd.buffer[1]) free(snd.buffer[1]); +#endif + + /* Resampling buffer */ + Fir_Resampler_shutdown(); +} int audio_update (void) { @@ -129,14 +209,10 @@ int audio_update (void) } /* clipping (16-bit samples) */ - if (l > 32767) - l = 32767; - else if (l < -32768) - l = -32768; - if (r > 32767) - r = 32767; - else if (r < -32768) - r = -32768; + if (l > 32767) l = 32767; + else if (l < -32768) l = -32768; + if (r > 32767) r = 32767; + else if (r < -32768) r = -32768; /* update sound buffer */ #ifndef NGC @@ -163,181 +239,58 @@ int audio_update (void) return size; } -/**************************************************************** - * AUDIO System initialization - ****************************************************************/ -int audio_init (int samplerate, float framerate) -{ - /* Shutdown first */ - audio_shutdown(); - - /* Clear the sound data context */ - memset(&snd, 0, sizeof (snd)); - - /* Default settings */ - snd.sample_rate = samplerate; - snd.frame_rate = framerate; - - /* Calculate the sound buffer size (for one frame) */ - snd.buffer_size = (int)(samplerate / framerate) + 32; - -#ifndef NGC - /* Output buffers */ - snd.buffer[0] = (int16 *) malloc(snd.buffer_size * sizeof(int16)); - snd.buffer[1] = (int16 *) malloc(snd.buffer_size * sizeof(int16)); - if (!snd.buffer[0] || !snd.buffer[1]) - return (-1); -#endif - - /* SN76489 stream buffers */ - snd.psg.buffer = (int16 *) malloc(snd.buffer_size * sizeof(int16)); - if (!snd.psg.buffer) - return (-1); - - /* YM2612 stream buffers */ - snd.fm.buffer = (int32 *) malloc(snd.buffer_size * sizeof(int32) * 2); - if (!snd.fm.buffer) - return (-1); - - /* Resampling buffer */ - if (config.hq_fm) - { - if (!Fir_Resampler_initialize(4096)) - return (-1); - } - - /* Set audio enable flag */ - snd.enabled = 1; - - /* Reset audio */ - audio_reset(); - - return (0); -} - -/**************************************************************** - * AUDIO System reset - ****************************************************************/ -void audio_reset(void) -{ - /* Low-Pass filter */ - llp = 0; - rrp = 0; - - /* 3 band EQ */ - audio_set_equalizer(); - - /* audio buffers */ - Fir_Resampler_clear(); - snd.psg.pos = snd.psg.buffer; - snd.fm.pos = snd.fm.buffer; -#ifndef NGC - if (snd.buffer[0]) - memset (snd.buffer[0], 0, snd.buffer_size * sizeof(int16)); - if (snd.buffer[1]) - memset (snd.buffer[1], 0, snd.buffer_size * sizeof(int16)); -#endif - if (snd.psg.buffer) - memset (snd.psg.buffer, 0, snd.buffer_size * sizeof(int16)); - if (snd.fm.buffer) - memset (snd.fm.buffer, 0, snd.buffer_size * sizeof(int32) * 2); -} - -/**************************************************************** - * AUDIO System shutdown - ****************************************************************/ -void audio_shutdown(void) -{ - /* Sound buffers */ -#ifndef NGC - if (snd.buffer[0]) - free(snd.buffer[0]); - if (snd.buffer[1]) - free(snd.buffer[1]); -#endif - if (snd.fm.buffer) - free(snd.fm.buffer); - if (snd.psg.buffer) - free(snd.psg.buffer); - - /* Resampling buffer */ - Fir_Resampler_shutdown(); -} - /**************************************************************** * Virtual Genesis initialization ****************************************************************/ -void system_init (void) +void system_init(void) { - /* Genesis hardware */ gen_init(); io_init(); vdp_init(); render_init(); - - /* Cartridge hardware */ - cart_hw_init(); - - /* Sound hardware */ sound_init(); + system_frame = (system_hw == SYSTEM_PBC) ? system_frame_sms : system_frame_md; } /**************************************************************** - * Virtual Genesis Hard Reset + * Virtual System emulation ****************************************************************/ -void system_reset (void) +void system_reset(void) { - /* Cartridge Hardware */ - cart_hw_reset(1); - - /* Genesis hardware */ - gen_hardreset(); + gen_reset(1); io_reset(); vdp_reset(); render_reset(); - - /* Sound hardware */ sound_reset(); - - /* Audio system */ audio_reset(); } -/**************************************************************** - * Virtual Genesis shutdown - ****************************************************************/ void system_shutdown (void) { - gen_shutdown (); - vdp_shutdown (); - render_shutdown (); - SN76489_Shutdown (); + gen_shutdown(); + SN76489_Shutdown(); } -/**************************************************************** - * Virtual Genesis Frame emulation - ****************************************************************/ - -void system_frame (int do_skip) +static void system_frame_md(int do_skip) { /* line counter */ int line = 0; /* Z80 interrupt flag */ - int zirq = 0; - + int zirq = 1; + /* reload H Counter */ int h_counter = reg[10]; + /* reset line master cycle count */ + mcycles_vdp = 0; + /* reload V Counter */ v_counter = lines_per_frame - 1; /* reset VDP FIFO */ - fifo_write_cnt = 0; - fifo_lastwrite = 0; - - /* reset line cycle count */ - mcycles_vdp = 0; + fifo_write_cnt = 0; + fifo_lastwrite = 0; /* update 6-Buttons & Lightguns */ input_refresh(); @@ -349,34 +302,43 @@ void system_frame (int do_skip) /* interlaced mode */ int old_interlaced = interlaced; - interlaced = (reg[12] & 2) >> 1; + interlaced = (reg[12] & 0x02) >> 1; if (old_interlaced != interlaced) { - im2_flag = ((reg[12] & 6) == 6); + im2_flag = ((reg[12] & 0x06) == 0x06); odd_frame = 1; bitmap.viewport.changed = 5; + + /* update rendering mode */ + if (reg[1] & 0x04) + { + if (im2_flag) + { + render_bg = (reg[11] & 0x04) ? render_bg_m5_im2_vs : render_bg_m5_im2; + render_obj = (reg[12] & 0x08) ? render_obj_m5_im2_ste : render_obj_m5_im2; + } + else + { + render_bg = (reg[11] & 0x04) ? render_bg_m5_vs : render_bg_m5; + render_obj = (reg[12] & 0x08) ? render_obj_m5_ste : render_obj_m5; + } + } } /* active screen height */ - bitmap.viewport.h = 224 + ((reg[1] & 8) << 1); - bitmap.viewport.y = (config.overscan & 1) * (8 - (reg[1] & 8) + vdp_pal*24); + if (reg[1] & 0x04) + { + bitmap.viewport.h = 224 + ((reg[1] & 0x08) << 1); + bitmap.viewport.y = (config.overscan & 1) * ((240 + 48*vdp_pal - bitmap.viewport.h) >> 1); + } + else + { + bitmap.viewport.h = 192; + bitmap.viewport.y = (config.overscan & 1) * 24 * (vdp_pal + 1); + } /* active screen width */ - bitmap.viewport.w = 256 + ((reg[12] & 1) << 6); - bitmap.viewport.x = (config.overscan & 2) * 7; - } - - /* render last line of overscan */ - if (!do_skip && bitmap.viewport.y) - { - render_line(v_counter); - } - - /* parse first line of sprites */ - object_which = 1; - if (reg[1] & 0x40) - { - parse_satb(0x80); + bitmap.viewport.w = 256 + ((reg[12] & 0x01) << 6); } /* clear VBLANK, DMA, FIFO FULL & field flags */ @@ -395,7 +357,19 @@ void system_frame (int do_skip) /* update VDP DMA */ if (dma_length) { - vdp_update_dma(0); + vdp_dma_update(0); + } + + /* render last line of overscan */ + if (bitmap.viewport.y) + { + blank_line(v_counter, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x); + } + + /* parse first line of sprites */ + if (reg[1] & 0x40) + { + parse_satb(-1); } /* run 68k & Z80 */ @@ -437,31 +411,22 @@ void system_frame (int do_skip) hint_pending = 0x10; if (reg[0] & 0x10) { - irq_status = (irq_status & 2) | 0x14; + m68k_irq_state |= 0x14; } } /* update VDP DMA */ if (dma_length) { - vdp_update_dma(mcycles_vdp); + vdp_dma_update(mcycles_vdp); } - /* swap sprite line buffers */ - object_which ^= 1; - /* render scanline */ if (!do_skip) { render_line(line); } - /* parse next line of sprites */ - if ((reg[1] & 0x40) && (line < (bitmap.viewport.h - 1))) - { - parse_satb(0x81 + line); - } - /* run 68k & Z80 */ m68k_run(mcycles_vdp + MCYCLES_PER_LINE); if (zstate == 1) @@ -487,22 +452,8 @@ void system_frame (int do_skip) /* end of active display */ v_counter = line; - /* update 6-Buttons & Lightguns */ - input_refresh(); - - /* H Interrupt */ - if(--h_counter < 0) - { - /* reload H Counter */ - h_counter = reg[10]; - - /* interrupt level 4 */ - hint_pending = 0x10; - if (reg[0] & 0x10) - { - irq_status = (irq_status & 2) | 0x14; - } - } + /* set VBLANK flag */ + status |= 0x08; /* overscan area */ int start = lines_per_frame - bitmap.viewport.y; @@ -516,19 +467,33 @@ void system_frame (int do_skip) bitmap.viewport.changed |= 1; } - /* set VBLANK flag */ - status |= 0x08; + /* update 6-Buttons & Lightguns */ + input_refresh(); + + /* H Interrupt */ + if(--h_counter < 0) + { + /* reload H Counter */ + h_counter = reg[10]; + + /* interrupt level 4 */ + hint_pending = 0x10; + if (reg[0] & 0x10) + { + m68k_irq_state |= 0x14; + } + } /* update VDP DMA */ if (dma_length) { - vdp_update_dma(mcycles_vdp); + vdp_dma_update(mcycles_vdp); } /* render overscan */ - if (!do_skip && (line < end)) + if (line < end) { - render_line(line); + blank_line(line, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x); } /* update inputs before VINT (Warriors of Eternal Sun) */ @@ -553,14 +518,13 @@ void system_frame (int do_skip) vint_pending = 0x20; if (reg[1] & 0x20) { - irq_status = 0x16; + m68k_irq_state = 0x16; } - /* Z80 interrupt */ - z80_set_irq_line(0, ASSERT_LINE); - zirq = 1; + /* assert Z80 interrupt */ + Z80.irq_state = ASSERT_LINE; - /* run 68k & Z80 */ + /* run 68k & Z80 until end of line */ m68k_run(mcycles_vdp + MCYCLES_PER_LINE); if (zstate == 1) { @@ -593,14 +557,14 @@ void system_frame (int do_skip) input_refresh(); /* render overscan */ - if (!do_skip && ((line < end) || (line >= start))) + if ((line < end) || (line >= start)) { - render_line(line); + blank_line(line, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x); } - /* Z80 interrupt is asserted for one line */ if (zirq) { + /* Z80 interrupt is asserted exactly for one line */ m68k_run(mcycles_vdp + 788); if (zstate == 1) { @@ -612,7 +576,7 @@ void system_frame (int do_skip) } /* clear Z80 interrupt */ - z80_set_irq_line(0, CLEAR_LINE); + Z80.irq_state = CLEAR_LINE; zirq = 0; } @@ -638,7 +602,277 @@ void system_frame (int do_skip) } while (++line < (lines_per_frame - 1)); - /* adjust cpu cycle count for next frame */ + /* adjust 68k & Z80 cycle count for next frame */ mcycles_68k -= mcycles_vdp; mcycles_z80 -= mcycles_vdp; } + + +static void system_frame_sms(int do_skip) +{ + /* line counter */ + int line = 0; + + /* reload H Counter */ + int h_counter = reg[10]; + + /* reset line master cycle count */ + mcycles_vdp = 0; + + /* reload V Counter */ + v_counter = lines_per_frame - 1; + + /* reset VDP FIFO */ + fifo_write_cnt = 0; + fifo_lastwrite = 0; + + /* update 6-Buttons & Lightguns */ + input_refresh(); + + /* display changed during VBLANK */ + if (bitmap.viewport.changed & 2) + { + bitmap.viewport.changed &= ~2; + + /* interlaced mode */ + int old_interlaced = interlaced; + interlaced = (reg[12] & 0x02) >> 1; + if (old_interlaced != interlaced) + { + im2_flag = ((reg[12] & 0x06) == 0x06); + odd_frame = 1; + bitmap.viewport.changed = 5; + + /* update rendering mode */ + if (reg[1] & 0x04) + { + if (im2_flag) + { + render_bg = (reg[11] & 0x04) ? render_bg_m5_im2_vs : render_bg_m5_im2; + render_obj = render_obj_m5_im2; + + } + else + { + render_bg = (reg[11] & 0x04) ? render_bg_m5_vs : render_bg_m5; + render_obj = render_obj_m5; + } + } + } + + /* active screen height */ + if (reg[1] & 0x04) + { + bitmap.viewport.h = 224 + ((reg[1] & 0x08) << 1); + bitmap.viewport.y = (config.overscan & 1) * ((240 + 48*vdp_pal - bitmap.viewport.h) >> 1); + } + else + { + bitmap.viewport.h = 192; + bitmap.viewport.y = (config.overscan & 1) * 24 * (vdp_pal + 1); + } + + /* active screen width */ + bitmap.viewport.w = 256 + ((reg[12] & 0x01) << 6); + } + + /* Detect pause button input */ + if (input.pad[0] & INPUT_START) + { + /* NMI is edge-triggered */ + if (!pause_b) + { + pause_b = 1; + z80_set_nmi_line(ASSERT_LINE); + z80_set_nmi_line(CLEAR_LINE); + } + } + else + { + pause_b = 0; + } + + /* 3-D glasses faking: skip rendering of left lens frame */ + do_skip |= (work_ram[0x1ffb] & cart.special); + + /* clear VBLANK, DMA, FIFO FULL & field flags */ + status &= 0xFEE5; + + /* set FIFO EMPTY flag */ + status |= 0x0200; + + /* even/odd field flag (interlaced modes only) */ + odd_frame ^= 1; + if (interlaced) + { + status |= (odd_frame << 4); + } + + /* update VDP DMA */ + if (dma_length) + { + vdp_dma_update(0); + } + + /* render last line of overscan */ + if (bitmap.viewport.y) + { + blank_line(v_counter, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x); + } + + /* parse first line of sprites */ + if (reg[1] & 0x40) + { + parse_satb(-1); + } + + /* latch Horizontal Scroll register (if modified during VBLANK) */ + hscroll = reg[0x08]; + + /* run Z80 */ + z80_run(MCYCLES_PER_LINE); + + /* update line cycle count */ + mcycles_vdp += MCYCLES_PER_LINE; + + /* latch Vertical Scroll register */ + vscroll = reg[0x09]; + + /* Active Display */ + do + { + /* update V Counter */ + v_counter = line; + + /* update 6-Buttons & Lightguns */ + input_refresh(); + + /* H Interrupt */ + if(--h_counter < 0) + { + /* reload H Counter */ + h_counter = reg[10]; + + /* interrupt level 4 */ + hint_pending = 0x10; + if (reg[0] & 0x10) + { + Z80.irq_state = ASSERT_LINE; + } + } + + /* update VDP DMA */ + if (dma_length) + { + vdp_dma_update(mcycles_vdp); + } + + /* render scanline */ + if (!do_skip) + { + render_line(line); + } + + /* run Z80 */ + z80_run(mcycles_vdp + MCYCLES_PER_LINE); + + /* update line cycle count */ + mcycles_vdp += MCYCLES_PER_LINE; + } + while (++line < bitmap.viewport.h); + + /* end of active display */ + v_counter = line; + + /* set VBLANK flag */ + status |= 0x08; + + /* overscan area */ + int start = lines_per_frame - bitmap.viewport.y; + int end = bitmap.viewport.h + bitmap.viewport.y; + + /* check viewport changes */ + if ((bitmap.viewport.w != bitmap.viewport.ow) || (bitmap.viewport.h != bitmap.viewport.oh)) + { + bitmap.viewport.ow = bitmap.viewport.w; + bitmap.viewport.oh = bitmap.viewport.h; + bitmap.viewport.changed |= 1; + } + + /* update 6-Buttons & Lightguns */ + input_refresh(); + + /* H Interrupt */ + if(--h_counter < 0) + { + /* reload H Counter */ + h_counter = reg[10]; + + /* interrupt level 4 */ + hint_pending = 0x10; + if (reg[0] & 0x10) + { + Z80.irq_state = ASSERT_LINE; + } + } + + /* update VDP DMA */ + if (dma_length) + { + vdp_dma_update(mcycles_vdp); + } + + /* render overscan */ + if (line < end) + { + blank_line(line, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x); + } + + /* update inputs before VINT (Warriors of Eternal Sun) */ + osd_input_Update(); + + /* run Z80 until end of line */ + z80_run(mcycles_vdp + MCYCLES_PER_LINE); + + /* VINT flag */ + status |= 0x80; + + /* V Interrupt */ + vint_pending = 0x20; + if (reg[1] & 0x20) + { + Z80.irq_state = ASSERT_LINE; + } + + /* update line cycle count */ + mcycles_vdp += MCYCLES_PER_LINE; + + /* increment line count */ + line++; + + /* Vertical Blanking */ + do + { + /* update V Counter */ + v_counter = line; + + /* update 6-Buttons & Lightguns */ + input_refresh(); + + /* render overscan */ + if ((line < end) || (line >= start)) + { + blank_line(line, -bitmap.viewport.x, bitmap.viewport.w + 2*bitmap.viewport.x); + } + + /* run Z80 */ + z80_run(mcycles_vdp + MCYCLES_PER_LINE); + + /* update line cycle count */ + mcycles_vdp += MCYCLES_PER_LINE; + } + while (++line < (lines_per_frame - 1)); + + /* adjust Z80 cycle count for next frame */ + mcycles_z80 -= mcycles_vdp; +} diff --git a/source/system.h b/source/system.h index 6fcc7e9..4a276e4 100644 --- a/source/system.h +++ b/source/system.h @@ -1,9 +1,9 @@ /*************************************************************************************** * Genesis Plus - * Main Emulation + * Virtual System Emulation * * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) - * Eke-Eke (2007,2008,2009), additional code & fixes for the GCN/Wii port + * Eke-Eke (2007-2011), additional code & fixes for the GCN/Wii port * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,18 +24,21 @@ #ifndef _SYSTEM_H_ #define _SYSTEM_H_ -#define SYSTEM_GENESIS 0 -#define SYSTEM_MEGADRIVE 1 -#define SYSTEM_PICO 2 +#define SYSTEM_PBC 0x00 +#define SYSTEM_GENESIS 0x01 +#define SYSTEM_MEGADRIVE 0x02 +#define SYSTEM_PICO 0x03 #define MCYCLES_PER_LINE 3420 +#define Z80_CYCLE_OFFSET 550 /* horizontal timings offset when running in SMS mode */ + typedef struct { uint8 *data; /* Bitmap data */ - int width; /* Bitmap width (32+512+32) */ - int height; /* Bitmap height (256) */ - int depth; /* Color depth (8 bits) */ + int width; /* Bitmap width */ + int height; /* Bitmap height */ + int depth; /* Color depth (8-32 bits) */ int pitch; /* Width of bitmap in bytes */ int granularity; /* Size of each pixel in bytes */ int remap; /* 1= Translate pixel data */ @@ -51,7 +54,6 @@ typedef struct } viewport; } t_bitmap; - typedef struct { int sample_rate; /* Output Sample rate (8000-48000) */ @@ -71,12 +73,13 @@ typedef struct } psg; } t_snd; + /* Global variables */ extern t_bitmap bitmap; extern t_snd snd; -extern uint32 mcycles_vdp; extern uint32 mcycles_z80; extern uint32 mcycles_68k; +extern uint32 mcycles_vdp; extern uint8 system_hw; /* Function prototypes */ @@ -88,7 +91,7 @@ extern void audio_set_equalizer(void); extern void system_init(void); extern void system_reset(void); extern void system_shutdown(void); -extern void system_frame(int do_skip); +extern void (*system_frame)(int do_skip); #endif /* _SYSTEM_H_ */ diff --git a/source/unused/dos/Makefile b/source/unused/dos/Makefile index ffe085d..22579ce 100644 --- a/source/unused/dos/Makefile +++ b/source/unused/dos/Makefile @@ -19,52 +19,63 @@ FLAGS = -I. -I.. -I../z80 -I../m68k -I../dos -I../sound -I../sound/SRC -I../cart LIBS = -lalleg -laudio -lz -lm -OBJ = obj/z80.o +OBJECTS = obj/z80.o -OBJ += obj/m68kcpu.o \ +OBJECTS += obj/m68kcpu.o \ obj/m68kops.o -OBJ += obj/genesis.o \ - obj/vdp.o \ - obj/render.o \ - obj/system.o \ - obj/gen_io.o \ - obj/gen_input.o \ - obj/mem68k.o \ - obj/memz80.o \ - obj/membnk.o \ + +OBJECTS += obj/genesis.o \ + obj/vdp_ctrl.o \ + obj/vdp_render.o \ + obj/system.o \ + obj/io_ctrl.o \ + obj/mem68k.o \ + obj/memz80.o \ + obj/membnk.o \ obj/state.o -OBJ += obj/sound.o \ - obj/sn76489.o \ +OBJECTS += obj/input.o \ + obj/gamepad.o \ + obj/lightgun.o \ + obj/mouse.o \ + obj/activator.o \ + obj/xe_a1p.o \ + obj/teamplayer.o \ + obj/paddle.o \ + obj/sportspad.o + + +OBJECTS += obj/sound.o \ + obj/sn76489.o \ obj/ym2612.o -OBJ += obj/samplerate.o \ - obj/src_linear.o \ - obj/src_sinc.o \ - obj/src_zoh.o \ +OBJECTS += obj/Fir_Resampler.o +OBJECTS += obj/blip.o -OBJ += obj/blip.o \ +OBJECTS += obj/eq.o \ -OBJ += obj/eq.o \ - -OBJ += obj/sram.o \ +OBJECTS += obj/sram.o \ obj/eeprom.o \ obj/svp.o \ obj/ssp16.o \ - obj/cart_hw.o + obj/ggenie.o \ + obj/areplay.o \ + obj/md_cart.o \ + obj/sms_cart.o -OBJ += obj/dos.o \ - obj/sealintf.o \ +OBJECTS += obj/dos.o \ + obj/sealintf.o \ obj/config.o \ obj/error.o \ obj/unzip.o \ obj/fileio.o \ obj/loadrom.o -OBJ += obj/sms_ntsc.o \ +OBJECTS += obj/sms_ntsc.o \ obj/md_ntsc.o + EXE = ../gen.exe all : $(EXE) @@ -88,6 +99,9 @@ obj/%.o : ../sound/SRC/%.c ../sound/SRC/%.h obj/%.o : ../sound/SRC/%.c $(CC) -c $< -o $@ $(FLAGS) +obj/%.o : ../input_hw/%.c ../input_hw/%.h + $(CC) -c $< -o $@ $(FLAGS) + obj/%.o : ../cart_hw/%.c ../cart_hw/%.h $(CC) -c $< -o $@ $(FLAGS) diff --git a/source/unused/dos/config.c b/source/unused/dos/config.c index 00c5dc9..f362fe2 100644 --- a/source/unused/dos/config.c +++ b/source/unused/dos/config.c @@ -122,13 +122,13 @@ void set_config_defaults(void) config.render = 0; /* controllers options */ - input.system[0] = SYSTEM_GAMEPAD; - input.system[1] = SYSTEM_GAMEPAD; + input.system[0] = SYSTEM_MD_GAMEPAD; + input.system[1] = SYSTEM_MD_GAMEPAD; config.gun_cursor[0] = 1; config.gun_cursor[1] = 1; config.invert_mouse = 0; for (i=0;i> 2) & 3][vdp_pal]; /* reinitialize overscan area */ bitmap.viewport.y = (config.overscan & 1) ? (((reg[1] & 8) ? 0 : 8) + (vdp_pal ? 24 : 0)) : 0; diff --git a/source/unused/dos/fileio.c b/source/unused/dos/fileio.c index 6c4b169..aaea1df 100644 --- a/source/unused/dos/fileio.c +++ b/source/unused/dos/fileio.c @@ -27,7 +27,7 @@ uint8 *load_archive(char *filename, int *file_size) return (NULL); } - ret = unzGetCurrentFileInfo(fd, &info, NULL, 0, NULL, 0, NULL, 0); + ret = unzGetCurrentFileInfo(fd, &info, filename, 128, NULL, 0, NULL, 0); if(ret != UNZ_OK) { unzClose(fd); diff --git a/source/unused/win/Makefile b/source/unused/win/Makefile index 7f2cfa0..585f214 100644 --- a/source/unused/win/Makefile +++ b/source/unused/win/Makefile @@ -19,7 +19,7 @@ CFLAGS = `sdl-config --cflags` -march=i686 -O6 -fomit-frame-pointer -Wall -Wn #LDFLAGS = -pg DEFINES = -DLSB_FIRST -INCLUDES = -I. -I.. -I../z80 -I../m68k -I../sound -I../cart_hw -I../cart_hw/svp -I../ntsc +INCLUDES = -I. -I.. -I../z80 -I../m68k -I../sound -I../input_hw -I../cart_hw -I../cart_hw/svp -I../ntsc LIBS = `sdl-config --libs` -lz -lm OBJECTS = obj/z80.o @@ -28,19 +28,29 @@ OBJECTS += obj/m68kcpu.o \ obj/m68kops.o -OBJECTS += obj/genesis.o \ - obj/vdp.o \ - obj/render.o \ - obj/system.o \ - obj/gen_io.o \ - obj/gen_input.o \ - obj/mem68k.o \ - obj/memz80.o \ - obj/membnk.o \ +OBJECTS += obj/genesis.o \ + obj/vdp_ctrl.o \ + obj/vdp_render.o \ + obj/system.o \ + obj/io_ctrl.o \ + obj/mem68k.o \ + obj/memz80.o \ + obj/membnk.o \ obj/state.o - -OBJECTS += obj/sound.o \ - obj/sn76489.o \ + +OBJECTS += obj/input.o \ + obj/gamepad.o \ + obj/lightgun.o \ + obj/mouse.o \ + obj/activator.o \ + obj/xe_a1p.o \ + obj/teamplayer.o \ + obj/paddle.o \ + obj/sportspad.o + + +OBJECTS += obj/sound.o \ + obj/sn76489.o \ obj/ym2612.o OBJECTS += obj/Fir_Resampler.o @@ -54,7 +64,8 @@ OBJECTS += obj/sram.o \ obj/ssp16.o \ obj/ggenie.o \ obj/areplay.o \ - obj/cart_hw.o + obj/md_cart.o \ + obj/sms_cart.o OBJECTS += obj/main.o \ obj/config.o \ @@ -84,6 +95,9 @@ obj/%.o : ../sound/%.c ../sound/%.h obj/%.o : ../sound/%.c $(CC) -c $(CFLAGS) $(INCLUDES) $(DEFINES) $< -o $@ +obj/%.o : ../input_hw/%.c ../input_hw/%.h + $(CC) -c $(CFLAGS) $(INCLUDES) $(DEFINES) $< -o $@ + obj/%.o : ../cart_hw/%.c ../cart_hw/%.h $(CC) -c $(CFLAGS) $(INCLUDES) $(DEFINES) $< -o $@ diff --git a/source/unused/win/config.c b/source/unused/win/config.c index 95594f4..8cbc943 100644 --- a/source/unused/win/config.c +++ b/source/unused/win/config.c @@ -28,7 +28,7 @@ void set_config_defaults(void) config.force_dtack = 0; config.addr_error = 1; config.tmss = 0; - config.lock_on = 0; + config.lock_on = 0;//TYPE_SK; config.romtype = 0; /* display options */ @@ -36,13 +36,13 @@ void set_config_defaults(void) config.render = 0; /* controllers options */ - input.system[0] = SYSTEM_GAMEPAD; - input.system[1] = SYSTEM_GAMEPAD; + input.system[0] = SYSTEM_MD_GAMEPAD; + input.system[1] = SYSTEM_MD_GAMEPAD; config.gun_cursor[0] = 1; config.gun_cursor[1] = 1; config.invert_mouse = 0; for (i=0;i> 2) & 3][vdp_pal]; /* reinitialize display area */ bitmap.viewport.changed = 3; @@ -389,11 +393,11 @@ static int sdl_control_update(SDLKey keystate) case SDLK_F12: { - while (input.dev[++joynum] == NO_DEVICE) + joynum = (joynum + 1) % MAX_DEVICES; + while (input.dev[joynum] == NO_DEVICE) { - joynum = joynum % MAX_DEVICES; + joynum = (joynum + 1) % MAX_DEVICES; } - joynum = joynum % MAX_DEVICES; break; } @@ -412,24 +416,9 @@ static int sdl_control_update(SDLKey keystate) int sdl_input_update(void) { uint8 *keystate = SDL_GetKeyState(NULL); - while (input.dev[joynum] == NO_DEVICE) - { - joynum ++; - if (joynum > MAX_DEVICES - 1) joynum = 0; - } /* reset input */ input.pad[joynum] = 0; - - /* keyboard */ - if(keystate[SDLK_a]) input.pad[joynum] |= INPUT_A; - if(keystate[SDLK_s]) input.pad[joynum] |= INPUT_B; - if(keystate[SDLK_d]) input.pad[joynum] |= INPUT_C; - if(keystate[SDLK_f]) input.pad[joynum] |= INPUT_START; - if(keystate[SDLK_z]) input.pad[joynum] |= INPUT_X; - if(keystate[SDLK_x]) input.pad[joynum] |= INPUT_Y; - if(keystate[SDLK_c]) input.pad[joynum] |= INPUT_Z; - if(keystate[SDLK_v]) input.pad[joynum] |= INPUT_MODE; switch (input.dev[joynum]) { @@ -440,13 +429,46 @@ int sdl_input_update(void) int state = SDL_GetMouseState(&x,&y); /* Calculate X Y axis values */ - input.analog[joynum - 4][0] = (x * bitmap.viewport.w) / 640; - input.analog[joynum - 4][1] = (y * bitmap.viewport.h) / 480; + input.analog[joynum][0] = (x * bitmap.viewport.w) / VIDEO_WIDTH; + input.analog[joynum][1] = (y * bitmap.viewport.h) / VIDEO_HEIGHT; - /* Map mouse buttons to player #1 inputs */ - if(state & SDL_BUTTON_MMASK) input.pad[joynum] |= INPUT_C; - if(state & SDL_BUTTON_RMASK) input.pad[joynum] |= INPUT_B; - if(state & SDL_BUTTON_LMASK) input.pad[joynum] |= INPUT_A; + /* Start,Left,Right,Middle buttons -> 0 0 0 0 START MIDDLE RIGHT LEFT */ + if(state & SDL_BUTTON_LMASK) input.pad[joynum] |= INPUT_B; + if(state & SDL_BUTTON_RMASK) input.pad[joynum] |= INPUT_C; + if(state & SDL_BUTTON_MMASK) input.pad[joynum] |= INPUT_A; + if(keystate[SDLK_f]) input.pad[joynum] |= INPUT_START; + + break; + } + + case DEVICE_PADDLE: + { + /* get mouse (absolute values) */ + int x; + int state = SDL_GetMouseState(&x, NULL); + + /* Range is [0;256], 128 being middle position */ + input.analog[joynum][0] = x * 256 /VIDEO_WIDTH; + + /* Button I -> 0 0 0 0 0 0 0 I*/ + if(state & SDL_BUTTON_LMASK) input.pad[joynum] |= INPUT_B; + + break; + } + + case DEVICE_SPORTSPAD: + { + /* get mouse (relative values) */ + int x,y; + int state = SDL_GetRelativeMouseState(&x,&y); + + /* Range is [0;256] */ + input.analog[joynum][0] = (unsigned char)(-x & 0xFF); + input.analog[joynum][1] = (unsigned char)(-y & 0xFF); + + /* Buttons I & II -> 0 0 0 0 0 0 II I*/ + if(state & SDL_BUTTON_LMASK) input.pad[joynum] |= INPUT_B; + if(state & SDL_BUTTON_RMASK) input.pad[joynum] |= INPUT_C; break; } @@ -457,24 +479,82 @@ int sdl_input_update(void) int x,y; int state = SDL_GetRelativeMouseState(&x,&y); - /* Sega Mouse range is -256;+256 */ - input.analog[2][0] = x * 2; - input.analog[2][1] = y * 2; + /* Sega Mouse range is [-256;+256] */ + input.analog[joynum][0] = x * 2; + input.analog[joynum][1] = y * 2; /* Vertical movement is upsidedown */ if (!config.invert_mouse) - input.analog[2][1] = 0 - input.analog[2][1]; + input.analog[joynum][1] = 0 - input.analog[joynum][1]; + + /* Start,Left,Right,Middle buttons -> 0 0 0 0 START MIDDLE RIGHT LEFT */ + if(state & SDL_BUTTON_LMASK) input.pad[joynum] |= INPUT_B; + if(state & SDL_BUTTON_RMASK) input.pad[joynum] |= INPUT_C; + if(state & SDL_BUTTON_MMASK) input.pad[joynum] |= INPUT_A; + if(keystate[SDLK_f]) input.pad[joynum] |= INPUT_START; - /* Map mouse buttons to player #1 inputs */ - if(state & SDL_BUTTON_MMASK) input.pad[joynum] |= INPUT_C; - if(state & SDL_BUTTON_RMASK) input.pad[joynum] |= INPUT_B; - if(state & SDL_BUTTON_LMASK) input.pad[joynum] |= INPUT_A; - break; } + case DEVICE_XE_A1P: + { + /* A,B,C,D,Select,START,E1,E2 buttons -> E1(?) E2(?) START SELECT(?) A B C D */ + if(keystate[SDLK_a]) input.pad[joynum] |= INPUT_START; + if(keystate[SDLK_s]) input.pad[joynum] |= INPUT_A; + if(keystate[SDLK_d]) input.pad[joynum] |= INPUT_C; + if(keystate[SDLK_f]) input.pad[joynum] |= INPUT_Y; + if(keystate[SDLK_z]) input.pad[joynum] |= INPUT_B; + if(keystate[SDLK_x]) input.pad[joynum] |= INPUT_X; + if(keystate[SDLK_c]) input.pad[joynum] |= INPUT_MODE; + if(keystate[SDLK_v]) input.pad[joynum] |= INPUT_Z; + + /* Left Analog Stick (bidirectional) */ + if(keystate[SDLK_UP]) input.analog[joynum][1]-=2; + else if(keystate[SDLK_DOWN]) input.analog[joynum][1]+=2; + else input.analog[joynum][1] = 128; + if(keystate[SDLK_LEFT]) input.analog[joynum][0]-=2; + else if(keystate[SDLK_RIGHT]) input.analog[joynum][0]+=2; + else input.analog[joynum][0] = 128; + + /* Right Analog Stick (unidirectional) */ + if(keystate[SDLK_KP8]) input.analog[joynum+1][0]-=2; + else if(keystate[SDLK_KP2]) input.analog[joynum+1][0]+=2; + else if(keystate[SDLK_KP4]) input.analog[joynum+1][0]-=2; + else if(keystate[SDLK_KP6]) input.analog[joynum+1][0]+=2; + else input.analog[joynum+1][0] = 128; + + /* Limiters */ + if (input.analog[joynum][0] > 0xFF) input.analog[joynum][0] = 0xFF; + else if (input.analog[joynum][0] < 0) input.analog[joynum][0] = 0; + if (input.analog[joynum][1] > 0xFF) input.analog[joynum][1] = 0xFF; + else if (input.analog[joynum][1] < 0) input.analog[joynum][1] = 0; + if (input.analog[joynum+1][0] > 0xFF) input.analog[joynum+1][0] = 0xFF; + else if (input.analog[joynum+1][0] < 0) input.analog[joynum+1][0] = 0; + if (input.analog[joynum+1][1] > 0xFF) input.analog[joynum+1][1] = 0xFF; + else if (input.analog[joynum+1][1] < 0) input.analog[joynum+1][1] = 0; + + break; + } + + case DEVICE_ACTIVATOR: + { + if(keystate[SDLK_g]) input.pad[joynum] |= INPUT_ACTIVATOR_7L; + if(keystate[SDLK_h]) input.pad[joynum] |= INPUT_ACTIVATOR_7U; + if(keystate[SDLK_j]) input.pad[joynum] |= INPUT_ACTIVATOR_8L; + if(keystate[SDLK_k]) input.pad[joynum] |= INPUT_ACTIVATOR_8U; + } + default: { + if(keystate[SDLK_a]) input.pad[joynum] |= INPUT_A; + if(keystate[SDLK_s]) input.pad[joynum] |= INPUT_B; + if(keystate[SDLK_d]) input.pad[joynum] |= INPUT_C; + if(keystate[SDLK_f]) input.pad[joynum] |= INPUT_START; + if(keystate[SDLK_z]) input.pad[joynum] |= INPUT_X; + if(keystate[SDLK_x]) input.pad[joynum] |= INPUT_Y; + if(keystate[SDLK_c]) input.pad[joynum] |= INPUT_Z; + if(keystate[SDLK_v]) input.pad[joynum] |= INPUT_MODE; + if(keystate[SDLK_UP]) input.pad[joynum] |= INPUT_UP; else if(keystate[SDLK_DOWN]) input.pad[joynum] |= INPUT_DOWN; diff --git a/source/vdp_ctrl.c b/source/vdp_ctrl.c new file mode 100644 index 0000000..b0290a2 --- /dev/null +++ b/source/vdp_ctrl.c @@ -0,0 +1,2205 @@ +/*************************************************************************************** + * Genesis Plus + * Video Display Processor (68k & Z80 CPU interface) + * + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) + * Eke-Eke (2007-2011), additional code & fixes for the GCN/Wii port + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************************************/ + +#include "shared.h" +#include "hvc.h" + +/* Mark a pattern as dirty */ +#define MARK_BG_DIRTY(addr) \ +{ \ + name = (addr >> 5) & 0x7FF; \ + if(bg_name_dirty[name] == 0) \ + { \ + bg_name_list[bg_list_index++] = name; \ + } \ + bg_name_dirty[name] |= (1 << ((addr >> 2) & 7)); \ +} + +/* VDP context */ +uint8 sat[0x400]; /* Internal copy of sprite attribute table */ +uint8 vram[0x10000]; /* Video RAM (64K x 8-bit) */ +uint8 cram[0x80]; /* On-chip color RAM (64 x 9-bit) */ +uint8 vsram[0x80]; /* On-chip vertical scroll RAM (40 x 11-bit) */ +uint8 reg[0x20]; /* Internal VDP registers (23 x 8-bit) */ +uint8 hint_pending; /* 0= Line interrupt is pending */ +uint8 vint_pending; /* 1= Frame interrupt is pending */ +uint8 m68k_irq_state; /* 68K IRQ status */ +uint16 status; /* VDP status flags */ +uint32 dma_length; /* DMA remaining length */ + +/* Global variables */ +uint16 ntab; /* Name table A base address */ +uint16 ntbb; /* Name table B base address */ +uint16 ntwb; /* Name table W base address */ +uint16 satb; /* Sprite attribute table base address */ +uint16 hscb; /* Horizontal scroll table base address */ +uint8 bg_name_dirty[0x800]; /* 1= This pattern is dirty */ +uint16 bg_name_list[0x800]; /* List of modified pattern indices */ +uint16 bg_list_index; /* # of modified patterns in list */ +uint8 bg_pattern_cache[0x80000]; /* Cached and flipped patterns */ +uint8 hscroll_mask; /* Horizontal Scrolling line mask */ +uint8 playfield_shift; /* Width of planes A, B (in bits) */ +uint8 playfield_col_mask; /* Playfield column mask */ +uint16 playfield_row_mask; /* Playfield row mask */ +uint16 hscroll; /* Latched horizontal scroll value */ +uint16 vscroll; /* Latched vertical scroll value */ +uint8 odd_frame; /* 1: odd field, 0: even field */ +uint8 im2_flag; /* 1= Interlace mode 2 is being used */ +uint8 interlaced; /* 1: Interlaced mode 1 or 2 */ +uint8 vdp_pal; /* 1: PAL , 0: NTSC (default) */ +uint16 v_counter; /* Vertical counter */ +uint16 vc_max; /* Vertical counter overflow value */ +uint16 lines_per_frame; /* PAL: 313 lines, NTSC: 262 lines */ +int32 fifo_write_cnt; /* VDP writes fifo count */ +uint32 fifo_lastwrite; /* last VDP write cycle */ +uint32 hvc_latch; /* latched HV counter */ +const uint8 *hctab; /* pointer to H Counter table */ + +/* Function pointers */ +void (*vdp_68k_data_w)(unsigned int data); +void (*vdp_z80_data_w)(unsigned int data); +unsigned int (*vdp_68k_data_r)(void); +unsigned int (*vdp_z80_data_r)(void); + +/* Tables that define the playfield layout */ +static const uint8 hscroll_mask_table[] = { 0x00, 0x07, 0xF8, 0xFF }; +static const uint8 shift_table[] = { 6, 7, 0, 8 }; +static const uint8 col_mask_table[] = { 0x0F, 0x1F, 0x0F, 0x3F }; +static const uint16 row_mask_table[] = { 0x0FF, 0x1FF, 0x2FF, 0x3FF }; + +static uint8 border; /* Border color index */ +static uint8 pending; /* Pending write flag */ +static uint8 code; /* Code register */ +static uint8 dma_type; /* DMA mode */ +static uint16 dmafill; /* DMA Fill setup */ +static uint16 addr; /* Address register */ +static uint16 addr_latch; /* Latched A15, A14 of address */ +static uint16 sat_base_mask; /* Base bits of SAT */ +static uint16 sat_addr_mask; /* Index bits of SAT */ +static uint32 dma_endCycles; /* 68k cycles to DMA end */ +static uint32 fifo_latency; /* CPU access latency */ +static int cached_write; /* 2nd part of 32-bit CTRL port write */ +static uint16 fifo[4]; /* FIFO buffer */ +static uint8 *irq_line; /* Z80 or 68k interrupt lines */ + +/* DMA Timings */ +static const uint8 dma_timing[2][2] = +{ +/* H32, H40 */ + {16 , 18}, /* active display */ + {167, 205} /* blank display */ +}; + +/* Vertical counter overflow values (see hvc.h) */ +static const uint16 vc_table[4][2] = +{ + /* NTSC, PAL */ + {0xDA , 0xF2}, /* Mode 4 (192 lines) */ + {0xEA , 0x102}, /* Mode 5 (224 lines) */ + {0xDA , 0xF2}, /* Mode 4 (192 lines) */ + {0x106, 0x10A} /* Mode 5 (240 lines) */ +}; + + +/*--------------------------------------------------------------------------*/ +/* Function prototypes */ +/*--------------------------------------------------------------------------*/ + +static void vdp_68k_data_w_m4(unsigned int data); +static void vdp_68k_data_w_m5(unsigned int data); +static unsigned int vdp_68k_data_r_m4(void); +static unsigned int vdp_68k_data_r_m5(void); +static void vdp_z80_data_w_m4(unsigned int data); +static void vdp_z80_data_w_m5(unsigned int data); +static unsigned int vdp_z80_data_r_m4(void); +static unsigned int vdp_z80_data_r_m5(void); +static void vdp_bus_w(unsigned int data); +static void vdp_fifo_update(unsigned int cycles); +static void vdp_reg_w(unsigned int r, unsigned int d, unsigned int cycles); +static void vdp_dma_copy(int length); +static void vdp_dma_vbus(int length); +static void vdp_dma_fill(unsigned int data, int length); + + +/*--------------------------------------------------------------------------*/ +/* Init, reset, context functions */ +/*--------------------------------------------------------------------------*/ + +void vdp_init(void) +{ + /* PAL/NTSC timings */ + lines_per_frame = vdp_pal ? 313: 262; + status = (status & ~1) | vdp_pal; + + /* CPU interrupt line(s)*/ + irq_line = (system_hw == SYSTEM_PBC) ? &Z80.irq_state : &m68k_irq_state; +} + +void vdp_reset(void) +{ + memset ((char *) sat, 0, sizeof (sat)); + memset ((char *) vram, 0, sizeof (vram)); + memset ((char *) cram, 0, sizeof (cram)); + memset ((char *) vsram, 0, sizeof (vsram)); + memset ((char *) reg, 0, sizeof (reg)); + + addr = 0; + addr_latch = 0; + code = 0; + pending = 0; + hint_pending = 0; + vint_pending = 0; + m68k_irq_state = 0; + dmafill = 0; + dma_type = 0; + dma_length = 0; + dma_endCycles = 0; + odd_frame = 0; + im2_flag = 0; + interlaced = 0; + fifo_write_cnt = 0; + fifo_lastwrite = 0; + cached_write = -1; + + ntab = 0; + ntbb = 0; + ntwb = 0; + satb = 0; + hscb = 0; + + hscroll = 0; + vscroll = 0; + + hscroll_mask = 0x00; + playfield_shift = 6; + playfield_col_mask = 0x0F; + playfield_row_mask = 0x0FF; + sat_base_mask = 0xFE00; + sat_addr_mask = 0x01FF; + + /* clear pattern cache */ + bg_list_index = 0; + memset ((char *) bg_name_dirty, 0, sizeof (bg_name_dirty)); + memset ((char *) bg_name_list, 0, sizeof (bg_name_list)); + memset ((char *) bg_pattern_cache, 0, sizeof (bg_pattern_cache)); + + /* default HVC */ + hvc_latch = 0x10000; + hctab = cycle2hc32; + vc_max = vc_table[0][vdp_pal]; + v_counter = lines_per_frame - 1; + + /* default Window clipping */ + window_clip(0,0); + + /* default FIFO timings */ + fifo_latency = 214; + + /* reset VDP status (FIFO empty flag is set) */ + status = vdp_pal | 0x200; + + /* default display area */ + bitmap.viewport.w = 256; + bitmap.viewport.h = 192; + bitmap.viewport.ow = 256; + bitmap.viewport.oh = 192; + + /* default overscan area */ + bitmap.viewport.x = (config.overscan & 2) * 7; + bitmap.viewport.y = (config.overscan & 1) * 24 * (vdp_pal + 1); + + /* default rendering mode */ + render_bg = render_bg_m4; + render_obj = render_obj_m4; + parse_satb = parse_satb_m4; + update_bg_pattern_cache = update_bg_pattern_cache_m4; + + /* default bus access mode */ + vdp_68k_data_w = vdp_68k_data_w_m4; + vdp_z80_data_w = vdp_z80_data_w_m4; + vdp_68k_data_r = vdp_68k_data_r_m4; + vdp_z80_data_r = vdp_z80_data_r_m4; + + /* initialize some registers if OS ROM is simulated */ + if (config.tmss == 1) + { + vdp_reg_w(0 , 0x04, 0); /* Palette bit set */ + vdp_reg_w(1 , 0x04, 0); /* Mode 5 enabled */ + vdp_reg_w(10, 0xff, 0); /* HINT disabled */ + vdp_reg_w(12, 0x81, 0); /* H40 mode */ + vdp_reg_w(15, 0x02, 0); /* auto increment */ + } +} + +int vdp_context_save(uint8 *state) +{ + int bufferptr = 0; + + save_param(sat, sizeof(sat)); + save_param(vram, sizeof(vram)); + save_param(cram, sizeof(cram)); + save_param(vsram, sizeof(vsram)); + save_param(reg, sizeof(reg)); + save_param(&addr, sizeof(addr)); + save_param(&addr_latch, sizeof(addr_latch)); + save_param(&code, sizeof(code)); + save_param(&pending, sizeof(pending)); + save_param(&status, sizeof(status)); + save_param(&dmafill, sizeof(dmafill)); + save_param(&hint_pending, sizeof(hint_pending)); + save_param(&vint_pending, sizeof(vint_pending)); + save_param(&m68k_irq_state, sizeof(m68k_irq_state)); + save_param(&dma_length, sizeof(dma_length)); + save_param(&dma_type, sizeof(dma_type)); + save_param(&cached_write, sizeof(cached_write)); + + return bufferptr; +} + +int vdp_context_load(uint8 *state, char *version) +{ + int i, bufferptr = 0; + uint8 temp_reg[0x20]; + + load_param(sat, sizeof(sat)); + load_param(vram, sizeof(vram)); + load_param(cram, sizeof(cram)); + load_param(vsram, sizeof(vsram)); + load_param(temp_reg, sizeof(temp_reg)); + load_param(&addr, sizeof(addr)); + load_param(&addr_latch, sizeof(addr_latch)); + load_param(&code, sizeof(code)); + load_param(&pending, sizeof(pending)); + load_param(&status, sizeof(status)); + + /* different size parameter starting from 1.5.0 */ + if ((version[11] > 0x31) || (version[13] > 0x34)) + { + load_param(&dmafill, sizeof(dmafill)); + } + else + { + load_param(&dmafill, sizeof(uint8)); + } + + load_param(&hint_pending, sizeof(hint_pending)); + load_param(&vint_pending, sizeof(vint_pending)); + load_param(&m68k_irq_state, sizeof(m68k_irq_state)); + + /* extended state (1.4.1 and above) */ + if ((version[11] > 0x31) || (version[13] > 0x34) || (version[15] > 0x30)) + { + load_param(&dma_length, sizeof(dma_length)); + load_param(&dma_type, sizeof(dma_type)); + + if ((version[11] == 0x31) && (version[13] == 0x34) && (version[15] == 0x31)) /* 1.4.1 only */ + { + uint16 temp; + load_param(&temp, sizeof(temp)); + } + + load_param(&cached_write, sizeof(cached_write)); + } + + /* restore VDP registers */ + for (i=0;i<0x20;i++) + { + vdp_reg_w(i, temp_reg[i], 0); + } + + /* restore FIFO timings */ + fifo_latency = 214 - (reg[12] & 1) * 24; + fifo_latency <<= ((code & 0x0F) == 0x01); + + /* restore current NTSC/PAL mode */ + status = (status & ~1) | vdp_pal; + + if (reg[1] & 0x04) + { + /* Mode 5 */ + bg_list_index = 0x800; + + /* reinitialize palette */ + color_update(0, *(uint16 *)&cram[border << 1]); + for(i = 1; i < 0x40; i++) + { + color_update(i, *(uint16 *)&cram[i << 1]); + } + } + else + { + /* Mode 4 */ + bg_list_index = 0x200; + + /* reinitialize palette */ + color_update(0x40, *(uint16 *)&cram[(0x10 | (border & 0x0F)) << 1]); + for(i = 0; i < 0x20; i ++) + { + color_update(i, *(uint16 *)&cram[i << 1]); + } + } + + /* invalidate cache */ + for (i=0;i VDP 32-cell Active 16 + Blanking 167 + 40-cell Active 18 + Blanking 205 + VRAM Fill 32-cell Active 15 + Blanking 166 + 40-cell Active 17 + Blanking 204 + VRAM Copy 32-cell Active 8 + Blanking 83 + 40-cell Active 9 + Blanking 102 + + 'Active' is the active display period, 'Blanking' is either the vertical + blanking period or when the display is forcibly blanked via register #1. + + The above transfer counts are all in bytes, unless the destination is + CRAM or VSRAM for a 68K > VDP transfer, in which case it is in words. + */ + unsigned int rate = dma_timing[(status & 8) || !(reg[1] & 0x40)][reg[12] & 1]; + + /* Adjust for 68k bus DMA to VRAM (one word = 2 access) or DMA Copy (one read + one write = 2 access) */ + rate = rate >> (dma_type & 1); + + /* Remaining DMA cycles */ + if (status & 8) + { + /* Process DMA until the end of VBLANK (speed optimization) */ + /* Note: This is not 100% accurate since rate could change if display width */ + /* is changed during VBLANK but no games seem to do this. */ + dma_cycles = (lines_per_frame * MCYCLES_PER_LINE) - cycles; + } + else + { + /* Process DMA until the end of current line */ + dma_cycles = (mcycles_vdp + MCYCLES_PER_LINE) - cycles; + } + + /* Remaining DMA bytes for that line */ + int dma_bytes = (dma_cycles * rate) / MCYCLES_PER_LINE; + +#ifdef LOGVDP + error("[%d(%d)][%d(%d)] DMA type %d (%d access/line)(%d cycles left)-> %d access (%d remaining) (%x)\n", v_counter, mcycles_68k/MCYCLES_PER_LINE, mcycles_68k, mcycles_68k%MCYCLES_PER_LINE,dma_type/4, rate, dma_cycles, dma_bytes, dma_length, m68k_get_reg (NULL, M68K_REG_PC)); +#endif + + /* Check if DMA can be finished before the end of current line */ + if (dma_length < dma_bytes) + { + /* Adjust remaining DMA bytes */ + dma_bytes = dma_length; + dma_cycles = (dma_bytes * MCYCLES_PER_LINE) / rate; + } + + /* Update DMA timings */ + if (dma_type < 2) + { + /* 68K is frozen during DMA from V-Bus */ + mcycles_68k = cycles + dma_cycles; + #ifdef LOGVDP + error("-->CPU frozen for %d cycles\n", dma_cycles); + #endif + } + else + { + /* Set DMA Busy flag */ + status |= 0x02; + + /* 68K is still running, set DMA end cycle */ + dma_endCycles = cycles + dma_cycles; +#ifdef LOGVDP + error("-->DMA ends in %d cycles\n", dma_cycles); +#endif + } + + /* Process DMA */ + if (dma_bytes > 0) + { + /* Update DMA length */ + dma_length -= dma_bytes; + + /* Select DMA operation */ + switch (dma_type) + { + case 0: + case 1: + { + /* 68K bus to VRAM, CRAM or VSRAM */ + vdp_dma_vbus(dma_bytes); + break; + } + + case 2: + { + /* VRAM Fill */ + vdp_dma_fill(dmafill, dma_bytes); + break; + } + + case 3: + { + /* VRAM Copy */ + vdp_dma_copy(dma_bytes); + break; + } + } + + /* Check if DMA is finished */ + if (!dma_length) + { + /* Reset DMA length registers */ + reg[19] = reg[20] = 0; + + /* Perform cached write, if any */ + if (cached_write >= 0) + { + vdp_68k_ctrl_w(cached_write); + cached_write = -1; + } + } + } +} + + +/*--------------------------------------------------------------------------*/ +/* Control port access functions */ +/*--------------------------------------------------------------------------*/ + +void vdp_68k_ctrl_w(unsigned int data) +{ + if (pending == 0) + { + /* A single long word write instruction could have started DMA with the first word */ + if (dma_length) + { + /* 68k is frozen during 68k bus DMA */ + /* Second word should be written after DMA completion */ + /* See Formula One & Kawasaki Superbike Challenge */ + if (dma_type < 2) + { + /* Latch second control word for later */ + cached_write = data; + return; + } + } + + if ((data & 0xC000) == 0x8000) + { + /* VDP register write */ + vdp_reg_w((data >> 8) & 0x1F, data & 0xFF, mcycles_68k); + } + else + { + /* Set pending flag (Mode 5 only) */ + pending = reg[1] & 4; + } + + /* Update address and code registers */ + addr = addr_latch | (data & 0x3FFF); + code = ((code & 0x3C) | ((data >> 14) & 0x03)); + } + else + { + /* Clear pending flag */ + pending = 0; + + /* Save address bits A15 and A14 */ + addr_latch = (data & 3) << 14; + + /* Update address and code registers */ + addr = addr_latch | (addr & 0x3FFF); + code = ((code & 0x03) | ((data >> 2) & 0x3C)); + + /* Detect DMA operation */ + if ((code & 0x20) && (reg[1] & 0x10)) + { + switch (reg[23] >> 6) + { + case 2: + { + /* VRAM write operation only (Williams Greatest Hits after soft reset) */ + if ((code & 0x0F) == 1) + { + /* VRAM fill will be triggered by next write to DATA port */ + dmafill = 0x100; + } + break; + } + + case 3: + { + /* VRAM read/write operation only */ + if ((code & 0x1F) == 0x10) + { + /* DMA length */ + dma_length = (reg[20] << 8) | reg[19]; + + /* Zero DMA length */ + if (!dma_length) + { + dma_length = 0x10000; + } + + /* VRAM copy */ + dma_type = 3; + vdp_dma_update(mcycles_68k); + } + break; + } + + default: + { + /* DMA length */ + dma_length = (reg[20] << 8) | reg[19]; + + /* Zero DMA length */ + if (!dma_length) + { + dma_length = 0x10000; + } + + /* SVP RAM transfer latency */ + reg[21] -= (svp && !(reg[23] & 0x60)); + + /* 68k to VDP DMA */ + dma_type = (code & 0x06) ? 0 : 1; + vdp_dma_update(mcycles_68k); + break; + } + } + } + } + + /* + FIFO emulation (Chaos Engine/Soldier of Fortune, Double Clutch, Sol Deace) + -------------------------------------------------------------------------- + + CPU access per line is limited during active display: + H32: 16 access --> 3420/16 = ~214 Mcycles between access + H40: 18 access --> 3420/18 = ~190 Mcycles between access + + This is an approximation, on real hardware, the delay between access is + more likely 16 pixels (128 or 160 Mcycles) with no access allowed during + HBLANK (~860 Mcycles), H40 mode being probably a little more restricted. + + Each VRAM access is byte wide, so one VRAM write (word) need twice cycles. + + */ + fifo_latency = 214 - (reg[12] & 1) * 24; + fifo_latency <<= ((code & 0x0F) == 0x01); +} + +void vdp_z80_ctrl_w(unsigned int data) +{ + switch (pending) + { + case 0: + { + /* Latch LSB */ + addr_latch = data; + + /* Set LSB pending flag */ + pending = 1; + return; + } + + case 1: + { + /* Update address and code registers */ + addr = (addr & 0xC000) | ((data & 0x3F) << 8) | addr_latch ; + code = ((code & 0x3C) | ((data >> 6) & 0x03)); + + if ((code & 0x03) == 0x02) + { + /* VDP register write */ + vdp_reg_w(data & 0x1F, addr_latch, mcycles_z80 + Z80_CYCLE_OFFSET); + + /* Clear pending flag */ + pending = 0; + return; + } + + /* Set Mode 5 pending flag */ + pending = (reg[1] & 4) >> 1; + + if (!pending && !(code & 0x03)) + { + /* Process VRAM read */ + fifo[0] = vram[addr & 0x3FFF]; + + /* Increment address register */ + addr += (reg[15] + 1); + } + return; + } + + case 2: + { + /* Latch LSB */ + addr_latch = data; + + /* Set LSB pending flag */ + pending = 3; + return; + } + + case 3: + { + /* Clear pending flag */ + pending = 0; + + /* Update address and code registers */ + addr = ((addr_latch & 3) << 14) | (addr & 0x3FFF); + code = ((code & 0x03) | ((addr_latch >> 2) & 0x3C)); + + /* Detect DMA operation */ + if ((code & 0x20) && (reg[1] & 0x10)) + { + switch (reg[23] >> 6) + { + case 2: + { + /* VRAM write operation only (Williams Greatest Hits after soft reset) */ + if ((code & 0x0F) == 1) + { + /* VRAM fill will be triggered by next write to DATA port */ + dmafill = 0x100; + } + break; + } + + case 3: + { + /* VRAM read/write operation only */ + if ((code & 0x1F) == 0x10) + { + /* DMA length */ + dma_length = (reg[20] << 8) | reg[19]; + + /* Zero DMA length */ + if (!dma_length) + { + dma_length = 0x10000; + } + + /* VRAM copy */ + dma_type = 3; + vdp_dma_update(mcycles_z80); + } + break; + } + + default: + { + /* DMA from V-Bus does not work when Z80 is in control */ + break; + } + } + } + } + return; + } +} + +unsigned int vdp_ctrl_r(unsigned int cycles) +{ + /* + * Status register + * + * Bits + * 0 NTSC(0)/PAL(1) + * 1 DMA Busy + * 2 During HBlank + * 3 During VBlank + * 4 0:1 even:odd field (interlaced modes only) + * 5 Sprite collision + * 6 Too many sprites per line + * 7 v interrupt occurred + * 8 Write FIFO full + * 9 Write FIFO empty + * 10 - 15 Open Bus + */ + + /* Update FIFO flags */ + vdp_fifo_update(cycles); + + /* Update DMA Busy flag */ + if ((status & 2) && !dma_length && (cycles >= dma_endCycles)) + { + status &= 0xFFFD; + } + + /* Return VDP status */ + unsigned int temp = status; + + /* Display OFF: VBLANK flag is set */ + if (!(reg[1] & 0x40)) + { + temp |= 0x08; + } + + /* HBLANK flag (Sonic 3 and Sonic 2 "VS Modes", Lemmings 2, Mega Turrican, V.R Troopers, Gouketsuji Ichizoku,...) */ + /* NB: this is not 100% accurate and need to be verified on real hardware */ + if ((cycles % MCYCLES_PER_LINE) < 588) + { + temp |= 0x04; + } + + /* Clear pending flag */ + pending = 0; + + /* Clear SPR/SCOL flags */ + status &= 0xFF9F; + + /* Mode 4 specific */ + if (!(reg[1] & 4)) + { + /* Cycle-accurate VINT flag (required by some Master System games) */ + if ((v_counter == bitmap.viewport.h) && ((cycles / MCYCLES_PER_LINE) > (v_counter + 1))) + { + temp |= 0x80; + } + + /* Clear HINT & VINT pending flags */ + hint_pending = vint_pending = 0; + *irq_line = 0x10; + + /* Clear VINT flag */ + status &= ~0x80; + } + +#ifdef LOGVDP + error("[%d(%d)][%d(%d)] VDP status read -> 0x%x (0x%x) (%x)\n", v_counter, cycles/MCYCLES_PER_LINE, cycles, cycles%MCYCLES_PER_LINE, temp, status, m68k_get_reg (NULL, M68K_REG_PC)); +#endif + return (temp); +} + + +/*--------------------------------------------------------------------------*/ +/* HV Counters */ +/*--------------------------------------------------------------------------*/ + +unsigned int vdp_hvc_r(unsigned int cycles) +{ + /* VCounter */ + int vc = (cycles / MCYCLES_PER_LINE) - 1; + + /* Check counter overflow */ + if (vc > vc_max) + { + vc -= lines_per_frame; + } + + /* Check interlaced modes */ + if (interlaced) + { + /* Interlace mode 2 (Sonic the Hedgehog 2, Combat Cars) */ + vc <<= im2_flag; + + /* Replace bit 0 with bit 8 */ + vc = (vc & ~1) | ((vc >> 8) & 1); + } + + /* Returned value */ + unsigned int temp = (vc & 0xff) << 8; + + /* Check if HVC is frozen */ + if (!hvc_latch) + { + /* Cycle-accurate HCounter (Striker, Mickey Mania, Skitchin, Road Rash I,II,III, Sonic 3D Blast...) */ + temp |= hctab[cycles % MCYCLES_PER_LINE]; + } + else + { + if (reg[1] & 4) + { + /* Mode5: both counters are frozen (Lightgun games, Sunset Riders) */ + temp = hvc_latch & 0xffff; + } + else + { + /* Mode 4: VCounter runs normally, HCounter is frozen */ + temp |= (hvc_latch & 0xff); + } + } + +#ifdef LOGVDP + error("[%d(%d)][%d(%d)] HVC read -> 0x%x (%x)\n", v_counter, cycles/MCYCLES_PER_LINE, cycles, cycles%MCYCLES_PER_LINE, temp, m68k_get_reg (NULL, M68K_REG_PC)); +#endif + return (temp); +} + + +/*--------------------------------------------------------------------------*/ +/* Test registers */ +/*--------------------------------------------------------------------------*/ + +void vdp_test_w(unsigned int data) +{ +#ifdef LOGERROR + error("Unused VDP Write 0x%x (%08x)\n", data, m68k_get_reg (NULL, M68K_REG_PC)); +#endif +} + + +/*--------------------------------------------------------------------------*/ +/* 68k interrupt handler (TODO: check how interrupts are handled in Mode 4) */ +/*--------------------------------------------------------------------------*/ + +int vdp_68k_irq_ack(int int_level) +{ +#ifdef LOGVDP + error("[%d(%d)][%d(%d)] INT Level %d ack (%x)\n", v_counter, mcycles_68k/MCYCLES_PER_LINE, mcycles_68k, mcycles_68k%MCYCLES_PER_LINE,int_level, m68k_get_reg (NULL, M68K_REG_PC)); +#endif + + /* VINT has higher priority */ + if ((m68k_irq_state & 6) == 6) + { +#ifdef LOGVDP + error("---> VINT cleared\n"); +#endif + + /* Clear VINT pending flag */ + vint_pending = 0; + status &= ~0x80; + + /* Update IRQ status */ + if (hint_pending & reg[0]) + { + m68k_irq_state = 0x14; + } + else + { + m68k_irq_state = 0; + m68k_set_irq(0); + } + } + else + { +#ifdef LOGVDP + error("---> HINT cleared\n"); +#endif + + /* Clear HINT pending flag */ + hint_pending = 0; + + /* Update IRQ status */ + if (vint_pending & reg[1]) + { + m68k_irq_state = 0x16; + } + else + { + m68k_irq_state = 0; + m68k_set_irq(0); + } + } + + return M68K_INT_ACK_AUTOVECTOR; +} + + +/*--------------------------------------------------------------------------*/ +/* Internal registers access function */ +/*--------------------------------------------------------------------------*/ + +static void vdp_reg_w(unsigned int r, unsigned int d, unsigned int cycles) +{ +#ifdef LOGVDP + error("[%d(%d)][%d(%d)] VDP register %d write -> 0x%x (%x)\n", v_counter, cycles/MCYCLES_PER_LINE, cycles, cycles%MCYCLES_PER_LINE, r, d, m68k_get_reg (NULL, M68K_REG_PC)); +#endif + + /* VDP registers #11 to #23 cannot be updated in Mode 4 (Captain Planet & Avengers, Bass Master Classic Pro Edition) */ + if (!(reg[1] & 4) && (r > 10)) + { + return; + } + + switch(r) + { + case 0: /* CTRL #1 */ + { + /* Look for changed bits */ + r = d ^ reg[0]; + reg[0] = d; + + /* Line Interrupt */ + if ((r & 0x10) && hint_pending) + { + /* Update IRQ status */ + *irq_line = 0x30; + + if (vint_pending & reg[1]) + { + *irq_line |= 0x06; + } + else if (d & 0x10) + { + *irq_line |= 0x04; + } + } + + /* Palette selection */ + if (r & 0x04) + { + /* Reset color palette */ + int i; + if (reg[1] & 0x04) + { + /* Mode 5 */ + color_update(0x00, *(uint16 *)&cram[border << 1]); + for (i = 1; i < 0x40; i++) + { + color_update (i, *(uint16 *)&cram[i << 1]); + } + } + else + { + /* Mode 4 */ + color_update(0x40, *(uint16 *)&cram[(0x10 | (border & 0x0F)) << 1]); + for (i = 0; i < 0x20; i++) + { + color_update (i, *(uint16 *)&cram[i << 1]); + } + } + } + + /* HVC latch (Sunset Riders, Lightgun games) */ + if (r & 0x02) + { + /* Mode 5 only */ + if (reg[1] & 0x04) + { + if (d & 0x02) + { + /* Latch current HVC */ + hvc_latch = vdp_hvc_r(cycles) | 0x10000; + } + else + { + /* Free-running HVC */ + hvc_latch = 0; + } + } + } + break; + } + + case 1: /* CTRL #2 */ + { + /* Look for changed bits */ + r = d ^ reg[1]; + reg[1] = d; + + /* Display status (modified during active display) */ + if ((r & 0x40) && (v_counter < bitmap.viewport.h)) + { + /* Cycle offset vs HBLANK */ + int offset = cycles - mcycles_vdp - 860; + if (offset <= 0) + { + /* If display was disabled during HBLANK (Mickey Mania 3D level), sprite rendering is limited */ + if ((d & 0x40) && (object_count > 5) && (offset >= -500)) + { + object_count = 5; + } + + /* Redraw entire line (Legend of Galahad, Lemmings 2, Formula One, Kawasaki Super Bike, Deadly Moves,...) */ + render_line(v_counter); + +#ifdef LOGVDP + error("Line redrawn (%d sprites) \n",object_count); +#endif + } + else + { + /* Active pixel offset */ + if (reg[12] & 1) + { + /* dot clock = MCLK / 8 */ + offset = (offset / 8); + } + else + { + /* dot clock = MCLK / 10 */ + offset = (offset / 10) + 16; + } + + /* Line is partially blanked (Nigel Mansell's World Championship Racing , Ren & Stimpy Show, ...) */ + if (offset < bitmap.viewport.w) + { +#ifdef LOGVDP + error("Line %d redrawn from pixel %d\n",v_counter,offset); +#endif + if (d & 0x40) + { + render_line(v_counter); + blank_line(v_counter, 0, offset); + } + else + { + blank_line(v_counter, offset, bitmap.viewport.w - offset); + } + } + } + } + + /* Frame Interrupt */ + if ((r & 0x20) && vint_pending) + { + /* Update IRQ status */ + *irq_line = 0x30; + + if (d & 0x20) + { + *irq_line |= 0x06; + } + else if (hint_pending & reg[0]) + { + *irq_line |= 0x04; + } + } + + /* Active display height (Mode 5 only) */ + if (r & 0x08) + { + /* Mode 5 only */ + if (d & 0x04) + { + if (v_counter < bitmap.viewport.h) + { + /* Update active display height */ + bitmap.viewport.h = 224 + ((d & 8) << 1); + bitmap.viewport.y = (config.overscan & 1) * (8 - (d & 8) + 24*vdp_pal); + } + else + { + /* Changes should be applied on next frame */ + bitmap.viewport.changed |= 2; + } + + /* Update vertical counter max value */ + vc_max = vc_table[(d >> 2) & 3][vdp_pal]; + } + } + + /* VDP Mode switch */ + if (r & 0x04) + { + int i; + if (d & 0x04) + { + /* Mode 5 rendering */ + parse_satb = parse_satb_m5; + update_bg_pattern_cache = update_bg_pattern_cache_m5; + if (im2_flag) + { + render_bg = (reg[11] & 0x04) ? render_bg_m5_im2_vs : render_bg_m5_im2; + render_obj = (reg[12] & 0x08) ? render_obj_m5_im2_ste : render_obj_m5_im2; + } + else + { + render_bg = (reg[11] & 0x04) ? render_bg_m5_vs : render_bg_m5; + render_obj = (reg[12] & 0x08) ? render_obj_m5_ste : render_obj_m5; + } + + /* Reset color palette */ + color_update(0x00, *(uint16 *)&cram[border << 1]); + for (i = 1; i < 0x40; i++) + { + color_update (i, *(uint16 *)&cram[i << 1]); + } + + /* Mode 5 bus access */ + vdp_68k_data_w = vdp_68k_data_w_m5; + vdp_z80_data_w = vdp_z80_data_w_m5; + vdp_68k_data_r = vdp_68k_data_r_m5; + vdp_z80_data_r = vdp_z80_data_r_m5; + + /* Change display height */ + if (v_counter < bitmap.viewport.h) + { + /* Update active display */ + bitmap.viewport.h = 224 + ((d & 8) << 1); + bitmap.viewport.y = (config.overscan & 1) * (8 - (d & 8) + 24*vdp_pal); + } + else + { + /* Changes should be applied on next frame */ + bitmap.viewport.changed |= 2; + } + + /* Clear HVC latched value */ + hvc_latch = 0; + + /* Check if HVC latch bit is set */ + if (reg[0] & 0x02) + { + /* Latch current HVC */ + hvc_latch = vdp_hvc_r(cycles) | 0x10000; + } + + /* max tiles to invalidate */ + bg_list_index = 0x800; + } + else + { + /* Mode 4 rendering */ + parse_satb = parse_satb_m4; + update_bg_pattern_cache = update_bg_pattern_cache_m4; + render_bg = render_bg_m4; + render_obj = render_obj_m4; + + /* Reset color palette */ + color_update(0x40, *(uint16 *)&cram[(0x10 | (border & 0x0F)) << 1]); + for (i = 0; i < 0x20; i++) + { + color_update (i, *(uint16 *)&cram[i << 1]); + } + + /* Mode 4 bus access */ + vdp_68k_data_w = vdp_68k_data_w_m4; + vdp_z80_data_w = vdp_z80_data_w_m4; + vdp_68k_data_r = vdp_68k_data_r_m4; + vdp_z80_data_r = vdp_z80_data_r_m4; + + if (v_counter < bitmap.viewport.h) + { + /* Update active display height */ + bitmap.viewport.h = 192; + bitmap.viewport.y = (config.overscan & 1) * 24 * (vdp_pal + 1); + } + else + { + /* Changes should be applied on next frame */ + bitmap.viewport.changed |= 2; + } + + /* Latch current HVC */ + hvc_latch = vdp_hvc_r(cycles) | 0x10000; + + /* max tiles to invalidate */ + bg_list_index = 0x200; + } + + /* Invalidate pattern cache */ + for (i=0;i> 2) & 3][vdp_pal]; + } + break; + } + + case 2: /* Plane A Name Table Base */ + { + reg[2] = d; + ntab = (d << 10) & 0xE000; + break; + } + + case 3: /* Window Plane Name Table Base */ + { + reg[3] = d; + if (reg[12] & 0x01) + { + ntwb = (d << 10) & 0xF000; + } + else + { + ntwb = (d << 10) & 0xF800; + } + break; + } + + case 4: /* Plane B Name Table Base */ + { + reg[4] = d; + ntbb = (d << 13) & 0xE000; + break; + } + + case 5: /* Sprite Attribute Table Base */ + { + reg[5] = d; + satb = (d << 9) & sat_base_mask; + break; + } + + case 7: /* Backdrop color */ + { + reg[7] = d; + + /* Check if backdrop color changed */ + d &= 0x3F; + + if (d != border) + { + /* Update backdrop color */ + border = d; + + /* Reset palette entry */ + if (reg[1] & 4) + { + /* Mode 5 */ + color_update(0x00, *(uint16 *)&cram[d << 1]); + } + else + { + /* Mode 4 */ + color_update(0x40, *(uint16 *)&cram[(0x10 | (d & 0x0F)) << 1]); + } + + /* Backdrop color modified during HBLANK (Road Rash 1,2,3)*/ + if ((v_counter < bitmap.viewport.h) && (cycles <= (mcycles_vdp + 860))) + { + /* remap entire line */ + remap_line(v_counter); + +#ifdef LOGVDP + error("Line remapped\n"); +#endif + } + +#ifdef LOGVDP + else + { + error("Line NOT remapped\n"); + } +#endif + } + break; + } + + case 8: /* HSCROLL (Mode 4) */ + { + reg[8] = d; + + /* HScroll is latched at the start of a line */ + /* so make sure we are not already next line */ + if ((cycles - Z80_CYCLE_OFFSET) < (mcycles_vdp + MCYCLES_PER_LINE - 4)) + { + /* Update Hscroll data for next line */ + hscroll = d; + } + break; + } + + case 11: /* CTRL #3 */ + { + reg[11] = d; + + /* Horizontal scrolling mode */ + hscroll_mask = hscroll_mask_table[d & 0x03]; + + /* Vertical Scrolling mode */ + if (d & 0x04) + { + render_bg = im2_flag ? render_bg_m5_im2_vs : render_bg_m5_vs; + } + else + { + render_bg = im2_flag ? render_bg_m5_im2 : render_bg_m5; + } + break; + } + + case 12: /* CTRL #4 */ + { + /* Look for changed bits */ + r = d ^ reg[12]; + reg[12] = d; + + /* Shadow & Highlight mode */ + if (r & 0x08) + { + /* Reset color palette */ + int i; + color_update(0x00, *(uint16 *)&cram[border << 1]); + for (i = 1; i < 0x40; i++) + { + color_update (i, *(uint16 *)&cram[i << 1]); + } + + /* Update sprite rendering function */ + if (d & 0x08) + { + render_obj = im2_flag ? render_obj_m5_im2_ste : render_obj_m5_ste; + } + else + { + render_obj = im2_flag ? render_obj_m5_im2 : render_obj_m5; + } + } + + /* Interlaced modes */ + if (r & 0x06) + { + /* changes should be applied on next frame */ + bitmap.viewport.changed |= 2; + } + + /* Active display width */ + if (r & 0x01) + { + if (d & 0x01) + { + /* Update display-dependant registers */ + ntwb = (reg[3] << 10) & 0xF000; + satb = (reg[5] << 9) & 0xFC00; + sat_base_mask = 0xFC00; + sat_addr_mask = 0x03FF; + + /* Update HC table */ + hctab = cycle2hc40; + + /* Update clipping */ + window_clip(reg[17], 1); + + /* Update fifo timings */ + fifo_latency = 190; + } + else + { + /* Update display-dependant registers */ + ntwb = (reg[3] << 10) & 0xF800; + satb = (reg[5] << 9) & 0xFE00; + sat_base_mask = 0xFE00; + sat_addr_mask = 0x01FF; + + /* Update HC table */ + hctab = cycle2hc32; + + /* Update clipping */ + window_clip(reg[17], 0); + + /* Update FIFO timings */ + fifo_latency = 214; + } + + /* Adjust FIFO timings for VRAM writes */ + fifo_latency <<= ((code & 0x0F) == 0x01); + + /* Active display width modified during HBLANK (Bugs Bunny Double Trouble) */ + if ((v_counter < bitmap.viewport.h) && (cycles <= (mcycles_vdp + 860))) + { + /* Update active display width */ + bitmap.viewport.w = 256 + ((d & 1) << 6); + + /* Redraw entire line */ + render_line(v_counter); + } + else + { + /* Changes should be applied on next frame (Golden Axe III intro) */ + /* NB: This is not 100% accurate but is required by GCN/Wii port (GX texture direct mapping) */ + /* and isn't noticeable anyway since display is generally disabled when active width is modified */ + bitmap.viewport.changed |= 2; + } + } + break; + } + + case 13: /* HScroll Base Address */ + { + reg[13] = d; + hscb = (d << 10) & 0xFC00; + break; + } + + case 16: /* Playfield size */ + { + reg[16] = d; + playfield_shift = shift_table[(d & 3)]; + playfield_col_mask = col_mask_table[(d & 3)]; + playfield_row_mask = row_mask_table[(d >> 4) & 3]; + break; + } + + case 17: /* Window/Plane A vertical clipping */ + { + reg[17] = d; + window_clip(d, reg[12] & 1); + break; + } + + default: + { + reg[r] = d; + break; + } + } +} + + +/*--------------------------------------------------------------------------*/ +/* FIFO update function */ +/*--------------------------------------------------------------------------*/ + +static void vdp_fifo_update(unsigned int cycles) +{ + if (fifo_write_cnt > 0) + { + /* Get number of FIFO reads */ + int fifo_read = ((cycles - fifo_lastwrite) / fifo_latency); + + if (fifo_read > 0) + { + /* Process FIFO entries */ + fifo_write_cnt -= fifo_read; + + /* Clear FIFO full flag */ + status &= 0xFEFF; + + /* Check remaining FIFO entries */ + if (fifo_write_cnt <= 0) + { + /* Set FIFO empty flag */ + status |= 0x200; + fifo_write_cnt = 0; + } + + /* Update FIFO cycle count */ + fifo_lastwrite += (fifo_read * fifo_latency); + } + } +} + + +/*--------------------------------------------------------------------------*/ +/* Internal 16-bit data bus access function (Mode 5 only) */ +/*--------------------------------------------------------------------------*/ + +static void vdp_bus_w(unsigned int data) +{ + /* Check destination code */ + switch (code & 0x0F) + { + case 0x01: /* VRAM */ + { +#ifdef LOGVDP + error("[%d(%d)][%d(%d)] VRAM 0x%x write -> 0x%x (%x)\n", v_counter, mcycles_68k/MCYCLES_PER_LINE, mcycles_68k, mcycles_68k%MCYCLES_PER_LINE, addr, data, m68k_get_reg (NULL, M68K_REG_PC)); +#endif + /* Byte-swap data if A0 is set */ + if (addr & 1) + { + data = ((data >> 8) | (data << 8)) & 0xFFFF; + } + + /* VRAM address */ + int index = addr & 0xFFFE; + + /* Pointer to VRAM */ + uint16 *p = (uint16 *)&vram[index]; + + /* Intercept writes to Sprite Attribute Table */ + if ((index & sat_base_mask) == satb) + { + /* Update internal SAT */ + *(uint16 *) &sat[index & sat_addr_mask] = data; + } + + /* Only write unique data to VRAM */ + if (data != *p) + { + /* Write data to VRAM */ + *p = data; + + /* Update pattern cache */ + int name; + MARK_BG_DIRTY (index); + } + break; + } + + case 0x03: /* CRAM */ + { +#ifdef LOGVDP + error("[%d(%d)][%d(%d)] CRAM 0x%x write -> 0x%x (%x)\n", v_counter, mcycles_68k/MCYCLES_PER_LINE, mcycles_68k, mcycles_68k%MCYCLES_PER_LINE, addr, data, m68k_get_reg (NULL, M68K_REG_PC)); +#endif + /* Pointer to CRAM 9-bit word */ + uint16 *p = (uint16 *)&cram[addr & 0x7E]; + + /* Pack 16-bit bus data (BBB0GGG0RRR0) to 9-bit CRAM data (BBBGGGRRR) */ + data = ((data & 0xE00) >> 3) | ((data & 0x0E0) >> 2) | ((data & 0x00E) >> 1); + + /* Check if CRAM data is being modified */ + if (data != *p) + { + /* Write CRAM data */ + *p = data; + + /* CRAM index (64 words) */ + int index = (addr >> 1) & 0x3F; + + /* Color entry 0 of each palette is never displayed (transparent pixel) */ + if (index & 0x0F) + { + /* Update color palette */ + color_update(index, data); + } + + /* Update backdrop color */ + if (index == border) + { + color_update(0x00, data); + } + + /* CRAM modified during HBLANK (Striker, Zero the Kamikaze, etc) */ + if ((v_counter < bitmap.viewport.h) && (reg[1]& 0x40) && (mcycles_68k <= (mcycles_vdp + 860))) + { + /* Remap current line */ + remap_line(v_counter); + #ifdef LOGVDP + error("Line remapped\n"); + #endif + } + #ifdef LOGVDP + else error("Line NOT remapped\n"); + #endif + } + break; + } + + case 0x05: /* VSRAM */ + { +#ifdef LOGVDP + error("[%d(%d)][%d(%d)] VSRAM 0x%x write -> 0x%x (%x)\n", v_counter, mcycles_68k/MCYCLES_PER_LINE, mcycles_68k, mcycles_68k%MCYCLES_PER_LINE, addr, data, m68k_get_reg (NULL, M68K_REG_PC)); +#endif + *(uint16 *)&vsram[addr & 0x7E] = data; + break; + } + +#ifdef LOGVDP + default: + { + error("[%d(%d)][%d(%d)] Unknown (%d) 0x%x write -> 0x%x (%x)\n", v_counter, mcycles_68k/MCYCLES_PER_LINE, mcycles_68k, mcycles_68k%MCYCLES_PER_LINE, code, addr, data, m68k_get_reg (NULL, M68K_REG_PC)); + break; + } +#endif + } + + /* Increment address register (TODO: see how address is incremented in Mode 4) */ + addr += reg[15]; +} + + +/*--------------------------------------------------------------------------*/ +/* 68k data port access functions (Genesis mode) */ +/*--------------------------------------------------------------------------*/ + +static void vdp_68k_data_w_m4(unsigned int data) +{ + /* Clear pending flag */ + pending = 0; + + /* Check destination code */ + if (code & 0x02) + { + /* CRAM index (32 words) */ + int index = addr & 0x1F; + + /* Pointer to CRAM 9-bit word */ + uint16 *p = (uint16 *)&cram[index << 1]; + + /* Pack 16-bit data (xxx000BBGGRR) to 9-bit CRAM data (xxxBBGGRR) */ + data = ((data & 0xE00) >> 3) | (data & 0x3F); + + /* Check if CRAM data is being modified */ + if (data != *p) + { + /* Write CRAM data */ + *p = data; + + /* Update color palette */ + color_update(index, data); + + /* Update backdrop color */ + if (index == (0x10 | (border & 0x0F))) + { + color_update(0x40, data); + } + } + } + else + { + /* Byte-swap data if A0 is set */ + if (addr & 1) + { + data = ((data >> 8) | (data << 8)) & 0xFFFF; + } + + /* VRAM address (interleaved format) */ + int index = ((addr << 1) & 0x3FC) | ((addr & 0x200) >> 8) | (addr & 0x3C00); + + /* Pointer to VRAM */ + uint16 *p = (uint16 *)&vram[index]; + + /* Only write unique data to VRAM */ + if (data != *p) + { + /* Write data to VRAM */ + *p = data; + + /* Update the pattern cache */ + int name; + MARK_BG_DIRTY (index); + } + } + + /* Increment address register */ + addr += (reg[15] + 1); +} + +static void vdp_68k_data_w_m5(unsigned int data) +{ + /* Clear pending flag */ + pending = 0; + + /* Restricted VDP writes during active display */ + if (!(status & 8) && (reg[1] & 0x40)) + { + /* Update VDP FIFO */ + vdp_fifo_update(mcycles_68k); + + /* Clear FIFO empty flag */ + status &= 0xFDFF; + + /* 4 words can be stored */ + if (fifo_write_cnt < 4) + { + /* Increment FIFO counter */ + fifo_write_cnt++; + + /* Set FIFO full flag if 4 words are stored */ + status |= ((fifo_write_cnt & 4) << 6); + } + else + { + /* CPU is locked until last FIFO entry has been processed (Chaos Engine, Soldiers of Fortune, Double Clutch) */ + fifo_lastwrite += fifo_latency; + mcycles_68k = fifo_lastwrite; + } + } + + /* Write data */ + vdp_bus_w(data); + + /* DMA Fill */ + if (dmafill & 0x100) + { + /* Fill data (DMA fill flag is cleared) */ + dmafill = data >> 8; + + /* DMA length */ + dma_length = (reg[20] << 8) | reg[19]; + + /* Zero DMA length */ + if (!dma_length) + { + dma_length = 0x10000; + } + + /* Perform DMA Fill*/ + dma_type = 2; + vdp_dma_update(mcycles_68k); + } +} + +static unsigned int vdp_68k_data_r_m4(void) +{ + /* Clear pending flag */ + pending = 0; + + /* VRAM address (interleaved format) */ + int index = ((addr << 1) & 0x3FC) | ((addr & 0x200) >> 8) | (addr & 0x3C00); + + /* Increment address register */ + addr += (reg[15] + 1); + + /* Read VRAM data */ + return *(uint16 *) &vram[index]; +} + +static unsigned int vdp_68k_data_r_m5(void) +{ + uint16 data = 0; + + /* Clear pending flag */ + pending = 0; + + switch (code & 0x0F) + { + case 0x00: /* VRAM */ + { + /* Read data */ + data = *(uint16 *)&vram[addr & 0xFFFE]; + +#ifdef LOGVDP + error("[%d(%d)][%d(%d)] VRAM 0x%x read -> 0x%x (%x)\n", v_counter, mcycles_68k/MCYCLES_PER_LINE, mcycles_68k, mcycles_68k%MCYCLES_PER_LINE, addr, data, m68k_get_reg (NULL, M68K_REG_PC)); +#endif + break; + } + + case 0x04: /* VSRAM */ + { + /* Read data */ + data = *(uint16 *)&vsram[addr & 0x7E]; + +#ifdef LOGVDP + error("[%d(%d)][%d(%d)] VSRAM 0x%x read -> 0x%x (%x)\n", v_counter, mcycles_68k/MCYCLES_PER_LINE, mcycles_68k, mcycles_68k%MCYCLES_PER_LINE, addr, data, m68k_get_reg (NULL, M68K_REG_PC)); +#endif + break; + } + + case 0x08: /* CRAM */ + { + /* Read data */ + data = *(uint16 *)&cram[addr & 0x7E]; + + /* Unpack 9-bit CRAM data (BBBGGGRRR) to 16-bit bus data (BBB0GGG0RRR0) */ + data = ((data & 0x1C0) << 3) | ((data & 0x038) << 2) | ((data & 0x007) << 1); + +#ifdef LOGVDP + error("[%d(%d)][%d(%d)] CRAM 0x%x read -> 0x%x (%x)\n", v_counter, mcycles_68k/MCYCLES_PER_LINE, mcycles_68k, mcycles_68k%MCYCLES_PER_LINE, addr, data, m68k_get_reg (NULL, M68K_REG_PC)); +#endif + break; + } + + default: + { + /* Invalid code value */ +#ifdef LOGVDP + error("[%d(%d)][%d(%d)] Invalid (%d) 0x%x read (%x)\n", v_counter, mcycles_68k/MCYCLES_PER_LINE, mcycles_68k, mcycles_68k%MCYCLES_PER_LINE, code, addr, m68k_get_reg (NULL, M68K_REG_PC)); +#endif + break; + } + } + + /* Increment address register */ + addr += reg[15]; + + /* Return data */ + return data; +} + + +/*--------------------------------------------------------------------------*/ +/* Z80 data port access functions (MS compatibilty mode) */ +/*--------------------------------------------------------------------------*/ + +static void vdp_z80_data_w_m4(unsigned int data) +{ + /* Clear pending flag */ + pending = 0; + + /* Check destination code */ + if (code & 0x02) + { + /* CRAM index (32 words) */ + int index = addr & 0x1F; + + /* Pointer to CRAM 9-bit word */ + uint16 *p = (uint16 *)&cram[index << 1]; + + /* Check if CRAM data is being modified */ + if (data != *p) + { + /* Write CRAM data */ + *p = data; + + /* Update color palette */ + color_update(index, data); + + /* Update backdrop color */ + if (index == (0x10 | (border & 0x0F))) + { + color_update(0x40, data); + } + } + } + else + { + /* VRAM address */ + int index = addr & 0x3FFF; + + /* Only write unique data to VRAM */ + if (data != vram[index]) + { + /* Write data */ + vram[index] = data; + + /* Update pattern cache */ + int name; + MARK_BG_DIRTY (index); + } + } + + /* Increment address register */ + addr += (reg[15] + 1); +} + +static void vdp_z80_data_w_m5(unsigned int data) +{ + /* Clear pending flag */ + pending = 0; + + /* Check destination code */ + switch (code & 0x0F) + { + case 0x01: /* VRAM */ + { + /* VRAM address (write low byte to even address & high byte to odd address) */ + int index = addr ^ 1; + + /* Intercept writes to Sprite Attribute Table */ + if ((index & sat_base_mask) == satb) + { + /* Update internal SAT */ + WRITE_BYTE(sat, index & sat_addr_mask, data); + } + + /* Only write unique data to VRAM */ + if (data != READ_BYTE(vram, index)) + { + /* Write data */ + WRITE_BYTE(vram, index, data); + + /* Update pattern cache */ + int name; + MARK_BG_DIRTY (index); + } + break; + } + + case 0x03: /* CRAM */ + { + /* Pointer to CRAM 9-bit word */ + uint16 *p = (uint16 *)&cram[addr & 0x7E]; + + /* Pack 8-bit value into 9-bit CRAM data */ + if (addr & 1) + { + /* Write high byte (0000BBB0 -> BBBxxxxxx) */ + data = (*p & 0x3F) | ((data & 0x0E) << 5); + } + else + { + /* Write low byte (GGG0RRR0 -> xxxGGGRRR) */ + data = (*p & 0x1C0) | ((data & 0x0E) >> 1)| ((data & 0xE0) >> 2); + } + + /* Check if CRAM data is being modified */ + if (data != *p) + { + /* Write CRAM data */ + *p = data; + + /* CRAM index (64 words) */ + int index = (addr >> 1) & 0x3F; + + /* Color entry 0 of each palette is never displayed (transparent pixel) */ + if (index & 0x0F) + { + /* Update color palette */ + color_update(index, data); + } + + /* Update backdrop color */ + if (index == border) + { + color_update(0x00, data); + } + } + break; + } + + case 0x05: /* VSRAM */ + { + /* Write low byte to even address & high byte to odd address */ + WRITE_BYTE(vsram, (addr & 0x7F) ^ 1, data); + break; + } + } + + /* Increment address register */ + addr += reg[15]; + + /* DMA Fill */ + if (dmafill & 0x100) + { + /* Fill data (DMA fill flag is cleared) */ + dmafill = data; + + /* DMA length */ + dma_length = (reg[20] << 8) | reg[19]; + + /* Zero DMA length */ + if (!dma_length) + { + dma_length = 0x10000; + } + + /* Perform DMA Fill */ + dma_type = 2; + vdp_dma_update(mcycles_z80); + } +} + +static unsigned int vdp_z80_data_r_m4(void) +{ + /* Clear pending flag */ + pending = 0; + + /* Read buffer */ + unsigned int data = fifo[0]; + + /* Process next read */ + fifo[0] = vram[addr & 0x3FFF]; + + /* Increment address register */ + addr += (reg[15] + 1); + + /* Return data */ + return data; +} + +static unsigned int vdp_z80_data_r_m5(void) +{ + unsigned int data = 0; + + /* Clear pending flag */ + pending = 0; + + switch (code & 0x0F) + { + case 0x00: /* VRAM */ + { + /* Return low byte from even address & high byte from odd address */ + data = READ_BYTE(vram, addr ^ 1); + break; + } + + case 0x04: /* VSRAM */ + { + /* Return low byte from even address & high byte from odd address */ + data = READ_BYTE(vsram, (addr & 0x7F) ^ 1); + break; + } + + case 0x08: /* CRAM */ + { + /* Read CRAM data */ + data = *(uint16 *)&cram[addr & 0x7E]; + + /* Unpack 9-bit CRAM data (BBBGGGRRR) to 16-bit data (BBB0GGG0RRR0) */ + data = ((data & 0x1C0) << 3) | ((data & 0x038) << 2) | ((data & 0x007) << 1); + + /* Return low byte from even address & high byte from odd address */ + if (addr & 1) + { + data = data >> 8; + } + + data &= 0xFF; + break; + } + } + + /* Increment address register (TODO: see how address is incremented in Mode 5) */ + addr += reg[15]; + + /* Return data */ + return data; +} + + +/*--------------------------------------------------------------------------*/ +/* DMA operations */ +/*--------------------------------------------------------------------------*/ + +/* 68K bus to VRAM, VSRAM or CRAM */ +static void vdp_dma_vbus(int length) +{ + unsigned int data; + unsigned int source = (reg[23] << 17 | reg[22] << 9 | reg[21] << 1) & 0xFFFFFE; + unsigned int base = source; + + /* DMA source */ + if ((source >> 17) == 0x50) + { + /* Z80 & I/O area ($A00000-$A1FFFF) */ + do + { + /* Return $FFFF only when the Z80 isn't hogging the Z-bus. + (e.g. Z80 isn't reset and 68000 has the bus) */ + if (source <= 0xA0FFFF) + { + data = ((zstate ^ 3) ? *(uint16 *)(work_ram + (source & 0xFFFF)) : 0xFFFF); + } + + /* The I/O chip and work RAM try to drive the data bus which results + in both values being combined in random ways when read. + We return the I/O chip values which seem to have precedence, */ + else if (source <= 0xA1001F) + { + data = io_68k_read((source >> 1) & 0x0F); + data = (data << 8 | data); + } + + /* All remaining locations access work RAM */ + else + { + data = *(uint16 *)(work_ram + (source & 0xFFFF)); + } + + /* Increment source address */ + source += 2; + + /* 128k DMA window (i.e reg #23 is not modified) */ + source = ((base & 0xFE0000) | (source & 0x1FFFF)); + + /* Write data on internal bus */ + vdp_bus_w(data); + } + while (--length); + } + else + { + do + { + /* Read from mapped memory (ROM/RAM) */ + data = *(uint16 *)(m68k_memory_map[source>>16].base + (source & 0xFFFF)); + + /* Increment source address */ + source += 2; + + /* 128k DMA window (i.e reg #23 is not modified) */ + source = ((base & 0xFE0000) | (source & 0x1FFFF)); + + /* Write data on internal bus */ + vdp_bus_w(data); + } + while (--length); + } + + /* Update source address registers (reg #23 has not been modified) */ + reg[21] = (source >> 1) & 0xFF; + reg[22] = (source >> 9) & 0xFF; +} + +/* VRAM Copy (TODO: check if CRAM or VSRAM copy is possible) */ +static void vdp_dma_copy(int length) +{ + int name; + unsigned int temp; + unsigned int source = (reg[22] << 8) | reg[21]; + + do + { + /* Read byte from source address */ + temp = READ_BYTE(vram, source); + + /* Intercept writes to Sprite Attribute Table */ + if ((addr & sat_base_mask) == satb) + { + /* Update internal SAT */ + WRITE_BYTE(sat, addr & sat_addr_mask, temp); + } + + /* Write byte to VRAM address */ + WRITE_BYTE(vram, addr, temp); + + /* Update pattern cache */ + MARK_BG_DIRTY(addr); + + /* Increment source address */ + source = (source + 1) & 0xFFFF; + + /* Increment VRAM address */ + addr += reg[15]; + } + while (--length); + + /* Update source address registers */ + reg[21] = source & 0xFF; + reg[22] = (source >> 8) & 0xFF; +} + +/* VRAM Fill (TODO: check if CRAM or VSRAM fill is possible) */ +static void vdp_dma_fill(unsigned int data, int length) +{ + int name; + + do + { + /* Intercept writes to Sprite Attribute Table */ + if ((addr & sat_base_mask) == satb) + { + /* Update internal SAT */ + WRITE_BYTE(sat, (addr & sat_addr_mask) ^ 1, data); + } + + /* Write byte to adjacent VRAM address */ + WRITE_BYTE(vram, addr ^ 1, data); + + /* Update pattern cache */ + MARK_BG_DIRTY (addr); + + /* Increment VRAM address */ + addr += reg[15]; + } + while (--length); +} diff --git a/source/vdp_ctrl.h b/source/vdp_ctrl.h new file mode 100644 index 0000000..7341cf8 --- /dev/null +++ b/source/vdp_ctrl.h @@ -0,0 +1,86 @@ +/*************************************************************************************** + * Genesis Plus + * Video Display Processor (68k & Z80 CPU interface) + * + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) + * Eke-Eke (2007-2011), additional code & fixes for the GCN/Wii port + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************************************/ + +#ifndef _VDP_H_ +#define _VDP_H_ + +/* VDP context */ +extern uint8 reg[0x20]; +extern uint8 sat[0x400]; +extern uint8 vram[0x10000]; +extern uint8 cram[0x80]; +extern uint8 vsram[0x80]; +extern uint8 hint_pending; +extern uint8 vint_pending; +extern uint8 m68k_irq_state; +extern uint16 status; +extern uint32 dma_length; + +/* Global variables */ +extern uint16 ntab; +extern uint16 ntbb; +extern uint16 ntwb; +extern uint16 satb; +extern uint16 hscb; +extern uint8 bg_name_dirty[0x800]; +extern uint16 bg_name_list[0x800]; +extern uint16 bg_list_index; +extern uint8 bg_pattern_cache[0x80000]; +extern uint8 hscroll_mask; +extern uint8 playfield_shift; +extern uint8 playfield_col_mask; +extern uint16 playfield_row_mask; +extern uint8 odd_frame; +extern uint8 im2_flag; +extern uint8 interlaced; +extern uint8 vdp_pal; +extern uint16 v_counter; +extern uint16 vc_max; +extern uint16 hscroll; +extern uint16 vscroll; +extern uint16 lines_per_frame; +extern int32 fifo_write_cnt; +extern uint32 fifo_lastwrite; +extern uint32 hvc_latch; +extern const uint8 *hctab; + +/* Function pointers */ +extern void (*vdp_68k_data_w)(unsigned int data); +extern void (*vdp_z80_data_w)(unsigned int data); +extern unsigned int (*vdp_68k_data_r)(void); +extern unsigned int (*vdp_z80_data_r)(void); + +/* Function prototypes */ +extern void vdp_init(void); +extern void vdp_reset(void); +extern int vdp_context_save(uint8 *state); +extern int vdp_context_load(uint8 *state, char *version); +extern void vdp_dma_update(unsigned int cycles); +extern void vdp_68k_ctrl_w(unsigned int data); +extern void vdp_z80_ctrl_w(unsigned int data); +extern unsigned int vdp_ctrl_r(unsigned int cycles); +extern unsigned int vdp_hvc_r(unsigned int cycles); +extern void vdp_test_w(unsigned int data); +extern int vdp_68k_irq_ack(int int_level); + +#endif /* _VDP_H_ */ diff --git a/source/vdp_render.c b/source/vdp_render.c new file mode 100644 index 0000000..2e68094 --- /dev/null +++ b/source/vdp_render.c @@ -0,0 +1,3705 @@ +/*************************************************************************************** + * Genesis Plus + * Video Display Processor (Mode 4 & Mode 5 rendering) + * + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) + * Eke-Eke (2007-2011), additional code & fixes for the GCN/Wii port + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************************************/ + +#include "shared.h" +#include "md_ntsc.h" +#include "sms_ntsc.h" + +/*** NTSC Filters ***/ +extern md_ntsc_t *md_ntsc; +extern sms_ntsc_t *sms_ntsc; + +/* Pixel priority look-up tables information */ +#define LUT_MAX (6) +#define LUT_SIZE (0x10000) + + +#ifdef ALIGN_LONG +/* Or change the names if you depend on these from elsewhere.. */ +#undef READ_LONG +#undef WRITE_LONG + +static __inline__ uint32 READ_LONG(void *address) +{ + if ((uint32)address & 3) + { +#ifdef LSB_FIRST /* little endian version */ + return ( *((uint8 *)address) + + (*((uint8 *)address+1) << 8) + + (*((uint8 *)address+2) << 16) + + (*((uint8 *)address+3) << 24) ); +#else /* big endian version */ + return ( *((uint8 *)address+3) + + (*((uint8 *)address+2) << 8) + + (*((uint8 *)address+1) << 16) + + (*((uint8 *)address) << 24) ); +#endif /* LSB_FIRST */ + } + else return *(uint32 *)address; +} + +static __inline__ void WRITE_LONG(void *address, uint32 data) +{ + if ((uint32)address & 3) + { +#ifdef LSB_FIRST + *((uint8 *)address) = data; + *((uint8 *)address+1) = (data >> 8); + *((uint8 *)address+2) = (data >> 16); + *((uint8 *)address+3) = (data >> 24); +#else + *((uint8 *)address+3) = data; + *((uint8 *)address+2) = (data >> 8); + *((uint8 *)address+1) = (data >> 16); + *((uint8 *)address) = (data >> 24); +#endif /* LSB_FIRST */ + return; + } + else *(uint32 *)address = data; +} + +#endif /* ALIGN_LONG */ + + +/* Draw 2-cell column (8-pixels high) */ +/* + Pattern cache base address: VHN NNNNNNNN NNYYYxxx + with : + x = Pattern Pixel (0-7) + Y = Pattern Row (0-7) + N = Pattern Number (0-2047) from pattern attribute + H = Horizontal Flip bit from pattern attribute + V = Vertical Flip bit from pattern attribute +*/ +#define GET_LSB_TILE(ATTR, LINE) \ + atex = atex_table[(ATTR >> 13) & 7]; \ + src = (uint32 *)&bg_pattern_cache[(ATTR & 0x00001FFF) << 6 | (LINE)]; +#define GET_MSB_TILE(ATTR, LINE) \ + atex = atex_table[(ATTR >> 29) & 7]; \ + src = (uint32 *)&bg_pattern_cache[(ATTR & 0x1FFF0000) >> 10 | (LINE)]; + +/* Draw 2-cell column (16 pixels high) */ +/* + Pattern cache base address: VHN NNNNNNNN NYYYYxxx + with : + x = Pattern Pixel (0-7) + Y = Pattern Row (0-15) + N = Pattern Number (0-1023) + H = Horizontal Flip bit + V = Vertical Flip bit +*/ +#define GET_LSB_TILE_IM2(ATTR, LINE) \ + atex = atex_table[(ATTR >> 13) & 7]; \ + src = (uint32 *)&bg_pattern_cache[((ATTR & 0x000003FF) << 7 | (ATTR & 0x00001800) << 6 | (LINE)) ^ ((ATTR & 0x00001000) >> 6)]; +#define GET_MSB_TILE_IM2(ATTR, LINE) \ + atex = atex_table[(ATTR >> 29) & 7]; \ + src = (uint32 *)&bg_pattern_cache[((ATTR & 0x03FF0000) >> 9 | (ATTR & 0x18000000) >> 10 | (LINE)) ^ ((ATTR & 0x10000000) >> 22)]; + +/* + One column = 2 tiles + Two pattern attributes are written in VRAM as two consecutives 16-bit words: + + P = priority bit + C = color palette (2 bits) + V = Vertical Flip bit + H = Horizontal Flip bit + N = Pattern Number (11 bits) + + (MSB) PCCVHNNN NNNNNNNN (LSB) (MSB) PCCVHNNN NNNNNNNN (LSB) + PATTERN1 PATTERN2 + + Both pattern attributes are read from VRAM as one 32-bit word: + + LIT_ENDIAN: (MSB) PCCVHNNN NNNNNNNN PCCVHNNN NNNNNNNN (LSB) + PATTERN2 PATTERN1 + + BIG_ENDIAN: (MSB) PCCVHNNN NNNNNNNN PCCVHNNN NNNNNNNN (LSB) + PATTERN1 PATTERN2 + + + In line buffers, one pixel = one byte: (msb) 0Pppcccc (lsb) + with: + P = priority bit (from pattern attribute) + p = color palette (from pattern attribute) + c = color data (from pattern cache) + + One pattern = 8 pixels = 8 bytes = two 32-bit writes per pattern +*/ + +#ifdef ALIGN_LONG +#ifdef LSB_FIRST +#define DRAW_COLUMN(ATTR, LINE) \ + GET_LSB_TILE(ATTR, LINE) \ + WRITE_LONG(dst, src[0] | atex); \ + dst++; \ + WRITE_LONG(dst, src[1] | atex); \ + dst++; \ + GET_MSB_TILE(ATTR, LINE) \ + WRITE_LONG(dst, src[0] | atex); \ + dst++; \ + WRITE_LONG(dst, src[1] | atex); \ + dst++; +#define DRAW_COLUMN_IM2(ATTR, LINE) \ + GET_LSB_TILE_IM2(ATTR, LINE) \ + WRITE_LONG(dst, src[0] | atex); \ + dst++; \ + WRITE_LONG(dst, src[1] | atex); \ + dst++; \ + GET_MSB_TILE_IM2(ATTR, LINE) \ + WRITE_LONG(dst, src[0] | atex); \ + dst++; \ + WRITE_LONG(dst, src[1] | atex); \ + dst++; +#else +#define DRAW_COLUMN(ATTR, LINE) \ + GET_MSB_TILE(ATTR, LINE) \ + WRITE_LONG(dst, src[0] | atex); \ + dst++; \ + WRITE_LONG(dst, src[1] | atex); \ + dst++; \ + GET_LSB_TILE(ATTR, LINE) \ + WRITE_LONG(dst, src[0] | atex); \ + dst++; \ + WRITE_LONG(dst, src[1] | atex); \ + dst++; +#define DRAW_COLUMN_IM2(ATTR, LINE) \ + GET_MSB_TILE_IM2(ATTR, LINE) \ + WRITE_LONG(dst, src[0] | atex); \ + dst++; \ + WRITE_LONG(dst, src[1] | atex); \ + dst++; \ + GET_LSB_TILE_IM2(ATTR, LINE) \ + WRITE_LONG(dst, src[0] | atex); \ + dst++; \ + WRITE_LONG(dst, src[1] | atex); \ + dst++; +#endif +#else /* NOT ALIGNED */ +#ifdef LSB_FIRST +#define DRAW_COLUMN(ATTR, LINE) \ + GET_LSB_TILE(ATTR, LINE) \ + *dst++ = (src[0] | atex); \ + *dst++ = (src[1] | atex); \ + GET_MSB_TILE(ATTR, LINE) \ + *dst++ = (src[0] | atex); \ + *dst++ = (src[1] | atex); +#define DRAW_COLUMN_IM2(ATTR, LINE) \ + GET_LSB_TILE_IM2(ATTR, LINE) \ + *dst++ = (src[0] | atex); \ + *dst++ = (src[1] | atex); \ + GET_MSB_TILE_IM2(ATTR, LINE) \ + *dst++ = (src[0] | atex); \ + *dst++ = (src[1] | atex); +#else +#define DRAW_COLUMN(ATTR, LINE) \ + GET_MSB_TILE(ATTR, LINE) \ + *dst++ = (src[0] | atex); \ + *dst++ = (src[1] | atex); \ + GET_LSB_TILE(ATTR, LINE) \ + *dst++ = (src[0] | atex); \ + *dst++ = (src[1] | atex); +#define DRAW_COLUMN_IM2(ATTR, LINE) \ + GET_MSB_TILE_IM2(ATTR, LINE) \ + *dst++ = (src[0] | atex); \ + *dst++ = (src[1] | atex); \ + GET_LSB_TILE_IM2(ATTR, LINE) \ + *dst++ = (src[0] | atex); \ + *dst++ = (src[1] | atex); +#endif +#endif /* ALIGN_LONG */ + +#ifdef ALT_RENDERER +/* Draw background tiles directly using priority look-up table */ +/* SRC_A = layer A rendered pixel line (4 bytes = 4 pixels at once) */ +/* SRC_B = layer B cached pixel line (4 bytes = 4 pixels at once) */ +/* Note: cache address is always aligned so no need to use READ_LONG macro */ +/* This might be faster or slower than original method, depending on */ +/* architecture (x86, PowerPC), cache size, memory access speed, etc... */ + +#ifdef LSB_FIRST +#define DRAW_BG_TILE(SRC_A, SRC_B) \ + *lb++ = table[((SRC_B << 8) & 0xff00) | (SRC_A & 0xff)]; \ + *lb++ = table[(SRC_B & 0xff00) | ((SRC_A >> 8) & 0xff)]; \ + *lb++ = table[((SRC_B >> 8) & 0xff00) | ((SRC_A >> 16) & 0xff)]; \ + *lb++ = table[((SRC_B >> 16) & 0xff00) | ((SRC_A >> 24) & 0xff)]; +#else +#define DRAW_BG_TILE(SRC_A, SRC_B) \ + *lb++ = table[((SRC_B >> 16) & 0xff00) | ((SRC_A >> 24) & 0xff)]; \ + *lb++ = table[((SRC_B >> 8) & 0xff00) | ((SRC_A >> 16) & 0xff)]; \ + *lb++ = table[(SRC_B & 0xff00) | ((SRC_A >> 8) & 0xff)]; \ + *lb++ = table[((SRC_B << 8) & 0xff00) | (SRC_A & 0xff)]; +#endif + +#ifdef ALIGN_LONG +#ifdef LSB_FIRST +#define DRAW_BG_COLUMN(ATTR, LINE, SRC_A, SRC_B) \ + GET_LSB_TILE(ATTR, LINE) \ + SRC_A = READ_LONG((uint32 *)lb); \ + SRC_B = (src[0] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + SRC_A = READ_LONG((uint32 *)lb); \ + SRC_B = (src[1] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + GET_MSB_TILE(ATTR, LINE) \ + SRC_A = READ_LONG((uint32 *)lb); \ + SRC_B = (src[0] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + SRC_A = READ_LONG((uint32 *)lb); \ + SRC_B = (src[1] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) +#define DRAW_BG_COLUMN_IM2(ATTR, LINE, SRC_A, SRC_B) \ + GET_LSB_TILE_IM2(ATTR, LINE) \ + SRC_A = READ_LONG((uint32 *)lb); \ + SRC_B = (src[0] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + SRC_A = READ_LONG((uint32 *)lb); \ + SRC_B = (src[1] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + GET_MSB_TILE_IM2(ATTR, LINE) \ + SRC_A = READ_LONG((uint32 *)lb); \ + SRC_B = (src[0] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + SRC_A = READ_LONG((uint32 *)lb); \ + SRC_B = (src[1] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) +#else +#define DRAW_BG_COLUMN(ATTR, LINE, SRC_A, SRC_B) \ + GET_MSB_TILE(ATTR, LINE) \ + SRC_A = READ_LONG((uint32 *)lb); \ + SRC_B = (src[0] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + SRC_A = READ_LONG((uint32 *)lb); \ + SRC_B = (src[1] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + GET_LSB_TILE(ATTR, LINE) \ + SRC_A = READ_LONG((uint32 *)lb); \ + SRC_B = (src[0] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + SRC_A = READ_LONG((uint32 *)lb); \ + SRC_B = (src[1] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) +#define DRAW_BG_COLUMN_IM2(ATTR, LINE, SRC_A, SRC_B) \ + GET_MSB_TILE_IM2(ATTR, LINE) \ + SRC_A = READ_LONG((uint32 *)lb); \ + SRC_B = (src[0] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + SRC_A = READ_LONG((uint32 *)lb); \ + SRC_B = (src[1] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + GET_LSB_TILE_IM2(ATTR, LINE) \ + SRC_A = READ_LONG((uint32 *)lb); \ + SRC_B = (src[0] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + SRC_A = READ_LONG((uint32 *)lb); \ + SRC_B = (src[1] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) +#endif +#else /* NOT ALIGNED */ +#ifdef LSB_FIRST +#define DRAW_BG_COLUMN(ATTR, LINE, SRC_A, SRC_B) \ + GET_LSB_TILE(ATTR, LINE) \ + SRC_A = *(uint32 *)(lb); \ + SRC_B = (src[0] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + SRC_A = *(uint32 *)(lb); \ + SRC_B = (src[1] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + GET_MSB_TILE(ATTR, LINE) \ + SRC_A = *(uint32 *)(lb); \ + SRC_B = (src[0] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + SRC_A = *(uint32 *)(lb); \ + SRC_B = (src[1] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) +#define DRAW_BG_COLUMN_IM2(ATTR, LINE, SRC_A, SRC_B) \ + GET_LSB_TILE_IM2(ATTR, LINE) \ + SRC_A = *(uint32 *)(lb); \ + SRC_B = (src[0] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + SRC_A = *(uint32 *)(lb); \ + SRC_B = (src[1] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + GET_MSB_TILE_IM2(ATTR, LINE) \ + SRC_A = *(uint32 *)(lb); \ + SRC_B = (src[0] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + SRC_A = *(uint32 *)(lb); \ + SRC_B = (src[1] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) +#else +#define DRAW_BG_COLUMN(ATTR, LINE, SRC_A, SRC_B) \ + GET_MSB_TILE(ATTR, LINE) \ + SRC_A = *(uint32 *)(lb); \ + SRC_B = (src[0] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + SRC_A = *(uint32 *)(lb); \ + SRC_B = (src[1] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + GET_LSB_TILE(ATTR, LINE) \ + SRC_A = *(uint32 *)(lb); \ + SRC_B = (src[0] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + SRC_A = *(uint32 *)(lb); \ + SRC_B = (src[1] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) +#define DRAW_BG_COLUMN_IM2(ATTR, LINE, SRC_A, SRC_B) \ + GET_MSB_TILE_IM2(ATTR, LINE) \ + SRC_A = *(uint32 *)(lb); \ + SRC_B = (src[0] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + SRC_A = *(uint32 *)(lb); \ + SRC_B = (src[1] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + GET_LSB_TILE_IM2(ATTR, LINE) \ + SRC_A = *(uint32 *)(lb); \ + SRC_B = (src[0] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) \ + SRC_A = *(uint32 *)(lb); \ + SRC_B = (src[1] | atex); \ + DRAW_BG_TILE(SRC_A, SRC_B) +#endif +#endif /* ALIGN_LONG */ +#endif /* ALT_RENDERER */ + +#define DRAW_SPRITE_TILE(WIDTH,ATTR,TABLE) \ + for (i=0;i> 1) << 5 | ((g) >> 1) << 2 | (b) >> 2) +/* 5:5:5 RGB */ +#define MAKE_PIXEL_15(r,g,b) ((r) << 11 | ((r) >> 3) << 10 | (g) << 6 | ((g) >> 3) << 5 | (b) << 1 | (b) >> 3) +/* 5:6:5 RGB */ +#define MAKE_PIXEL_16(r,g,b) ((r) << 12 | ((r) >> 3) << 11 | (g) << 7 | ((g) >> 2) << 5 | (b) << 1 | (b) >> 3) +/* 8:8:8 RGB */ +#define MAKE_PIXEL_32(r,g,b) ((r) << 20 | (r) << 16 | (g) << 12 | (g) << 8 | (b) << 4 | (b)) + + +/* Window & Plane A clipping */ +static struct clip_t +{ + uint8 left; + uint8 right; + uint8 enable; +} clip[2]; + +/* Pattern attribute (priority + palette bits) expansion table */ +static const uint32 atex_table[] = +{ + 0x00000000, + 0x10101010, + 0x20202020, + 0x30303030, + 0x40404040, + 0x50505050, + 0x60606060, + 0x70707070 +}; + +/* Sprite pattern name offset look-up table (Mode 5) */ +static uint8 name_lut[0x400]; + +/* Bitplane to packed pixel look-up table (Mode 4) */ +static uint32 bp_lut[0x10000]; + +/* Layer priority pixel look-up tables */ +static uint8 lut[LUT_MAX][LUT_SIZE]; + +#ifdef NGC +/* 16-bit pixel color mapping */ +static uint16 pixel[0x100]; +static uint16 pixel_lut[3][0x200]; +static uint16 pixel_lut_m4[0x40]; +#else +/* 8-bit pixel color mapping */ +static uint8 pixel_8[0x100]; +static uint8 pixel_8_lut[3][0x200]; +static uint8 pixel_8_lut_m4[0x40]; +/* 15-bit pixel color mapping */ +static uint16 pixel_15[0x100]; +static uint16 pixel_15_lut[3][0x200]; +static uint16 pixel_15_lut_m4[0x40]; +/* 16-bit pixel color mapping */ +static uint16 pixel_16[0x100]; +static uint16 pixel_16_lut[3][0x200]; +static uint16 pixel_16_lut_m4[0x40]; +/* 32-bit pixel color mapping */ +static uint32 pixel_32[0x100]; +static uint32 pixel_32_lut[3][0x200]; +static uint32 pixel_32_lut_m4[0x40]; +#endif + +/* Background & Sprite line buffers */ +static uint8 linebuf[2][0x200]; + +/* Sprite limit flag */ +static int spr_over = 0; + +/* Sprites parsing */ +static struct +{ + uint16 ypos; + uint16 xpos; + uint16 attr; + uint16 size; +} object_info[20]; + +uint8 object_count; + +/* Function pointers */ +void (*render_bg)(int line, int width); +void (*render_obj)(int max_width); +void (*parse_satb)(int line); +void (*update_bg_pattern_cache)(int index); + +#ifndef NGC +void (*color_update)(int index, unsigned int data); +#endif + + +/*--------------------------------------------------------------------------*/ +/* Sprite pattern name offset look-up table function (Mode 5) */ +/*--------------------------------------------------------------------------*/ + +static void make_name_lut(void) +{ + int vcol, vrow; + int width, height; + int flipx, flipy; + int i; + + for (i = 0; i < 0x400; i += 1) + { + /* Sprite settings */ + vcol = i & 3; + vrow = (i >> 2) & 3; + height = (i >> 4) & 3; + width = (i >> 6) & 3; + flipx = (i >> 8) & 1; + flipy = (i >> 9) & 1; + + if ((vrow > height) || vcol > width) + { + /* Invalid settings (unused) */ + name_lut[i] = -1; + } + else + { + /* Adjust column & row index if sprite is flipped */ + if(flipx) vcol = (width - vcol); + if(flipy) vrow = (height - vrow); + + /* Pattern offset (pattern order is up->down->left->right) */ + name_lut[i] = vrow + (vcol * (height + 1)); + } + } +} + + +/*--------------------------------------------------------------------------*/ +/* Bitplane to packed pixel look-up table function (Mode 4) */ +/*--------------------------------------------------------------------------*/ + +static void make_bp_lut(void) +{ + int x,i,j; + uint32 out; + + /* ---------------------- */ + /* Pattern color encoding */ + /* -------------------------------------------------------------------------*/ + /* 4 byteplanes are required to define one pattern line (8 pixels) */ + /* A single pixel color is coded with 4 bits (c3 c2 c1 c0) */ + /* Each bit is coming from byteplane bits, as explained below: */ + /* pixel 0: c3 = bp3 bit 7, c2 = bp2 bit 7, c1 = bp1 bit 7, c0 = bp0 bit 7 */ + /* pixel 1: c3 = bp3 bit 6, c2 = bp2 bit 6, c1 = bp1 bit 6, c0 = bp0 bit 6 */ + /* ... */ + /* pixel 7: c3 = bp3 bit 0, c2 = bp2 bit 0, c1 = bp1 bit 0, c0 = bp0 bit 0 */ + /* -------------------------------------------------------------------------*/ + + for(i = 0; i < 0x100; i++) + for(j = 0; j < 0x100; j++) + { + out = 0; + for(x = 0; x < 8; x++) + { + /* pixel line data = hh00gg00ff00ee00dd00cc00bb00aa00 (32-bit) */ + /* aa-hh = upper or lower 2-bit values of pixels 0-7 (shifted) */ + out |= (j & (0x80 >> x)) ? (uint32)(8 << (x << 2)) : 0; + out |= (i & (0x80 >> x)) ? (uint32)(4 << (x << 2)) : 0; + } + + /* i = low byte in VRAM (bp0 or bp2) */ + /* j = high byte in VRAM (bp1 or bp3) */ + #ifdef LSB_FIRST + bp_lut[(j << 8) | (i)] = out; + #else + bp_lut[(i << 8) | (j)] = out; + #endif + } +} + + +/*--------------------------------------------------------------------------*/ +/* Layers priority pixel look-up tables functions */ +/*--------------------------------------------------------------------------*/ + +/* Input (bx): d5-d0=color, d6=priority, d7=unused */ +/* Input (ax): d5-d0=color, d6=priority, d7=unused */ +/* Output: d5-d0=color, d6=priority, d7=zero */ +static uint32 make_lut_bg(uint32 bx, uint32 ax) +{ + int bf = (bx & 0x7F); + int bp = (bx & 0x40); + int b = (bx & 0x0F); + + int af = (ax & 0x7F); + int ap = (ax & 0x40); + int a = (ax & 0x0F); + + int c = (ap ? (a ? af : bf) : (bp ? (b ? bf : af) : (a ? af : bf))); + + /* Strip palette & priority bits from transparent pixels */ + if((c & 0x0F) == 0x00) c &= 0x80; + + return (c); +} + +/* Input (bx): d5-d0=color, d6=priority, d7=unused */ +/* Input (sx): d5-d0=color, d6=priority, d7=unused */ +/* Output: d5-d0=color, d6=priority, d7=intensity select (0=half/1=normal) */ +static uint32 make_lut_bg_ste(uint32 bx, uint32 ax) +{ + int bf = (bx & 0x7F); + int bp = (bx & 0x40); + int b = (bx & 0x0F); + + int af = (ax & 0x7F); + int ap = (ax & 0x40); + int a = (ax & 0x0F); + + int c = (ap ? (a ? af : bf) : (bp ? (b ? bf : af) : (a ? af : bf))); + + /* Half intensity when both pixels are low priority */ + c |= ((ap | bp) << 1); + + /* Strip palette & priority bits from transparent pixels */ + if((c & 0x0F) == 0x00) c &= 0x80; + + return (c); +} + +/* Input (bx): d5-d0=color, d6=priority/1, d7=sprite pixel marker */ +/* Input (sx): d5-d0=color, d6=priority, d7=unused */ +/* Output: d5-d0=color, d6=priority, d7=sprite pixel marker */ +static uint32 make_lut_obj(uint32 bx, uint32 sx) +{ + int bf = (bx & 0x7F); + int bs = (bx & 0x80); + int sf = (sx & 0x7F); + + if((sx & 0x0F) == 0) return bx; + + int c = (bs ? bf : sf); + + /* Strip palette bits from transparent pixels */ + if((c & 0x0F) == 0x00) c &= 0xC0; + + return (c | 0x80); +} + + +/* Input (bx): d5-d0=color, d6=priority, d7=opaque sprite pixel marker */ +/* Input (sx): d5-d0=color, d6=priority, d7=unused */ +/* Output: d5-d0=color, d6=zero/priority, d7=opaque sprite pixel marker */ +static uint32 make_lut_bgobj(uint32 bx, uint32 sx) +{ + int bf = (bx & 0x3F); + int bs = (bx & 0x80); + int bp = (bx & 0x40); + int b = (bx & 0x0F); + + int sf = (sx & 0x3F); + int sp = (sx & 0x40); + int s = (sx & 0x0F); + + if(s == 0) return bx; + + /* Previous sprite has higher priority */ + if(bs) return bx; + + int c = (sp ? sf : (bp ? (b ? bf : sf) : sf)); + + /* Strip palette & priority bits from transparent pixels */ + if((c & 0x0F) == 0x00) c &= 0x80; + + return (c | 0x80); +} + +/* Input (bx): d5-d0=color, d6=priority, d7=intensity (half/normal) */ +/* Input (sx): d5-d0=color, d6=priority, d7=sprite marker */ +/* Output: d5-d0=color, d6=intensity (half/normal), d7=(double/invalid) */ +static uint32 make_lut_bgobj_ste(uint32 bx, uint32 sx) +{ + int c; + + int bf = (bx & 0x3F); + int bp = (bx & 0x40); + int b = (bx & 0x0F); + int bi = (bx & 0x80) >> 1; + + int sf = (sx & 0x3F); + int sp = (sx & 0x40); + int s = (sx & 0x0F); + int si = sp | bi; + + if(sp) + { + if(s) + { + if((sf & 0x3E) == 0x3E) + { + if(sf & 1) + { + c = (bf | 0x00); + } + else + { + c = (bx & 0x80) ? (bf | 0x80) : (bf | 0x40); + } + } + else + { + if(sf == 0x0E || sf == 0x1E || sf == 0x2E) + { + c = (sf | 0x40); + } + else + { + c = (sf | si); + } + } + } + else + { + c = (bf | bi); + } + } + else + { + if(bp) + { + if(b) + { + c = (bf | bi); + } + else + { + if(s) + { + if((sf & 0x3E) == 0x3E) + { + if(sf & 1) + { + c = (bf | 0x00); + } + else + { + c = (bx & 0x80) ? (bf | 0x80) : (bf | 0x40); + } + } + else + { + if(sf == 0x0E || sf == 0x1E || sf == 0x2E) + { + c = (sf | 0x40); + } + else + { + c = (sf | si); + } + } + } + else + { + c = (bf | bi); + } + } + } + else + { + if(s) + { + if((sf & 0x3E) == 0x3E) + { + if(sf & 1) + { + c = (bf | 0x00); + } + else + { + c = (bx & 0x80) ? (bf | 0x80) : (bf | 0x40); + } + } + else + { + if(sf == 0x0E || sf == 0x1E || sf == 0x2E) + { + c = (sf | 0x40); + } + else + { + c = (sf | si); + } + } + } + else + { + c = (bf | bi); + } + } + } + + if((c & 0x0f) == 0x00) c &= 0xC0; + + return (c); +} + +/* Input (bx): d3-d0=color, d4=palette, d5=priority, d6=zero, d7=sprite pixel marker */ +/* Input (sx): d3-d0=color, d7-d4=zero */ +/* Output: d3-d0=color, d4=palette, d5=zero/priority, d6=zero, d7=sprite pixel marker */ +static uint32 make_lut_bgobj_m4(uint32 bx, uint32 sx) +{ + int bf = (bx & 0x3F); + int bs = (bx & 0x80); + int bp = (bx & 0x20); + int b = (bx & 0x0F); + + int s = (sx & 0x0F); + int sf = (s | 0x10); /* force palette bit */ + + if(s == 0) return bx; + + /* Previous sprite has higher priority */ + if(bs) return bx; + + int c = (bp ? (b ? bf : sf) : sf); + + return (c | 0x80); +} + + +/*--------------------------------------------------------------------------*/ +/* Pixel layer merging function */ +/*--------------------------------------------------------------------------*/ + +static inline void merge(uint8 *srca, uint8 *srcb, uint8 *dst, uint8 *table, int width) +{ + do + { + *dst++ = table[(*srcb++ << 8) | (*srca++)]; + } + while (--width); +} + + +#ifndef NGC + +/*--------------------------------------------------------------------------*/ +/* Pixel color mapping functions */ +/*--------------------------------------------------------------------------*/ + +static inline void remap_8(uint8 *src, uint8 *dst, int length) +{ + do + { + *dst++ = pixel_8[*src++]; + } + while (--length); +} + +static inline void remap_16(uint8 *src, uint16 *dst, int length) +{ + do + { + *dst++ = pixel_16[*src++]; + } + while (--length); +} + +static inline void remap_32(uint8 *src, uint32 *dst, int length) +{ + do + { + *dst++ = pixel_32[*src++]; + } + while (--length); +} + +#endif + + +/*--------------------------------------------------------------------------*/ +/* Pixel color lookup tables initialization */ +/*--------------------------------------------------------------------------*/ + +static void palette_init(void) +{ + int r, g, b, i; + + /************************************************/ + /* Each R,G,B color channel is 4-bit with a */ + /* total of 15 different intensity levels. */ + /* */ + /* Color intensity depends on the mode: */ + /* */ + /* normal : xxx0 (0-14) */ + /* shadow : 0xxx (0-7) */ + /* highlight: 1xxx - 1 (7-14) */ + /* mode4 : xx00 ? (0-12) */ + /* */ + /* with x = original 2-bit or 3-bit CRAM value */ + /************************************************/ + + /* Initialize Mode 5 pixel color look-up tables */ + for (i = 0; i < 0x200; i++) + { + /* CRAM value in mode 5 (BBBGGGRRR) */ + r = (i >> 0) & 7; + g = (i >> 3) & 7; + b = (i >> 6) & 7; + + /* Convert to output pixel format */ +#ifdef NGC + /* 5:6:5 RGB */ + pixel_lut[0][i] = MAKE_PIXEL_16(r,g,b); + pixel_lut[1][i] = MAKE_PIXEL_16(r<<1,g<<1,b<<1); + pixel_lut[2][i] = MAKE_PIXEL_16(r+7,g+7,b+7); +#else + /* 3:3:2 RGB */ + pixel_8_lut[0][i] = MAKE_PIXEL_8(r,g,b); + pixel_8_lut[1][i] = MAKE_PIXEL_8(r<<1,g<<1,b<<1); + pixel_8_lut[2][i] = MAKE_PIXEL_8(r+7,g+7,b+7); + + /* 5:5:5 RGB */ + pixel_15_lut[0][i] = MAKE_PIXEL_15(r,g,b); + pixel_15_lut[1][i] = MAKE_PIXEL_15(r<<1,g<<1,b<<1); + pixel_15_lut[2][i] = MAKE_PIXEL_15(r+7,g+7,b+7); + + /* 5:6:5 RGB */ + pixel_16_lut[0][i] = MAKE_PIXEL_16(r,g,b); + pixel_16_lut[1][i] = MAKE_PIXEL_16(r<<1,g<<1,b<<1); + pixel_16_lut[2][i] = MAKE_PIXEL_16(r+7,g+7,b+7); + + /* 8:8:8 RGB */ + pixel_32_lut[0][i] = MAKE_PIXEL_32(r,g,b); + pixel_32_lut[1][i] = MAKE_PIXEL_32(r<<1,g<<1,b<<1); + pixel_32_lut[2][i] = MAKE_PIXEL_32(r+7,g+7,b+7); +#endif + } + + /* Initialize Mode 4 pixel color look-up table */ + for (i = 0; i < 0x40; i++) + { + /* CRAM value in mode 4 (000BBGGRR) */ + r = (i >> 0) & 3; + g = (i >> 2) & 3; + b = (i >> 4) & 3; + + /* Convert to output pixel format (expand to 4-bit for brighter colors ?) */ +#ifdef NGC + /* 5:6:5 RGB */ + pixel_lut_m4[i] = MAKE_PIXEL_16(r << 2,g << 2,b<< 2); +#else + /* 3:3:2 RGB */ + pixel_8_lut_m4[i] = MAKE_PIXEL_8(r << 2,g << 2,b<< 2); + /* 5:5:5 RGB */ + pixel_15_lut_m4[i] = MAKE_PIXEL_15(r << 2,g << 2,b<< 2); + /* 5:6:5 RGB */ + pixel_16_lut_m4[i] = MAKE_PIXEL_16(r << 2,g << 2,b<< 2); + /* 8:8:8 RGB */ + pixel_32_lut_m4[i] = MAKE_PIXEL_32(r << 2,g << 2,b<< 2); +#endif + } +} + + +/*--------------------------------------------------------------------------*/ +/* Color palette update functions */ +/*--------------------------------------------------------------------------*/ + +#ifndef NGC + +static void color_update_8(int index, unsigned int data) +{ + /* Mode 5 */ + if (reg[1] & 4) + { + /* VDP Palette Selection bit */ + if (!(reg[0] & 4)) + { + /* Color value is limited to 00X00X00X */ + data &= 0x49; + } + + if(reg[12] & 8) + { + /* Mode 5 (Shadow/Normal/Highlight) */ + pixel_8[0x00 | index] = pixel_8_lut[0][data]; + pixel_8[0x40 | index] = pixel_8_lut[1][data]; + pixel_8[0x80 | index] = pixel_8_lut[2][data]; + } + else + { + /* Mode 5 (Normal) */ + data = pixel_8_lut[1][data]; + + /* Output pixel: xxiiiiii */ + pixel_8[0x00 | index] = data; + pixel_8[0x40 | index] = data; + pixel_8[0x80 | index] = data; + } + } + else + { + /* Test M4 bit */ + if (reg[0] & 4) + { + /* Mode 4 */ + data = pixel_8_lut_m4[data & 0x3F]; + } + else + { + /* Invalid Mode (black screen) */ + data = 0x00; + } + + /* Output pixel: x0xiiiii */ + /* Backdrop pixel: 01000000 */ + pixel_8[0x00 | index] = data; + pixel_8[0x20 | index] = data; + pixel_8[0x80 | index] = data; + pixel_8[0xA0 | index] = data; + } +} + +static void color_update_15(int index, unsigned int data) +{ + /* Mode 5 */ + if (reg[1] & 4) + { + /* VDP Palette Selection bit */ + if (!(reg[0] & 4)) + { + /* Color value is limited to 00X00X00X */ + data &= 0x49; + } + + if(reg[12] & 8) + { + /* Mode 5 (Shadow/Normal/Highlight) */ + pixel_15[0x00 | index] = pixel_15_lut[0][data]; + pixel_15[0x40 | index] = pixel_15_lut[1][data]; + pixel_15[0x80 | index] = pixel_15_lut[2][data]; + } + else + { + /* Mode 5 (Normal) */ + data = pixel_15_lut[1][data]; + + /* Output pixel: xxiiiiii */ + pixel_15[0x00 | index] = data; + pixel_15[0x40 | index] = data; + pixel_15[0x80 | index] = data; + } + } + else + { + /* Test M4 bit */ + if (reg[0] & 4) + { + /* Mode 4 */ + data = pixel_15_lut_m4[data & 0x3F]; + } + else + { + /* Invalid Mode (black screen) */ + data = 0x00; + } + + /* Output pixel: x0xiiiii */ + /* Backdrop pixel: 01000000 */ + pixel_15[0x00 | index] = data; + pixel_15[0x20 | index] = data; + pixel_15[0x80 | index] = data; + pixel_15[0xA0 | index] = data; + } +} + +static void color_update_16(int index, unsigned int data) +{ + /* Mode 5 */ + if (reg[1] & 4) + { + /* VDP Palette Selection bit */ + if (!(reg[0] & 4)) + { + /* Color value is limited to 00X00X00X */ + data &= 0x49; + } + + if(reg[12] & 8) + { + /* Mode 5 (Shadow/Normal/Highlight) */ + pixel_16[0x00 | index] = pixel_16_lut[0][data]; + pixel_16[0x40 | index] = pixel_16_lut[1][data]; + pixel_16[0x80 | index] = pixel_16_lut[2][data]; + } + else + { + /* Mode 5 (Normal) */ + data = pixel_16_lut[1][data]; + + /* Output pixel: xxiiiiii */ + pixel_16[0x00 | index] = data; + pixel_16[0x40 | index] = data; + pixel_16[0x80 | index] = data; + } + } + else + { + /* Test M4 bit */ + if (reg[0] & 4) + { + /* Mode 4 */ + data = pixel_16_lut_m4[data & 0x3F]; + } + else + { + /* Invalid Mode (black screen) */ + data = 0x00; + } + + /* Output pixel: x0xiiiii */ + /* Backdrop pixel: 01000000 */ + pixel_16[0x00 | index] = data; + pixel_16[0x20 | index] = data; + pixel_16[0x80 | index] = data; + pixel_16[0xA0 | index] = data; + } +} + +static void color_update_32(int index, unsigned int data) +{ + /* Mode 5 */ + if (reg[1] & 4) + { + /* VDP Palette Selection bit */ + if (!(reg[0] & 4)) + { + /* Color value is limited to 00X00X00X */ + data &= 0x49; + } + + if(reg[12] & 8) + { + /* Mode 5 (Shadow/Normal/Highlight) */ + pixel_32[0x00 | index] = pixel_32_lut[0][data]; + pixel_32[0x40 | index] = pixel_32_lut[1][data]; + pixel_32[0x80 | index] = pixel_32_lut[2][data]; + } + else + { + /* Mode 5 (Normal) */ + data = pixel_32_lut[1][data]; + + /* Output pixel: xxiiiiii */ + pixel_32[0x00 | index] = data; + pixel_32[0x40 | index] = data; + pixel_32[0x80 | index] = data; + } + } + else + { + /* Test M4 bit */ + if (reg[0] & 4) + { + /* Mode 4 */ + data = pixel_32_lut_m4[data & 0x3F]; + } + else + { + /* Invalid Mode (black screen) */ + data = 0x00; + } + + /* Output pixel: x0xiiiii */ + /* Backdrop pixel: 01000000 */ + pixel_32[0x00 | index] = data; + pixel_32[0x20 | index] = data; + pixel_32[0x80 | index] = data; + pixel_32[0xA0 | index] = data; + } +} + +#else + +void color_update(int index, unsigned int data) +{ + /* Mode 5 */ + if (reg[1] & 4) + { + /* Palette selection */ + if (!(reg[0] & 4)) + { + /* Color value is limited to 00X00X00X */ + data &= 0x49; + } + + if(reg[12] & 8) + { + /* Mode 5 (Shadow/Normal/Highlight) */ + pixel[0x00 | index] = pixel_lut[0][data]; + pixel[0x40 | index] = pixel_lut[1][data]; + pixel[0x80 | index] = pixel_lut[2][data]; + } + else + { + /* Mode 5 (Normal) */ + data = pixel_lut[1][data]; + + /* Output pixel: xxiiiiii */ + pixel[0x00 | index] = data; + pixel[0x40 | index] = data; + pixel[0x80 | index] = data; + } + } + else + { + /* Test M4 bit */ + if (reg[0] & 4) + { + /* Mode 4 */ + data = pixel_lut_m4[data & 0x3F]; + } + else + { + /* Invalid Mode (black screen) */ + data = 0x00; + } + + /* Output pixel: x0xiiiii */ + /* Backdrop pixel: 01000000 */ + pixel[0x00 | index] = data; + pixel[0x20 | index] = data; + pixel[0x80 | index] = data; + pixel[0xA0 | index] = data; + } +} + +#endif + + + +/*--------------------------------------------------------------------------*/ +/* Background layers rendering functions */ +/*--------------------------------------------------------------------------*/ + +void render_bg_m4(int line, int width) +{ + int column; + uint32 attr, atex, *src; + + /* Horizontal scrolling */ + int index = ((reg[0] & 0x40) && (line < 0x10)) ? 0x100 : hscroll; + int shift = index & 7; + + /* Background line buffer */ + uint32 *dst = (uint32 *)&linebuf[0][0x20 + shift]; + + /* Vertical scrolling */ + int v_line = (line + vscroll) % 224; + + /* Pattern name table */ + uint16 *nt = (uint16 *)&vram[((reg[2] << 10) & 0x3800) + ((v_line >> 3) << 6)]; + + /* Pattern row index */ + v_line = (v_line & 7) << 3; + + /* Tile column index */ + index = (0x100 - index) >> 3; + + /* Clip left-most column if required */ + if (shift) + { + memset(&linebuf[0][0x20], 0, shift); + index++; + } + + /* Number of tiles to draw */ + width >>= 3; + + /* Draw tiles (TODO: test what happens when H40 mode has been set while in Mode 5 */ + for(column = 0; column < width; column++, index++) + { + /* Stop vertical scrolling for rightmost eight tiles */ + if((column == 24) && (reg[0] & 0x80)) + { + /* Clear name table base */ + nt = (uint16 *)&vram[((reg[2] << 10) & 0x3800) + ((line >> 3) << 6)]; + + /* Clear Pattern row index */ + v_line = (line & 7) << 3; + } + + /* Read name table attribute word */ + attr = nt[index % width]; +#ifndef LSB_FIRST + attr = (((attr & 0xFF) << 8) | ((attr & 0xFF00) >> 8)); +#endif + + /* Expand priority and palette bits */ + atex = atex_table[(attr >> 11) & 3]; + + /* Cached pattern data line (4 bytes = 4 pixels at once) */ + src = (uint32 *)&bg_pattern_cache[((attr & 0x7FF) << 6) | (v_line)]; + + /* Copy left & right half, adding the attribute bits in */ +#ifdef ALIGN_DWORD + WRITE_LONG(dst, src[0] | atex); + dst++; + WRITE_LONG(dst, src[1] | atex); + dst++; +#else + *dst++ = (src[0] | atex); + *dst++ = (src[1] | atex); +#endif + } + + /* latch horizontal scroll value */ + hscroll = reg[0x08]; +} + +#ifndef ALT_RENDERER +void render_bg_m5(int line, int width) +{ + int column; + uint32 atex, atbuf, *src, *dst; + + /* Common data */ + uint32 xscroll = *(uint32 *)&vram[hscb + ((line & hscroll_mask) << 2)]; + uint32 yscroll = *(uint32 *)&vsram[0]; + + /* Plane B width */ + int start = 0; + int end = width >> 4; + + /* Plane B scroll */ +#ifdef LSB_FIRST + uint32 shift = (xscroll >> 16) & 0x0F; + uint32 index = playfield_col_mask + 1 - ((xscroll >> 20) & playfield_col_mask); + uint32 v_line = (line + ((yscroll >> 16) & 0x3FF)) & playfield_row_mask; +#else + uint32 shift = (xscroll & 0x0F); + uint32 index = playfield_col_mask + 1 - ((xscroll >> 4) & playfield_col_mask); + uint32 v_line = (line + (yscroll & 0x3FF)) & playfield_row_mask; +#endif + + /* Plane B name table */ + uint32 *nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << playfield_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (v_line & 7) << 3; + + if(shift) + { + /* Plane B line buffer */ + dst = (uint32 *)&linebuf[0][0x10 + shift]; + + atbuf = nt[(index - 1) & playfield_col_mask]; + DRAW_COLUMN(atbuf, v_line) + } + else + { + /* Plane B line buffer */ + dst = (uint32 *)&linebuf[0][0x20]; + } + + for(column = 0; column < end; column++, index++) + { + atbuf = nt[index & playfield_col_mask]; + DRAW_COLUMN(atbuf, v_line) + } + + /* Window & Plane A */ + int a = (reg[18] & 0x1F) << 3; + int w = (reg[18] >> 7) & 1; + + if (w == (line >= a)) + { + /* Window takes up entire line */ + a = 0; + w = 1; + } + else + { + /* Window and Plane A share the line */ + a = clip[0].enable; + w = clip[1].enable; + } + + /* Plane A */ + if (a) + { + /* Plane A width */ + start = clip[0].left; + end = clip[0].right; + + /* Plane A scroll */ +#ifdef LSB_FIRST + shift = (xscroll & 0x0F); + index = playfield_col_mask + start + 1 - ((xscroll >> 4) & playfield_col_mask); + v_line = (line + (yscroll & 0x3FF)) & playfield_row_mask; +#else + shift = (xscroll >> 16) & 0x0F; + index = playfield_col_mask + start + 1 - ((xscroll >> 20) & playfield_col_mask); + v_line = (line + ((yscroll >> 16) & 0x3FF)) & playfield_row_mask; +#endif + + /* Plane A name table */ + nt = (uint32 *)&vram[ntab + (((v_line >> 3) << playfield_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (v_line & 7) << 3; + + if(shift) + { + /* Plane A line buffer */ + dst = (uint32 *)&linebuf[1][0x10 + shift + (start << 4)]; + + /* Window bug */ + if (start) + { + atbuf = nt[index & playfield_col_mask]; + } + else + { + atbuf = nt[(index - 1) & playfield_col_mask]; + } + + DRAW_COLUMN(atbuf, v_line) + } + else + { + /* Plane A line buffer */ + dst = (uint32 *)&linebuf[1][0x20 + (start << 4)]; + } + + for(column = start; column < end; column++, index++) + { + atbuf = nt[index & playfield_col_mask]; + DRAW_COLUMN(atbuf, v_line) + } + + /* Window width */ + start = clip[1].left; + end = clip[1].right; + } + + /* Window */ + if (w) + { + /* Window name table */ + nt = (uint32 *)&vram[ntwb | ((line >> 3) << (6 + (reg[12] & 1)))]; + + /* Pattern row index */ + v_line = (line & 7) << 3; + + /* Plane A line buffer */ + dst = (uint32 *)&linebuf[1][0x20 + (start << 4)]; + + for(column = start; column < end; column++) + { + atbuf = nt[column]; + DRAW_COLUMN(atbuf, v_line) + } + } +} + +void render_bg_m5_vs(int line, int width) +{ + int column; + uint32 atex, atbuf, *src, *dst; + uint32 v_line, *nt; + + /* Common data */ + uint32 xscroll = *(uint32 *)&vram[hscb + ((line & hscroll_mask) << 2)]; + uint32 yscroll = 0; + uint32 pf_col_mask = playfield_col_mask; + uint32 pf_row_mask = playfield_row_mask; + uint32 pf_shift = playfield_shift; + uint32 *vs = (uint32 *)&vsram[0]; + + /* Plane B width */ + int start = 0; + int end = width >> 4; + + /* Plane B horizontal scroll */ +#ifdef LSB_FIRST + uint32 shift = (xscroll >> 16) & 0x0F; + uint32 index = pf_col_mask + 1 - ((xscroll >> 20) & pf_col_mask); +#else + uint32 shift = (xscroll & 0x0F); + uint32 index = pf_col_mask + 1 - ((xscroll >> 4) & pf_col_mask); +#endif + + /* Left-most column vertical scrolling when partially shown horizontally */ + /* Same value for both planes, only in 40-cell mode, verified on PAL MD2 */ + /* See Gynoug, Cutie Suzuki no Ringside Angel, Formula One, Kawasaki Superbike Challenge */ + if (reg[12] & 1) + { + yscroll = vs[19] & (vs[19] >> 16); + yscroll &= 0x3FF; + } + + if(shift) + { + /* Plane B vertical scroll */ + v_line = (line + yscroll) & pf_row_mask; + + /* Plane B name table */ + nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (v_line & 7) << 3; + + /* Plane B line buffer */ + dst = (uint32 *)&linebuf[0][0x10 + shift]; + + atbuf = nt[(index - 1) & pf_col_mask]; + DRAW_COLUMN(atbuf, v_line) + } + else + { + /* Plane B line buffer */ + dst = (uint32 *)&linebuf[0][0x20]; + } + + for(column = 0; column < end; column++, index++) + { + /* Plane B vertical scroll */ +#ifdef LSB_FIRST + v_line = (line + ((vs[column] >> 16) & 0x3FF)) & pf_row_mask; +#else + v_line = (line + (vs[column] & 0x3FF)) & pf_row_mask; +#endif + + /* Plane B name table */ + nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (v_line & 7) << 3; + + atbuf = nt[index & pf_col_mask]; + DRAW_COLUMN(atbuf, v_line) + } + + /* Window & Plane A */ + int a = (reg[18] & 0x1F) << 3; + int w = (reg[18] >> 7) & 1; + + if (w == (line >= a)) + { + /* Window takes up entire line */ + a = 0; + w = 1; + } + else + { + /* Window and Plane A share the line */ + a = clip[0].enable; + w = clip[1].enable; + } + + /* Plane A */ + if (a) + { + /* Plane A width */ + start = clip[0].left; + end = clip[0].right; + + /* Plane A horizontal scroll */ +#ifdef LSB_FIRST + shift = (xscroll & 0x0F); + index = pf_col_mask + start + 1 - ((xscroll >> 4) & pf_col_mask); +#else + shift = (xscroll >> 16) & 0x0F; + index = pf_col_mask + start + 1 - ((xscroll >> 20) & pf_col_mask); +#endif + + if(shift) + { + /* Plane A vertical scroll */ + v_line = (line + yscroll) & pf_row_mask; + + /* Plane A name table */ + nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (v_line & 7) << 3; + + /* Plane A line buffer */ + dst = (uint32 *)&linebuf[1][0x10 + shift + (start << 4)]; + + /* Window bug */ + if (start) + { + atbuf = nt[index & pf_col_mask]; + } + else + { + atbuf = nt[(index - 1) & pf_col_mask]; + } + + DRAW_COLUMN(atbuf, v_line) + } + else + { + /* Plane A line buffer */ + dst = (uint32 *)&linebuf[1][0x20 + (start << 4)]; + } + + for(column = start; column < end; column++, index++) + { + /* Plane A vertical scroll */ +#ifdef LSB_FIRST + v_line = (line + (vs[column] & 0x3FF)) & pf_row_mask; +#else + v_line = (line + ((vs[column] >> 16) & 0x3FF)) & pf_row_mask; +#endif + + /* Plane A name table */ + nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (v_line & 7) << 3; + + atbuf = nt[index & pf_col_mask]; + DRAW_COLUMN(atbuf, v_line) + } + + /* Window width */ + start = clip[1].left; + end = clip[1].right; + } + + /* Window */ + if (w) + { + /* Window name table */ + nt = (uint32 *)&vram[ntwb | ((line >> 3) << (6 + (reg[12] & 1)))]; + + /* Pattern row index */ + v_line = (line & 7) << 3; + + /* Plane A line buffer */ + dst = (uint32 *)&linebuf[1][0x20 + (start << 4)]; + + for(column = start; column < end; column++) + { + atbuf = nt[column]; + DRAW_COLUMN(atbuf, v_line) + } + } +} + +void render_bg_m5_im2(int line, int width) +{ + int column; + uint32 atex, atbuf, *src, *dst; + + /* Common data */ + uint32 xscroll = *(uint32 *)&vram[hscb + ((line & hscroll_mask) << 2)]; + uint32 yscroll = *(uint32 *)&vsram[0]; + uint32 pf_col_mask = playfield_col_mask; + uint32 pf_row_mask = playfield_row_mask; + uint32 pf_shift = playfield_shift; + + int odd = odd_frame; + + /* Plane B width */ + int start = 0; + int end = width >> 4; + + /* Plane B scroll */ +#ifdef LSB_FIRST + uint32 shift = (xscroll >> 16) & 0x0F; + uint32 index = pf_col_mask + 1 - ((xscroll >> 20) & pf_col_mask); + uint32 v_line = (line + ((yscroll >> 17) & 0x3FF)) & pf_row_mask; +#else + uint32 shift = (xscroll & 0x0F); + uint32 index = pf_col_mask + 1 - ((xscroll >> 4) & pf_col_mask); + uint32 v_line = (line + ((yscroll >> 1) & 0x3FF)) & pf_row_mask; +#endif + + /* Plane B name table */ + uint32 *nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (((v_line & 7) << 1) | odd) << 3; + + if(shift) + { + /* Plane B line buffer */ + dst = (uint32 *)&linebuf[0][0x10 + shift]; + + atbuf = nt[(index - 1) & pf_col_mask]; + DRAW_COLUMN_IM2(atbuf, v_line) + } + else + { + /* Plane B line buffer */ + dst = (uint32 *)&linebuf[0][0x20]; + } + + for(column = 0; column < end; column++, index++) + { + atbuf = nt[index & pf_col_mask]; + DRAW_COLUMN_IM2(atbuf, v_line) + } + + /* Window & Plane A */ + int a = (reg[18] & 0x1F) << 3; + int w = (reg[18] >> 7) & 1; + + if (w == (line >= a)) + { + /* Window takes up entire line */ + a = 0; + w = 1; + } + else + { + /* Window and Plane A share the line */ + a = clip[0].enable; + w = clip[1].enable; + } + + /* Plane A */ + if (a) + { + /* Plane A width */ + start = clip[0].left; + end = clip[0].right; + + /* Plane A scroll */ +#ifdef LSB_FIRST + shift = (xscroll & 0x0F); + index = pf_col_mask + start + 1 - ((xscroll >> 4) & pf_col_mask); + v_line = (line + ((yscroll >> 1) & 0x3FF)) & pf_row_mask; +#else + shift = (xscroll >> 16) & 0x0F; + index = pf_col_mask + start + 1 - ((xscroll >> 20) & pf_col_mask); + v_line = (line + ((yscroll >> 17) & 0x3FF)) & pf_row_mask; +#endif + + /* Plane A name table */ + nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (((v_line & 7) << 1) | odd) << 3; + + if(shift) + { + /* Plane A line buffer */ + dst = (uint32 *)&linebuf[1][0x10 + shift + (start << 4)]; + + /* Window bug */ + if (start) + { + atbuf = nt[index & pf_col_mask]; + } + else + { + atbuf = nt[(index - 1) & pf_col_mask]; + } + + DRAW_COLUMN_IM2(atbuf, v_line) + } + else + { + /* Plane A line buffer */ + dst = (uint32 *)&linebuf[1][0x20 + (start << 4)]; + } + + for(column = start; column < end; column++, index++) + { + atbuf = nt[index & pf_col_mask]; + DRAW_COLUMN_IM2(atbuf, v_line) + } + + /* Window width */ + start = clip[1].left; + end = clip[1].right; + } + + /* Window */ + if (w) + { + /* Window name table */ + nt = (uint32 *)&vram[ntwb | ((line >> 3) << (6 + (reg[12] & 1)))]; + + /* Pattern row index */ + v_line = ((line & 7) << 1 | odd) << 3; + + /* Plane A line buffer */ + dst = (uint32 *)&linebuf[1][0x20 + (start << 4)]; + + for(column = start; column < end; column++) + { + atbuf = nt[column]; + DRAW_COLUMN_IM2(atbuf, v_line) + } + } +} + +void render_bg_m5_im2_vs(int line, int width) +{ + int column; + uint32 atex, atbuf, *src, *dst; + uint32 v_line, *nt; + + /* Common data */ + uint32 xscroll = *(uint32 *)&vram[hscb + ((line & hscroll_mask) << 2)]; + uint32 yscroll = 0; + uint32 pf_col_mask = playfield_col_mask; + uint32 pf_row_mask = playfield_row_mask; + uint32 pf_shift = playfield_shift; + uint32 *vs = (uint32 *)&vsram[0]; + + int odd = odd_frame; + + /* Plane B width */ + int start = 0; + int end = width >> 4; + + /* Plane B horizontal scroll */ +#ifdef LSB_FIRST + uint32 shift = (xscroll >> 16) & 0x0F; + uint32 index = pf_col_mask + 1 - ((xscroll >> 20) & pf_col_mask); +#else + uint32 shift = (xscroll & 0x0F); + uint32 index = pf_col_mask + 1 - ((xscroll >> 4) & pf_col_mask); +#endif + + /* Left-most column vertical scrolling when partially shown horizontally */ + /* Same value for both planes, only in 40-cell mode, verified on PAL MD2 */ + /* See Gynoug, Cutie Suzuki no Ringside Angel, Formula One, Kawasaki Superbike Challenge */ + if (reg[12] & 1) + { + /* only in 40-cell mode, verified on MD2 */ + yscroll = (vs[19] >> 1) & (vs[19] >> 17); + yscroll &= 0x3FF; + } + + if(shift) + { + /* Plane B vertical scroll */ + v_line = (line + yscroll) & pf_row_mask; + + /* Plane B name table */ + nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (((v_line & 7) << 1) | odd) << 3; + + /* Plane B line buffer */ + dst = (uint32 *)&linebuf[0][0x10 + shift]; + + atbuf = nt[(index - 1) & pf_col_mask]; + DRAW_COLUMN_IM2(atbuf, v_line) + } + else + { + /* Plane B line buffer */ + dst = (uint32 *)&linebuf[0][0x20]; + } + + for(column = 0; column < end; column++, index++) + { + /* Plane B vertical scroll */ +#ifdef LSB_FIRST + v_line = (line + ((vs[column] >> 17) & 0x3FF)) & pf_row_mask; +#else + v_line = (line + ((vs[column] >> 1) & 0x3FF)) & pf_row_mask; +#endif + + /* Plane B name table */ + nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (((v_line & 7) << 1) | odd) << 3; + + atbuf = nt[index & pf_col_mask]; + DRAW_COLUMN_IM2(atbuf, v_line) + } + + /* Window & Plane A */ + int a = (reg[18] & 0x1F) << 3; + int w = (reg[18] >> 7) & 1; + + if (w == (line >= a)) + { + /* Window takes up entire line */ + a = 0; + w = 1; + } + else + { + /* Window and Plane A share the line */ + a = clip[0].enable; + w = clip[1].enable; + } + + /* Plane A */ + if (a) + { + /* Plane A width */ + start = clip[0].left; + end = clip[0].right; + + /* Plane A horizontal scroll */ +#ifdef LSB_FIRST + shift = (xscroll & 0x0F); + index = pf_col_mask + start + 1 - ((xscroll >> 4) & pf_col_mask); +#else + shift = (xscroll >> 16) & 0x0F; + index = pf_col_mask + start + 1 - ((xscroll >> 20) & pf_col_mask); +#endif + + if(shift) + { + /* Plane A vertical scroll */ + v_line = (line + yscroll) & pf_row_mask; + + /* Plane A name table */ + nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (((v_line & 7) << 1) | odd) << 3; + + /* Plane A line buffer */ + dst = (uint32 *)&linebuf[1][0x10 + shift + (start << 4)]; + + /* Window bug */ + if (start) + { + atbuf = nt[index & pf_col_mask]; + } + else + { + atbuf = nt[(index - 1) & pf_col_mask]; + } + + DRAW_COLUMN_IM2(atbuf, v_line) + } + else + { + /* Plane A line buffer */ + dst = (uint32 *)&linebuf[1][0x20 + (start << 4)]; + } + + for(column = start; column < end; column++, index++) + { + /* Plane A vertical scroll */ +#ifdef LSB_FIRST + v_line = (line + ((vs[column] >> 1) & 0x3FF)) & pf_row_mask; +#else + v_line = (line + ((vs[column] >> 17) & 0x3FF)) & pf_row_mask; +#endif + + /* Plane A name table */ + nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (((v_line & 7) << 1) | odd) << 3; + + atbuf = nt[index & pf_col_mask]; + DRAW_COLUMN_IM2(atbuf, v_line) + } + + /* Window width */ + start = clip[1].left; + end = clip[1].right; + } + + /* Window */ + if (w) + { + /* Window name table */ + nt = (uint32 *)&vram[ntwb | ((line >> 3) << (6 + (reg[12] & 1)))]; + + /* Pattern row index */ + v_line = ((line & 7) << 1 | odd) << 3; + + /* Plane A line buffer */ + dst = (uint32 *)&linebuf[1][0x20 + (start << 4)]; + + for(column = start; column < end; column++) + { + atbuf = nt[column]; + DRAW_COLUMN_IM2(atbuf, v_line) + } + } +} + +#else + +void render_bg_m5(int line, int width) +{ + int column, start, end; + uint32 atex, atbuf, *src, *dst; + uint32 shift, index, v_line, *nt; + + /* Scroll Planes common data */ + uint32 xscroll = *(uint32 *)&vram[hscb + ((line & hscroll_mask) << 2)]; + uint32 yscroll = *(uint32 *)&vsram[0]; + uint32 pf_col_mask = playfield_col_mask; + uint32 pf_row_mask = playfield_row_mask; + uint32 pf_shift = playfield_shift; + + /* Layer priority table */ + uint8 *table = lut[(reg[12] & 8) >> 2]; + + /* Window vertical range (cell 0-31) */ + int a = (reg[18] & 0x1F) << 3; + + /* Window position (0=top, 1=bottom) */ + int w = (reg[18] >> 7) & 1; + + /* Test against current line */ + if (w == (line >= a)) + { + /* Window takes up entire line */ + a = 0; + w = 1; + } + else + { + /* Window and Plane A share the line */ + a = clip[0].enable; + w = clip[1].enable; + } + + /* Number of columns to draw */ + width >>= 4; + + /* Plane A */ + if (a) + { + /* Plane A width */ + start = clip[0].left; + end = clip[0].right; + + /* Plane A scroll */ +#ifdef LSB_FIRST + shift = (xscroll & 0x0F); + index = pf_col_mask + start + 1 - ((xscroll >> 4) & pf_col_mask); + v_line = (line + (yscroll & 0x3FF)) & pf_row_mask; +#else + shift = (xscroll >> 16) & 0x0F; + index = pf_col_mask + start + 1 - ((xscroll >> 20) & pf_col_mask); + v_line = (line + ((yscroll >> 16) & 0x3FF)) & pf_row_mask; +#endif + + /* Background line buffer */ + dst = (uint32 *)&linebuf[0][0x20 + (start << 4) + shift]; + + /* Plane A name table */ + nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (v_line & 7) << 3; + + if(shift) + { + /* Left-most column is partially shown */ + dst -= 4; + + /* Window bug */ + if (start) + { + atbuf = nt[index & pf_col_mask]; + } + else + { + atbuf = nt[(index-1) & pf_col_mask]; + } + + DRAW_COLUMN(atbuf, v_line) + } + + for(column = start; column < end; column++, index++) + { + atbuf = nt[index & pf_col_mask]; + DRAW_COLUMN(atbuf, v_line) + } + + /* Window width */ + start = clip[1].left; + end = clip[1].right; + } + else + { + /* Window width */ + start = 0; + end = width; + } + + /* Window Plane */ + if (w) + { + /* Background line buffer */ + dst = (uint32 *)&linebuf[0][0x20 + (start << 4)]; + + /* Window name table */ + nt = (uint32 *)&vram[ntwb | ((line >> 3) << (6 + (reg[12] & 1)))]; + + /* Pattern row index */ + v_line = (line & 7) << 3; + + for(column = start; column < end; column++) + { + atbuf = nt[column]; + DRAW_COLUMN(atbuf, v_line) + } + } + + /* Plane B scroll */ +#ifdef LSB_FIRST + shift = (xscroll >> 16) & 0x0F; + index = pf_col_mask + 1 - ((xscroll >> 20) & pf_col_mask); + v_line = (line + ((yscroll >> 16) & 0x3FF)) & pf_row_mask; +#else + shift = (xscroll & 0x0F); + index = pf_col_mask + 1 - ((xscroll >> 4) & pf_col_mask); + v_line = (line + (yscroll & 0x3FF)) & pf_row_mask; +#endif + + /* Plane B name table */ + nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (v_line & 7) << 3; + + /* Background line buffer */ + uint8 *lb = &linebuf[0][0x20]; + + if(shift) + { + /* Left-most column is partially shown */ + lb -= (0x10 - shift); + + atbuf = nt[(index-1) & pf_col_mask]; + DRAW_BG_COLUMN(atbuf, v_line, xscroll, yscroll) + } + + for(column = 0; column < width; column++, index++) + { + atbuf = nt[index & pf_col_mask]; + DRAW_BG_COLUMN(atbuf, v_line, xscroll, yscroll) + } +} + +void render_bg_m5_vs(int line, int width) +{ + int column, start, end; + uint32 atex, atbuf, *src, *dst; + uint32 shift, index, v_line, *nt; + + /* Scroll Planes common data */ + uint32 xscroll = *(uint32 *)&vram[hscb + ((line & hscroll_mask) << 2)]; + uint32 yscroll = 0; + uint32 pf_col_mask = playfield_col_mask; + uint32 pf_row_mask = playfield_row_mask; + uint32 pf_shift = playfield_shift; + uint32 *vs = (uint32 *)&vsram[0]; + + /* Layer priority table */ + uint8 *table = lut[(reg[12] & 8) >> 2]; + + /* Window vertical range (cell 0-31) */ + int a = (reg[18] & 0x1F) << 3; + + /* Window position (0=top, 1=bottom) */ + int w = (reg[18] >> 7) & 1; + + /* Test against current line */ + if (w == (line >= a)) + { + /* Window takes up entire line */ + a = 0; + w = 1; + } + else + { + /* Window and Plane A share the line */ + a = clip[0].enable; + w = clip[1].enable; + } + + /* Left-most column vertical scrolling when partially shown horizontally */ + /* Same value for both planes, only in 40-cell mode, verified on PAL MD2 */ + /* See Gynoug, Cutie Suzuki no Ringside Angel, Formula One, Kawasaki Superbike Challenge */ + if (reg[12] & 1) + { + yscroll = vs[19] & (vs[19] >> 16); + yscroll &= 0x3FF; + } + + /* Number of columns to draw */ + width >>= 4; + + /* Plane A*/ + if (a) + { + /* Plane A width */ + start = clip[0].left; + end = clip[0].right; + + /* Plane A horizontal scroll */ +#ifdef LSB_FIRST + shift = (xscroll & 0x0F); + index = pf_col_mask + start + 1 - ((xscroll >> 4) & pf_col_mask); +#else + shift = (xscroll >> 16) & 0x0F; + index = pf_col_mask + start + 1 - ((xscroll >> 20) & pf_col_mask); +#endif + + /* Background line buffer */ + dst = (uint32 *)&linebuf[0][0x20 + (start << 4) + shift]; + + if(shift) + { + /* Left-most column is partially shown */ + dst -= 4; + + /* Plane A vertical scroll */ + v_line = (line + yscroll) & pf_row_mask; + + /* Plane A name table */ + nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (v_line & 7) << 3; + + /* Window bug */ + if (start) + { + atbuf = nt[index & pf_col_mask]; + } + else + { + atbuf = nt[(index-1) & pf_col_mask]; + } + + DRAW_COLUMN(atbuf, v_line) + } + + for(column = start; column < end; column++, index++) + { + /* Plane A vertical scroll */ +#ifdef LSB_FIRST + v_line = (line + (vs[column] & 0x3FF)) & pf_row_mask; +#else + v_line = (line + ((vs[column] >> 16) & 0x3FF)) & pf_row_mask; +#endif + + /* Plane A name table */ + nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (v_line & 7) << 3; + + atbuf = nt[index & pf_col_mask]; + DRAW_COLUMN(atbuf, v_line) + } + + /* Window width */ + start = clip[1].left; + end = clip[1].right; + } + else + { + /* Window width */ + start = 0; + end = width; + } + + /* Window Plane */ + if (w) + { + /* Background line buffer */ + dst = (uint32 *)&linebuf[0][0x20 + (start << 4)]; + + /* Window name table */ + nt = (uint32 *)&vram[ntwb | ((line >> 3) << (6 + (reg[12] & 1)))]; + + /* Pattern row index */ + v_line = (line & 7) << 3; + + for(column = start; column < end; column++) + { + atbuf = nt[column]; + DRAW_COLUMN(atbuf, v_line) + } + } + + /* Plane B horizontal scroll */ +#ifdef LSB_FIRST + shift = (xscroll >> 16) & 0x0F; + index = pf_col_mask + 1 - ((xscroll >> 20) & pf_col_mask); +#else + shift = (xscroll & 0x0F); + index = pf_col_mask + 1 - ((xscroll >> 4) & pf_col_mask); +#endif + + /* Background line buffer */ + uint8 *lb = &linebuf[0][0x20]; + + if(shift) + { + /* Left-most column is partially shown */ + lb -= (0x10 - shift); + + /* Plane B vertical scroll */ + v_line = (line + yscroll) & pf_row_mask; + + /* Plane B name table */ + nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (v_line & 7) << 3; + + atbuf = nt[(index-1) & pf_col_mask]; + DRAW_BG_COLUMN(atbuf, v_line, xscroll, yscroll) + } + + for(column = 0; column < width; column++, index++) + { + /* Plane B vertical scroll */ +#ifdef LSB_FIRST + v_line = (line + ((vs[column] >> 16) & 0x3FF)) & pf_row_mask; +#else + v_line = (line + (vs[column] & 0x3FF)) & pf_row_mask; +#endif + + /* Plane B name table */ + nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (v_line & 7) << 3; + + atbuf = nt[index & pf_col_mask]; + DRAW_BG_COLUMN(atbuf, v_line, xscroll, yscroll) + } +} + +void render_bg_m5_im2(int line, int width) +{ + int column, start, end; + uint32 atex, atbuf, *src, *dst; + uint32 shift, index, v_line, *nt; + + /* Scroll Planes common data */ + int odd = odd_frame; + uint32 xscroll = *(uint32 *)&vram[hscb + ((line & hscroll_mask) << 2)]; + uint32 yscroll = *(uint32 *)&vsram[0]; + uint32 pf_col_mask = playfield_col_mask; + uint32 pf_row_mask = playfield_row_mask; + uint32 pf_shift = playfield_shift; + + /* Layer priority table */ + uint8 *table = lut[(reg[12] & 8) >> 2]; + + /* Window vertical range (cell 0-31) */ + int a = (reg[18] & 0x1F) << 3; + + /* Window position (0=top, 1=bottom) */ + int w = (reg[18] >> 7) & 1; + + /* Test against current line */ + if (w == (line >= a)) + { + /* Window takes up entire line */ + a = 0; + w = 1; + } + else + { + /* Window and Plane A share the line */ + a = clip[0].enable; + w = clip[1].enable; + } + + /* Number of columns to draw */ + width >>= 4; + + /* Plane A */ + if (a) + { + /* Plane A width */ + start = clip[0].left; + end = clip[0].right; + + /* Plane A scroll */ +#ifdef LSB_FIRST + shift = (xscroll & 0x0F); + index = pf_col_mask + start + 1 - ((xscroll >> 4) & pf_col_mask); + v_line = (line + ((yscroll >> 1) & 0x3FF)) & pf_row_mask; +#else + shift = (xscroll >> 16) & 0x0F; + index = pf_col_mask + start + 1 - ((xscroll >> 20) & pf_col_mask); + v_line = (line + ((yscroll >> 17) & 0x3FF)) & pf_row_mask; +#endif + + /* Background line buffer */ + dst = (uint32 *)&linebuf[0][0x20 + (start << 4) + shift]; + + /* Plane A name table */ + nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (((v_line & 7) << 1) | odd) << 3; + + if(shift) + { + /* Left-most column is partially shown */ + dst -= 4; + + /* Window bug */ + if (start) + { + atbuf = nt[index & pf_col_mask]; + } + else + { + atbuf = nt[(index-1) & pf_col_mask]; + } + + DRAW_COLUMN_IM2(atbuf, v_line) + } + + for(column = start; column < end; column++, index++) + { + atbuf = nt[index & pf_col_mask]; + DRAW_COLUMN_IM2(atbuf, v_line) + } + + /* Window width */ + start = clip[1].left; + end = clip[1].right; + } + else + { + /* Window width */ + start = 0; + end = width; + } + + /* Window Plane */ + if (w) + { + /* Background line buffer */ + dst = (uint32 *)&linebuf[0][0x20 + (start << 4)]; + + /* Window name table */ + nt = (uint32 *)&vram[ntwb | ((line >> 3) << (6 + (reg[12] & 1)))]; + + /* Pattern row index */ + v_line = ((line & 7) << 1 | odd) << 3; + + for(column = start; column < end; column++) + { + atbuf = nt[column]; + DRAW_COLUMN_IM2(atbuf, v_line) + } + } + + /* Plane B scroll */ +#ifdef LSB_FIRST + shift = (xscroll >> 16) & 0x0F; + index = pf_col_mask + 1 - ((xscroll >> 20) & pf_col_mask); + v_line = (line + ((yscroll >> 17) & 0x3FF)) & pf_row_mask; +#else + shift = (xscroll & 0x0F); + index = pf_col_mask + 1 - ((xscroll >> 4) & pf_col_mask); + v_line = (line + ((yscroll >> 1) & 0x3FF)) & pf_row_mask; +#endif + + /* Plane B name table */ + nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (((v_line & 7) << 1) | odd) << 3; + + /* Background line buffer */ + uint8 *lb = &linebuf[0][0x20]; + + if(shift) + { + /* Left-most column is partially shown */ + lb -= (0x10 - shift); + + atbuf = nt[(index-1) & pf_col_mask]; + DRAW_BG_COLUMN_IM2(atbuf, v_line, xscroll, yscroll) + } + + for(column = 0; column < width; column++, index++) + { + atbuf = nt[index & pf_col_mask]; + DRAW_BG_COLUMN_IM2(atbuf, v_line, xscroll, yscroll) + } +} + +void render_bg_m5_im2_vs(int line, int width) +{ + int column, start, end; + uint32 atex, atbuf, *src, *dst; + uint32 shift, index, v_line, *nt; + + /* common data */ + int odd = odd_frame; + uint32 xscroll = *(uint32 *)&vram[hscb + ((line & hscroll_mask) << 2)]; + uint32 yscroll = 0; + uint32 pf_col_mask = playfield_col_mask; + uint32 pf_row_mask = playfield_row_mask; + uint32 pf_shift = playfield_shift; + uint32 *vs = (uint32 *)&vsram[0]; + + /* Layer priority table */ + uint8 *table = lut[(reg[12] & 8) >> 2]; + + /* Window vertical range (cell 0-31) */ + uint32 a = (reg[18] & 0x1F) << 3; + + /* Window position (0=top, 1=bottom) */ + uint32 w = (reg[18] >> 7) & 1; + + /* Test against current line */ + if (w == (line >= a)) + { + /* Window takes up entire line */ + a = 0; + w = 1; + } + else + { + /* Window and Plane A share the line */ + a = clip[0].enable; + w = clip[1].enable; + } + + /* Left-most column vertical scrolling when partially shown horizontally */ + /* Same value for both planes, only in 40-cell mode, verified on PAL MD2 */ + /* See Gynoug, Cutie Suzuki no Ringside Angel, Formula One, Kawasaki Superbike Challenge */ + if (reg[12] & 1) + { + /* only in 40-cell mode, verified on MD2 */ + yscroll = (vs[19] >> 1) & (vs[19] >> 17); + yscroll &= 0x3FF; + } + + /* Number of columns to draw */ + width >>= 4; + + /* Plane A */ + if (a) + { + /* Plane A width */ + start = clip[0].left; + end = clip[0].right; + + /* Plane A horizontal scroll */ +#ifdef LSB_FIRST + shift = (xscroll & 0x0F); + index = pf_col_mask + start + 1 - ((xscroll >> 4) & pf_col_mask); +#else + shift = (xscroll >> 16) & 0x0F; + index = pf_col_mask + start + 1 - ((xscroll >> 20) & pf_col_mask); +#endif + + /* Background line buffer */ + dst = (uint32 *)&linebuf[0][0x20 + (start << 4) + shift]; + + if(shift) + { + /* Left-most column is partially shown */ + dst -= 4; + + /* Plane A vertical scroll */ + v_line = (line + yscroll) & pf_row_mask; + + /* Plane A name table */ + nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (((v_line & 7) << 1) | odd) << 3; + + /* Window bug */ + if (start) + { + atbuf = nt[index & pf_col_mask]; + } + else + { + atbuf = nt[(index-1) & pf_col_mask]; + } + + DRAW_COLUMN_IM2(atbuf, v_line) + } + + for(column = start; column < end; column++, index++) + { + /* Plane A vertical scroll */ +#ifdef LSB_FIRST + v_line = (line + ((vs[column] >> 1) & 0x3FF)) & pf_row_mask; +#else + v_line = (line + ((vs[column] >> 17) & 0x3FF)) & pf_row_mask; +#endif + + /* Plane A name table */ + nt = (uint32 *)&vram[ntab + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (((v_line & 7) << 1) | odd) << 3; + + atbuf = nt[index & pf_col_mask]; + DRAW_COLUMN_IM2(atbuf, v_line) + } + + /* Window width */ + start = clip[1].left; + end = clip[1].right; + } + else + { + /* Window width */ + start = 0; + end = width; + } + + /* Window Plane */ + if (w) + { + /* Background line buffer */ + dst = (uint32 *)&linebuf[0][0x20 + (start << 4)]; + + /* Window name table */ + nt = (uint32 *)&vram[ntwb | ((line >> 3) << (6 + (reg[12] & 1)))]; + + /* Pattern row index */ + v_line = ((line & 7) << 1 | odd) << 3; + + for(column = start; column < end; column++) + { + atbuf = nt[column]; + DRAW_COLUMN_IM2(atbuf, v_line) + } + } + + /* Plane B horizontal scroll */ +#ifdef LSB_FIRST + shift = (xscroll >> 16) & 0x0F; + index = pf_col_mask + 1 - ((xscroll >> 20) & pf_col_mask); +#else + shift = (xscroll & 0x0F); + index = pf_col_mask + 1 - ((xscroll >> 4) & pf_col_mask); +#endif + + /* Background line buffer */ + uint8 *lb = &linebuf[0][0x20]; + + if(shift) + { + /* Left-most column is partially shown */ + lb -= (0x10 - shift); + + /* Plane B vertical scroll */ + v_line = (line + yscroll) & pf_row_mask; + + /* Plane B name table */ + nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (((v_line & 7) << 1) | odd) << 3; + + atbuf = nt[(index-1) & pf_col_mask]; + DRAW_BG_COLUMN_IM2(atbuf, v_line, xscroll, yscroll) + } + + for(column = 0; column < width; column++, index++) + { + /* Plane B vertical scroll */ +#ifdef LSB_FIRST + v_line = (line + ((vs[column] >> 17) & 0x3FF)) & pf_row_mask; +#else + v_line = (line + ((vs[column] >> 1) & 0x3FF)) & pf_row_mask; +#endif + + /* Plane B name table */ + nt = (uint32 *)&vram[ntbb + (((v_line >> 3) << pf_shift) & 0x1FC0)]; + + /* Pattern row index */ + v_line = (((v_line & 7) << 1) | odd) << 3; + + atbuf = nt[index & pf_col_mask]; + DRAW_BG_COLUMN_IM2(atbuf, v_line, xscroll, yscroll) + } +} +#endif + + +/*--------------------------------------------------------------------------*/ +/* Sprite layer rendering functions */ +/*--------------------------------------------------------------------------*/ + +void render_obj_m4(int max_width) +{ + int i, count, xpos, width; + uint8 *src, *lb; + uint16 temp; + + /* Set SOVR flag */ + status |= spr_over; + spr_over = 0; + + /* Draw sprites in front-to-back order */ + for (count = 0; count < object_count; count++) + { + /* Sprite X position */ + xpos = object_info[count].xpos; + + /* X position shift */ + xpos -= (reg[0] & 0x08); + + if (xpos < 0) + { + /* Clip sprites on left edge */ + width = xpos + 8; + xpos = 0; + } + else if ((xpos + 8) > max_width) + { + /* Clip sprites on right edge */ + width = max_width - xpos; + } + else + { + /* Sprite default width */ + width = 8; + } + + /* Pointer to line buffer */ + lb = &linebuf[0][0x20 + xpos]; + + /* Sprite pattern index */ + temp = object_info[count].attr; + + /* Add MSB of pattern name */ + temp |= ((reg[6] & 0x04) << 6); + + /* Mask LSB for 8x16 sprites */ + temp &= ~((reg[1] & 0x02) >> 1); + + /* Pointer to pattern cache line */ + src = (uint8 *)&bg_pattern_cache[(temp << 6) | (object_info[count].ypos << 3)]; + + /* Draw sprite pattern */ + DRAW_SPRITE_TILE(width,0,lut[5]) + } +} + +void render_obj_m5(int max_width) +{ + int i, count, column; + int xpos, width; + int pixelcount = 0; + int masked = 0; + + uint8 *src, *s, *lb; + uint32 temp, v_line; + uint32 attr, name, atex; + +#ifndef ALT_RENDERER + /* Merge background layers */ + merge(&linebuf[1][0x20], &linebuf[0][0x20], &linebuf[0][0x20], lut[0], max_width); +#endif + + /* Draw sprites in front-to-back order */ + for (count = 0; count < object_count; count++) + { + /* Sprite X position */ + xpos = object_info[count].xpos; + + /* Sprite masking */ + if (xpos) + { + /* Requires at least one sprite with xpos > 0 */ + spr_over = 1; + } + else if (spr_over) + { + /* Remaining sprites are not drawn */ + masked = 1; + } + + /* Display area offset */ + xpos = xpos - 0x80; + + /* Sprite size */ + temp = object_info[count].size; + + /* Sprite width */ + width = 8 + ((temp & 0x0C) << 1); + + /* Update pixel count (off-screen sprites are included) */ + pixelcount += width; + + /* Is sprite across visible area ? */ + if (((xpos + width) > 0) && (xpos < max_width) && !masked) + { + /* Sprite attributes */ + attr = object_info[count].attr; + + /* Sprite vertical offset */ + v_line = object_info[count].ypos; + + /* Sprite priority + palette bits */ + atex = (attr >> 9) & 0x70; + + /* Pattern name base */ + name = attr & 0x07FF; + + /* Mask vflip/hflip */ + attr &= 0x1800; + + /* Pointer into pattern name offset look-up table */ + s = &name_lut[((attr >> 3) & 0x300) | (temp << 4) | ((v_line & 0x18) >> 1)]; + + /* Pointer into line buffer */ + lb = &linebuf[0][0x20 + xpos]; + + /* Adjust number of pixels to draw for sprite limit */ + if (pixelcount > max_width) + { + width = width - pixelcount + max_width; + } + + /* Number of tiles to draw */ + width = width >> 3; + + /* Pattern row index */ + v_line = (v_line & 7) << 3; + + /* Draw sprite patterns */ + for(column = 0; column < width; column++, lb+=8) + { + temp = attr | ((name + s[column]) & 0x07FF); + src = &bg_pattern_cache[(temp << 6) | (v_line)]; + DRAW_SPRITE_TILE(8,atex,lut[1]) + } + } + + /* Sprite limit */ + if (pixelcount >= max_width) + { + /* Sprite masking will be effective on next line */ + spr_over = 1; + + /* Stop sprite rendering */ + return; + } + } + + /* Clear sprite masking for next line */ + spr_over = 0; +} + +void render_obj_m5_ste(int max_width) +{ + int i, count, column; + int xpos, width; + int pixelcount = 0; + int masked = 0; + + uint8 *src, *s, *lb; + uint32 temp, v_line; + uint32 attr, name, atex; + +#ifndef ALT_RENDERER + /* Merge background layers */ + merge(&linebuf[1][0x20], &linebuf[0][0x20], &linebuf[0][0x20], lut[2], max_width); +#endif + + /* Clear sprite line buffer */ + memset(&linebuf[1][0], 0, max_width + 0x40); + + /* Draw sprites in front-to-back order */ + for (count = 0; count < object_count; count++) + { + /* Sprite X position */ + xpos = object_info[count].xpos; + + /* Sprite masking */ + if (xpos) + { + /* Requires at least one sprite with xpos > 0 */ + spr_over = 1; + } + else if (spr_over) + { + /* Remaining sprites are not drawn */ + masked = 1; + } + + /* Display area offset */ + xpos = xpos - 0x80; + + /* Sprite size */ + temp = object_info[count].size; + + /* Sprite width */ + width = 8 + ((temp & 0x0C) << 1); + + /* Update pixel count (off-screen sprites are included) */ + pixelcount += width; + + /* Is sprite across visible area ? */ + if (((xpos + width) > 0) && (xpos < max_width) && !masked) + { + /* Sprite attributes */ + attr = object_info[count].attr; + + /* Sprite vertical offset */ + v_line = object_info[count].ypos; + + /* Sprite priority + palette bits */ + atex = (attr >> 9) & 0x70; + + /* Pattern name base */ + name = attr & 0x07FF; + + /* Mask vflip/hflip */ + attr &= 0x1800; + + /* Pointer into pattern name offset look-up table */ + s = &name_lut[((attr >> 3) & 0x300) | (temp << 4) | ((v_line & 0x18) >> 1)]; + + /* Pointer into line buffer */ + lb = &linebuf[1][0x20 + xpos]; + + /* Adjust number of pixels to draw for sprite limit */ + if (pixelcount > max_width) + { + width = width - pixelcount + max_width; + } + + /* Number of tiles to draw */ + width = width >> 3; + + /* Pattern row index */ + v_line = (v_line & 7) << 3; + + /* Draw sprite patterns */ + for(column = 0; column < width; column++, lb+=8) + { + temp = attr | ((name + s[column]) & 0x07FF); + src = &bg_pattern_cache[(temp << 6) | (v_line)]; + DRAW_SPRITE_TILE(8,atex,lut[3]) + } + } + + /* Sprite limit */ + if (pixelcount >= max_width) + { + /* Sprite masking will be effective on next line */ + spr_over = 1; + + /* Merge background & sprite layers */ + merge(&linebuf[1][0x20],&linebuf[0][0x20],&linebuf[0][0x20],lut[4], max_width); + + /* Stop sprite rendering */ + return; + } + } + + /* Clear sprite masking for next line */ + spr_over = 0; + + /* Merge background & sprite layers */ + merge(&linebuf[1][0x20],&linebuf[0][0x20],&linebuf[0][0x20],lut[4], max_width); +} + +void render_obj_m5_im2(int max_width) +{ + int i, count, column; + int xpos, width; + int pixelcount = 0; + int masked = 0; + int odd = odd_frame; + + uint8 *src, *s, *lb; + uint32 temp, v_line; + uint32 attr, name, atex; + +#ifndef ALT_RENDERER + /* Merge background layers */ + merge(&linebuf[1][0x20], &linebuf[0][0x20], &linebuf[0][0x20], lut[0], max_width); +#endif + + /* Draw sprites in front-to-back order */ + for (count = 0; count < object_count; count++) + { + /* Sprite X position */ + xpos = object_info[count].xpos; + + /* Sprite masking */ + if (xpos) + { + /* Requires at least one sprite with xpos > 0 */ + spr_over = 1; + } + else if (spr_over) + { + /* Remaining sprites are not drawn */ + masked = 1; + } + + /* Display area offset */ + xpos = xpos - 0x80; + + /* Sprite size */ + temp = object_info[count].size; + + /* Sprite width */ + width = 8 + ((temp & 0x0C) << 1); + + /* Update pixel count (off-screen sprites are included) */ + pixelcount += width; + + /* Is sprite across visible area ? */ + if (((xpos + width) > 0) && (xpos < max_width) && !masked) + { + /* Sprite attributes */ + attr = object_info[count].attr; + + /* Sprite y offset */ + v_line = object_info[count].ypos; + + /* Sprite priority + palette bits */ + atex = (attr >> 9) & 0x70; + + /* Pattern name base */ + name = attr & 0x03FF; + + /* Mask vflip/hflip */ + attr &= 0x1800; + + /* Pattern name offset lookup table */ + s = &name_lut[((attr >> 3) & 0x300) | (temp << 4) | ((v_line & 0x18) >> 1)]; + + /* Pointer into line buffer */ + lb = &linebuf[0][0x20 + xpos]; + + /* Adjust width for sprite limit */ + if (pixelcount > max_width) + { + width = width - pixelcount + max_width; + } + + /* Number of tiles to draw */ + width = width >> 3; + + /* Pattern row index */ + v_line = (((v_line & 7) << 1) | odd) << 3; + + /* Render sprite patterns */ + for(column = 0; column < width; column ++, lb+=8) + { + temp = attr | (((name + s[column]) & 0x3ff) << 1); + src = &bg_pattern_cache[((temp << 6) | (v_line)) ^ ((attr & 0x1000) >> 6)]; + DRAW_SPRITE_TILE(8,atex,lut[1]) + } + } + + /* Sprite Limit */ + if (pixelcount >= max_width) + { + /* Enable sprite masking for next line */ + spr_over = 1; + + /* Stop sprite rendering */ + return; + } + } + + /* Clear sprite masking for next line */ + spr_over = 0; +} + +void render_obj_m5_im2_ste(int max_width) +{ + int i, count, column; + int xpos, width; + int pixelcount = 0; + int masked = 0; + int odd = odd_frame; + + uint8 *src, *s, *lb; + uint32 temp, v_line; + uint32 attr, name, atex; + +#ifndef ALT_RENDERER + /* Merge background layers */ + merge(&linebuf[1][0x20], &linebuf[0][0x20], &linebuf[0][0x20], lut[2], max_width); +#endif + + /* Clear sprite line buffer */ + memset(&linebuf[1][0], 0, max_width + 0x40); + + /* Draw sprites in front-to-back order */ + for (count = 0; count < object_count; count++) + { + /* Sprite X position */ + xpos = object_info[count].xpos; + + /* Sprite masking */ + if (xpos) + { + /* Requires at least one sprite with xpos > 0 */ + spr_over = 1; + } + else if (spr_over) + { + /* Remaining sprites are not drawn */ + masked = 1; + } + + /* Display area offset */ + xpos = xpos - 0x80; + + /* Sprite size */ + temp = object_info[count].size; + + /* Sprite width */ + width = 8 + ((temp & 0x0C) << 1); + + /* Update pixel count (off-screen sprites are included) */ + pixelcount += width; + + /* Is sprite across visible area ? */ + if (((xpos + width) > 0) && (xpos < max_width) && !masked) + { + /* Sprite attributes */ + attr = object_info[count].attr; + + /* Sprite y offset */ + v_line = object_info[count].ypos; + + /* Sprite priority + palette bits */ + atex = (attr >> 9) & 0x70; + + /* Pattern name base */ + name = attr & 0x03FF; + + /* Mask vflip/hflip */ + attr &= 0x1800; + + /* Pattern name offset lookup table */ + s = &name_lut[((attr >> 3) & 0x300) | (temp << 4) | ((v_line & 0x18) >> 1)]; + + /* Pointer into line buffer */ + lb = &linebuf[1][0x20 + xpos]; + + /* Adjust width for sprite limit */ + if (pixelcount > max_width) + { + width = width - pixelcount + max_width; + } + + /* Number of tiles to draw */ + width = width >> 3; + + /* Pattern row index */ + v_line = (((v_line & 7) << 1) | odd) << 3; + + /* Render sprite patterns */ + for(column = 0; column < width; column ++, lb+=8) + { + temp = attr | (((name + s[column]) & 0x3ff) << 1); + src = &bg_pattern_cache[((temp << 6) | (v_line)) ^ ((attr & 0x1000) >> 6)]; + DRAW_SPRITE_TILE(8,atex,lut[3]) + } + } + + /* Sprite Limit */ + if (pixelcount >= max_width) + { + /* Enable sprite masking for next line */ + spr_over = 1; + + /* Merge background & sprite layers */ + merge(&linebuf[1][0x20],&linebuf[0][0x20],&linebuf[0][0x20],lut[4], max_width); + + /* Stop sprite rendering */ + return; + } + } + + /* Clear sprite masking for next line */ + spr_over = 0; + + /* Merge background & sprite layers */ + merge(&linebuf[1][0x20],&linebuf[0][0x20],&linebuf[0][0x20],lut[4], max_width); +} + + +/*--------------------------------------------------------------------------*/ +/* Sprites Parsing functions */ +/*--------------------------------------------------------------------------*/ + +void parse_satb_m4(int line) +{ + int i; + + /* Pointer to sprite attribute table */ + uint8 *st = &vram[(reg[5] << 7) & 0x3F00]; + + /* Sprite counter (64 max.) */ + int count = 0; + + /* Y position */ + int ypos; + + /* Sprite height (8x8 by default) */ + int height = 8; + + /* Adjust height for 8x16 sprites */ + height <<= ((reg[1] & 0x02) >> 1); + + /* Parse Sprite Table (64 entries) */ + for(i = 0; i < 64; i++) + { + /* Sprite Y position */ + ypos = st[i]; + + /* Found end of sprite list marker for non-extended modes? */ + if(ypos == 208) + { + break; + } + + /* Wrap Y coordinate for sprites > 240 */ + if(ypos > 240) + { + ypos -= 256; + } + + /* Y range */ + ypos = line - ypos; + + /* Sprite is visble on this line ? */ + if((ypos >= 0) && (ypos < height)) + { + /* Sprite overflow */ + if(count == 8) + { + spr_over = 0x40; + break; + } + + /* Store sprite attributes for later processing */ + object_info[count].ypos = ypos; + object_info[count].xpos = st[0x80 + (i << 1)]; + object_info[count].attr = st[0x81 + (i << 1)]; + + /* Increment Sprite count */ + ++count; + } + } + + /* Update sprite count for next line */ + object_count = count; +} + +void parse_satb_m5(int line) +{ + /* Y position */ + int ypos; + + /* Sprite height (8,16,24,32 pixels)*/ + int height; + + /* Sprite size data */ + int size; + + /* Sprite link data */ + int link = 0; + + /* Sprite counter */ + int count = 0; + + /* 16 or 20 sprites max. per line */ + int max = 16 + ((reg[12] & 1) << 2); + + /* 64 or 80 sprites max. */ + int total = max << 2; + + /* Pointer to sprite attribute table */ + uint16 *p = (uint16 *) &vram[satb]; + + /* Pointer to internal RAM */ + uint16 *q = (uint16 *) &sat[0]; + + /* Adjust line offset */ + line += 0x81; + + do + { + /* Read Y position & size from internal SAT */ + ypos = (q[link] >> im2_flag) & 0x1FF; + size = q[link + 1] >> 8; + + /* Sprite height */ + height = 8 + ((size & 3) << 3); + + /* Y range */ + ypos = line - ypos; + + /* Sprite is visble on this line ? */ + if ((ypos >= 0) && (ypos < height)) + { + /* Sprite overflow */ + if(count == max) + { + status |= 0x40; + break; + } + + /* Update sprite list */ + /* name, attribute & xpos are parsed from VRAM */ + object_info[count].attr = p[link + 2]; + object_info[count].xpos = p[link + 3] & 0x1ff; + object_info[count].ypos = ypos; + object_info[count].size = size & 0x0f; + ++count; + } + + /* Read link data from internal SAT */ + link = (q[link + 1] & 0x7F) << 2; + + /* Last sprite */ + if(link == 0) break; + } + while (--total); + + /* Update sprite count for next line */ + object_count = count; +} + + +/*--------------------------------------------------------------------------*/ +/* Pattern cache update function */ +/*--------------------------------------------------------------------------*/ + +void update_bg_pattern_cache_m4(int index) +{ + int i; + uint8 x, y, c; + uint8 *dst; + uint16 name, bp01, bp23; + uint32 bp; + + for(i = 0; i < index; i++) + { + /* Get modified pattern name index */ + name = bg_name_list[i]; + + /* Check modified lines */ + for(y = 0; y < 8; y++) + { + if(bg_name_dirty[name] & (1 << y)) + { + /* Pattern cache base address */ + dst = &bg_pattern_cache[name << 6]; + + /* Byteplane data */ + bp01 = *(uint16 *)&vram[(name << 5) | (y << 2) | (0)]; + bp23 = *(uint16 *)&vram[(name << 5) | (y << 2) | (2)]; + + /* Convert to pixel line data (4 bytes = 8 pixels)*/ + /* (msb) p7p6 p5p4 p3p2 p1p0 (lsb) */ + bp = (bp_lut[bp01] >> 2) | (bp_lut[bp23]); + + /* Update cached line (8 pixels = 8 bytes) */ + for(x = 0; x < 8; x++) + { + /* Extract pixel data */ + c = bp & 0x0F; + + /* Pattern cache data (one pattern = 8 bytes) */ + /* byte0 <-> p0 p1 p2 p3 p4 p5 p6 p7 <-> byte7 (hflip = 0) */ + /* byte0 <-> p7 p6 p5 p4 p3 p2 p1 p0 <-> byte7 (hflip = 1) */ + dst[0x00000 | (y << 3) | (x)] = (c); /* vflip=0 & hflip=0 */ + dst[0x08000 | (y << 3) | (x ^ 7)] = (c); /* vflip=0 & hflip=1 */ + dst[0x10000 | ((y ^ 7) << 3) | (x)] = (c); /* vflip=1 & hflip=0 */ + dst[0x18000 | ((y ^ 7) << 3) | (x ^ 7)] = (c); /* vflip=1 & hflip=1 */ + + /* Next pixel */ + bp = bp >> 4; + } + } + } + + /* Clear modified pattern flag */ + bg_name_dirty[name] = 0; + } +} + +void update_bg_pattern_cache_m5(int index) +{ + int i; + uint8 x, y, c; + uint8 *dst; + uint16 name; + uint32 bp; + + for(i = 0; i < index; i++) + { + /* Get modified pattern name index */ + name = bg_name_list[i]; + + /* Check modified lines */ + for(y = 0; y < 8; y ++) + { + if(bg_name_dirty[name] & (1 << y)) + { + /* Pattern cache base address */ + dst = &bg_pattern_cache[name << 6]; + + /* Byteplane data (one pattern = 4 bytes) */ + /* LIT_ENDIAN: byte0 (lsb) p2p3 p0p1 p6p7 p4p5 (msb) byte3 */ + /* BIG_ENDIAN: byte0 (msb) p0p1 p2p3 p4p5 p6p7 (lsb) byte3 */ + bp = *(uint32 *)&vram[(name << 5) | (y << 2)]; + + /* Update cached line (8 pixels = 8 bytes) */ + for(x = 0; x < 8; x ++) + { + /* Extract pixel data */ + c = bp & 0x0F; + + /* Pattern cache data (one pattern = 8 bytes) */ + /* byte0 <-> p0 p1 p2 p3 p4 p5 p6 p7 <-> byte7 (hflip = 0) */ + /* byte0 <-> p7 p6 p5 p4 p3 p2 p1 p0 <-> byte7 (hflip = 1) */ +#ifdef LSB_FIRST + /* Byteplane data = (msb) p4p5 p6p7 p0p1 p2p3 (lsb) */ + dst[0x00000 | (y << 3) | (x ^ 3)] = (c); /* vflip=0, hflip=0 */ + dst[0x20000 | (y << 3) | (x ^ 4)] = (c); /* vflip=0, hflip=1 */ + dst[0x40000 | ((y ^ 7) << 3) | (x ^ 3)] = (c); /* vflip=1, hflip=0 */ + dst[0x60000 | ((y ^ 7) << 3) | (x ^ 4)] = (c); /* vflip=1, hflip=1 */ +#else + /* Byteplane data = (msb) p0p1 p2p3 p4p5 p6p7 (lsb) */ + dst[0x00000 | (y << 3) | (x ^ 7)] = (c); /* vflip=0, hflip=0 */ + dst[0x20000 | (y << 3) | (x)] = (c); /* vflip=0, hflip=1 */ + dst[0x40000 | ((y ^ 7) << 3) | (x ^ 7)] = (c); /* vflip=1, hflip=0 */ + dst[0x60000 | ((y ^ 7) << 3) | (x)] = (c); /* vflip=1, hflip=1 */ +#endif + /* Next pixel */ + bp = bp >> 4; + } + } + } + + /* Clear modified pattern flag */ + bg_name_dirty[name] = 0; + } +} + + +/*--------------------------------------------------------------------------*/ +/* Window & Plane A clipping update function */ +/*--------------------------------------------------------------------------*/ + +void window_clip(unsigned int data, unsigned int sw) +{ + /* Window size and invert flags */ + int hp = (data & 0x1f); + int hf = (data >> 7) & 1; + + /* Display width (16 or 20 columns) */ + sw = 16 + (sw << 2); + + /* Perform horizontal clipping; the results are applied in reverse + if the horizontal inversion flag is set + */ + int a = hf; + int w = hf ^ 1; + + if(hp) + { + if(hp > sw) + { + /* Plane W takes up entire line */ + clip[w].left = 0; + clip[w].right = sw; + clip[w].enable = 1; + clip[a].enable = 0; + } + else + { + /* Plane W takes left side, Plane A takes right side */ + clip[w].left = 0; + clip[a].right = sw; + clip[a].left = clip[w].right = hp; + clip[0].enable = clip[1].enable = 1; + } + } + else + { + /* Plane A takes up entire line */ + clip[a].left = 0; + clip[a].right = sw; + clip[a].enable = 1; + clip[w].enable = 0; + } +} + + +/*--------------------------------------------------------------------------*/ +/* Init, reset routines */ +/*--------------------------------------------------------------------------*/ + +void render_init(void) +{ + int bx, ax; + + /* Initialize layers priority pixel look-up tables */ + uint16 index; + for (bx = 0; bx < 0x100; bx++) + { + for (ax = 0; ax < 0x100; ax++) + { + index = (bx << 8) | (ax); + + lut[0][index] = make_lut_bg (bx, ax); + lut[1][index] = make_lut_bgobj (bx, ax); + lut[2][index] = make_lut_bg_ste (bx, ax); + lut[3][index] = make_lut_obj (bx, ax); + lut[4][index] = make_lut_bgobj_ste (bx, ax); + lut[5][index] = make_lut_bgobj_m4 (bx,ax); + } + } + + /* Initialize pixel color look-up tables */ + palette_init(); + +#ifndef NGC + /* Set default color palette update function */ + switch(bitmap.depth) + { + case 8: color_update = color_update_8; break; + case 15: color_update = color_update_15; break; + case 16: color_update = color_update_16; break; + case 32: color_update = color_update_32; break; + } +#endif + + /* Make sprite pattern name index look-up table */ + make_name_lut(); + + /* Make bitplane to pixel look-up table (Mode 4) */ + make_bp_lut(); +} + +void render_reset(void) +{ + /* Clear display bitmap */ + memset(bitmap.data, 0, bitmap.pitch * bitmap.height); + + /* Clear line buffers */ + memset(linebuf, 0, sizeof(linebuf)); + + /* Clear color palettes */ +#ifdef NGC + memset(&pixel, 0, sizeof(pixel)); +#else + memset(&pixel_8, 0, sizeof(pixel_8)); + memset(&pixel_15, 0, sizeof(pixel_15)); + memset(&pixel_16, 0, sizeof(pixel_16)); + memset(&pixel_32, 0, sizeof(pixel_32)); +#endif +} + + +/*--------------------------------------------------------------------------*/ +/* Line rendering functions */ +/*--------------------------------------------------------------------------*/ + +void render_line(int line) +{ + int width = bitmap.viewport.w; + int x_offset = bitmap.viewport.x; + + /* Check display status */ + if (reg[1] & 0x40) + { + /* Update pattern cache */ + if (bg_list_index) + { + update_bg_pattern_cache(bg_list_index); + bg_list_index = 0; + } + + /* Render BG layer(s) */ + render_bg(line, width); + + /* Render sprite layer */ + render_obj(width); + + /* Left-most column blanking */ + if(reg[0] & 0x20) + { + memset(&linebuf[0][0x20], 0x40, 8); + } + + /* Horizontal borders */ + if (x_offset) + { + memset(&linebuf[0][0x20 - x_offset], 0x40, x_offset); + memset(&linebuf[0][0x20 + width], 0x40, x_offset); + } + + /* Parse sprites for next line */ + if (line < (bitmap.viewport.h - 1)) + { + parse_satb(line); + } + } + else + { + /* Display disabled */ + memset(&linebuf[0][0x20 - x_offset], 0x40, width + (x_offset << 1)); + } + + /* Pixel color remapping */ + remap_line(line); +} + +void blank_line(int line, int offset, int width) +{ + memset(&linebuf[0][0x20 + offset], 0x40, width); + remap_line(line); +} + +void remap_line(int line) +{ + /* Line width */ + int x_offset = bitmap.viewport.x; + int width = bitmap.viewport.w + (x_offset << 1); + + /* Adjust line offset in framebuffer */ + line = (line + bitmap.viewport.y) % lines_per_frame; + + /* Adjust for interlaced output */ + if (interlaced && config.render) + { + line = (line << 1) + odd_frame; + } + +#ifdef NGC + /* NTSC Filter */ + if (config.ntsc) + { + if (reg[12]&1) + { + md_ntsc_blit(md_ntsc, ( MD_NTSC_IN_T const * )pixel, &linebuf[0][0x20 - x_offset], width, line); + } + else + { + sms_ntsc_blit(sms_ntsc, ( SMS_NTSC_IN_T const * )pixel, &linebuf[0][0x20 - x_offset], width, line); + } + } + else + { + /* Directly fill a RGB565 texture */ + /* One tile is 32 byte = 4x4 pixels */ + /* Tiles are stored continuously in texture memory */ + width >>= 2; + uint8 *src = &linebuf[0][0x20 - x_offset]; + uint16 *dst = (uint16 *) (texturemem + (((width << 5) * (line >> 2)) + ((line & 3) << 3))); + do + { + *dst++ = pixel[*src++]; + *dst++ = pixel[*src++]; + *dst++ = pixel[*src++]; + *dst++ = pixel[*src++]; + + /* next tile */ + dst += 12; + } + while (--width); + } +#else + void *out =((void *)&bitmap.data[(line * bitmap.pitch)]); + switch(bitmap.depth) + { + case 8: + remap_8(&linebuf[0][0x20 - x_offset], (uint8 *)out, width); + break; + case 15: + remap_16(&linebuf[0][0x20 - x_offset], (uint16 *)out, width); + break; + case 16: + remap_16(&linebuf[0][0x20 - x_offset], (uint16 *)out, width); + break; + case 32: + remap_32(&linebuf[0][0x20 - x_offset], (uint32 *)out, width); + break; + } +#endif +} diff --git a/source/vdp_render.h b/source/vdp_render.h new file mode 100644 index 0000000..7d77383 --- /dev/null +++ b/source/vdp_render.h @@ -0,0 +1,65 @@ +/*************************************************************************************** + * Genesis Plus + * Video Display Processor (Mode 4 & Mode 5 rendering) + * + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Charles Mac Donald (original code) + * Eke-Eke (2007-2011), additional code & fixes for the GCN/Wii port + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************************************/ + +#ifndef _RENDER_H_ +#define _RENDER_H_ + +/* Global variables */ +extern uint8 object_count; + +/* Function prototypes */ +extern void render_init(void); +extern void render_reset(void); +extern void render_line(int line); +extern void blank_line(int line, int offset, int width); +extern void remap_line(int line); +extern void window_clip(unsigned int data, unsigned int sw); +extern void render_bg_m4(int line, int width); +extern void render_bg_m5(int line, int width); +extern void render_bg_m5_vs(int line, int width); +extern void render_bg_m5_im2(int line, int width); +extern void render_bg_m5_im2_vs(int line, int width); +extern void render_obj_m4(int max_width); +extern void render_obj_m5(int max_width); +extern void render_obj_m5_ste(int max_width); +extern void render_obj_m5_im2(int max_width); +extern void render_obj_m5_im2_ste(int max_width); +extern void parse_satb_m4(int line); +extern void parse_satb_m5(int line); +extern void update_bg_pattern_cache_m4(int index); +extern void update_bg_pattern_cache_m5(int index); +#ifdef NGC +extern void color_update(int index, unsigned int data); +#endif + +/* Function pointers */ +extern void (*render_bg)(int line, int width); +extern void (*render_obj)(int max_width); +extern void (*parse_satb)(int line); +extern void (*update_bg_pattern_cache)(int index); +#ifndef NGC +extern void (*color_update)(int index, unsigned int data); +#endif + +#endif /* _RENDER_H_ */ + diff --git a/source/z80/osd_cpu.h b/source/z80/osd_cpu.h index 1a6bf6f..e9bce93 100644 --- a/source/z80/osd_cpu.h +++ b/source/z80/osd_cpu.h @@ -1,27 +1,26 @@ /******************************************************************************* -* * -* Define size independent data types and operations. * -* * -* The following types must be supported by all platforms: * -* * -* UINT8 - Unsigned 8-bit Integer INT8 - Signed 8-bit integer * -* UINT16 - Unsigned 16-bit Integer INT16 - Signed 16-bit integer * -* UINT32 - Unsigned 32-bit Integer INT32 - Signed 32-bit integer * -* UINT64 - Unsigned 64-bit Integer INT64 - Signed 64-bit integer * -* * -* * +* * +* Define size independent data types and operations. * +* * +* The following types must be supported by all platforms: * +* * +* UINT8 - Unsigned 8-bit Integer INT8 - Signed 8-bit integer * +* UINT16 - Unsigned 16-bit Integer INT16 - Signed 16-bit integer * +* UINT32 - Unsigned 32-bit Integer INT32 - Signed 32-bit integer * +* UINT64 - Unsigned 64-bit Integer INT64 - Signed 64-bit integer * +* * +* * * The macro names for the artithmatic operations are composed as follows: * -* * -* XXX_R_A_B, where XXX - 3 letter operation code (ADD, SUB, etc.) * -* R - The type of the result * -* A - The type of operand 1 * -* B - The type of operand 2 (if binary operation) * -* * -* Each type is one of: U8,8,U16,16,U32,32,U64,64 * -* * +* * +* XXX_R_A_B, where XXX - 3 letter operation code (ADD, SUB, etc.) * +* R - The type of the result * +* A - The type of operand 1 * +* B - The type of operand 2 (if binary operation) * +* * +* Each type is one of: U8,8,U16,16,U32,32,U64,64 * +* * *******************************************************************************/ - #ifndef OSD_CPU_H #define OSD_CPU_H @@ -52,6 +51,7 @@ __extension__ typedef signed long long INT64; * thus PAIR.d can be used to pass arguments to the memory system * which expects 'int' really. ******************************************************************************/ + typedef union { #ifdef LSB_FIRST struct { UINT8 l,h,h2,h3; } b; diff --git a/source/z80/z80.c b/source/z80/z80.c index 907095e..2e13587 100644 --- a/source/z80/z80.c +++ b/source/z80/z80.c @@ -35,6 +35,7 @@ * - Fixed cycle counting for FD and DD prefixed instructions * - Fixed behavior of chained FD and DD prefixes (R register should be only incremented by one * - Implemented cycle-accurate INI/IND (needed by SMS emulation) + * - Fixed Z80 reset * Changes in 3.9: * - Fixed cycle counts for LD IYL/IXL/IYH/IXH,n [Marshmellow] * - Fixed X/Y flags in CCF/SCF/BIT, ZEXALL is happy now [hap] @@ -123,6 +124,9 @@ #include "shared.h" #include "z80.h" +/* execute main opcodes inside a big switch statement */ +#define BIG_SWITCH 1 + #define VERBOSE 0 #if VERBOSE @@ -131,11 +135,9 @@ #define LOG(x) #endif -/* execute main opcodes inside a big switch statement */ -#define BIG_SWITCH 1 -#define cpu_readop(a) zram[(a) & 0x1fff] -#define cpu_readop_arg(a) zram[(a) & 0x1fff] +#define cpu_readop(a) z80_readmap[(a) >> 10][(a) & 0x03FF] +#define cpu_readop_arg(a) z80_readmap[(a) >> 10][(a) & 0x03FF] #define CF 0x01 #define NF 0x02 @@ -201,6 +203,15 @@ extern unsigned int mcycles_z80; Z80_Regs Z80; + +unsigned char *z80_readmap[64]; +unsigned char *z80_writemap[64]; + +void (*z80_writemem)(unsigned int address, unsigned char data); +unsigned char (*z80_readmem)(unsigned int port); +void (*z80_writeport)(unsigned int port, unsigned char data); +unsigned char (*z80_readport)(unsigned int port); + static UINT32 EA; static UINT8 SZ[256]; /* zero and sign flags */ @@ -587,17 +598,22 @@ INLINE void BURNODD(int cycles, int opcodes, int cyclesum) /*************************************************************** * Input a byte from given I/O port ***************************************************************/ -#define IN(port) ((UINT8)cpu_readport16(port)) +#define IN(port) z80_readport(port) /*************************************************************** * Output a byte to given I/O port ***************************************************************/ -#define OUT(port,value) cpu_writeport16(port,value) +#define OUT(port,value) z80_writeport(port,value) /*************************************************************** * Read a byte from given memory location ***************************************************************/ -#define RM(addr) (UINT8)cpu_readmem16(addr) +#define RM(addr) z80_readmem(addr) + +/*************************************************************** + * Write a byte to given memory location + ***************************************************************/ +#define WM(addr,value) z80_writemem(addr,value) /*************************************************************** * Read a word from given memory location @@ -608,11 +624,6 @@ INLINE void RM16( UINT32 addr, PAIR *r ) r->b.h = RM((addr+1)&0xffff); } -/*************************************************************** - * Write a byte to given memory location - ***************************************************************/ -#define WM(addr,value) cpu_writemem16(addr,value) - /*************************************************************** * Write a word to given memory location ***************************************************************/ @@ -676,7 +687,7 @@ INLINE UINT32 ARG16(void) ***************************************************************/ #define JP { \ PCD = ARG16(); \ - WZ = PCD; \ + WZ = PCD; \ } /*************************************************************** @@ -686,21 +697,21 @@ INLINE UINT32 ARG16(void) if (cond) \ { \ PCD = ARG16(); \ - WZ = PCD; \ + WZ = PCD; \ } \ else \ { \ - WZ = ARG16(); /* implicit do PC += 2 */ \ + WZ = ARG16(); /* implicit do PC += 2 */ \ } \ } /*************************************************************** * JR ***************************************************************/ -#define JR() { \ - INT8 arg = (INT8)ARG(); /* ARG() also increments PC */ \ - PC += arg; /* so don't do PC += ARG() */ \ - WZ = PC; \ +#define JR() { \ + INT8 arg = (INT8)ARG(); /* ARG() also increments PC */ \ + PC += arg; /* so don't do PC += ARG() */ \ + WZ = PC; \ } /*************************************************************** @@ -720,7 +731,7 @@ INLINE UINT32 ARG16(void) ***************************************************************/ #define CALL() { \ EA = ARG16(); \ - WZ = EA; \ + WZ = EA; \ PUSH(pc); \ PCD = EA; \ } @@ -732,7 +743,7 @@ INLINE UINT32 ARG16(void) if (cond) \ { \ EA = ARG16(); \ - WZ = EA; \ + WZ = EA; \ PUSH(pc); \ PCD = EA; \ CC(ex, opcode); \ @@ -750,7 +761,7 @@ INLINE UINT32 ARG16(void) if (cond) \ { \ POP(pc); \ - WZ = PC; \ + WZ = PC; \ CC(ex, opcode); \ } \ } while (0) @@ -778,41 +789,41 @@ INLINE UINT32 ARG16(void) /*************************************************************** * LD R,A ***************************************************************/ -#define LD_R_A { \ - R = A; \ - R2 = A & 0x80; /* keep bit 7 of R */ \ +#define LD_R_A { \ + R = A; \ + R2 = A & 0x80; /* keep bit 7 of R */ \ } /*************************************************************** * LD A,R ***************************************************************/ -#define LD_A_R { \ - A = (R & 0x7f) | R2; \ - F = (F & CF) | SZ[A] | ( IFF2 << 2 ); \ +#define LD_A_R { \ + A = (R & 0x7f) | R2; \ + F = (F & CF) | SZ[A] | ( IFF2 << 2 ); \ } /*************************************************************** * LD I,A ***************************************************************/ -#define LD_I_A { \ - I = A; \ +#define LD_I_A { \ + I = A; \ } /*************************************************************** * LD A,I ***************************************************************/ -#define LD_A_I { \ - A = I; \ - F = (F & CF) | SZ[A] | ( IFF2 << 2 ); \ +#define LD_A_I { \ + A = I; \ + F = (F & CF) | SZ[A] | ( IFF2 << 2 ); \ } /*************************************************************** * RST ***************************************************************/ -#define RST(addr) \ - PUSH( pc ); \ - PCD = addr; \ - WZ = PC; \ +#define RST(addr) \ + PUSH( pc ); \ + PCD = addr; \ + WZ = PC; \ /*************************************************************** * INC r8 @@ -839,7 +850,7 @@ INLINE UINT8 DEC(UINT8 value) ***************************************************************/ #define RLCA \ A = (A << 1) | (A >> 7); \ - F = (F & (SF | ZF | PF)) | (A & (YF | XF | CF)) + F = (F & (SF | ZF | PF)) | (A & (YF | XF | CF)) /*************************************************************** * RRCA @@ -847,16 +858,16 @@ INLINE UINT8 DEC(UINT8 value) #define RRCA \ F = (F & (SF | ZF | PF)) | (A & CF); \ A = (A >> 1) | (A << 7); \ - F |= (A & (YF | XF) ) + F |= (A & (YF | XF) ) /*************************************************************** * RLA ***************************************************************/ -#define RLA { \ +#define RLA { \ UINT8 res = (A << 1) | (F & CF); \ UINT8 c = (A & 0x80) ? CF : 0; \ F = (F & (SF | ZF | PF)) | c | (res & (YF | XF)); \ - A = res; \ + A = res; \ } /*************************************************************** @@ -874,7 +885,7 @@ INLINE UINT8 DEC(UINT8 value) ***************************************************************/ #define RRD { \ UINT8 n = RM(HL); \ - WZ = HL+1; \ + WZ = HL+1; \ WM( HL, (n >> 4) | (A << 4) ); \ A = (A & 0xf0) | (n & 0x0f); \ F = (F & CF) | SZP[A]; \ @@ -883,197 +894,200 @@ INLINE UINT8 DEC(UINT8 value) /*************************************************************** * RLD ***************************************************************/ -#define RLD { \ - UINT8 n = RM(HL); \ - WZ = HL+1; \ - WM( HL, (n << 4) | (A & 0x0f) ); \ - A = (A & 0xf0) | (n >> 4); \ - F = (F & CF) | SZP[A]; \ +#define RLD { \ + UINT8 n = RM(HL); \ + WZ = HL+1; \ + WM( HL, (n << 4) | (A & 0x0f) ); \ + A = (A & 0xf0) | (n >> 4); \ + F = (F & CF) | SZP[A]; \ } /*************************************************************** * ADD A,n ***************************************************************/ -#define ADD(value) \ -{ \ - UINT32 ah = AFD & 0xff00; \ +#define ADD(value) \ +{ \ + UINT32 ah = AFD & 0xff00; \ UINT32 res = (UINT8)((ah >> 8) + value); \ - F = SZHVC_add[ah | res]; \ - A = res; \ + F = SZHVC_add[ah | res]; \ + A = res; \ } /*************************************************************** * ADC A,n ***************************************************************/ -#define ADC(value) \ -{ \ +#define ADC(value) \ +{ \ UINT32 ah = AFD & 0xff00, c = AFD & 1; \ - UINT32 res = (UINT8)((ah >> 8) + value + c); \ - F = SZHVC_add[(c << 16) | ah | res]; \ - A = res; \ + UINT32 res = (UINT8)((ah >> 8) + value + c); \ + F = SZHVC_add[(c << 16) | ah | res]; \ + A = res; \ } /*************************************************************** * SUB n ***************************************************************/ -#define SUB(value) \ -{ \ - UINT32 ah = AFD & 0xff00; \ +#define SUB(value) \ +{ \ + UINT32 ah = AFD & 0xff00; \ UINT32 res = (UINT8)((ah >> 8) - value); \ - F = SZHVC_sub[ah | res]; \ - A = res; \ + F = SZHVC_sub[ah | res]; \ + A = res; \ } /*************************************************************** * SBC A,n ***************************************************************/ -#define SBC(value) \ -{ \ +#define SBC(value) \ +{ \ UINT32 ah = AFD & 0xff00, c = AFD & 1; \ - UINT32 res = (UINT8)((ah >> 8) - value - c); \ - F = SZHVC_sub[(c<<16) | ah | res]; \ - A = res; \ + UINT32 res = (UINT8)((ah >> 8) - value - c); \ + F = SZHVC_sub[(c<<16) | ah | res]; \ + A = res; \ } /*************************************************************** * NEG ***************************************************************/ -#define NEG { \ - UINT8 value = A; \ - A = 0; \ - SUB(value); \ +#define NEG { \ + UINT8 value = A; \ + A = 0; \ + SUB(value); \ } /*************************************************************** * DAA ***************************************************************/ -#define DAA { \ - UINT8 a = A; \ - if (F & NF) { \ - if ((F&HF) | ((A&0xf)>9)) a-=6; \ - if ((F&CF) | (A>0x99)) a-=0x60; \ - } \ - else { \ - if ((F&HF) | ((A&0xf)>9)) a+=6; \ - if ((F&CF) | (A>0x99)) a+=0x60; \ - } \ - \ - F = (F&(CF|NF)) | (A>0x99) | ((A^a)&HF) | SZP[a]; \ - A = a; \ -} +#define DAA { \ + UINT8 a = A; \ + if (F & NF) { \ + if ((F&HF) | ((A&0xf)>9)) a-=6; \ + if ((F&CF) | (A>0x99)) a-=0x60; \ + } \ + else { \ + if ((F&HF) | ((A&0xf)>9)) a+=6; \ + if ((F&CF) | (A>0x99)) a+=0x60; \ + } \ + \ + F = (F&(CF|NF)) | (A>0x99) | ((A^a)&HF) | SZP[a]; \ + A = a; \ +} /*************************************************************** * AND n ***************************************************************/ -#define AND(value) \ - A &= value; \ - F = SZP[A] | HF +#define AND(value) \ + A &= value; \ + F = SZP[A] | HF /*************************************************************** * OR n ***************************************************************/ -#define OR(value) \ - A |= value; \ - F = SZP[A] +#define OR(value) \ + A |= value; \ + F = SZP[A] /*************************************************************** * XOR n ***************************************************************/ -#define XOR(value) \ - A ^= value; \ - F = SZP[A] +#define XOR(value) \ + A ^= value; \ + F = SZP[A] /*************************************************************** * CP n ***************************************************************/ -#define CP(value) { \ - unsigned val = value; \ - UINT32 ah = AFD & 0xff00; \ - UINT32 res = (UINT8)((ah >> 8) - val); \ - F = (SZHVC_sub[ah | res] & ~(YF | XF)) | \ - (val & (YF | XF)); \ +#define CP(value) \ +{ \ + unsigned val = value; \ + UINT32 ah = AFD & 0xff00; \ + UINT32 res = (UINT8)((ah >> 8) - val); \ + F = (SZHVC_sub[ah | res] & ~(YF | XF)) | (val & (YF | XF)); \ } /*************************************************************** * EX AF,AF' ***************************************************************/ -#define EX_AF { \ - PAIR tmp; \ - tmp = Z80.af; Z80.af = Z80.af2; Z80.af2 = tmp; \ +#define EX_AF \ +{ \ + PAIR tmp; \ + tmp = Z80.af; Z80.af = Z80.af2; Z80.af2 = tmp; \ } /*************************************************************** * EX DE,HL ***************************************************************/ -#define EX_DE_HL { \ - PAIR tmp; \ - tmp = Z80.de; Z80.de = Z80.hl; Z80.hl = tmp; \ +#define EX_DE_HL \ +{ \ + PAIR tmp; \ + tmp = Z80.de; Z80.de = Z80.hl; Z80.hl = tmp; \ } /*************************************************************** * EXX ***************************************************************/ -#define EXX { \ - PAIR tmp; \ - tmp = Z80.bc; Z80.bc = Z80.bc2; Z80.bc2 = tmp; \ - tmp = Z80.de; Z80.de = Z80.de2; Z80.de2 = tmp; \ - tmp = Z80.hl; Z80.hl = Z80.hl2; Z80.hl2 = tmp; \ +#define EXX \ +{ \ + PAIR tmp; \ + tmp = Z80.bc; Z80.bc = Z80.bc2; Z80.bc2 = tmp; \ + tmp = Z80.de; Z80.de = Z80.de2; Z80.de2 = tmp; \ + tmp = Z80.hl; Z80.hl = Z80.hl2; Z80.hl2 = tmp; \ } /*************************************************************** * EX (SP),r16 ***************************************************************/ -#define EXSP(DR) \ -{ \ - PAIR tmp = { { 0, 0, 0, 0 } }; \ - RM16( SPD, &tmp ); \ - WM16( SPD, &Z80.DR ); \ - Z80.DR = tmp; \ - WZ = Z80.DR.d; \ +#define EXSP(DR) \ +{ \ + PAIR tmp = { { 0, 0, 0, 0 } }; \ + RM16( SPD, &tmp ); \ + WM16( SPD, &Z80.DR ); \ + Z80.DR = tmp; \ + WZ = Z80.DR.d; \ } /*************************************************************** * ADD16 ***************************************************************/ -#define ADD16(DR,SR) \ -{ \ - UINT32 res = Z80.DR.d + Z80.SR.d; \ - WZ = Z80.DR.d + 1; \ - F = (F & (SF | ZF | VF)) | \ - (((Z80.DR.d ^ res ^ Z80.SR.d) >> 8) & HF) | \ - ((res >> 16) & CF) | ((res >> 8) & (YF | XF)); \ - Z80.DR.w.l = (UINT16)res; \ +#define ADD16(DR,SR) \ +{ \ + UINT32 res = Z80.DR.d + Z80.SR.d; \ + WZ = Z80.DR.d + 1; \ + F = (F & (SF | ZF | VF)) | \ + (((Z80.DR.d ^ res ^ Z80.SR.d) >> 8) & HF) | \ + ((res >> 16) & CF) | ((res >> 8) & (YF | XF)); \ + Z80.DR.w.l = (UINT16)res; \ } /*************************************************************** * ADC r16,r16 ***************************************************************/ -#define ADC16(Reg) \ -{ \ - UINT32 res = HLD + Z80.Reg.d + (F & CF); \ - WZ = HL + 1; \ - F = (((HLD ^ res ^ Z80.Reg.d) >> 8) & HF) | \ - ((res >> 16) & CF) | \ - ((res >> 8) & (SF | YF | XF)) | \ - ((res & 0xffff) ? 0 : ZF) | \ - (((Z80.Reg.d ^ HLD ^ 0x8000) & (Z80.Reg.d ^ res) & 0x8000) >> 13); \ - HL = (UINT16)res; \ +#define ADC16(Reg) \ +{ \ + UINT32 res = HLD + Z80.Reg.d + (F & CF); \ + WZ = HL + 1; \ + F = (((HLD ^ res ^ Z80.Reg.d) >> 8) & HF) | \ + ((res >> 16) & CF) | \ + ((res >> 8) & (SF | YF | XF)) | \ + ((res & 0xffff) ? 0 : ZF) | \ + (((Z80.Reg.d ^ HLD ^ 0x8000) & (Z80.Reg.d ^ res) & 0x8000) >> 13); \ + HL = (UINT16)res; \ } /*************************************************************** * SBC r16,r16 ***************************************************************/ -#define SBC16(Reg) \ -{ \ - UINT32 res = HLD - Z80.Reg.d - (F & CF); \ - WZ = HL + 1; \ +#define SBC16(Reg) \ +{ \ + UINT32 res = HLD - Z80.Reg.d - (F & CF); \ + WZ = HL + 1; \ F = (((HLD ^ res ^ Z80.Reg.d) >> 8) & HF) | NF | \ - ((res >> 16) & CF) | \ - ((res >> 8) & (SF | YF | XF)) | \ - ((res & 0xffff) ? 0 : ZF) | \ - (((Z80.Reg.d ^ HLD) & (HLD ^ res) &0x8000) >> 13); \ - HL = (UINT16)res; \ + ((res >> 16) & CF) | \ + ((res >> 8) & (SF | YF | XF)) | \ + ((res & 0xffff) ? 0 : ZF) | \ + (((Z80.Reg.d ^ HLD) & (HLD ^ res) &0x8000) >> 13); \ + HL = (UINT16)res; \ } /*************************************************************** @@ -1176,19 +1190,19 @@ INLINE UINT8 SRL(UINT8 value) * BIT bit,r8 ***************************************************************/ #undef BIT -#define BIT(bit,reg) \ - F = (F & CF) | HF | (SZ_BIT[reg & (1<>8) & (YF|XF)) /*************************************************************** @@ -1210,227 +1224,227 @@ INLINE UINT8 SET(UINT8 bit, UINT8 value) /*************************************************************** * LDI ***************************************************************/ -#define LDI { \ - UINT8 io = RM(HL); \ - WM( DE, io ); \ - F &= SF | ZF | CF; \ - if( (A + io) & 0x02 ) F |= YF; /* bit 1 -> flag 5 */ \ - if( (A + io) & 0x08 ) F |= XF; /* bit 3 -> flag 3 */ \ - HL++; DE++; BC--; \ - if( BC ) F |= VF; \ +#define LDI { \ + UINT8 io = RM(HL); \ + WM( DE, io ); \ + F &= SF | ZF | CF; \ + if( (A + io) & 0x02 ) F |= YF; /* bit 1 -> flag 5 */ \ + if( (A + io) & 0x08 ) F |= XF; /* bit 3 -> flag 3 */ \ + HL++; DE++; BC--; \ + if( BC ) F |= VF; \ } /*************************************************************** * CPI ***************************************************************/ -#define CPI { \ - UINT8 val = RM(HL); \ - UINT8 res = A - val; \ - WZ++; \ - HL++; BC--; \ +#define CPI { \ + UINT8 val = RM(HL); \ + UINT8 res = A - val; \ + WZ++; \ + HL++; BC--; \ F = (F & CF) | (SZ[res]&~(YF|XF)) | ((A^val^res)&HF) | NF; \ - if( F & HF ) res -= 1; \ - if( res & 0x02 ) F |= YF; /* bit 1 -> flag 5 */ \ - if( res & 0x08 ) F |= XF; /* bit 3 -> flag 3 */ \ - if( BC ) F |= VF; \ + if( F & HF ) res -= 1; \ + if( res & 0x02 ) F |= YF; /* bit 1 -> flag 5 */ \ + if( res & 0x08 ) F |= XF; /* bit 3 -> flag 3 */ \ + if( BC ) F |= VF; \ } /*************************************************************** * INI ***************************************************************/ -#define INI { \ - unsigned t; \ - UINT8 io = IN(BC); \ - WZ = BC + 1; \ - CC(ex,0xa2); \ - B--; \ - WM( HL, io ); \ - HL++; \ - F = SZ[B]; \ +#define INI { \ + unsigned t; \ + UINT8 io = IN(BC); \ + WZ = BC + 1; \ + CC(ex,0xa2); \ + B--; \ + WM( HL, io ); \ + HL++; \ + F = SZ[B]; \ t = (unsigned)((C + 1) & 0xff) + (unsigned)io; \ - if( io & SF ) F |= NF; \ - if( t & 0x100 ) F |= HF | CF; \ - F |= SZP[(UINT8)(t & 0x07) ^ B] & PF; \ + if( io & SF ) F |= NF; \ + if( t & 0x100 ) F |= HF | CF; \ + F |= SZP[(UINT8)(t & 0x07) ^ B] & PF; \ } /*************************************************************** * OUTI ***************************************************************/ -#define OUTI { \ - unsigned t; \ - UINT8 io = RM(HL); \ - B--; \ - WZ = BC + 1; \ - OUT( BC, io ); \ - HL++; \ - F = SZ[B]; \ - t = (unsigned)L + (unsigned)io; \ - if( io & SF ) F |= NF; \ - if( t & 0x100 ) F |= HF | CF; \ - F |= SZP[(UINT8)(t & 0x07) ^ B] & PF; \ +#define OUTI { \ + unsigned t; \ + UINT8 io = RM(HL); \ + B--; \ + WZ = BC + 1; \ + OUT( BC, io ); \ + HL++; \ + F = SZ[B]; \ + t = (unsigned)L + (unsigned)io; \ + if( io & SF ) F |= NF; \ + if( t & 0x100 ) F |= HF | CF; \ + F |= SZP[(UINT8)(t & 0x07) ^ B] & PF; \ } /*************************************************************** * LDD ***************************************************************/ -#define LDD { \ - UINT8 io = RM(HL); \ - WM( DE, io ); \ - F &= SF | ZF | CF; \ - if( (A + io) & 0x02 ) F |= YF; /* bit 1 -> flag 5 */ \ - if( (A + io) & 0x08 ) F |= XF; /* bit 3 -> flag 3 */ \ - HL--; DE--; BC--; \ - if( BC ) F |= VF; \ +#define LDD { \ + UINT8 io = RM(HL); \ + WM( DE, io ); \ + F &= SF | ZF | CF; \ + if( (A + io) & 0x02 ) F |= YF; /* bit 1 -> flag 5 */ \ + if( (A + io) & 0x08 ) F |= XF; /* bit 3 -> flag 3 */ \ + HL--; DE--; BC--; \ + if( BC ) F |= VF; \ } /*************************************************************** * CPD ***************************************************************/ -#define CPD { \ - UINT8 val = RM(HL); \ - UINT8 res = A - val; \ - WZ--; \ - HL--; BC--; \ +#define CPD { \ + UINT8 val = RM(HL); \ + UINT8 res = A - val; \ + WZ--; \ + HL--; BC--; \ F = (F & CF) | (SZ[res]&~(YF|XF)) | ((A^val^res)&HF) | NF; \ - if( F & HF ) res -= 1; \ - if( res & 0x02 ) F |= YF; /* bit 1 -> flag 5 */ \ - if( res & 0x08 ) F |= XF; /* bit 3 -> flag 3 */ \ - if( BC ) F |= VF; \ + if( F & HF ) res -= 1; \ + if( res & 0x02 ) F |= YF; /* bit 1 -> flag 5 */ \ + if( res & 0x08 ) F |= XF; /* bit 3 -> flag 3 */ \ + if( BC ) F |= VF; \ } /*************************************************************** * IND ***************************************************************/ -#define IND { \ - unsigned t; \ - UINT8 io = IN(BC); \ - WZ = BC - 1; \ - CC(ex,0xaa); \ - B--; \ - WM( HL, io ); \ - HL--; \ - F = SZ[B]; \ +#define IND { \ + unsigned t; \ + UINT8 io = IN(BC); \ + WZ = BC - 1; \ + CC(ex,0xaa); \ + B--; \ + WM( HL, io ); \ + HL--; \ + F = SZ[B]; \ t = ((unsigned)(C - 1) & 0xff) + (unsigned)io; \ - if( io & SF ) F |= NF; \ - if( t & 0x100 ) F |= HF | CF; \ - F |= SZP[(UINT8)(t & 0x07) ^ B] & PF; \ + if( io & SF ) F |= NF; \ + if( t & 0x100 ) F |= HF | CF; \ + F |= SZP[(UINT8)(t & 0x07) ^ B] & PF; \ } /*************************************************************** * OUTD ***************************************************************/ -#define OUTD { \ - unsigned t; \ - UINT8 io = RM(HL); \ - B--; \ - WZ = BC - 1; \ - OUT( BC, io ); \ - HL--; \ - F = SZ[B]; \ - t = (unsigned)L + (unsigned)io; \ - if( io & SF ) F |= NF; \ - if( t & 0x100 ) F |= HF | CF; \ - F |= SZP[(UINT8)(t & 0x07) ^ B] & PF; \ +#define OUTD { \ + unsigned t; \ + UINT8 io = RM(HL); \ + B--; \ + WZ = BC - 1; \ + OUT( BC, io ); \ + HL--; \ + F = SZ[B]; \ + t = (unsigned)L + (unsigned)io; \ + if( io & SF ) F |= NF; \ + if( t & 0x100 ) F |= HF | CF; \ + F |= SZP[(UINT8)(t & 0x07) ^ B] & PF; \ } /*************************************************************** * LDIR ***************************************************************/ -#define LDIR \ - LDI; \ - if( BC ) \ - { \ - PC -= 2; \ - WZ = PC + 1; \ - CC(ex,0xb0); \ +#define LDIR \ + LDI; \ + if( BC ) \ + { \ + PC -= 2; \ + WZ = PC + 1; \ + CC(ex,0xb0); \ } /*************************************************************** * CPIR ***************************************************************/ -#define CPIR \ - CPI; \ - if( BC && !(F & ZF) ) \ - { \ - PC -= 2; \ - WZ = PC + 1; \ - CC(ex,0xb1); \ +#define CPIR \ + CPI; \ + if( BC && !(F & ZF) ) \ + { \ + PC -= 2; \ + WZ = PC + 1; \ + CC(ex,0xb1); \ } /*************************************************************** * INIR ***************************************************************/ -#define INIR \ - INI; \ - if( B ) \ - { \ - PC -= 2; \ - CC(ex,0xb2); \ +#define INIR \ + INI; \ + if( B ) \ + { \ + PC -= 2; \ + CC(ex,0xb2); \ } /*************************************************************** * OTIR ***************************************************************/ -#define OTIR \ - OUTI; \ - if( B ) \ - { \ - PC -= 2; \ - CC(ex,0xb3); \ +#define OTIR \ + OUTI; \ + if( B ) \ + { \ + PC -= 2; \ + CC(ex,0xb3); \ } /*************************************************************** * LDDR ***************************************************************/ -#define LDDR \ - LDD; \ - if( BC ) \ - { \ - PC -= 2; \ - WZ = PC + 1; \ - CC(ex,0xb8); \ +#define LDDR \ + LDD; \ + if( BC ) \ + { \ + PC -= 2; \ + WZ = PC + 1; \ + CC(ex,0xb8); \ } /*************************************************************** * CPDR ***************************************************************/ -#define CPDR \ - CPD; \ - if( BC && !(F & ZF) ) \ - { \ - PC -= 2; \ - WZ = PC + 1; \ - CC(ex,0xb9); \ +#define CPDR \ + CPD; \ + if( BC && !(F & ZF) ) \ + { \ + PC -= 2; \ + WZ = PC + 1; \ + CC(ex,0xb9); \ } /*************************************************************** * INDR ***************************************************************/ -#define INDR \ - IND; \ - if( B ) \ - { \ - PC -= 2; \ - CC(ex,0xba); \ +#define INDR \ + IND; \ + if( B ) \ + { \ + PC -= 2; \ + CC(ex,0xba); \ } /*************************************************************** * OTDR ***************************************************************/ -#define OTDR \ - OUTD; \ - if( B ) \ - { \ - PC -= 2; \ - CC(ex,0xbb); \ +#define OTDR \ + OUTD; \ + if( B ) \ + { \ + PC -= 2; \ + CC(ex,0xbb); \ } /*************************************************************** * EI ***************************************************************/ -#define EI { \ - IFF1 = IFF2 = 1; \ - Z80.after_ei = TRUE; \ +#define EI { \ + IFF1 = IFF2 = 1; \ + Z80.after_ei = TRUE; \ } /********************************************************** @@ -3200,38 +3214,37 @@ OP(op,ff) { RST(0x38); static void take_interrupt(void) { - int irq_vector; - /* Check if processor was halted */ LEAVE_HALT; /* Clear both interrupt flip flops */ IFF1 = IFF2 = 0; - /* call back the cpu interface to retrieve the vector */ - irq_vector = (*Z80.irq_callback)(0); - LOG(("Z80 #%d single int. irq_vector $%02x\n", cpu_getactivecpu(), irq_vector)); - /* Interrupt mode 2. Call [Z80.i:databyte] */ - if( IM == 2 ) + /* Interrupt mode 1. RST 38h */ + if( IM == 1 ) { - irq_vector = (irq_vector & 0xff) | (I << 8); + LOG(("Z80 #%d IM1 $0038\n",cpu_getactivecpu() )); PUSH( pc ); - RM16( irq_vector, &Z80.pc ); - LOG(("Z80 #%d IM2 [$%04x] = $%04x\n",cpu_getactivecpu() , irq_vector, PCD)); - /* CALL $xxxx + 'interrupt latency' cycles */ - mcycles_z80 += cc[Z80_TABLE_op][0xcd] + cc[Z80_TABLE_ex][0xff]; + PCD = 0x0038; + /* RST $38 + 'interrupt latency' cycles */ + mcycles_z80 += cc[Z80_TABLE_op][0xff] + cc[Z80_TABLE_ex][0xff]; } else - /* Interrupt mode 1. RST 38h */ - if( IM == 1 ) + { + /* call back the cpu interface to retrieve the vector */ + int irq_vector = (*Z80.irq_callback)(0); + + /* Interrupt mode 2. Call [Z80.i:databyte] */ + if( IM == 2 ) { - LOG(("Z80 #%d IM1 $0038\n",cpu_getactivecpu() )); + irq_vector = (irq_vector & 0xff) | (I << 8); PUSH( pc ); - PCD = 0x0038; - /* RST $38 + 'interrupt latency' cycles */ - mcycles_z80 += cc[Z80_TABLE_op][0xff] + cc[Z80_TABLE_ex][0xff]; + RM16( irq_vector, &Z80.pc ); + LOG(("Z80 #%d IM2 [$%04x] = $%04x\n",cpu_getactivecpu() , irq_vector, PCD)); + /* CALL $xxxx + 'interrupt latency' cycles */ + mcycles_z80 += cc[Z80_TABLE_op][0xcd] + cc[Z80_TABLE_ex][0xff]; } else { @@ -3260,6 +3273,7 @@ static void take_interrupt(void) break; } } + } WZ=PCD; } @@ -3352,13 +3366,17 @@ void z80_init(const void *config, int (*irqcallback)(int)) if( (i & 0x0f) == 0x0f ) SZHV_dec[i] |= HF; } - /* Reset registers to their initial values */ + /* Initialize Z80 */ memset(&Z80, 0, sizeof(Z80)); - IX = IY = 0xffff; /* IX and IY are FFFF after a reset! */ - F = ZF; /* Zero flag is set */ Z80.daisy = config; Z80.irq_callback = irqcallback; + /* Reset registers to their initial values (NB: should be random on real hardware) */ + AF = BC = DE = HL = 0; + F = ZF; /* Zero flag is set */ + IX = IY = 0xffff; /* IX and IY are FFFF after a reset! (from MAME) */ + SP = 0xdfff; /* required by some SMS games that don't initialize SP */ + /* setup cycle tables */ cc[Z80_TABLE_op] = cc_op; cc[Z80_TABLE_cb] = cc_cb; @@ -3402,7 +3420,7 @@ void z80_run(unsigned int cycles) while( mcycles_z80 < cycles ) { /* check for IRQs before each instruction */ - if (Z80.irq_state != CLEAR_LINE && IFF1 && !Z80.after_ei) + if ((Z80.irq_state & 7) && IFF1 && !Z80.after_ei) { take_interrupt(); if (mcycles_z80 >= cycles) return; @@ -3449,31 +3467,22 @@ void z80_set_context (void *src) /**************************************************************************** * Set IRQ line state ****************************************************************************/ -void z80_set_irq_line(int irqline, int state) +void z80_set_nmi_line(int state) { - if (irqline != INPUT_LINE_NMI) + /* mark an NMI pending on the rising edge */ + if (Z80.nmi_state == CLEAR_LINE && state != CLEAR_LINE) { - /* update the IRQ state via the daisy chain */ - Z80.irq_state = state; + LOG(("Z80 #%d take NMI\n", cpu_getactivecpu())); + LEAVE_HALT; /* Check if processor was halted */ - /* the main execute loop will take the interrupt */ + IFF1 = 0; + PUSH( pc ); + PCD = 0x0066; + WZ=PCD; + + mcycles_z80 += 11*15; } - else - { - /* mark an NMI pending on the rising edge */ - if (Z80.nmi_state == CLEAR_LINE && state != CLEAR_LINE) - { - LOG(("Z80 #%d take NMI\n", cpu_getactivecpu())); - LEAVE_HALT; /* Check if processor was halted */ - IFF1 = 0; - PUSH( pc ); - PCD = 0x0066; - WZ=PCD; - - mcycles_z80 += 11*15; - } - Z80.nmi_state = state; - } + Z80.nmi_state = state; } diff --git a/source/z80/z80.h b/source/z80/z80.h index b373088..c3a3ea4 100644 --- a/source/z80/z80.h +++ b/source/z80/z80.h @@ -1,11 +1,18 @@ #ifndef Z80_H_ #define Z80_H_ -#include "cpuintrf.h" -#include "memz80.h" +#include "osd_cpu.h" -enum +enum { + /* line states */ + CLEAR_LINE = 0, /* clear (a fired, held or pulsed) line */ + ASSERT_LINE, /* assert an interrupt immediately */ + HOLD_LINE, /* hold interrupt line until acknowledged */ + PULSE_LINE /* pulse interrupt line for one instruction */ +}; + +enum { Z80_PC, Z80_SP, Z80_A, Z80_B, Z80_C, Z80_D, Z80_E, Z80_H, Z80_L, Z80_AF, Z80_BC, Z80_DE, Z80_HL, @@ -43,14 +50,22 @@ typedef struct extern Z80_Regs Z80; -void z80_init(const void *config, int (*irqcallback)(int)); -void z80_reset (void); -void z80_exit (void); -void z80_run(unsigned int cycles); -void z80_burn(unsigned int cycles); -void z80_get_context (void *dst); -void z80_set_context (void *src); -void z80_set_irq_line(int irqline, int state); +extern unsigned char *z80_readmap[64]; +extern unsigned char *z80_writemap[64]; + +extern void (*z80_writemem)(unsigned int address, unsigned char data); +extern unsigned char (*z80_readmem)(unsigned int port); +extern void (*z80_writeport)(unsigned int port, unsigned char data); +extern unsigned char (*z80_readport)(unsigned int port); + +extern void z80_init(const void *config, int (*irqcallback)(int)); +extern void z80_reset (void); +extern void z80_exit (void); +extern void z80_run(unsigned int cycles); +extern void z80_burn(unsigned int cycles); +extern void z80_get_context (void *dst); +extern void z80_set_context (void *src); +extern void z80_set_nmi_line(int state); #endif