diff options
Diffstat (limited to 'src/mame/video/genesis.c')
-rw-r--r-- | src/mame/video/genesis.c | 1382 |
1 files changed, 1382 insertions, 0 deletions
diff --git a/src/mame/video/genesis.c b/src/mame/video/genesis.c new file mode 100644 index 00000000000..440621a3b61 --- /dev/null +++ b/src/mame/video/genesis.c @@ -0,0 +1,1382 @@ +/*********************************************************************************************** + + Sega System C/C2 Driver + ----------------------- + Version 0.54 - 02 Feb 2003 + (for changes see drivers\segac2.c) + +***********************************************************************************************/ + +#include "driver.h" +#include "genesis.h" + +/* in video/segasyse.c */ +void megaplay_start_video_normal(running_machine *machine); +void megaplay_update_video_normal(running_machine *machine, mame_bitmap *bitmap, const rectangle *cliprect ); + +static int genesis_screen_number; + + +/****************************************************************************** + Macros +******************************************************************************/ + +#define BITMAP_WIDTH 320 +#define BITMAP_HEIGHT 240 + +#define VRAM_SIZE 0x10000 +#define VRAM_MASK (VRAM_SIZE - 1) +#define VSRAM_SIZE 0x80 +#define VSRAM_MASK (VSRAM_SIZE - 1) +#define CRAM_SIZE 0x40 +#define CRAM_MASK (CRAM_SIZE - 1) + +#define VDP_VRAM_BYTE(x) (vdp_vram[(x) & VRAM_MASK]) +#define VDP_VSRAM_BYTE(x) (vdp_vsram[(x) & VSRAM_MASK]) +#define VDP_VRAM_WORD(x) ((VDP_VRAM_BYTE(x) << 8) | VDP_VRAM_BYTE((x) + 1)) +#define VDP_VSRAM_WORD(x) ((VDP_VSRAM_BYTE(x) << 8) | VDP_VSRAM_BYTE((x) + 1)) + +#ifdef LSB_FIRST +#define EXTRACT_PIXEL(x,i) (((x) >> (((i) ^ 1) * 4)) & 0x0f) +#else +#define EXTRACT_PIXEL(x,i) (((x) >> (((i) ^ 7) * 4)) & 0x0f) +#endif + + + +/****************************************************************************** + Function Prototypes +******************************************************************************/ + +static int vdp_data_r(void); +static void vdp_data_w(int data); +static int vdp_control_r(void); +static void vdp_control_w(int data); +static void vdp_register_w(int data, int vblank); +static void vdp_control_dma(int data); +static void vdp_dma_68k(void); +static void vdp_dma_fill(int); +static void vdp_dma_copy(void); + +static void drawline(UINT16 *bitmap, int line, int bgfill); +static void get_scroll_tiles(int line, int scrollnum, UINT32 scrollbase, UINT32 *tiles, int *offset); +static void get_window_tiles(int line, UINT32 scrollbase, UINT32 *tiles); +static void drawline_tiles(UINT32 *tiles, UINT16 *bmap, int pri, int offset, int lclip, int rclip); +static void drawline_sprite(int line, UINT16 *bmap, int priority, UINT8 *spritebase); + + + +/****************************************************************************** + Global variables +******************************************************************************/ + +/* EXTERNALLY ACCESSIBLE */ + UINT16 genesis_bg_pal_lookup[4]; /* lookup table for background tiles */ + UINT16 genesis_sp_pal_lookup[4]; /* lookup table for sprites */ + UINT8 genesis_vdp_regs[32]; /* VDP registers */ + +/* LOCAL */ +static UINT8 * vdp_vram; /* VDP video RAM */ +static UINT8 * vdp_vsram; /* VDP vertical scroll RAM */ +static UINT8 display_enable; /* is the display enabled? */ +static int genesis_palette_base; /* offset to use when doing palette writes */ + +/* updates */ +static UINT16 * transparent_lookup; /* fast transparent mapping table */ + +/* vram bases */ +static UINT32 vdp_scrollabase; /* base address of scroll A tiles */ +static UINT32 vdp_scrollbbase; /* base address of scroll B tiles */ +static UINT32 vdp_windowbase; /* base address of window tiles */ +static UINT32 vdp_spritebase; /* base address of sprite data */ +static UINT32 vdp_hscrollbase; /* base address of H scroll values */ + +/* other vdp variables */ +static INT32 vdp_hscrollmask; /* mask for H scrolling */ +static UINT32 vdp_hscrollsize; /* size of active H scroll values */ +static UINT8 vdp_vscrollmode; /* current V scrolling mode */ + +static UINT8 vdp_cmdpart; /* partial command flag */ +static UINT8 vdp_code; /* command code value */ +static UINT32 vdp_address; /* current I/O address */ +static UINT8 vdp_dmafill; /* DMA filling flag */ +static UINT8 scrollheight; /* height of the scroll area in tiles */ +static UINT8 scrollwidth; /* width of the scroll area in tiles */ +static UINT8 bgcol; /* current background color */ +static UINT8 window_down; /* window Y direction */ +static UINT32 window_vpos; /* window Y position */ +static UINT8 window_right; /* window X direction */ +static UINT32 window_hpos; /* window X position */ +static UINT8 window_width; /* window width */ + + +/****************************************************************************** + Video Start / Stop Functions +******************************************************************************* + + Here we allocate memory used by the VDP and various other video related parts + of the System C/C2 hardware such as the Palette RAM. Here is what is needed + + 64kb of VRAM (multi-purpose, storing tiles, tilemaps, hscroll data, + spritelist etc.) + + 80bytes of VSRAM (used exclusively for storing Vertical Scroll values) + +******************************************************************************/ + + +static void start_genesis_vdp(int screen_number) +{ + static const UINT8 vdp_init[24] = + { + 0x04, 0x44, 0x30, 0x3C, 0x07, 0x6C, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x00, 0x01, 0x37, 0x00, 0x02, + 0x01, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x80, + }; + int i; + + genesis_screen_number = screen_number; + + /* allocate memory for the VDP, the lookup table, and the buffer bitmap */ + vdp_vram = auto_malloc(VRAM_SIZE); + vdp_vsram = auto_malloc(VSRAM_SIZE); + transparent_lookup = auto_malloc(0x1000 * sizeof(UINT16)); + + /* clear the VDP memory, prevents corrupt tile in Puyo Puyo 2 */ + memset(vdp_vram, 0, VRAM_SIZE); + memset(vdp_vsram, 0, VSRAM_SIZE); + + /* init transparency table */ + for (i = 0; i < 0x1000; i++) + { + int orig_color = i & 0x7ff; + int half_bright = i & 0x800; + + if (orig_color & 0x100) + transparent_lookup[i] = orig_color; + else if (half_bright) + transparent_lookup[i] = orig_color | 0x800; + else + transparent_lookup[i] = orig_color | 0x1000; + } + + /* reset the palettes */ + genesis_palette_base = 0; + genesis_bg_pal_lookup[0] = genesis_sp_pal_lookup[0] = 0x00; + genesis_bg_pal_lookup[1] = genesis_sp_pal_lookup[1] = 0x10; + genesis_bg_pal_lookup[2] = genesis_sp_pal_lookup[2] = 0x20; + genesis_bg_pal_lookup[3] = genesis_sp_pal_lookup[3] = 0x30; + + /* reset VDP */ + for (i = 0; i < 24; i++) + vdp_register_w(0x8000 | (i << 8) | vdp_init[i], 1); + vdp_cmdpart = 0; + vdp_code = 0; + vdp_address = 0; + + /* Save State Stuff */ + state_save_register_global_array(genesis_vdp_regs); + state_save_register_global_pointer(vdp_vram, 0x10000); + state_save_register_global_pointer(vdp_vsram, 0x80); + state_save_register_global_array(genesis_bg_pal_lookup); + state_save_register_global_array(genesis_sp_pal_lookup); + state_save_register_global(display_enable); + state_save_register_global(vdp_scrollabase); + state_save_register_global(vdp_scrollbbase); + state_save_register_global(vdp_windowbase); + state_save_register_global(vdp_spritebase); + state_save_register_global(vdp_hscrollbase); + state_save_register_global(vdp_hscrollmask); + state_save_register_global(vdp_hscrollsize); + state_save_register_global(vdp_vscrollmode); + state_save_register_global(vdp_cmdpart); + state_save_register_global(vdp_code); + state_save_register_global(vdp_address); + state_save_register_global(vdp_dmafill); + state_save_register_global(scrollheight); + state_save_register_global(scrollwidth); + state_save_register_global(bgcol); + state_save_register_global(window_down); + state_save_register_global(window_vpos); +} + +VIDEO_START(genesis) +{ + start_genesis_vdp(0); +} + + +VIDEO_START( segac2 ) +{ + start_genesis_vdp(0); + + /* C2 has separate sprite/background palettes */ + genesis_sp_pal_lookup[0] = 0x100; + genesis_sp_pal_lookup[1] = 0x110; + genesis_sp_pal_lookup[2] = 0x120; + genesis_sp_pal_lookup[3] = 0x130; +} + + + + + + +void start_system18_vdp(void) +{ + start_genesis_vdp(0); + + genesis_palette_base = 0x1800; + genesis_bg_pal_lookup[0] = genesis_sp_pal_lookup[0] = 0x1800; + genesis_bg_pal_lookup[1] = genesis_sp_pal_lookup[1] = 0x1810; + genesis_bg_pal_lookup[2] = genesis_sp_pal_lookup[2] = 0x1820; + genesis_bg_pal_lookup[3] = genesis_sp_pal_lookup[3] = 0x1830; +} + + + +/****************************************************************************** + Screen Refresh Functions +******************************************************************************* + + These are responsible for the core drawing. The update_display function + can be called under several circumstances to cache all the currently + displayed lines before a significant palette or scrolling change is + set to occur. The actual refresh routine marks the accumulated palette + entries and then converts the raw pens in the cache bitmap to their + final remapped values. + +******************************************************************************/ + +/* set the display enable bit */ +void segac2_enable_display(int enable) +{ + if (!video_screen_get_vblank(0)) + video_screen_update_partial(0, video_screen_get_vpos(0)); + display_enable = enable; +} + + +VIDEO_UPDATE( genesis ) +{ + int y; + + for (y = cliprect->min_y; y <= cliprect->max_y; y++) + drawline(BITMAP_ADDR16(bitmap, y, 0), y, 0); + return 0; +} + + +VIDEO_UPDATE( segac2 ) +{ + if (!display_enable) + fillbitmap(bitmap, get_black_pen(machine), cliprect); + else + video_update_genesis(machine, screen, bitmap, cliprect); + return 0; +} + + + + +/* megaplay, draws either Genesis or SMS (single screen display) */ + +/* core refresh: computes the final screen */ +VIDEO_UPDATE( megaplay ) +{ + int y; + + + for (y = cliprect->min_y; y <= cliprect->max_y; y++) + drawline(BITMAP_ADDR16(bitmap, y, 0), y, 0); + + megaplay_update_video_normal(machine, bitmap, cliprect); + + return 0; +} + +void update_system18_vdp( mame_bitmap *bitmap, const rectangle *cliprect ) +{ + int y; + + /* generate the final screen */ + for (y = cliprect->min_y; y <= cliprect->max_y; y++) + drawline(BITMAP_ADDR16(bitmap, y, 0), y, 0xffff); +} + +/****************************************************************************** + VDP Read & Write Handlers +******************************************************************************* + + The VDP is accessed via 8 & 16 bit reads / writes at the addresses 0xC00000 - + 0xC0001F. Different locations have different functions in the following + layout. (based on Information in Charles MacDonalds Document) + + 0xC00000 - DATA port (8 or 16 bit, Read or Write) + 0xC00002 - mirror of above + 0xC00004 - CTRL port (8 or 16 bit, Read or Write) + 0xC00006 - mirror of above + 0xC00008 - HV counter (8 or 16 bit Read Only) + 0xC0000A - mirror of above + 0xC0000C - mirror of above + 0xC0000E - mirror of above + 0xC00010 - SN76489 (8 bit Write Only) + 0xC00012 - mirror of above + 0xC00014 - mirror of above + 0xC00016 - mirror of above + + The SN76489 PSG Writes are intercepted before we get here and handled + directly. + +******************************************************************************/ + +READ16_HANDLER( genesis_vdp_r ) +{ + switch (offset) + { + case 0x00: /* Read Data */ + case 0x01: + return vdp_data_r(); + + case 0x02: /* Status Register */ + case 0x03: + return vdp_control_r(); + + case 0x04: /* HV counter */ + case 0x05: + case 0x06: + case 0x07: + { + int xpos = video_screen_get_hpos(0); + int ypos = video_screen_get_vpos(0); + + /* adjust for the weird counting rules */ + if (xpos > 0xe9) xpos -= (342 - 0x100); + if (ypos > 0xea) ypos -= (262 - 0x100); + + /* kludge for ichidant: it sets the H counter to 160, then */ + /* expects that a read from here will return 159 */ + if (ypos > 0) ypos -= 2; + return (ypos << 8) | xpos; + } + } + return 0; +} + + +WRITE16_HANDLER( genesis_vdp_w ) +{ + switch (offset) + { + case 0x00: /* Write data */ + case 0x01: + if (mem_mask) + { + data &= ~mem_mask; + if (ACCESSING_MSB) + data |= data >> 8; + else + data |= data << 8; + } + vdp_data_w(data); + break; + + case 0x02: /* Control Write */ + case 0x03: + if (mem_mask) + { + data &= ~mem_mask; + if (ACCESSING_MSB) + data |= data >> 8; + else + data |= data << 8; + } + vdp_control_w(data); + break; + + case 0x08: /* SN76489 Write */ + case 0x09: + case 0x0a: + case 0x0b: + if (ACCESSING_LSB && sndti_exists(SOUND_SN76496, 0)) + SN76496_0_w(0, data & 0xff); + break; + } +} + + + +/****************************************************************************** + VDP DATA Reads / Writes (Accesses to 0xC00000 - 0xC00003) +******************************************************************************* + + The functions here are called by the read / write handlers for the VDP. + They deal with Data Reads and Writes to and from the VDP Chip. + + Read / write mode is set by Writes to VDP Control, Can't Read when in + write mode or vice versa. + +******************************************************************************/ + +/* Games needing Read to Work .. bloxeed (attract) .. puyo puyo .. probably more */ +static int vdp_data_r(void) +{ + int read = 0; + + /* kill 2nd write pending flag */ + vdp_cmdpart = 0; + + /* which memory is based on the code */ + switch (vdp_code & 0x0f) + { + case 0x00: /* VRAM read */ + read = VDP_VRAM_WORD(vdp_address & ~1); + break; + + case 0x04: /* VSRAM read */ + read = VDP_VSRAM_WORD(vdp_address & ~1); + break; + + default: /* Illegal read attempt */ + logerror("%06x: VDP illegal read type %02x\n", activecpu_get_previouspc(), vdp_code); + read = 0x00; + break; + } + + /* advance the address */ + vdp_address += genesis_vdp_regs[15]; + return read; +} + + +static void vdp_data_w(int data) +{ + /* kill 2nd write pending flag */ + vdp_cmdpart = 0; + + /* handle the fill case */ + if (vdp_dmafill) + { + vdp_dma_fill(data); + vdp_dmafill = 0; + return; + } + + /* which memory is based on the code */ + switch (vdp_code & 0x0f) + { + case 0x01: /* VRAM write */ + + /* if the hscroll RAM is changing during screen refresh, force an update */ + if (!video_screen_get_vblank(0) && + vdp_address >= vdp_hscrollbase && + vdp_address < vdp_hscrollbase + vdp_hscrollsize) + video_screen_update_partial(0, video_screen_get_vpos(0)); + + /* write to VRAM */ + if (vdp_address & 1) + data = ((data & 0xff) << 8) | ((data >> 8) & 0xff); + VDP_VRAM_BYTE(vdp_address & ~1) = data >> 8; + VDP_VRAM_BYTE(vdp_address | 1) = data; + break; + + case 0x03: /* Palette write */ + { + int offset = (vdp_address/2) % CRAM_SIZE; + palette_set_color_rgb(Machine, offset + genesis_palette_base, pal3bit(data >> 1), pal3bit(data >> 5), pal3bit(data >> 9)); + } + break; + + case 0x05: /* VSRAM write */ + + /* if the vscroll RAM is changing during screen refresh, force an update */ + if (!video_screen_get_vblank(0)) + video_screen_update_partial(0, video_screen_get_vpos(0)); + + /* write to VSRAM */ + if (vdp_address & 1) + data = ((data & 0xff) << 8) | ((data >> 8) & 0xff); + VDP_VSRAM_BYTE(vdp_address & ~1) = data >> 8; + VDP_VSRAM_BYTE(vdp_address | 1) = data; + break; + + default: /* Illegal write attempt */ + logerror("PC:%06x: VDP illegal write type %02x data %04x\n", activecpu_get_previouspc(), vdp_code, data); + break; + } + + /* advance the address */ + vdp_address += genesis_vdp_regs[15]; +} + + + +/****************************************************************************** + VDP CTRL Reads / Writes (Accesses to 0xC00004 - 0xC00007) +******************************************************************************* + + A Read from the Control Port will return the Status Register Value. + 16-bits are used to report the VDP status + | 0 | 0 | 1 | 1 | 0 | 1 | FIFE | FIFF | + | VIP | SOF | SCL | ODD | VBLK | HBLK | DMA | PAL | + + 0,1 = Set Values + FIFE = FIFO Empty + FIFF = FIFO Full + VIP = Vertical Interrupt Pending + SOF = Sprite Overflow (Not used in C2 afaik) + SCL = Sprite Collision (Not used in C2 afaik) + ODD = Odd Frame (Interlace Mode) (Not used in C2 afaik) + VBLK = In Vertical Blank + HBLK = In Horizontal Blank + DMA = DMA In Progress + PAL = Pal Mode Flag + + Control Writes are used for setting VDP Registers, setting up DMA Transfers + etc. + + A Write to the Control port can consist of 2 16-bit Words. + When the VDP _isn't_ expecting the 2nd part of a command the highest 2 bits + of the data written will determine what it is we want to do. + 10xxxxxx xxxxxxxx will cause a register to be set + anything else will trigger the 1st half of a 2 Part Mode Setting Command + If the VDP is already expecting the 2nd half of a command the data written + will be the 2nd half of the Mode setup. + +******************************************************************************/ + +static int vdp_control_r(void) +{ + int status = 0x3600; // wwally needs fifo empty set + + /* kill 2nd write pending flag */ + vdp_cmdpart = 0; + + /* set the VBLANK bit */ + if (video_screen_get_vblank(0)) + status |= 0x0008; + + /* set the HBLANK bit */ + if (video_screen_get_hblank(0)) + status |= 0x0004; + + return (status); +} + + +static void vdp_control_w(int data) +{ + /* case 1: we're not expecting the 2nd half of a command */ + if (!vdp_cmdpart) + { + /* if 10xxxxxx xxxxxxxx this is a register setting command */ + if ((data & 0xc000) == 0x8000) + vdp_register_w(data, video_screen_get_vblank(0)); + + /* otherwise this is the First part of a mode setting command */ + else + { + vdp_code = (vdp_code & 0x3c) | ((data >> 14) & 0x03); + vdp_address = (vdp_address & 0xc000) | (data & 0x3fff); + vdp_cmdpart = 1; + } + } + + /* case 2: this is the 2nd part of a mode setting command */ + else + { + vdp_code = (vdp_code & 0x03) | ((data >> 2) & 0x3c); + vdp_address = (vdp_address & 0x3fff) | ((data << 14) & 0xc000); + vdp_cmdpart = 0; + vdp_control_dma(data); + } +} + + +static void vdp_register_w(int data, int vblank) +{ + int scrwidth = 0; + static const UINT8 is_important[32] = { 0,0,1,1,1,1,0,1,0,0,0,1,0,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0 }; + + UINT8 regnum = (data & 0x1f00) >> 8; /* ---R RRRR ---- ---- */ + UINT8 regdat = (data & 0x00ff); /* ---- ---- DDDD DDDD */ + + genesis_vdp_regs[regnum] = regdat; + + /* these are mostly important writes; force an update if they */ + /* are written during a screen refresh */ + if (!vblank && is_important[regnum]) + video_screen_update_partial(0, video_screen_get_vpos(0)); + + /* For quite a few of the registers its a good idea to set a couple of variable based + upon the writes here */ + switch (regnum) + { + case 0x01: /* video modes */ + if (regdat & 8) + popmessage("Video height = 240!"); + break; + + case 0x02: /* Scroll A Name Table Base */ + vdp_scrollabase = (regdat & 0x38) << 10; + break; + + case 0x03: /* Window Name Table Base */ + vdp_windowbase = (regdat & 0x3e) << 10; + break; + + case 0x04: /* Scroll B Name Table Base */ + vdp_scrollbbase = (regdat & 0x07) << 13; + break; + + case 0x05: /* Sprite Table Base */ + vdp_spritebase = (regdat & 0x7e) << 9; + break; + + case 0x07: /* BG Colour */ + bgcol = regdat & 0x3f; + break; + + case 0x0b: /* Scroll Modes */ + { + static const UINT16 mask_table[4] = { 0x000, 0x007, 0xff8, 0xfff }; + vdp_vscrollmode = (regdat & 0x04) >> 2; + + vdp_hscrollmask = mask_table[regdat & 3]; + vdp_hscrollsize = 4 * ((vdp_hscrollmask < 224) ? (vdp_hscrollmask + 1) : 224); + break; + } + + case 0x0c: /* video modes */ + /* HDG: taken from mess/machine/genesis.c */ + switch (regdat&0x81) + { + case 0x00: // 32 cell + scrwidth=32; + window_width=32; + break; + case 0x01: // 40 cell corrupted + scrwidth=40; + window_width=64; + break; + case 0x80: // illegal! + scrwidth=40; + window_width=64; + break; + case 0x81: // 40 cell + scrwidth=40; + window_width=64; + break; + } + { + screen_state *state = &Machine->screen[genesis_screen_number]; + rectangle visarea = state->visarea; + + /* this gets called from the init! */ + visarea.max_x = scrwidth*8-1; + video_screen_configure(genesis_screen_number, scrwidth*8, state->height, &visarea, state->refresh); + } + break; + + case 0x0d: /* HScroll Base */ + vdp_hscrollbase = (regdat & 0x3f) << 10; + break; + + case 0x10: /* Scroll Size */ + { + static const UINT8 size_table[4] = { 32, 64, 128, 128 }; + scrollwidth = size_table[regdat & 0x03]; + scrollheight = size_table[(regdat & 0x30) >> 4]; + break; + } + + case 0x11: /* Window H Position .. Doesn't Matter for any of the C2 Games */ + window_right = regdat & 0x80; + window_hpos = (regdat & 0x1f) << 4; + break; + + case 0x12: /* Window V Position */ + window_down = regdat & 0x80; + window_vpos = (regdat & 0x1f) << 3; + break; + } +} + + +static void vdp_control_dma(int data) +{ + if ((vdp_code & 0x20) && (genesis_vdp_regs[1] & 0x10)) + { + switch(genesis_vdp_regs[23] & 0xc0) + { + case 0x00: + case 0x40: /* 68k -> VRAM Tranfser */ + vdp_dma_68k(); + break; + + case 0x80: /* VRAM fill, can't be done here, requires data write */ + vdp_dmafill = 1; + break; + + case 0xC0: /* VRAM copy */ + vdp_dma_copy(); + break; + } + } +} + + + +/****************************************************************************** + DMA handling +******************************************************************************* + + These are currently Pretty much directly from the C2 emu + +******************************************************************************/ + +static void vdp_dma_68k(void) +{ + int length = genesis_vdp_regs[19] | (genesis_vdp_regs[20] << 8); + int source = (genesis_vdp_regs[21] << 1) | (genesis_vdp_regs[22] << 9) | ((genesis_vdp_regs[23] & 0x7f) << 17); + int count; + + /* length of 0 means 64k likely */ + if (!length) + length = 0xffff; + + /* handle the DMA */ + for (count = 0; count < length; count++) + { + vdp_data_w(program_read_word(source)); + source += 2; + } +} + + +static void vdp_dma_copy(void) +{ + int length = genesis_vdp_regs[19] | (genesis_vdp_regs[20] << 8); + int source = genesis_vdp_regs[21] | (genesis_vdp_regs[22] << 8); + int count; + + /* length of 0 means 64k likely */ + if (!length) + length = 0xffff; + + /* handle the DMA */ + for (count = 0; count < length; count++) + { + VDP_VRAM_BYTE(vdp_address) = VDP_VRAM_BYTE(source++); + vdp_address += genesis_vdp_regs[15]; + } +} + + +static void vdp_dma_fill(int data) +{ + int length = genesis_vdp_regs[19] | (genesis_vdp_regs[20] << 8); + int count; + + /* length of 0 means 64k likely */ + if (!length) + length = 0xffff; + + /* handle the fill */ + VDP_VRAM_BYTE(vdp_address) = data; + data >>= 8; + for(count = 0; count < length; count++) + { + VDP_VRAM_BYTE(vdp_address ^ 1) = data; + vdp_address += genesis_vdp_regs[15]; + } +} + + + +/****************************************************************************** + Scroll calculations +******************************************************************************* + + These special routines handle the transparency effects in sprites. + +******************************************************************************/ + +/*** Useful Little Functions **************************************************/ + +/* Note: We Expect plane = 0 for Scroll A, plane = 2 for Scroll B */ +INLINE int vdp_gethscroll(int plane, int line) +{ + line &= vdp_hscrollmask; + return 0x400 - (VDP_VRAM_WORD(vdp_hscrollbase + (4 * line) + plane) & 0x3ff); +} + + +/* Note: We expect plane = 0 for Scroll A, plane = 2 for Scroll B + A Column is 8 Pixels Wide */ +static int vdp_getvscroll(int plane, int column) +{ + UINT32 vsramoffset; + + switch (vdp_vscrollmode) + { + case 0x00: /* Overall Scroll */ + return VDP_VSRAM_WORD(plane) & 0x7ff; + + case 0x01: /* Column Scroll */ + if (column == 40) column = 39; /* Fix Minor Innacuracy Only affects PotoPoto */ + vsramoffset = (4 * (column >> 1)) + plane; + return VDP_VSRAM_WORD(vsramoffset) & 0x7ff; + } + return 0; +} + + + +/****************************************************************************** + Drawing Functions +******************************************************************************* + + These are used by the Screen Refresh functions to do the actual rendering + of a screenline to the bitmap. + + Draw Planes in Order + Scroll B Low + Scroll A Low / Window Low + Sprites Low + Scroll B High + Scroll A High / Window High + Sprites High + + NOTE: Low Sprites _can_ overlap High sprites, however none of the C2 + games do this ever so its safe to draw them in this order. + +******************************************************************************/ + +static void drawline(UINT16 *bitmap, int line, int bgfill) +{ + int lowsprites, highsprites, link; + UINT32 scrolla_tiles[41], scrollb_tiles[41], window_tiles[41]; + int scrolla_offset, scrollb_offset; + UINT8 *lowlist[81], *highlist[81]; + int bgcolor = bgfill ? bgfill : genesis_bg_pal_lookup[0]; + int window_lclip, window_rclip; + int scrolla_lclip, scrolla_rclip; + int column, sprite; + + /* clear to the background color */ + for (column = 0; column < BITMAP_WIDTH; column++) + bitmap[column] = bgcolor; + + /* if display is disabled, stop */ + if (!(genesis_vdp_regs[1] & 0x40)) + return; + + /* Sprites need to be Drawn in Reverse order .. may as well sort them here */ + link = lowsprites = highsprites = 0; + for (sprite = 0; sprite < 80; sprite++) + { + UINT8 *spritebase = &VDP_VRAM_BYTE(vdp_spritebase + 8 * link); + + /* sort into high/low priorities */ + if (spritebase[4] & 0x0080) + highlist[++highsprites] = spritebase; + else + lowlist[++lowsprites] = spritebase; + + /* get the link; if 0, stop processing */ + link = spritebase[3] & 0x7F; + if (!link) + break; + } + + /* get tiles for the B scroll layer */ + get_scroll_tiles(line, 2, vdp_scrollbbase, scrollb_tiles, &scrollb_offset); + + /* get tiles for the A scroll layer */ + get_scroll_tiles(line, 0, vdp_scrollabase, scrolla_tiles, &scrolla_offset); + + /* get tiles for the window layer */ + get_window_tiles(line, vdp_windowbase, window_tiles); + + /* compute the windowing for this line */ + if ((window_down && line >= window_vpos) || (!window_down && line < window_vpos)) + window_lclip = 0, window_rclip = BITMAP_WIDTH - 1; + else if (window_right) + window_lclip = window_hpos, window_rclip = BITMAP_WIDTH - 1; + else + window_lclip = 0, window_rclip = window_hpos - 1; + + /* compute the clipping of the scroll A layer */ + if (window_lclip == 0) + { + scrolla_lclip = window_rclip + 1; + scrolla_rclip = BITMAP_WIDTH - 1; + } + else + { + scrolla_lclip = 0; + scrolla_rclip = window_lclip - 1; + } + + /* Scroll B Low */ + drawline_tiles(scrollb_tiles, bitmap, 0, scrollb_offset, 0, BITMAP_WIDTH - 1); + + /* Scroll A Low */ + drawline_tiles(scrolla_tiles, bitmap, 0, scrolla_offset, scrolla_lclip, scrolla_rclip); + + /* Window Low */ + drawline_tiles(window_tiles, bitmap, 0, 0, window_lclip, window_rclip); + + /* Sprites Low */ + for (sprite = lowsprites; sprite > 0; sprite--) + drawline_sprite(line, bitmap, 0, lowlist[sprite]); + + /* Scroll B High */ + drawline_tiles(scrollb_tiles, bitmap, 1, scrollb_offset, 0, BITMAP_WIDTH - 1); + + /* Scroll A High */ + drawline_tiles(scrolla_tiles, bitmap, 1, scrolla_offset, scrolla_lclip, scrolla_rclip); + + /* Window High */ + drawline_tiles(window_tiles, bitmap, 1, 0, window_lclip, window_rclip); + + /* Sprites High */ + for (sprite = highsprites; sprite > 0; sprite--) + drawline_sprite(line, bitmap, 1, highlist[sprite]); +} + + + +/****************************************************************************** + Background rendering +******************************************************************************* + + This code handles all the scroll calculations and rendering of the + background layers. + +******************************************************************************/ + +/* determine the tiles we will draw on a scrolling layer */ +static void get_scroll_tiles(int line, int scrollnum, UINT32 scrollbase, UINT32 *tiles, int *offset) +{ + int linehscroll = vdp_gethscroll(scrollnum, line); + int column; + + /* adjust for partial tiles and then pre-divide hscroll to get the tile offset */ + *offset = -(linehscroll % 8); + linehscroll /= 8; + + /* loop over columns */ + for (column = 0; column < 41; column++) + { + int columnvscroll = vdp_getvscroll(scrollnum, (column - (linehscroll & 1)) & 0x3f) + line; + + /* determine the base of the tilemap row */ + int temp = ((columnvscroll / 8) & (scrollheight - 1)) * scrollwidth; + int tilebase = scrollbase + 2 * temp; + + /* offset into the tilemap based on the column */ + temp = (linehscroll + column) & (scrollwidth - 1); + tilebase += 2 * temp; + + /* get the tile info */ + *tiles++ = ((columnvscroll % 8) << 16) | VDP_VRAM_WORD(tilebase); + } +} + + +/* determine the tiles we will draw on a non-scrolling window layer */ +static void get_window_tiles(int line, UINT32 scrollbase, UINT32 *tiles) +{ + int column; + + /* loop over columns */ + for (column = 0; column < 40; column++) + { + /* determine the base of the tilemap row */ + int temp = (line / 8) * window_width + column; + int tilebase = scrollbase + 2 * temp; + + /* get the tile info */ + *tiles++ = ((line % 8) << 16) | VDP_VRAM_WORD(tilebase); + } +} + + +/* draw a line of tiles */ +static void drawline_tiles(UINT32 *tiles, UINT16 *bmap, int pri, int offset, int lclip, int rclip) +{ + /* adjust for the 8-pixel slop */ + bmap += offset; + if (lclip > rclip) + return; + + /* loop over columns */ + for ( ; offset < BITMAP_WIDTH; offset += 8, bmap += 8) + { + UINT32 tile = *tiles++; + + /* if the tile is the correct priority, draw it */ + if (((tile >> 15) & 1) == pri && offset < BITMAP_WIDTH) + { + int colbase = genesis_bg_pal_lookup[(tile & 0x6000) >> 13]; + UINT32 *tp = (UINT32 *)&VDP_VRAM_BYTE((tile & 0x7ff) * 32); + UINT32 mytile; + int col; + + /* vertical flipping */ + if (!(tile & 0x1000)) + mytile = tp[tile >> 16]; + else + mytile = tp[(tile >> 16) ^ 7]; + + /* skip if all-transparent */ + if (!mytile) + continue; + + /* non-clipped */ + if (offset >= lclip && offset <= rclip - 7) + { + /* non-flipped */ + if (!(tile & 0x0800)) + { + col = EXTRACT_PIXEL(mytile, 0); if (col) bmap[0] = colbase + col; + col = EXTRACT_PIXEL(mytile, 1); if (col) bmap[1] = colbase + col; + col = EXTRACT_PIXEL(mytile, 2); if (col) bmap[2] = colbase + col; + col = EXTRACT_PIXEL(mytile, 3); if (col) bmap[3] = colbase + col; + col = EXTRACT_PIXEL(mytile, 4); if (col) bmap[4] = colbase + col; + col = EXTRACT_PIXEL(mytile, 5); if (col) bmap[5] = colbase + col; + col = EXTRACT_PIXEL(mytile, 6); if (col) bmap[6] = colbase + col; + col = EXTRACT_PIXEL(mytile, 7); if (col) bmap[7] = colbase + col; + } + + /* horizontal flip */ + else + { + col = EXTRACT_PIXEL(mytile, 7); if (col) bmap[0] = colbase + col; + col = EXTRACT_PIXEL(mytile, 6); if (col) bmap[1] = colbase + col; + col = EXTRACT_PIXEL(mytile, 5); if (col) bmap[2] = colbase + col; + col = EXTRACT_PIXEL(mytile, 4); if (col) bmap[3] = colbase + col; + col = EXTRACT_PIXEL(mytile, 3); if (col) bmap[4] = colbase + col; + col = EXTRACT_PIXEL(mytile, 2); if (col) bmap[5] = colbase + col; + col = EXTRACT_PIXEL(mytile, 1); if (col) bmap[6] = colbase + col; + col = EXTRACT_PIXEL(mytile, 0); if (col) bmap[7] = colbase + col; + } + } + + /* clipped */ + else if (offset >= lclip - 8 && offset <= rclip) + { + /* non-flipped */ + if (!(tile & 0x0800)) + { + col = EXTRACT_PIXEL(mytile, 0); if (col && (offset + 0) >= lclip && (offset + 0) <= rclip) bmap[0] = colbase + col; + col = EXTRACT_PIXEL(mytile, 1); if (col && (offset + 1) >= lclip && (offset + 1) <= rclip) bmap[1] = colbase + col; + col = EXTRACT_PIXEL(mytile, 2); if (col && (offset + 2) >= lclip && (offset + 2) <= rclip) bmap[2] = colbase + col; + col = EXTRACT_PIXEL(mytile, 3); if (col && (offset + 3) >= lclip && (offset + 3) <= rclip) bmap[3] = colbase + col; + col = EXTRACT_PIXEL(mytile, 4); if (col && (offset + 4) >= lclip && (offset + 4) <= rclip) bmap[4] = colbase + col; + col = EXTRACT_PIXEL(mytile, 5); if (col && (offset + 5) >= lclip && (offset + 5) <= rclip) bmap[5] = colbase + col; + col = EXTRACT_PIXEL(mytile, 6); if (col && (offset + 6) >= lclip && (offset + 6) <= rclip) bmap[6] = colbase + col; + col = EXTRACT_PIXEL(mytile, 7); if (col && (offset + 7) >= lclip && (offset + 7) <= rclip) bmap[7] = colbase + col; + } + + /* horizontal flip */ + else + { + col = EXTRACT_PIXEL(mytile, 7); if (col && (offset + 0) >= lclip && (offset + 0) <= rclip) bmap[0] = colbase + col; + col = EXTRACT_PIXEL(mytile, 6); if (col && (offset + 1) >= lclip && (offset + 1) <= rclip) bmap[1] = colbase + col; + col = EXTRACT_PIXEL(mytile, 5); if (col && (offset + 2) >= lclip && (offset + 2) <= rclip) bmap[2] = colbase + col; + col = EXTRACT_PIXEL(mytile, 4); if (col && (offset + 3) >= lclip && (offset + 3) <= rclip) bmap[3] = colbase + col; + col = EXTRACT_PIXEL(mytile, 3); if (col && (offset + 4) >= lclip && (offset + 4) <= rclip) bmap[4] = colbase + col; + col = EXTRACT_PIXEL(mytile, 2); if (col && (offset + 5) >= lclip && (offset + 5) <= rclip) bmap[5] = colbase + col; + col = EXTRACT_PIXEL(mytile, 1); if (col && (offset + 6) >= lclip && (offset + 6) <= rclip) bmap[6] = colbase + col; + col = EXTRACT_PIXEL(mytile, 0); if (col && (offset + 7) >= lclip && (offset + 7) <= rclip) bmap[7] = colbase + col; + } + } + } + } +} + + + +/****************************************************************************** + Sprite drawing +******************************************************************************* + + These special routines handle the transparency effects in sprites. + +******************************************************************************/ + +/* draw a non-horizontally-flipped section of a sprite */ +INLINE void draw8pixs(UINT16 *bmap, int sxpos, int patno, int priority, int colbase, int patline) +{ + UINT32 tile = *(UINT32 *)&VDP_VRAM_BYTE(patno * 32 + 4 * patline); + int col; + + /* skip if all-transparent */ + if (!tile) + return; + + /* non-transparent */ + if ((colbase & 0x30) != 0x30 || !(genesis_vdp_regs[12] & 0x08)) + { + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 0); if (col) bmap[sxpos] = colbase + col; } sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 1); if (col) bmap[sxpos] = colbase + col; } sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 2); if (col) bmap[sxpos] = colbase + col; } sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 3); if (col) bmap[sxpos] = colbase + col; } sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 4); if (col) bmap[sxpos] = colbase + col; } sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 5); if (col) bmap[sxpos] = colbase + col; } sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 6); if (col) bmap[sxpos] = colbase + col; } sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 7); if (col) bmap[sxpos] = colbase + col; } sxpos++; + } + + /* transparent */ + else + { + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 0); if (col) { if (col < 0x0e) bmap[sxpos] = colbase + col; else bmap[sxpos] = transparent_lookup[((col & 1) << 11) | (bmap[sxpos] & 0x7ff)]; }} sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 1); if (col) { if (col < 0x0e) bmap[sxpos] = colbase + col; else bmap[sxpos] = transparent_lookup[((col & 1) << 11) | (bmap[sxpos] & 0x7ff)]; }} sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 2); if (col) { if (col < 0x0e) bmap[sxpos] = colbase + col; else bmap[sxpos] = transparent_lookup[((col & 1) << 11) | (bmap[sxpos] & 0x7ff)]; }} sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 3); if (col) { if (col < 0x0e) bmap[sxpos] = colbase + col; else bmap[sxpos] = transparent_lookup[((col & 1) << 11) | (bmap[sxpos] & 0x7ff)]; }} sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 4); if (col) { if (col < 0x0e) bmap[sxpos] = colbase + col; else bmap[sxpos] = transparent_lookup[((col & 1) << 11) | (bmap[sxpos] & 0x7ff)]; }} sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 5); if (col) { if (col < 0x0e) bmap[sxpos] = colbase + col; else bmap[sxpos] = transparent_lookup[((col & 1) << 11) | (bmap[sxpos] & 0x7ff)]; }} sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 6); if (col) { if (col < 0x0e) bmap[sxpos] = colbase + col; else bmap[sxpos] = transparent_lookup[((col & 1) << 11) | (bmap[sxpos] & 0x7ff)]; }} sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 7); if (col) { if (col < 0x0e) bmap[sxpos] = colbase + col; else bmap[sxpos] = transparent_lookup[((col & 1) << 11) | (bmap[sxpos] & 0x7ff)]; }} sxpos++; + } +} + + +/* draw a horizontally-flipped section of a sprite */ +INLINE void draw8pixs_hflip(UINT16 *bmap, int sxpos, int patno, int priority, int colbase, int patline) +{ + UINT32 tile = *(UINT32 *)&VDP_VRAM_BYTE(patno * 32 + 4 * patline); + int col; + + /* skip if all-transparent */ + if (!tile) + return; + + /* non-transparent */ + if ((colbase & 0x30) != 0x30 || !(genesis_vdp_regs[12] & 0x08)) + { + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 7); if (col) bmap[sxpos] = colbase + col; } sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 6); if (col) bmap[sxpos] = colbase + col; } sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 5); if (col) bmap[sxpos] = colbase + col; } sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 4); if (col) bmap[sxpos] = colbase + col; } sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 3); if (col) bmap[sxpos] = colbase + col; } sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 2); if (col) bmap[sxpos] = colbase + col; } sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 1); if (col) bmap[sxpos] = colbase + col; } sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 0); if (col) bmap[sxpos] = colbase + col; } sxpos++; + } + + /* transparent */ + else + { + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 7); if (col) { if (col < 0x0e) bmap[sxpos] = colbase + col; else bmap[sxpos] = transparent_lookup[((col & 1) << 11) | (bmap[sxpos] & 0x7ff)]; }} sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 6); if (col) { if (col < 0x0e) bmap[sxpos] = colbase + col; else bmap[sxpos] = transparent_lookup[((col & 1) << 11) | (bmap[sxpos] & 0x7ff)]; }} sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 5); if (col) { if (col < 0x0e) bmap[sxpos] = colbase + col; else bmap[sxpos] = transparent_lookup[((col & 1) << 11) | (bmap[sxpos] & 0x7ff)]; }} sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 4); if (col) { if (col < 0x0e) bmap[sxpos] = colbase + col; else bmap[sxpos] = transparent_lookup[((col & 1) << 11) | (bmap[sxpos] & 0x7ff)]; }} sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 3); if (col) { if (col < 0x0e) bmap[sxpos] = colbase + col; else bmap[sxpos] = transparent_lookup[((col & 1) << 11) | (bmap[sxpos] & 0x7ff)]; }} sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 2); if (col) { if (col < 0x0e) bmap[sxpos] = colbase + col; else bmap[sxpos] = transparent_lookup[((col & 1) << 11) | (bmap[sxpos] & 0x7ff)]; }} sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 1); if (col) { if (col < 0x0e) bmap[sxpos] = colbase + col; else bmap[sxpos] = transparent_lookup[((col & 1) << 11) | (bmap[sxpos] & 0x7ff)]; }} sxpos++; + if (sxpos>=0 && sxpos<BITMAP_WIDTH) { col = EXTRACT_PIXEL(tile, 0); if (col) { if (col < 0x0e) bmap[sxpos] = colbase + col; else bmap[sxpos] = transparent_lookup[((col & 1) << 11) | (bmap[sxpos] & 0x7ff)]; }} sxpos++; + } +} + + +static void drawline_sprite(int line, UINT16 *bmap, int priority, UINT8 *spritebase) +{ + int spriteypos = (((spritebase[0] & 0x01) << 8) | spritebase[1]) - 0x80; + int spritexpos = (((spritebase[6] & 0x01) << 8) | spritebase[7]) - 0x80; + int spriteheight = ((spritebase[2] & 0x03) + 1) * 8; + int spritewidth = (((spritebase[2] & 0x0c) >> 2) + 1) * 8; + int spriteattr, patno, patflip, patline, colbase, x; + + /* skip if out of range */ + if (line < spriteypos || line >= spriteypos + spriteheight) + return; + if (spritexpos + spritewidth < 0 || spritexpos >= BITMAP_WIDTH) + return; + + /* extract the remaining data */ + spriteattr = (spritebase[4] << 8) | spritebase[5]; + patno = spriteattr & 0x07FF; + patflip = (spriteattr & 0x1800) >> 11; + patline = line - spriteypos; + + /* determine the color base */ + colbase = genesis_sp_pal_lookup[(spriteattr & 0x6000) >> 13]; + + /* adjust for the X position */ + spritewidth >>= 3; + spriteheight >>= 3; + + /* switch off the flip mode */ + switch (patflip) + { + case 0x00: /* No Flip */ + for (x = 0; x < spritewidth; x++) + { + if (spritexpos >= -7 && spritexpos < BITMAP_WIDTH) + draw8pixs(bmap, spritexpos, patno, priority, colbase, patline); + spritexpos += 8; + patno += spriteheight; + } + break; + + case 0x01: /* Horizontal Flip */ + patno += spriteheight * (spritewidth - 1); + for (x = 0; x < spritewidth; x++) + { + if (spritexpos >= -7 && spritexpos < BITMAP_WIDTH) + draw8pixs_hflip(bmap, spritexpos, patno, priority, colbase, patline); + spritexpos += 8; + patno -= spriteheight; + } + break; + + case 0x02: /* Vertical Flip */ + patline = 8 * spriteheight - patline - 1; + for (x = 0; x < spritewidth; x++) + { + if (spritexpos >= -7 && spritexpos < BITMAP_WIDTH) + draw8pixs(bmap, spritexpos, patno, priority, colbase, patline); + spritexpos += 8; + patno += spriteheight; + } + break; + + case 0x03: /* Both Flip */ + patno += spriteheight * (spritewidth - 1); + patline = 8 * spriteheight - patline - 1; + for (x = 0; x < spritewidth; x++) + { + if (spritexpos >= -7 && spritexpos < BITMAP_WIDTH) + draw8pixs_hflip(bmap, spritexpos, patno, priority, colbase, patline); + spritexpos += 8; + patno -= spriteheight; + } + break; + } +} + + + +/******************************************************************************/ +/* General Information (Mainly Genesis Related */ +/******************************************************************************/ +/* Genesis VDP Registers (from sega2.doc) + +Reg# : | Bit7 | Bit6 | Bit5 | Bit4 | Bit3 | Bit2 | Bit1 | Bit0 | General Function +-------------------------------------------------------------------------------- +0x00 : | 0 | 0 | 0 | IE1 | 0 | 1 | M3 | 0 | Mode Set Register #1 +IE = Enabled H Interrupt (Lev 4), M3 = Enable HV Counter Read / Stopped +0x01 : | 0 | DISP | IE0 | M1 | M2 | 1 | 0 | 0 | Mode Set Register #2 +DISP = Display Enable, IE0 = Enabled V Interrupt (Lev 6), M1 = DMA Enabled, M2 = 30 Cell Mode +0x02 : | 0 | 0 | SA15 | SA14 | SA13 | 0 | 0 | 0 | Scroll A Base Address +SA13-15 = Bits 13-15 of the Scroll A Name Table Base Address in VRAM +0x03 : | 0 | 0 | WD15 | WD14 | WD13 | WD12 | WD11 | 0 | Window Base Address +WD11-15 = Bits 11-15 of the Window Name Table Base Address in VRAM +0x04 : | 0 | 0 | 0 | 0 | 0 | SB15 | SB14 | SB13 | Scroll B Base Address +SB13-15 = Bits 13-15 of the Scroll B Name Table Base Address in VRAM +0x05 : | 0 | AT15 | AT14 | AT13 | AT12 | AT11 | AT10 | AT9 | Sprite Table Base Address +AT9=15 = Bits 9-15 of the Sprite Name Table Base Address in VRAM +0x06 : | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | Unused +0x07 : | 0 | 0 | CPT1 | CPT0 | COL3 | COL2 | COL1 | COL0 | Background Colour Select +CPT0-1 = Palette Number, COL = Colour in Palette +0x08 : | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | Unused +0x09 : | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | Unused +0x0A : | BIT7 | BIT6 | BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0 | H-Interrupt Register +BIT0-7 = Controls Level 4 Interrupt Timing +0x0B : | 0 | 0 | 0 | 0 | IE2 | VSCR | HSCR | LSCR | Mode Set Register #3 +IE2 = Enable E Interrupt (Lev 2), VSCR = Vertical Scroll Mode, HSCR / LSCR = Horizontal Scroll Mode +0x0C : | RS0 | 0 | 0 | 0 | S/TE | LSM1 | LSM0 | RS1 | Mode Set Register #4 +RS0 / RS1 = Cell Mode, S/TE = Shadow/Hilight Enable, LSM0 / LSM1 = Interlace Mode Setting +0x0D : | 0 | 0 | HS15 | HS14 | HS13 | HS12 | HS11 | HS10 | HScroll Base Address +HS10-15 = Bits 10-15 of the HScroll Name Table Base Address in VRAM +0x0E : | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | Unused +0x0F : | INC7 | INC6 | INC5 | INC4 | INC3 | INC2 | INC1 | INC0 | Auto-Increment +INC0-7 = Auto Increment Value (after VRam Access) +0x10 : | 0 | 0 | VSZ1 | VSZ0 | 0 | 0 | HSZ1 | HSZ0 | Scroll Size +VSZ0-1 = Vertical Plane Size, HSZ0-1 = Horizontal Plane Size +0x11 : | RIGT | 0 | 0 | WHP5 | WHP4 | WHP3 | WHP2 | WHP1 | Window H Position +RIGT = Window Right Side of Base Point, WHP1-5 = Bits1-5 of Window H Point +0x12 : | DOWN | 0 | 0 | WVP4 | WVP3 | WVP2 | WVP1 | WVP0 | Window V Position +DOWN = Window Below Base Point, WVP0-4 = Bits0-4 of Window V Point +0x13 : | LG7 | LG6 | LG5 | LG4 | LG3 | LG2 | LG1 | LG0 | DMA Length Counter LOW +0x14 : | LG15 | LG14 | LG13 | LG12 | LG11 | LG10 | LG9 | LG8 | DMA Length Counter HIGH +LG0-15 = Bits 0-15 of DMA Length Counter +0x15 : | SA8 | SA7 | SA6 | SA5 | SA4 | SA3 | SA2 | SA1 | DMA Source Address LOW +0x16 : | SA16 | SA15 | SA14 | SA13 | SA12 | SA11 | SA10 | SA9 | DMA Source Address MID +0x17 : | DMD1 | DMD0 | SA22 | SA21 | SA20 | SA19 | SA18 | S17 | DMA Source Address HIGH +LG0-15 = Bits 1-22 of DMA Source Address +DMD0-1 = DMA Mode + +Memory Layouts ... + +Scroll Name Table +16-bits are used to Define a Tile in the Scroll Plane +| PRI | CP1 | CP0 | VF | HF | PT10 | PT9 | PT8 | +| PT7 | PT6 | PT5 | PT4 | PT3 | PT2 | PT1 | PT0 | +PRI = Priority, CP = Colour Palette, VF = VFlip, HF = HFlip, PT0-9 = Tile # in VRAM + +HScroll Data (in VRAM) +0x00 | +0x01 / H Scroll of Plane A (Used in Overall, Cell & Line Modes) +0x02 | +0x03 / H Scroll of Plane B (Used in Overall, Cell & Line Modes) +0x04 | +0x05 / H Scroll of Plane A (Used in Line Mode) +0x06 | +0x07 / H Scroll of Plane B (Used in Line Mode) +... +0x20 | +0x21 / H Scroll of Plane A (Used in Cell & Line Mode) +0x22 | +0x23 / H Scroll of Plane B (Used in Cell & Line Mode) +etc.. That kinda thing :) +Data is in Format .. +| x | x | x | x | x | x | HS9 | HS8 | +| HS7 | HS6 | HS5 | HS4 | HS3 | HS2 | HS1 | HS0 | +HS = HScroll Amount for Overall / Cell / Line depending on Mode. + +VScroll Data (in VSRAM) +0x00 | +0x01 / V Scroll of Plane A (Used in Overall & Cell Modes) +0x02 | +0x03 / V Scroll of Plane B (Used in Overall & Cell Modes) +0x04 | +0x05 / V Scroll of Plane A (Used in Cell Mode) +0x06 | +0x07 / V Scroll of Plane B (Used in Cell Modes) +etc.. +Data is in Format .. +| x | x | x | x | x | VS10 | VS9 | VS8 | +| VS7 | VS6 | VS5 | VS4 | VS3 | VS2 | VS1 | VS0 | +VS = HScroll Amount for Overall / Cell / Line depending on Mode. + +Sprite Attributes Table (in VRAM) +Each Sprite uses 64-bits of Information (8 bytes) +| x | x | x | x | x | x | YP9 | YP8 | +| YP7 | YP6 | YP5 | YP4 | YP3 | YP2 | YP1 | YP0 | +| x | x | x | x | HS1 | HS0 | VS1 | VS0 | +| x | LN6 | LN5 | LN4 | LN3 | LN2 | LN1 | LN0 | +| PRI | CP1 | CP0 | VF | HF | PT10 | PT9 | PT8 | +| PT7 | PT6 | PT5 | PT4 | PT3 | PT2 | PT1 | PT0 | +| x | x | x | x | x | x | XP9 | XP8 | +| XP7 | XP6 | XP5 | XP4 | XP3 | XP2 | XP1 | XP0 | +YP = Y Position of Sprite +HS = Horizontal Size (Blocks) +VS = Vertical Size (Blocks) +LN = Link Field +PRI = Priority +CP = Colour Palette +VF = VFlip +HF = HFlip +PT = Pattern Number +XP = X Position of Sprite + + + +*/ + |