MCUME/MCUME_pico2/picogen/gwenesis/vdp/gwenesis_vdp_mem.c

1032 wiersze
31 KiB
C
Executable File

/*
Gwenesis : Genesis & megadrive Emulator.
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 3 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, see <http://www.gnu.org/licenses/>.
__author__ = "bzhxx"
__contact__ = "https://github.com/bzhxx"
__license__ = "GPLv3"
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "../cpus/M68K/m68k.h"
#include "../sound/ym2612.h"
#include "gwenesis_vdp.h"
#include "../io/gwenesis_io.h"
#include "../bus/gwenesis_bus.h"
#include "../sound/gwenesis_sn76489.h"
#include "../savestate/gwenesis_savestate.h"
#include <assert.h>
#include "pico/stdlib.h" // overclock
#pragma GCC optimize("Ofast")
#define VDP_MEM_DISABLE_LOGGING 1
#if !VDP_MEM_DISABLE_LOGGING
#include <stdarg.h>
void vdpm_log(const char *subs, const char *fmt, ...) {
extern int frame_counter;
extern int scan_line;
va_list va;
printf("%06d:%03d :[%s] vc:%04x hc:%04x hv:%04x ", frame_counter, scan_line, subs,gwenesis_vdp_vcounter(),gwenesis_vdp_hcounter(),gwenesis_vdp_hvcounter());
va_start(va, fmt);
vfprintf(stdout, fmt, va);
va_end(va);
printf("\n");
}
#else
#define vdpm_log(...) do {} while(0)
#endif
//#define _DMA_TRACE_
/* Setup VDP Memories */
//extern uint8_t emulator_framebuffer[1024*64];
//unsigned char* VRAM = &emulator_framebuffer[0];
unsigned char VRAM[VRAM_MAX_SIZE];
//unsigned char* VRAM = NULL;
unsigned short CRAM[CRAM_MAX_SIZE]; // CRAM - Palettes
unsigned char SAT_CACHE[SAT_CACHE_MAX_SIZE]; // Sprite cache
unsigned char gwenesis_vdp_regs[REG_SIZE]; // Registers
unsigned short fifo[FIFO_SIZE]; // Fifo
unsigned short CRAM565[CRAM_MAX_SIZE * 4]; // CRAM - Palettes
unsigned short VSRAM[VSRAM_MAX_SIZE]; // VSRAM - Scrolling
// Define VDP control code and set initial code
static unsigned char code_reg = 0;
// Define VDP control address and set initial address
static unsigned short address_reg = 0;
// Define VDP control pending and set initial state
int command_word_pending = 0;
// Define VDP status and set initial status value
unsigned short gwenesis_vdp_status = 0x3C00;
extern int scan_line;
// Define DMA
//static unsigned int dma_length;
//static unsigned int dma_source;
// Define and set DMA FILL pending as initial state
int dma_fill_pending = 0;
// Define HVCounter latch and set initial state
static int hvcounter_latch = 0;
static int hvcounter_latched = 0;
int hint_pending;
// Define VIDEO MODE
extern int mode_pal;
extern int sprite_overflow;
extern bool sprite_collision;
// Store last address r/w
//static unsigned int gwenesis_vdp_laddress_r=0;
//unsigned int gwenesis_vdp_laddress_w=0;
//static int DMA_RUN=0;
// 16 bits access to VRAM
#define FETCH16(A) ( ( (*(unsigned short *)&VRAM[(A)]) >> 8 ) | ( (*(unsigned short *)&VRAM[(A)]) << 8 ) )
/******************************************************************************
*
* SEGA 315-5313 Reset
* Clear all volatile memory
*
******************************************************************************/
int m68k_irq_acked(int irq) {
/* VINT has higher priority (Fatal Rewind) */
if (REG1_VBLANK_INTERRUPT && (gwenesis_vdp_status & STATUS_VIRQPENDING))
{
/* Clear VINT pending flag */
gwenesis_vdp_status &= ~STATUS_VIRQPENDING;
if (hint_pending && REG0_LINE_INTERRUPT)
m68k_set_irq(4);
else
m68k_set_irq(0);
}
else
{
/* Clear HINT pending flag */
hint_pending = 0;
/* Update IRQ status */
m68k_set_irq(0);
}
return M68K_INT_ACK_AUTOVECTOR;
}
void gwenesis_vdp_reset() {
memset(VRAM, 0, VRAM_MAX_SIZE);
memset(SAT_CACHE, 0, sizeof(SAT_CACHE));
memset(CRAM, 0, sizeof(CRAM));
memset(CRAM565, 0, sizeof(CRAM565));
memset(VSRAM, 0, sizeof(VSRAM));
memset(gwenesis_vdp_regs, 0, sizeof(gwenesis_vdp_regs));
command_word_pending = 0;
address_reg = 0;
code_reg = 0;
hint_pending = 0;
// _vcounter = 0;
gwenesis_vdp_status = 0x3C00;
// //line_counter_interrupt = 0;
hvcounter_latched = 0;
// register the M68K interrupt
m68k_set_int_ack_callback(m68k_irq_acked);
}
/******************************************************************************
*
* SEGA 315-5313 HCOUNTER
* Process SEGA 315-5313 HCOUNTER based on M68K Cycles
*
******************************************************************************/
//static inline __attribute__((always_inline))
int gwenesis_vdp_hcounter()
{
int mclk = m68k_cycles_run() ;
int pixclk;
// Accurate 9-bit hcounter emulation, from timing posted here:
// http://gendev.spritesmind.net/forum/viewtopic.php?p=17683#17683
if (REG12_MODE_H40)
{
pixclk = mclk * 420 / VDP_CYCLES_PER_LINE;
pixclk += 0xD;
if (pixclk >= 0x16D)
pixclk += 0x1C9 - 0x16D;
}
else
{
pixclk = mclk * 342 / VDP_CYCLES_PER_LINE;
pixclk += 0xB;
if (pixclk >= 0x128)
pixclk += 0x1D2 - 0x128;
}
return pixclk & 0x1FF;
}
/******************************************************************************
*
* SEGA 315-5313 VCOUNTER
* Process SEGA 315-5313 VCOUNTER based on M68K Cycles
*
******************************************************************************/
//static inline __attribute__((always_inline))
int gwenesis_vdp_vcounter()
{
int vc = scan_line;
int VERSION_PAL = gwenesis_vdp_status & 1;
/*
if (VERSION_PAL && mode_pal && (vc >= 0x10B))
vc += 0x1D2 - 0x10B;
else if (VERSION_PAL && (mode_pal==0) && (vc >= 0x103))
vc += 0x1CA - 0x103;
else if ((VERSION_PAL ==0 ) && (vc >= 0xEB))
vc += 0x1E5 - 0xEB;
assert(vc < 0x200);
*/
if (VERSION_PAL && mode_pal && (vc >= 267))
vc = scan_line - 58;
else if (VERSION_PAL && (mode_pal==0) && (vc >= 259))
vc = scan_line - 42;
else if ((VERSION_PAL == 0 ) && (vc >= 235))
vc = scan_line -6;
assert(vc < 0x200);
// printf("VERSION_PAL:%d , mode_pal:%d,line:%d,vc:%d\n",VERSION_PAL,mode_pal,scan_line,vc);
return vc;
}
/******************************************************************************
*
* SEGA 315-5313 HVCOUNTER
* Process SEGA 315-5313 HVCOUNTER based on HCOUNTER and VCOUNTER
*
******************************************************************************/
//static inline __attribute__((always_inline))
unsigned short gwenesis_vdp_hvcounter()
{
/* H/V Counter */
if (hvcounter_latched == 1)
return hvcounter_latch;
int hc = gwenesis_vdp_hcounter();
int vc = gwenesis_vdp_vcounter();
assert(vc < 512);
assert(hc < 512);
return ((vc & 0xFF) << 8) | (hc >> 1);
}
//static inline __attribute__((always_inline))
bool vblank(void)
{
int vc = gwenesis_vdp_vcounter();
// printf("vc=%d,REG1_DISP_ENABLED=%d,VBLAN?%d\n",vc,REG1_DISP_ENABLED,
// mode_pal?((vc >= 0xF0) && (vc < 0x1FF)):((vc >= 0xE0) && (vc < 0x1FF)));
if (REG1_DISP_ENABLED ==0)
return true;
if (mode_pal)
return ((vc >= 0xF0) && (vc < 0x1FF));
else
return ((vc >= 0xE0) && (vc < 0x1FF));
}
/******************************************************************************
*
* SEGA 315-5313 Set Register
* Write an value to specified register
*
******************************************************************************/
static inline __attribute__((always_inline)) void gwenesis_vdp_register_w(int reg, unsigned char value)
{
// Mode4 is not emulated yet. Anyway, access to registers > 0xA is blocked.
if ((BIT(gwenesis_vdp_regs[0x1], 2)==0) && reg > 0xA)
return;
gwenesis_vdp_regs[reg] = value;
vdpm_log(__FUNCTION__, "reg:%02d <- %02x", reg, value);
// Writing a register clear the first command word
// (see sonic3d intro wrong colors, and vdpfifotesting)
code_reg &= ~0x3;
address_reg &= ~0x3FFF;
switch (reg)
{
case 0:
if (REG0_HVLATCH && (hvcounter_latched == 0))
{
hvcounter_latch = gwenesis_vdp_hvcounter();
hvcounter_latched = 1;
//printf("HVcounter latched:%x\n",hvcounter_latch);
}
else if ((REG0_HVLATCH ==0) && (hvcounter_latched == 1)){
//printf("HVcounter released\n");
hvcounter_latched = 0;
}
break;
}
}
/******************************************************************************
*
* Simulate FIFO
*
******************************************************************************/
static inline __attribute__((always_inline))
void push_fifo(unsigned int value)
{
fifo[3] = fifo[2];
fifo[2] = fifo[1];
fifo[1] = fifo[0];
fifo[0] = value;
}
/******************************************************************************
*
* SEGA 315-5313 VRAM Write
* Write an value to VRAM on specified address
*
******************************************************************************/
//static inline __attribute__((always_inline))
void __not_in_flash_func(gwenesis_vdp_vram_write)(unsigned int address, unsigned int value)
{
VRAM[address] = value;
// Update internal SAT Cache
// used in Castlevania Bloodlines
if (address >= REG5_SAT_ADDRESS && address < REG5_SAT_ADDRESS + REG5_SAT_SIZE)
SAT_CACHE[address - REG5_SAT_ADDRESS] = value;
}
static inline __attribute__((always_inline))
unsigned short status_register_r(void)
{
unsigned short status = gwenesis_vdp_status; // & 0xF800;
// unsigned short status = gwenesis_vdp_status;// & 0xFC00;
int hc = gwenesis_vdp_hcounter();
// int vc = gwenesis_vdp_vcounter();
// TODO: FIFO not emulated
status |= STATUS_FIFO_EMPTY;
// VBLANK bit
if (vblank())
status |= STATUS_VBLANK;
// HBLANK bit (see Nemesis doc, as linked in hcounter())
if (REG12_MODE_H40)
{
if (hc < 0xA || hc >= 0x166)
status |= STATUS_HBLANK;
}
else
{
if (hc < 0x9 || hc >= 0x126)
status |= STATUS_HBLANK;
}
if (sprite_overflow)
status |= STATUS_SPRITEOVERFLOW;
if (sprite_collision)
status |= STATUS_SPRITECOLLISION;
if (mode_pal)
status |= STATUS_PAL;
// reading the status clears the pending flag for command words
command_word_pending = 0;
//gwenesis_vdp_status = status;
// printf("VDP status read:%04X H?%d V?%d line=%d\n",status, status & STATUS_HBLANK ,status & STATUS_VBLANK,scan_line);
return status;
}
/******************************************************************************
*
* SEGA 315-5313 Get Register
* Read an value from specified register
*
******************************************************************************/
unsigned int gwenesis_vdp_get_reg(int reg)
{
return gwenesis_vdp_regs[reg];
}
/******************************************************************************
*
* SEGA 315-5313 DMA Fill
* DMA process to fill memory
*
******************************************************************************/
static inline __attribute__((always_inline))
void gwenesis_vdp_dma_fill(unsigned short value)
{
//vdpm_log(__FUNCTION__,"@%x len:%x val:%x",REG21_DMA_SRCADDR_LOW,REG19_DMA_LENGTH,value);
int dma_length = REG19_DMA_LENGTH;
// This address is not required for fills,
// but it's still updated by the DMA engine.
unsigned short src_addr_low = REG21_DMA_SRCADDR_LOW;
if (dma_length == 0)
dma_length = 0xFFFF;
/*
vdpm_log(__FUNCTION__, "DMA %s fill: dst:%04x, length:%d, increment:%d, value=%02x",
(code_reg&0xF)==1 ? "VRAM" : ( (code_reg&0xF)==3 ? "CRAM" : "VSRAM"),
address_reg, dma_length, REG15_DMA_INCREMENT, value>>8);
*/
switch (code_reg & 0xF) {
case 0x1:
do {
gwenesis_vdp_vram_write((address_reg ^ 1) & 0xFFFF, value >> 8);
address_reg += REG15_DMA_INCREMENT;
src_addr_low++;
} while (--dma_length);
break;
case 0x3: // undocumented and buggy, see vdpfifotesting
do {
CRAM[(address_reg & 0x7f) >> 1] = fifo[3];
unsigned short pixel;
// Blue >> 9 << 2
pixel = (fifo[3] & 0xe00) >> 7;
// Green >> 5 << 5 << 3
pixel |= (fifo[3] & 0x0e0) << 3;
// Red >>1 << 11 << 2
pixel |= (fifo[3] & 0x00e) << 12;
// pixel_shadow = pixel >> 1;
// pixel_highlight = pixel_shadow | 0x8410;
// Normal pixel values when SHI is not enabled
CRAM565[(address_reg & 0x7f) >> 1] = pixel;
CRAM565[0x40 + ((address_reg & 0x7f) >> 1)] = pixel;
CRAM565[0x80 + ((address_reg & 0x7f) >> 1)] = pixel;
CRAM565[0xC0 + ((address_reg & 0x7f) >> 1)] = pixel;
address_reg += REG15_DMA_INCREMENT;
src_addr_low++;
} while (--dma_length);
break;
case 0x5: // undocumented and buggy, see vdpfifotesting:
do {
VSRAM[(address_reg & 0x7f) >> 1] = fifo[3] & 0x03FF;
address_reg += REG15_DMA_INCREMENT;
src_addr_low++;
} while (--dma_length);
break;
default:
printf("Invalid code during DMA fill\n");
}
// Clear DMA length at the end of transfer
gwenesis_vdp_regs[19] = gwenesis_vdp_regs[20] = 0;
// Update DMA source address after end of transfer
gwenesis_vdp_regs[21] = src_addr_low & 0xFF;
gwenesis_vdp_regs[22] = src_addr_low >> 8;
// gwenesis_vdp_regs[21] = src_addr_low >> 1 & 0xFF;
// gwenesis_vdp_regs[22] = src_addr_low >> 9 & 0xFF;
// gwenesis_vdp_regs[23] = src_addr_low >> 17 & 0xFF;
}
/******************************************************************************
*
* SEGA 315-5313 DMA M68K
* DMA process to copy from m68k to memory
*
******************************************************************************/
static inline __attribute__((always_inline))
void gwenesis_vdp_dma_m68k()
{
int dma_length = REG19_DMA_LENGTH;
// This address is not required for fills,
// but it's still updated by the DMA engine.
unsigned short src_addr_low = REG21_DMA_SRCADDR_LOW;
unsigned int src_addr_high = REG23_DMA_SRCADDR_HIGH;
unsigned int src_addr = (src_addr_high | src_addr_low) << 1;
unsigned int value;
if (dma_length == 0)
dma_length = 0xFFFF;
/*
vdpm_log(__FUNCTION__,"DMA M68k->%s copy: src:%04x, dst:%04x, length:%d, increment:%d",
(code_reg&0xF)==1 ? "VRAM" : ( (code_reg&0xF)==3 ? "CRAM" : "VSRAM"),
(src_addr_high | src_addr_low) << 1, address_reg, dma_length, REG15_DMA_INCREMENT);
*/
/* Source is :
68K_RAM if dma_source_high == 0x00FF : FETCH16RAM(dma_source_low << 1)
68K_ROM otherwise : FETCH16ROM((dma_source_high | dma_source_low) << 1))
*/
/* Source is 68K RAM */
if ( src_addr & 0x800000) {
switch (code_reg & 0xF) {
case 0x1: // dest is VRAM
do {
value = FETCH16RAM( src_addr );
push_fifo(value);
gwenesis_vdp_vram_write((address_reg)&0xFFFF, value >> 8);
gwenesis_vdp_vram_write((address_reg ^ 1) & 0xFFFF, value & 0xFF);
address_reg += REG15_DMA_INCREMENT;
src_addr += 2;
} while (--dma_length);
break;
case 0x3: // dest is CRAM
do {
value = FETCH16RAM( src_addr );
push_fifo(value);
CRAM[(address_reg & 0x7f) >> 1] = value;
unsigned short pixel;
// unsigned short pixel_shadow, pixel_highlight;
// Blue >> 9 << 2
pixel = (value & 0xe00) >> 7;
// Green >> 5 << 5 << 3
pixel |= (value & 0x0e0) << 3;
// Red >>1 << 11 << 2
pixel |= (value & 0x00e) << 12;
// Normal pixel values when SHI is not enabled
// add mirror 0x80 when high priority flag is set
CRAM565[(address_reg & 0x7f) >> 1] = pixel;
CRAM565[0x40 + ((address_reg & 0x7f) >> 1)] = pixel;
CRAM565[0x80 + ((address_reg & 0x7f) >> 1)] = pixel;
CRAM565[0xC0 + ((address_reg & 0x7f) >> 1)] = pixel;
address_reg += REG15_DMA_INCREMENT;
src_addr += 2;
} while (--dma_length);
break;
case 0x5: // dest is VSRAM
do {
value = FETCH16RAM( src_addr );
push_fifo(value);
VSRAM[(address_reg & 0x7f) >> 1] = value & 0x03FF;
address_reg += REG15_DMA_INCREMENT;
src_addr += 2;
} while (--dma_length);
break;
default: // dest in unknown
break;
}
/* source is 68K ROM */
} else {
// unsigned int dma_source_address = (dma_source_high | dma_source_low) << 1;
switch (code_reg & 0xF) {
case 0x1: // dest is VRAM
do {
value = FETCH16ROM(src_addr);
push_fifo(value);
gwenesis_vdp_vram_write((address_reg)&0xFFFF, value >> 8);
gwenesis_vdp_vram_write((address_reg ^ 1) & 0xFFFF, value & 0xFF);
address_reg += REG15_DMA_INCREMENT;
src_addr += 2;
} while (--dma_length);
break;
case 0x3: // dest is CRAM
do {
value = FETCH16ROM(src_addr);
push_fifo(value);
CRAM[(address_reg & 0x7f) >> 1] = value;
unsigned short pixel;
// unsigned short pixel_shadow, pixel_highlight;
// Blue >> 9 << 2
pixel = (value & 0xe00) >> 7;
// Green >> 5 << 5 << 3
pixel |= (value & 0x0e0) << 3;
// Red >>1 << 11 << 2
pixel |= (value & 0x00e) << 12;
// pixel_shadow = pixel >> 1;
// pixel_highlight = pixel_shadow | 0x8410;
// Normal pixel values when SHI is not enabled
// add mirror 0x80 when high priority flag is set
CRAM565[(address_reg & 0x7f) >> 1] = pixel;
CRAM565[0x40 + ((address_reg & 0x7f) >> 1)] = pixel;
CRAM565[0x80 + ((address_reg & 0x7f) >> 1)] = pixel;
CRAM565[0xC0 + ((address_reg & 0x7f) >> 1)] = pixel;
address_reg += REG15_DMA_INCREMENT;
src_addr += 2;
} while (--dma_length);
break;
case 0x5: // dest is VSRAM
do {
value = FETCH16ROM(src_addr);
push_fifo(value);
VSRAM[(address_reg & 0x7f) >> 1] = value & 0x03FF;
address_reg += REG15_DMA_INCREMENT;
src_addr += 2;
} while (--dma_length);
break;
default: // dest in unknown
break;
}
}
// Update DMA source address after end of transfer
gwenesis_vdp_regs[21] = src_addr & 0xFF; //src_addr_low & 0xFF;
gwenesis_vdp_regs[22] = (src_addr >> 8 ) & 0xFF; //src_addr_low >> 8;
// Clear DMA length at the end of transfer
gwenesis_vdp_regs[19] = gwenesis_vdp_regs[20] = 0;
}
/******************************************************************************
*
* SEGA 315-5313 DMA Copy
* DMA process to copy from memory to memory
*
******************************************************************************/
static inline __attribute__((always_inline))
void gwenesis_vdp_dma_copy()
{
// DMA_RUN=1;
int dma_length = REG19_DMA_LENGTH;
unsigned short src_addr_low = REG21_DMA_SRCADDR_LOW;
//vdpm_log(__FUNCTION__,"length:%x src:%x",dma_length,src_addr_low);
do
{
unsigned short value = VRAM[src_addr_low ^ 1];
gwenesis_vdp_vram_write((address_reg ^ 1) & 0xFFFF, value);
address_reg += REG15_DMA_INCREMENT;
src_addr_low++;
} while (--dma_length);
// Update DMA source address after end of transfer
gwenesis_vdp_regs[21] = src_addr_low & 0xFF;
gwenesis_vdp_regs[22] = src_addr_low >> 8;
// Clear DMA length at the end of transfer
gwenesis_vdp_regs[19] = gwenesis_vdp_regs[20] = 0;
}
/******************************************************************************
*
* SEGA 315-5313 read data R16
* Read an data value from mapped memory on specified address
* and return as word
*
******************************************************************************/
static inline __attribute__((always_inline))
unsigned int gwenesis_vdp_read_data_port_16()
{
enum
{
CRAM_BITMASK = 0x0EEE,
VSRAM_BITMASK = 0x07FF,
VRAM8_BITMASK = 0x00FF
};
unsigned int value;
command_word_pending = 0;
//if (code_reg & 1) /* check if write is set */
// {
switch (code_reg & 0xF)
{
case 0x0:
// No byteswapping here
value = VRAM[(address_reg)&0xFFFE] << 8;
value |= VRAM[(address_reg | 1) & 0xFFFF];
address_reg += REG15_DMA_INCREMENT;
address_reg &= 0xFFFF;
//vdpm_log(__FUNCTION__,"%04x",value);
return value;
case 0x4:
if (((address_reg & 0x7f) >> 1) >= 0x28)
value = VSRAM[0];
else
value = VSRAM[(address_reg & 0x7f) >> 1];
value = (value & VSRAM_BITMASK) | (fifo[3] & ~VSRAM_BITMASK);
address_reg += REG15_DMA_INCREMENT;
address_reg &= 0x7F;
// vdpm_log(__FUNCTION__,"%04x",value);
return value;
case 0x8:
value = CRAM[(address_reg & 0x7f) >> 1];
value = (value & CRAM_BITMASK) | (fifo[3] & ~CRAM_BITMASK);
address_reg += REG15_DMA_INCREMENT;
address_reg &= 0x7F;
// vdpm_log(__FUNCTION__,"%04x",value);
return value;
case 0xC: /* 8-Bit memory access */
value = VRAM[(address_reg ^ 1) & 0xFFFF];
value = (value & VRAM8_BITMASK) | (fifo[3] & ~VRAM8_BITMASK);
address_reg += REG15_DMA_INCREMENT;
address_reg &= 0xFFFF;
// vdpm_log(__FUNCTION__,"%04x",value);
return value;
default:
printf("unhandled gwenesis_vdp_read_data_port_16(%x)\n", address_reg);
return 0xFF;
}
// }
// return 0x00;
}
/******************************************************************************
*
* SEGA 315-5313 write to control port
* Write an control value to SEGA 315-5313 control port
*
******************************************************************************/
static inline __attribute__((always_inline))
void gwenesis_vdp_control_port_write(unsigned int value)
{
//vdpm_log(__FUNCTION__,"%04x",value);
if (command_word_pending == 1) {
// second half of the command word
code_reg &= ~0x3C;
code_reg |= (value >> 2) & 0x3C;
address_reg &= 0x3FFF;
address_reg |= value << 14;
command_word_pending = 0;
//vdpm_log(__FUNCTION__,"command word 2nd code:%x address:%x", code_reg, address_reg);
// DMA trigger
if (code_reg & (1<<5)) {
// Check master DMA enable, otherwise skip
if (REG1_DMA_ENABLED == 0)
return;
// gwenesis_vdp_status |= 0x2;
switch (REG23_DMA_TYPE) {
case 0:
case 1:
gwenesis_vdp_dma_m68k();
break;
case 2:
// VRAM fill will trigger on next data port write
dma_fill_pending = 1;
break;
case 3:
gwenesis_vdp_dma_copy();
break;
}
}
return;
}
if ((value >> 14) == 2) {
gwenesis_vdp_register_w((value >> 8) & 0x1F, value & 0xFF);
return;
}
// Anything else is treated as first half of the command word
// We directly update the code reg and address reg
code_reg &= ~0x3;
code_reg |= value >> 14;
address_reg &= ~0x3FFF;
address_reg |= value & 0x3FFF;
command_word_pending = 1;
// vdpm_log(__FUNCTION__,"command word 1st code:%x address:%x", code_reg, address_reg);
}
/******************************************************************************
*
* SEGA 315-5313 write data W16
* Write an data value to mapped memory on specified address
*
******************************************************************************/
static inline __attribute__((always_inline))
void gwenesis_vdp_write_data_port_16(unsigned int value)
{
vdpm_log(__FUNCTION__,"%04x",value);
command_word_pending = 0;
push_fifo(value);
switch (code_reg & 0xF)
{
case 0x1: /* VRAM write */
//vdpm_log(__FUNCTION__,"VRAM write : addr:%x increment:%d value:%04x",
// address_reg, REG15_DMA_INCREMENT, value);
gwenesis_vdp_vram_write(address_reg& 0xFFFF, (value >> 8) & 0xFF);
gwenesis_vdp_vram_write((address_reg^1)& 0xFFFF, (value)&0xFF);
address_reg += REG15_DMA_INCREMENT;
address_reg &= 0xFFFF;
break;
case 0x3: /* CRAM write */
//vdpm_log(__FUNCTION__,"CRAM write : addr:%x increment:%d value:%04x",
// address_reg, REG15_DMA_INCREMENT, value);
CRAM[(address_reg & 0x7f) >> 1] = value;
unsigned short pixel;
//unsigned short pixel_shadow,pixel_highlight;
// Blue >> 9 << 2
pixel = (value & 0xe00) >> 7;
// Green >> 5 << 5 << 3
pixel |= (value & 0x0e0) << 3;
// Red >>1 << 11 << 2
pixel |= (value & 0x00e) << 12;
// Normal pixel values when SHI is not enabled
CRAM565[(address_reg & 0x7f) >> 1] = pixel;
CRAM565[0x40 + ((address_reg & 0x7f) >> 1)] = pixel;
CRAM565[0x80 + ((address_reg & 0x7f) >> 1)] = pixel;
CRAM565[0xC0 + ((address_reg & 0x7f) >> 1)] = pixel;
address_reg += REG15_DMA_INCREMENT;
address_reg &= 0xFFFF;
break;
case 0x5: /* VSRAM write */
//vdpm_log(__FUNCTION__,"VSRAM write : addr:%x increment:%d value:%04x",
// address_reg, REG15_DMA_INCREMENT, value);
// printf("write dataport 16: VSRAM@%04x:%04x\n",address_reg,value);
VSRAM[(address_reg & 0x7f) >> 1] = value & 0X03FF;
address_reg += REG15_DMA_INCREMENT;
address_reg &= 0xFFFF;
break;
case 0x0:
case 0x4:
case 0x8: // Write operation after setting up
// Makes Compatible with Alladin and Ecco 2
break;
case 0x9: // VDP FIFO TEST
break;
default:
printf("VDP Data Port invalid");
}
/* if a DMA is scheduled, do it */
if (dma_fill_pending)
{
dma_fill_pending = 0;
gwenesis_vdp_dma_fill(value);
return;
}
}
/******************************************************************************
*
* SEGA 315-5313 Get Status
* Return current VDP Status
*
******************************************************************************/
unsigned int gwenesis_vdp_get_status()
{
return gwenesis_vdp_status;
}
/******************************************************************************
*
* SEGA 315-5313 read from memory R8
* Read an value from mapped memory on specified address
* and return as byte
*
******************************************************************************/
//static inline
unsigned int gwenesis_vdp_read_memory_8(unsigned int address)
{
unsigned int ret = gwenesis_vdp_read_memory_16(address & ~1);
if (address & 1)
return ret & 0xFF;
// vdpm_log(__FUNCTION__,"%04x : %04x",address,ret);
return ret >> 8;
}
/******************************************************************************
*
* SEGA 315-5313 read from memory R16
* Read an value from mapped memory on specified address
* and return as word
*
******************************************************************************/
//static inline
unsigned int gwenesis_vdp_read_memory_16(unsigned int address)
{
address &= 0x1F;
if (address < 0X4)
return gwenesis_vdp_read_data_port_16();
else if (address < 0x8)
return status_register_r();
else if (address < 0xf)
return gwenesis_vdp_hvcounter();
else
return 0xff;
}
/******************************************************************************
*
* SEGA 315-5313 write to memory W8
* Write an byte value to mapped memory on specified address
*
******************************************************************************/
//static inline
void gwenesis_vdp_write_memory_8(unsigned int address, unsigned int value)
{
gwenesis_vdp_write_memory_16(address & ~1, (value << 8) | value);
}
/******************************************************************************
*
* SEGA 315-5313 write to memory W16
* Write an word value to mapped memory on specified address
*
******************************************************************************/
//static inline
extern int system_clock;
void gwenesis_vdp_write_memory_16(unsigned int address, unsigned int value) {
address = address & 0x1F;
if (address < 0x4) {
gwenesis_vdp_write_data_port_16(value);
return;
}
if (address < 0x8) {
gwenesis_vdp_control_port_write(value);
return;
}
if (address < 0x18) { // PSG 8 bits write
vdpm_log(__FUNCTION__,"PSG sclk=%d,mclk=%d", system_clock, m68k_cycles_master());
gwenesis_SN76489_Write(value, m68k_cycles_master());
return;
}
// UNHANDLED
printf("unhandled gwenesis_vdp_write(%x, %x)\n", address, value);
}
void gwenesis_vdp_mem_save_state() {
SaveState* state;
state = saveGwenesisStateOpenForWrite("vdp_mem");
saveGwenesisStateSetBuffer(state, "VRAM", VRAM, VRAM_MAX_SIZE);
saveGwenesisStateSetBuffer(state, "CRAM", CRAM, sizeof(CRAM));
saveGwenesisStateSetBuffer(state, "SAT_CACHE", SAT_CACHE, sizeof(SAT_CACHE));
saveGwenesisStateSetBuffer(state, "gwenesis_vdp_regs", gwenesis_vdp_regs, sizeof(gwenesis_vdp_regs));
saveGwenesisStateSetBuffer(state, "fifo", fifo, sizeof(fifo));
saveGwenesisStateSetBuffer(state, "CRAM565", CRAM565, sizeof(CRAM565));
saveGwenesisStateSetBuffer(state, "VSRAM", VSRAM, sizeof(VSRAM));
saveGwenesisStateSet(state, "code_reg", code_reg);
saveGwenesisStateSet(state, "address_reg", address_reg);
saveGwenesisStateSet(state, "command_word_pending", command_word_pending);
saveGwenesisStateSet(state, "gwenesis_vdp_status", gwenesis_vdp_status);
saveGwenesisStateSet(state, "dma_fill_pending", dma_fill_pending);
saveGwenesisStateSet(state, "hvcounter_latch", hvcounter_latch);
saveGwenesisStateSet(state, "hvcounter_latched", hvcounter_latched);
saveGwenesisStateSet(state, "hint_pending", hint_pending);
}
void gwenesis_vdp_mem_load_state() {
SaveState* state = saveGwenesisStateOpenForRead("vdp_mem");
saveGwenesisStateGetBuffer(state, "VRAM", VRAM, VRAM_MAX_SIZE);
saveGwenesisStateGetBuffer(state, "CRAM", CRAM, sizeof(CRAM));
saveGwenesisStateGetBuffer(state, "SAT_CACHE", SAT_CACHE, sizeof(SAT_CACHE));
saveGwenesisStateGetBuffer(state, "gwenesis_vdp_regs", gwenesis_vdp_regs, sizeof(gwenesis_vdp_regs));
saveGwenesisStateGetBuffer(state, "fifo", fifo, sizeof(fifo));
saveGwenesisStateGetBuffer(state, "CRAM565", CRAM565, sizeof(CRAM565));
saveGwenesisStateGetBuffer(state, "VSRAM", VSRAM, sizeof(VSRAM));
code_reg = saveGwenesisStateGet(state, "code_reg");
address_reg = saveGwenesisStateGet(state, "address_reg");
command_word_pending = saveGwenesisStateGet(state, "command_word_pending");
gwenesis_vdp_status = saveGwenesisStateGet(state, "gwenesis_vdp_status");
dma_fill_pending = saveGwenesisStateGet(state, "dma_fill_pending");
hvcounter_latch = saveGwenesisStateGet(state, "hvcounter_latch");
hvcounter_latched = saveGwenesisStateGet(state, "hvcounter_latched");
hint_pending = saveGwenesisStateGet(state, "hint_pending");
}