📄 neogeo.c
字号:
/***************************************************************************
vidhrdw.c
Functions to emulate the video hardware of the machine.
Important! There are two types of NeoGeo romdump - MVS & MGD2. They are both
converted to a standard format in the vh_start routines.
Graphics information:
0x00000 - 0xdfff : Blocks of sprite data, each 0x80 bytes:
Each 0x80 block is made up of 0x20 double words, their format is:
Word: Sprite number (16 bits)
Byte: Palette number (8 bits)
Byte: Bit 0: X flip
Bit 1: Y flip
Bit 2: Automatic animation flag (4 tiles?)
Bit 3: Automatic animation flag (8 tiles?)
Bit 4: MSB of sprite number (confirmed, Karnov_r, Mslug). See note.
Bit 5: MSB of sprite number (MSlug2)
Bit 6: MSB of sprite number (Kof97)
Bit 7: Unknown for now
Each double word sprite is drawn directly underneath the previous one,
based on the starting coordinates.
0x0e000 - 0x0ea00 : Front plane fix tiles (8*8), 2 bytes each
0x10000: Control for sprites banks, arranged in words
Bit 0 to 3 - Y zoom LSB
Bit 4 to 7 - Y zoom MSB (ie, 1 byte for Y zoom).
Bit 8 to 11 - X zoom, 0xf is full size (no scale).
Bit 12 to 15 - Unknown, probably unused
0x10400: Control for sprite banks, arranged in words
Bit 0 to 5: Number of sprites in this bank (see note below).
Bit 6 - If set, this bank is placed to right of previous bank (same Y-coord).
Bit 7 to 15 - Y position for sprite bank.
0x10800: Control for sprite banks, arranged in words
Bit 0 to 5: Unknown
Bit 7 to 15 - X position for sprite bank.
Notes:
* If rom set has less than 0x10000 tiles then msb of tile must be ignored
(see Magician Lord).
***************************************************************************/
#include "driver.h"
#include "common.h"
/*#include "usrintrf.h"*/
#include "vidhrdw/generic.h"
/*#define NEO_DEBUG*/
/* The following two will save precomputed palette & graphics data to disk for fast
game startup - good for developers but uses lots of disk space! :) */
static unsigned char *vidram;
static unsigned char *neogeo_paletteram; /* pointer to 1 of the 2 palette banks */
static unsigned char *pal_bank1; /* 0x100*16 2 byte palette entries */
static unsigned char *pal_bank2; /* 0x100*16 2 byte palette entries */
static int palno,modulo,where,high_tile,vhigh_tile,vvhigh_tile;
int no_of_tiles;
static int palette_swap_pending,fix_bank;
extern unsigned char *neogeo_ram;
extern unsigned int neogeo_frame_counter;
extern int neogeo_game_fix;
int neogeo_red_mask,neogeo_green_mask,neogeo_blue_mask;
void NeoMVSDrawGfx(unsigned char **line,const struct GfxElement *gfx,
unsigned int code,unsigned int color,int flipx,int flipy,int sx,int sy,
int zx,int zy);
static char dda_x_skip[16];
static char dda_y_skip[17];
static char full_y_skip[16]={0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
#ifdef NEO_DEBUG
int neo_unknown[32];
void neo_unknown1(int offset, int data) {WRITE_WORD(&neo_unknown[0],data);}
void neo_unknown2(int offset, int data) {WRITE_WORD(&neo_unknown[2],data);}
void neo_unknown3(int offset, int data) {WRITE_WORD(&neo_unknown[4],data);}
void neo_unknown4(int offset, int data) {if (cpu_getpc()!=0x4a44) WRITE_WORD(&neo_unknown[6],data>>7);}
int dotiles = 0;
int screen_offs = 0x0000;
#endif
/******************************************************************************/
static void swap_palettes(void)
{
int i,newword,red,green,blue;
for (i=0; i<0x2000; i+=2) {
newword = READ_WORD(&neogeo_paletteram[i]);
red= ((newword>>8)&neogeo_red_mask)*0x11;
green= ((newword>>4)&neogeo_green_mask)*0x11;
blue= ((newword>>0)&neogeo_blue_mask)*0x11;
palette_change_color(i / 2,red,green,blue);
}
palette_swap_pending=0;
}
void neogeo_setpalbank0(int offset,int data)
{
if (palno != 0) {
palno = 0;
neogeo_paletteram = pal_bank1;
palette_swap_pending=1;
}
}
void neogeo_setpalbank1(int offset,int data)
{
if (palno != 1) {
palno = 1;
neogeo_paletteram = pal_bank2;
palette_swap_pending=1;
}
}
int neogeo_paletteram_r(int offset)
{
return READ_WORD(&neogeo_paletteram[offset]);
}
void neogeo_paletteram_w(int offset,int data)
{
int oldword = READ_WORD (&neogeo_paletteram[offset]);
int newword = COMBINE_WORD (oldword, data);
int red=((newword>>8)&neogeo_red_mask);
int green=((newword>>4)&neogeo_green_mask);
int blue=((newword>>0)&neogeo_blue_mask);
WRITE_WORD (&neogeo_paletteram[offset], newword);
red = red*0x11;
green = green*0x11;
blue = blue*0x11;
palette_change_color(offset / 2,red,green,blue);
}
/******************************************************************************/
void neogeo_vh_stop(void)
{
if (pal_bank1) free (pal_bank1);
if (pal_bank2) free (pal_bank2);
if (vidram) free (vidram);
if (neogeo_ram) free (neogeo_ram);
pal_bank1=pal_bank2=vidram=neogeo_ram=0;
}
static int common_vh_start(void)
{
pal_bank1=pal_bank2=vidram=0;
pal_bank1 = malloc(0x2000);
if (!pal_bank1) {
neogeo_vh_stop();
return 1;
}
pal_bank2 = malloc(0x2000);
if (!pal_bank2) {
neogeo_vh_stop();
return 1;
}
vidram = malloc(0x20000); /* 0x20000 bytes even though only 0x10c00 is used */
if (!vidram) {
neogeo_vh_stop();
return 1;
}
memset(vidram,0,0x20000);
neogeo_paletteram = pal_bank1;
palette_transparent_color = 4095;
palno=0;
modulo=1;
where=0;
fix_bank=0;
palette_swap_pending=0;
return 0;
}
/******************************************************************************/
static const unsigned char *neogeo_palette(void)
{
int color,code,pal_base,y,my=0,x,count,offs,i;
int colmask[256];
unsigned int *pen_usage; /* Save some struct derefs */
int sx =0,sy =0,oy =0,zx = 1, rzy = 1;
int tileno,tileatr,t1,t2,t3;
char fullmode=0;
int ddax=16,dday=256,rzx=15,yskip=0;
memset(palette_used_colors,PALETTE_COLOR_UNUSED,4096);
/* character foreground */
pen_usage= Machine->gfx[fix_bank]->pen_usage;
pal_base = Machine->drv->gfxdecodeinfo[fix_bank].color_codes_start;
for (color = 0;color < 16;color++) colmask[color] = 0;
for (offs=0xe000;offs<0xea00;offs+=2) {
code = READ_WORD( &vidram[offs] );
color = code >> 12;
colmask[color] |= pen_usage[code&0xfff];
}
for (color = 0;color < 16;color++)
{
if (colmask[color] & (1 << 0))
palette_used_colors[pal_base + 16 * color] = PALETTE_COLOR_TRANSPARENT;
for (i = 1;i < 16;i++)
{
if (colmask[color] & (1 << i))
palette_used_colors[pal_base + 16 * color + i] = PALETTE_COLOR_USED;
}
}
/* Tiles */
pen_usage= Machine->gfx[2]->pen_usage;
pal_base = Machine->drv->gfxdecodeinfo[2].color_codes_start;
for (color = 0;color < 256;color++) colmask[color] = 0;
for (count=0;count<0x300;count+=2) {
t3 = READ_WORD( &vidram[0x10000 + count] );
t1 = READ_WORD( &vidram[0x10400 + count] );
t2 = READ_WORD( &vidram[0x10800 + count] );
/* If this bit is set this new column is placed next to last one */
if (t1 & 0x40) {
sx += rzx;
if ( sx >= 0x1F0 )
sx -= 0x200;
/* Get new zoom for this column */
zx = (t3 >> 8) & 0x0f;
if(neogeo_game_fix==7 && (t3==0 || t3==0x147f))
zx=0xf;
sy = oy;
} else { /* nope it is a new block */
/* Sprite scaling */
zx = (t3 >> 8) & 0x0f;
rzy = t3 & 0xff;
if(neogeo_game_fix==7 && (t3==0 || t3==0x147f))
{
zx=0xf;
rzy=0xff;
}
sx = (t2 >> 7);
if ( sx >= 0x1F0 )
sx -= 0x200;
/* Number of tiles in this strip */
my = t1 & 0x3f;
if (my == 0x20) fullmode = 1;
else if (my >= 0x21) fullmode = 2; /* most games use 0x21, but */
/* Alpha Mission II uses 0x3f */
else fullmode = 0;
sy = 0x1F0 - (t1 >> 7);
if (sy > 0x100) sy -= 0x200;
if (fullmode == 2 || (fullmode == 1 && rzy == 0xff))
{
while (sy < -16) sy += 2 * (rzy + 1);
}
oy = sy;
if(my==0x21) my=0x20;
else if(rzy!=0xff && my!=0)
my=((my*16*256)/(rzy+1) + 15)/16;
if(my>0x20) my=0x20;
ddax=16; /* setup x zoom */
}
/* No point doing anything if tile strip is 0 */
if (my==0) continue;
/* Process x zoom */
if(zx!=15) {
rzx=0;
for(i=0;i<16;i++) {
ddax-=zx+1;
if(ddax<=0) {
ddax+=15+1;
rzx++;
}
}
}
else rzx=16;
if(sx>311) continue;
/* Setup y zoom */
if(rzy==255)
yskip=16;
else
dday=256;
offs = count<<6;
/* my holds the number of tiles in each vertical multisprite block */
for (y=0; y < my ;y++) {
tileno = READ_WORD(&vidram[offs]);
offs+=2;
tileatr = READ_WORD(&vidram[offs]);
offs+=2;
if (high_tile && tileatr&0x10) tileno+=0x10000;
if (vhigh_tile && tileatr&0x20) tileno+=0x20000;
if (vvhigh_tile && tileatr&0x40) tileno+=0x40000;
if (tileatr&0x8) tileno=(tileno&~7)+((tileno+neogeo_frame_counter)&7);
else if (tileatr&0x4) tileno=(tileno&~3)+((tileno+neogeo_frame_counter)&3);
if (fullmode == 2 || (fullmode == 1 && rzy == 0xff))
{
if (sy >= 224) sy -= 2 * (rzy + 1);
}
else if (fullmode == 1)
{
if (y == 0x10) sy -= 2 * (rzy + 1);
}
if(rzy!=255) {
yskip=0;
for(i=0;i<16;i++) {
dday-=rzy+1;
if(dday<=0) {
dday+=256;
yskip++;
}
}
}
if ( (tileatr>>8) != 0)
if (sy<224)
{
tileatr=tileatr>>8;
colmask[tileatr] |= pen_usage[tileno];
}
sy +=yskip;
} /* for y */
} /* for count */
for (color = 0;color < 256;color++)
{
if (colmask[color] & (1 << 0))
palette_used_colors[pal_base + 16 * color] = PALETTE_COLOR_TRANSPARENT;
for (i = 1;i < 16;i++)
{
if (colmask[color] & (1 << i))
palette_used_colors[pal_base + 16 * color + i] = PALETTE_COLOR_USED;
}
}
return palette_recalc();
}
/******************************************************************************/
void vidram_offset_w(int offset, int data)
{
where = data*2;
}
int vidram_data_r(int offset)
{
return (READ_WORD(&vidram[where & 0x1ffff]));
}
void vidram_data_w(int offset,int data)
{
WRITE_WORD(&vidram[where & 0x1ffff],data);
where += modulo;
}
/* Modulo can become negative , Puzzle Bobble Super Sidekicks and a lot */
/* of other games use this */
void vidram_modulo_w(int offset, int data) {
if (data & 0x8000) {
/* Sign extend it. */
/* Where is the SEX instruction when you need it :-) */
modulo = (data - 0x10000) << 1;
}
else
modulo = data << 1;
}
int vidram_modulo_r(int offset) {
return modulo >> 1;
}
/* Two routines to enable videoram to be read in debugger */
int mish_vid_r(int offset)
{
return READ_WORD(&vidram[offset]);
}
void mish_vid_w(int offset, int data)
{
COMBINE_WORD_MEM(&vidram[offset],data);
}
void neo_board_fix(int offset, int data)
{
fix_bank=1;
}
void neo_game_fix(int offset, int data)
{
fix_bank=0;
}
/******************************************************************************/
/* DWORD read - copied from common.c */
#ifdef ACORN /* GSL 980108 read/write nonaligned dword routine for ARM processor etc */
INLINE int read_dword(int *address)
{
if ((int)address & 3)
{
#ifdef LSB_FIRST /* little endian version */
return ( *((unsigned char *)address) +
(*((unsigned char *)address+1) << 8) +
(*((unsigned char *)address+2) << 16) +
(*((unsigned char *)address+3) << 24) );
#else /* big endian version */
return ( *((unsigned char *)address+3) +
(*((unsigned char *)address+2) << 8) +
(*((unsigned char *)address+1) << 16) +
(*((unsigned char *)address) << 24) );
#endif
}
else
return *(int *)address;
}
#else
#define read_dword(address) *(int *)address
#endif
#ifdef LSB_FIRST
#define BL0 0
#define BL1 1
#define BL2 2
#define BL3 3
#define WL0 0
#define WL1 1
#else
#define BL0 3
#define BL1 2
#define BL2 1
#define BL3 0
#define WL0 1
#define WL1 0
#endif
void NeoMVSDrawGfx(unsigned char **line,const struct GfxElement *gfx, /* AJP */
unsigned int code,unsigned int color,int flipx,int flipy,int sx,int sy,
int zx,int zy)
{
int /*ox,*/oy,ex,ey,y,start,dy;
unsigned char *bm;
int col;
int l; /* Line skipping counter */
int mydword;
unsigned char *fspr=0;
unsigned char *PL1 = Machine->memory_region[2];
unsigned char *PL2 = Machine->memory_region[3];
char *l_y_skip;
/* Safety feature */
code=code%no_of_tiles;
/* Check for total transparency, no need to draw */
if ((gfx->pen_usage[code] & ~1) == 0)
return;
if(zy==16)
l_y_skip=full_y_skip;
else
l_y_skip=dda_y_skip;
if(code < no_of_tiles/2)
{
fspr=PL1;
}
else
{
fspr=PL2;
code-=no_of_tiles/2;
}
/* Mish/AJP - Most clipping is done in main loop */
oy=sy;
ey = sy + zy -1; /* Clip for size of zoomed object */
if (sx <= -8) return;
if (sy < 0) sy = 0;
if (ey >= 224) ey = 224-1;
if (flipy) /* Y flip */
{
dy = -8;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -