1075 lines
28 KiB
C
1075 lines
28 KiB
C
/*
|
|
* Hatari - blitter.c
|
|
*
|
|
* This file is distributed under the GNU General Public License, version 2
|
|
* or at your option any later version. Read the file gpl.txt for details.
|
|
*
|
|
* Blitter emulation. The 'Blitter' chip is found in the Mega-ST, STE/Mega-STE
|
|
* and Falcon. It provides a very fast BitBlit function in hardware.
|
|
*
|
|
* This file has originally been taken from STonX, but it has been completely
|
|
* modified for better maintainability and higher compatibility.
|
|
*
|
|
* NOTES:
|
|
* ----------------------------------------------------------------------------
|
|
* Strange end mask condition ((~(0xffff>>skew)) > end_mask_1)
|
|
*
|
|
* """Similarly the NFSR (aka post-flush) bit, when set, will prevent the last
|
|
* source read of the line. This read may not be necessary with certain
|
|
* combinations of end masks and skews."""
|
|
* - doesn't mean the blitter will skip source read by itself, just a hint
|
|
* for developers as far as i understand it.
|
|
* ----------------------------------------------------------------------------
|
|
* Does smudge mode change the line register ?
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
const char Blitter_fileid[] = "Hatari blitter.c : " __DATE__ " " __TIME__;
|
|
|
|
#include <SDL_types.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "main.h"
|
|
#include "blitter.h"
|
|
#include "configuration.h"
|
|
#include "dmaSnd.h"
|
|
#include "ioMem.h"
|
|
#include "m68000.h"
|
|
#include "mfp.h"
|
|
#include "memorySnapShot.h"
|
|
#include "stMemory.h"
|
|
#include "screen.h"
|
|
#include "video.h"
|
|
|
|
/* Cycles to run for in non-hog mode */
|
|
#define NONHOG_CYCLES (64*4)
|
|
|
|
/* BLiTTER registers, incs are signed, others unsigned */
|
|
#define REG_HT_RAM 0xff8a00 /* - 0xff8a1e */
|
|
|
|
#define REG_SRC_X_INC 0xff8a20
|
|
#define REG_SRC_Y_INC 0xff8a22
|
|
#define REG_SRC_ADDR 0xff8a24
|
|
|
|
#define REG_END_MASK1 0xff8a28
|
|
#define REG_END_MASK2 0xff8a2a
|
|
#define REG_END_MASK3 0xff8a2c
|
|
|
|
#define REG_DST_X_INC 0xff8a2e
|
|
#define REG_DST_Y_INC 0xff8a30
|
|
#define REG_DST_ADDR 0xff8a32
|
|
|
|
#define REG_X_COUNT 0xff8a36
|
|
#define REG_Y_COUNT 0xff8a38
|
|
|
|
#define REG_BLIT_HOP 0xff8a3a /* halftone blit operation byte */
|
|
#define REG_BLIT_LOP 0xff8a3b /* logical blit operation byte */
|
|
#define REG_CONTROL 0xff8a3c
|
|
#define REG_SKEW 0xff8a3d
|
|
|
|
|
|
#define BLITTER_READ_WORD_BUS_ERR 0x0000 /* This value is returned when the blitter try to read a word */
|
|
/* in a region that would cause a bus error */
|
|
/* [NP] FIXME : for now we return a constant, but it should depend on the bus activity */
|
|
|
|
/* Blitter registers */
|
|
typedef struct
|
|
{
|
|
Uint32 src_addr;
|
|
Uint32 dst_addr;
|
|
Uint32 words;
|
|
Uint32 lines;
|
|
short src_x_incr;
|
|
short src_y_incr;
|
|
short dst_x_incr;
|
|
short dst_y_incr;
|
|
Uint16 end_mask_1;
|
|
Uint16 end_mask_2;
|
|
Uint16 end_mask_3;
|
|
Uint8 hop;
|
|
Uint8 lop;
|
|
Uint8 ctrl;
|
|
Uint8 skew;
|
|
} BLITTERREGS;
|
|
|
|
/* Blitter vars */
|
|
typedef struct
|
|
{
|
|
int pass_cycles;
|
|
int op_cycles;
|
|
Uint32 buffer;
|
|
Uint32 src_words_reset;
|
|
Uint32 dst_words_reset;
|
|
Uint32 src_words;
|
|
Uint8 hog;
|
|
Uint8 smudge;
|
|
Uint8 line;
|
|
Uint8 fxsr;
|
|
Uint8 nfsr;
|
|
Uint8 skew;
|
|
} BLITTERVARS;
|
|
|
|
/* Blitter state */
|
|
typedef struct
|
|
{
|
|
Uint16 src_word;
|
|
Uint16 dst_word;
|
|
Uint16 end_mask;
|
|
Uint8 have_src;
|
|
Uint8 have_dst;
|
|
Uint8 fxsr;
|
|
Uint8 nfsr;
|
|
} BLITTERSTATE;
|
|
|
|
/* Blitter logical op func */
|
|
typedef Uint16 (*BLITTER_OP_FUNC)(void);
|
|
|
|
static BLITTERREGS BlitterRegs;
|
|
static BLITTERVARS BlitterVars;
|
|
static BLITTERSTATE BlitterState;
|
|
static Uint16 BlitterHalftone[16];
|
|
|
|
static BLITTER_OP_FUNC Blitter_ComputeHOP;
|
|
static BLITTER_OP_FUNC Blitter_ComputeLOP;
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Count blitter cycles
|
|
*/
|
|
|
|
static void Blitter_AddCycles(int cycles)
|
|
{
|
|
int all_cycles = cycles + nWaitStateCycles;
|
|
|
|
BlitterVars.op_cycles += all_cycles;
|
|
|
|
nCyclesMainCounter += all_cycles >> nCpuFreqShift;
|
|
CyclesGlobalClockCounter += all_cycles >> nCpuFreqShift;
|
|
nWaitStateCycles = 0;
|
|
}
|
|
|
|
static void Blitter_FlushCycles(void)
|
|
{
|
|
int op_cycles = INT_CONVERT_TO_INTERNAL(BlitterVars.op_cycles, INT_CPU_CYCLE);
|
|
|
|
BlitterVars.pass_cycles += BlitterVars.op_cycles;
|
|
BlitterVars.op_cycles = 0;
|
|
|
|
PendingInterruptCount -= op_cycles;
|
|
while (PendingInterruptCount <= 0 && PendingInterruptFunction)
|
|
CALL_VAR(PendingInterruptFunction);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Handle bus arbitration when switching between CPU and Blitter
|
|
* When a write is made to FF8A3C to start the blitter, it will take a few cycles
|
|
* before doing the bus arbitration. During this time the CPU will be able to
|
|
* partially execute the next instruction in parallel to the blitter
|
|
* (until an access to the BUS is needed by the CPU).
|
|
*
|
|
* NOTE [NP] : this is mostly handled with hardcoded cases for now, as it
|
|
* requires cycle exact emulation to exactly know when bus is accessed
|
|
* by the CPU to prefetch the next word.
|
|
* More tests are needed on a real STE to have a proper model of this.
|
|
*
|
|
* Based on several examples, possible sequence when starting the blitter seems to be :
|
|
* - t+0 : write to FF8A3C
|
|
* - t+0 : CPU can still run during 4 cycles and access bus
|
|
* - t+4 : bus arbitration takes 4 cycles (no access for cpu and blitter during this time)
|
|
* - t+8 : blitter owns the bus and starts tranferring data
|
|
*
|
|
* When blitter stops owning the bus in favor of the cpu, this seems to always take 4 cycles
|
|
*/
|
|
static void Blitter_BusArbitration ( int RequestBusMode )
|
|
{
|
|
int cycles;
|
|
|
|
if ( RequestBusMode == BUS_MODE_BLITTER ) /* Bus is requested by the blitter */
|
|
{
|
|
//fprintf ( stderr , "blitter start pc %x %x\n" , M68000_GetPC() , M68000_InstrPC );
|
|
cycles = 4; /* Default case : take 4 cycles when going from cpu to blitter */
|
|
|
|
/* Different timing for some specific cases */
|
|
|
|
/* 'Relapse - Graphix Sound 2' by Cybernetics (overscan plasma using blitter) */
|
|
/* $e764 : move.b d5,(a4) + dbra d1,$fff2 : 4 cycles of the dbra can be executed while blitter starts */
|
|
if ( STMemory_ReadLong ( M68000_InstrPC ) == 0x188551c9 ) /* PC = E764 */
|
|
cycles = 4-4; /* 4 cycles less than default case */
|
|
}
|
|
|
|
else /* Bus is requested by the cpu */
|
|
{
|
|
cycles = 4; /* Always 4 cycles ? */
|
|
}
|
|
|
|
/* Add arbitration cycles and update BusMode */
|
|
if ( cycles > 0 )
|
|
{
|
|
Blitter_AddCycles(cycles);
|
|
Blitter_FlushCycles();
|
|
}
|
|
BusMode = RequestBusMode;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read & Write operations
|
|
*/
|
|
static Uint16 Blitter_ReadWord(Uint32 addr)
|
|
{
|
|
Uint16 value;
|
|
|
|
/* When reading from a bus error region, just return a constant */
|
|
if ( STMemory_CheckRegionBusError ( addr ) )
|
|
value = BLITTER_READ_WORD_BUS_ERR;
|
|
else
|
|
value = (Uint16)get_word ( addr );
|
|
//fprintf ( stderr , "read %x %x %x\n" , addr , value , STMemory_CheckRegionBusError(addr) );
|
|
|
|
Blitter_AddCycles(4);
|
|
|
|
return value;
|
|
}
|
|
|
|
static void Blitter_WriteWord(Uint32 addr, Uint16 value)
|
|
{
|
|
/* Call put_word only if the address doesn't point to a bus error region */
|
|
if ( STMemory_CheckRegionBusError ( addr ) == false )
|
|
put_word ( addr , (Uint32)(value) );
|
|
//fprintf ( stderr , "write %x %x %x\n" , addr , value , STMemory_CheckRegionBusError(addr) );
|
|
|
|
Blitter_AddCycles(4);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Blitter emulation - level 1
|
|
*/
|
|
|
|
static void Blitter_BeginLine(void)
|
|
{
|
|
BlitterVars.src_words = BlitterVars.src_words_reset;
|
|
}
|
|
|
|
static void Blitter_SetState(Uint8 fxsr, Uint8 nfsr, Uint16 end_mask)
|
|
{
|
|
BlitterState.end_mask = end_mask;
|
|
BlitterState.have_src = false;
|
|
BlitterState.have_dst = false;
|
|
BlitterState.fxsr = fxsr;
|
|
BlitterState.nfsr = nfsr;
|
|
}
|
|
|
|
static void Blitter_SourceShift(void)
|
|
{
|
|
if (BlitterRegs.src_x_incr < 0)
|
|
BlitterVars.buffer >>= 16;
|
|
else
|
|
BlitterVars.buffer <<= 16;
|
|
}
|
|
|
|
static void Blitter_SourceFetch(void)
|
|
{
|
|
Uint32 src_word = (Uint32)Blitter_ReadWord(BlitterRegs.src_addr);
|
|
|
|
if (BlitterRegs.src_x_incr < 0)
|
|
BlitterVars.buffer |= src_word << 16;
|
|
else
|
|
BlitterVars.buffer |= src_word;
|
|
|
|
if (BlitterVars.src_words == 1)
|
|
{
|
|
BlitterRegs.src_addr += BlitterRegs.src_y_incr;
|
|
}
|
|
else
|
|
{
|
|
--BlitterVars.src_words;
|
|
BlitterRegs.src_addr += BlitterRegs.src_x_incr;
|
|
}
|
|
}
|
|
|
|
static Uint16 Blitter_SourceRead(void)
|
|
{
|
|
if (!BlitterState.have_src)
|
|
{
|
|
if (BlitterState.fxsr)
|
|
{
|
|
Blitter_SourceShift();
|
|
Blitter_SourceFetch();
|
|
}
|
|
|
|
Blitter_SourceShift();
|
|
|
|
if (!BlitterState.nfsr)
|
|
{
|
|
Blitter_SourceFetch();
|
|
}
|
|
|
|
BlitterState.src_word = (Uint16)(BlitterVars.buffer >> BlitterVars.skew);
|
|
BlitterState.have_src = true;
|
|
}
|
|
|
|
return BlitterState.src_word;
|
|
}
|
|
|
|
static Uint16 Blitter_GetHalftoneWord(void)
|
|
{
|
|
if (BlitterVars.smudge)
|
|
return BlitterHalftone[Blitter_SourceRead() & 15];
|
|
else
|
|
return BlitterHalftone[BlitterVars.line];
|
|
}
|
|
|
|
/* HOP */
|
|
|
|
static Uint16 Blitter_HOP_0(void)
|
|
{
|
|
return 0xFFFF;
|
|
}
|
|
|
|
static Uint16 Blitter_HOP_1(void)
|
|
{
|
|
return Blitter_GetHalftoneWord();
|
|
}
|
|
|
|
static Uint16 Blitter_HOP_2(void)
|
|
{
|
|
return Blitter_SourceRead();
|
|
}
|
|
|
|
static Uint16 Blitter_HOP_3(void)
|
|
{
|
|
return Blitter_SourceRead() & Blitter_GetHalftoneWord();
|
|
}
|
|
|
|
static BLITTER_OP_FUNC Blitter_HOP_Table [4] =
|
|
{
|
|
Blitter_HOP_0,
|
|
Blitter_HOP_1,
|
|
Blitter_HOP_2,
|
|
Blitter_HOP_3
|
|
};
|
|
|
|
static void Blitter_Select_HOP(void)
|
|
{
|
|
Blitter_ComputeHOP = Blitter_HOP_Table[BlitterRegs.hop];
|
|
}
|
|
|
|
/* end HOP */
|
|
|
|
static Uint16 Blitter_DestRead(void)
|
|
{
|
|
if (!BlitterState.have_dst)
|
|
{
|
|
BlitterState.dst_word = Blitter_ReadWord(BlitterRegs.dst_addr);
|
|
BlitterState.have_dst = true;
|
|
}
|
|
|
|
return BlitterState.dst_word;
|
|
}
|
|
|
|
/* LOP */
|
|
|
|
static Uint16 Blitter_LOP_0(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static Uint16 Blitter_LOP_1(void)
|
|
{
|
|
return Blitter_ComputeHOP() & Blitter_DestRead();
|
|
}
|
|
|
|
static Uint16 Blitter_LOP_2(void)
|
|
{
|
|
return Blitter_ComputeHOP() & ~Blitter_DestRead();
|
|
}
|
|
|
|
static Uint16 Blitter_LOP_3(void)
|
|
{
|
|
return Blitter_ComputeHOP();
|
|
}
|
|
|
|
static Uint16 Blitter_LOP_4(void)
|
|
{
|
|
return ~Blitter_ComputeHOP() & Blitter_DestRead();
|
|
}
|
|
|
|
static Uint16 Blitter_LOP_5(void)
|
|
{
|
|
return Blitter_DestRead();
|
|
}
|
|
|
|
static Uint16 Blitter_LOP_6(void)
|
|
{
|
|
return Blitter_ComputeHOP() ^ Blitter_DestRead();
|
|
}
|
|
|
|
static Uint16 Blitter_LOP_7(void)
|
|
{
|
|
return Blitter_ComputeHOP() | Blitter_DestRead();
|
|
}
|
|
|
|
static Uint16 Blitter_LOP_8(void)
|
|
{
|
|
return ~Blitter_ComputeHOP() & ~Blitter_DestRead();
|
|
}
|
|
|
|
static Uint16 Blitter_LOP_9(void)
|
|
{
|
|
return ~Blitter_ComputeHOP() ^ Blitter_DestRead();
|
|
}
|
|
|
|
static Uint16 Blitter_LOP_A(void)
|
|
{
|
|
return ~Blitter_DestRead();
|
|
}
|
|
|
|
static Uint16 Blitter_LOP_B(void)
|
|
{
|
|
return Blitter_ComputeHOP() | ~Blitter_DestRead();
|
|
}
|
|
|
|
static Uint16 Blitter_LOP_C(void)
|
|
{
|
|
return ~Blitter_ComputeHOP();
|
|
}
|
|
|
|
static Uint16 Blitter_LOP_D(void)
|
|
{
|
|
return ~Blitter_ComputeHOP() | Blitter_DestRead();
|
|
}
|
|
|
|
static Uint16 Blitter_LOP_E(void)
|
|
{
|
|
return ~Blitter_ComputeHOP() | ~Blitter_DestRead();
|
|
}
|
|
|
|
static Uint16 Blitter_LOP_F(void)
|
|
{
|
|
return 0xFFFF;
|
|
}
|
|
|
|
static BLITTER_OP_FUNC Blitter_LOP_Table [16] =
|
|
{
|
|
Blitter_LOP_0,
|
|
Blitter_LOP_1,
|
|
Blitter_LOP_2,
|
|
Blitter_LOP_3,
|
|
Blitter_LOP_4,
|
|
Blitter_LOP_5,
|
|
Blitter_LOP_6,
|
|
Blitter_LOP_7,
|
|
Blitter_LOP_8,
|
|
Blitter_LOP_9,
|
|
Blitter_LOP_A,
|
|
Blitter_LOP_B,
|
|
Blitter_LOP_C,
|
|
Blitter_LOP_D,
|
|
Blitter_LOP_E,
|
|
Blitter_LOP_F
|
|
};
|
|
|
|
static void Blitter_Select_LOP(void)
|
|
{
|
|
Blitter_ComputeLOP = Blitter_LOP_Table[BlitterRegs.lop];
|
|
}
|
|
|
|
/* end LOP */
|
|
|
|
static Uint16 Blitter_ComputeMask(void)
|
|
{
|
|
return (Blitter_ComputeLOP() & BlitterState.end_mask) |
|
|
(Blitter_DestRead() & ~BlitterState.end_mask);
|
|
}
|
|
|
|
static void Blitter_ProcessWord(void)
|
|
{
|
|
/* when NFSR, a read-modify-write is always performed */
|
|
Uint16 dst_data = ((BlitterState.nfsr || BlitterState.end_mask != 0xFFFF)
|
|
? Blitter_ComputeMask()
|
|
: Blitter_ComputeLOP());
|
|
|
|
Blitter_WriteWord(BlitterRegs.dst_addr, dst_data);
|
|
|
|
if (BlitterRegs.words == 1)
|
|
{
|
|
BlitterRegs.dst_addr += BlitterRegs.dst_y_incr;
|
|
}
|
|
else
|
|
{
|
|
--BlitterRegs.words;
|
|
BlitterRegs.dst_addr += BlitterRegs.dst_x_incr;
|
|
}
|
|
}
|
|
|
|
static void Blitter_EndLine(void)
|
|
{
|
|
--BlitterRegs.lines;
|
|
BlitterRegs.words = BlitterVars.dst_words_reset;
|
|
|
|
if (BlitterRegs.dst_y_incr >= 0)
|
|
BlitterVars.line = (BlitterVars.line+1) & 15;
|
|
else
|
|
BlitterVars.line = (BlitterVars.line-1) & 15;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Blitter emulation - level 2
|
|
*/
|
|
|
|
static void Blitter_SingleWord(void)
|
|
{
|
|
Blitter_BeginLine();
|
|
Blitter_SetState(BlitterVars.fxsr, BlitterVars.nfsr, BlitterRegs.end_mask_1);
|
|
Blitter_ProcessWord();
|
|
Blitter_EndLine();
|
|
}
|
|
|
|
static void Blitter_FirstWord(void)
|
|
{
|
|
Blitter_BeginLine();
|
|
Blitter_SetState(BlitterVars.fxsr, 0, BlitterRegs.end_mask_1);
|
|
Blitter_ProcessWord();
|
|
}
|
|
|
|
static void Blitter_MiddleWord(void)
|
|
{
|
|
Blitter_SetState(0, 0, BlitterRegs.end_mask_2);
|
|
Blitter_ProcessWord();
|
|
}
|
|
|
|
static void Blitter_LastWord(void)
|
|
{
|
|
Blitter_SetState(0, BlitterVars.nfsr, BlitterRegs.end_mask_3);
|
|
Blitter_ProcessWord();
|
|
Blitter_EndLine();
|
|
}
|
|
|
|
static void Blitter_Step(void)
|
|
{
|
|
if (BlitterVars.dst_words_reset == 1)
|
|
{
|
|
Blitter_SingleWord();
|
|
}
|
|
else if (BlitterRegs.words == BlitterVars.dst_words_reset)
|
|
{
|
|
Blitter_FirstWord();
|
|
}
|
|
else if (BlitterRegs.words == 1)
|
|
{
|
|
Blitter_LastWord();
|
|
}
|
|
else
|
|
{
|
|
Blitter_MiddleWord();
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Let's do the blit.
|
|
* Note that in non-HOG mode, the blitter only runs for 64 bus cycles (2 MHz!)
|
|
* before giving the bus back to the CPU. Due to this mode, this function must
|
|
* be able to abort and resume the blitting at any time.
|
|
*/
|
|
static void Blitter_Start(void)
|
|
{
|
|
/* select HOP & LOP funcs */
|
|
Blitter_Select_HOP();
|
|
Blitter_Select_LOP();
|
|
|
|
/* setup vars */
|
|
BlitterVars.pass_cycles = 0;
|
|
BlitterVars.op_cycles = 0;
|
|
BlitterVars.src_words_reset = BlitterVars.dst_words_reset + BlitterVars.fxsr - BlitterVars.nfsr;
|
|
|
|
/* bus arbitration */
|
|
Blitter_BusArbitration ( BUS_MODE_BLITTER );
|
|
|
|
/* Busy=1, set line to high/1 and clear interrupt */
|
|
MFP_GPIP_Set_Line_Input ( MFP_GPIP_LINE_GPU_DONE , MFP_GPIP_STATE_HIGH );
|
|
|
|
/* Now we enter the main blitting loop */
|
|
do
|
|
{
|
|
Blitter_Step();
|
|
Blitter_FlushCycles();
|
|
}
|
|
while (BlitterRegs.lines > 0
|
|
&& (BlitterVars.hog || BlitterVars.pass_cycles < NONHOG_CYCLES));
|
|
|
|
/* bus arbitration */
|
|
Blitter_BusArbitration ( BUS_MODE_CPU );
|
|
|
|
BlitterRegs.ctrl = (BlitterRegs.ctrl & 0xF0) | BlitterVars.line;
|
|
|
|
if (BlitterRegs.lines == 0)
|
|
{
|
|
/* We're done, clear busy and hog bits */
|
|
BlitterRegs.ctrl &= ~(0x80|0x40);
|
|
|
|
/* Busy=0, set line to low/0 and request interrupt */
|
|
MFP_GPIP_Set_Line_Input ( MFP_GPIP_LINE_GPU_DONE , MFP_GPIP_STATE_LOW );
|
|
}
|
|
else
|
|
{
|
|
/* Continue blitting later */
|
|
CycInt_AddRelativeInterrupt(NONHOG_CYCLES, INT_CPU_CYCLE, INTERRUPT_BLITTER);
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read blitter halftone ram.
|
|
*/
|
|
static void Blitter_Halftone_ReadWord(int index)
|
|
{
|
|
IoMem_WriteWord(REG_HT_RAM + index + index, BlitterHalftone[index]);
|
|
}
|
|
|
|
void Blitter_Halftone00_ReadWord(void) { Blitter_Halftone_ReadWord(0); }
|
|
void Blitter_Halftone01_ReadWord(void) { Blitter_Halftone_ReadWord(1); }
|
|
void Blitter_Halftone02_ReadWord(void) { Blitter_Halftone_ReadWord(2); }
|
|
void Blitter_Halftone03_ReadWord(void) { Blitter_Halftone_ReadWord(3); }
|
|
void Blitter_Halftone04_ReadWord(void) { Blitter_Halftone_ReadWord(4); }
|
|
void Blitter_Halftone05_ReadWord(void) { Blitter_Halftone_ReadWord(5); }
|
|
void Blitter_Halftone06_ReadWord(void) { Blitter_Halftone_ReadWord(6); }
|
|
void Blitter_Halftone07_ReadWord(void) { Blitter_Halftone_ReadWord(7); }
|
|
void Blitter_Halftone08_ReadWord(void) { Blitter_Halftone_ReadWord(8); }
|
|
void Blitter_Halftone09_ReadWord(void) { Blitter_Halftone_ReadWord(9); }
|
|
void Blitter_Halftone10_ReadWord(void) { Blitter_Halftone_ReadWord(10); }
|
|
void Blitter_Halftone11_ReadWord(void) { Blitter_Halftone_ReadWord(11); }
|
|
void Blitter_Halftone12_ReadWord(void) { Blitter_Halftone_ReadWord(12); }
|
|
void Blitter_Halftone13_ReadWord(void) { Blitter_Halftone_ReadWord(13); }
|
|
void Blitter_Halftone14_ReadWord(void) { Blitter_Halftone_ReadWord(14); }
|
|
void Blitter_Halftone15_ReadWord(void) { Blitter_Halftone_ReadWord(15); }
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read blitter source x increment (0xff8a20).
|
|
*/
|
|
void Blitter_SourceXInc_ReadWord(void)
|
|
{
|
|
IoMem_WriteWord(REG_SRC_X_INC, (Uint16)(BlitterRegs.src_x_incr));
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read blitter source y increment (0xff8a22).
|
|
*/
|
|
void Blitter_SourceYInc_ReadWord(void)
|
|
{
|
|
IoMem_WriteWord(REG_SRC_Y_INC, (Uint16)(BlitterRegs.src_y_incr));
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read blitter source address (0xff8a24).
|
|
*/
|
|
void Blitter_SourceAddr_ReadLong(void)
|
|
{
|
|
IoMem_WriteLong(REG_SRC_ADDR, BlitterRegs.src_addr);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read blitter endmask 1.
|
|
*/
|
|
void Blitter_Endmask1_ReadWord(void)
|
|
{
|
|
IoMem_WriteWord(REG_END_MASK1, BlitterRegs.end_mask_1);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read blitter endmask 2.
|
|
*/
|
|
void Blitter_Endmask2_ReadWord(void)
|
|
{
|
|
IoMem_WriteWord(REG_END_MASK2, BlitterRegs.end_mask_2);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read blitter endmask 3.
|
|
*/
|
|
void Blitter_Endmask3_ReadWord(void)
|
|
{
|
|
IoMem_WriteWord(REG_END_MASK3, BlitterRegs.end_mask_3);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read blitter destination x increment (0xff8a2E).
|
|
*/
|
|
void Blitter_DestXInc_ReadWord(void)
|
|
{
|
|
IoMem_WriteWord(REG_DST_X_INC, (Uint16)(BlitterRegs.dst_x_incr));
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read blitter destination y increment (0xff8a30).
|
|
*/
|
|
void Blitter_DestYInc_ReadWord(void)
|
|
{
|
|
IoMem_WriteWord(REG_DST_Y_INC, (Uint16)(BlitterRegs.dst_y_incr));
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read blitter destination address.
|
|
*/
|
|
void Blitter_DestAddr_ReadLong(void)
|
|
{
|
|
IoMem_WriteLong(REG_DST_ADDR, BlitterRegs.dst_addr);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read blitter words-per-line register.
|
|
*/
|
|
void Blitter_WordsPerLine_ReadWord(void)
|
|
{
|
|
IoMem_WriteWord(REG_X_COUNT, (Uint16)(BlitterRegs.words & 0xFFFF));
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read blitter lines-per-bitblock register.
|
|
*/
|
|
void Blitter_LinesPerBitblock_ReadWord(void)
|
|
{
|
|
IoMem_WriteWord(REG_Y_COUNT, (Uint16)(BlitterRegs.lines & 0xFFFF));
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read blitter halftone operation register.
|
|
*/
|
|
void Blitter_HalftoneOp_ReadByte(void)
|
|
{
|
|
IoMem_WriteByte(REG_BLIT_HOP, BlitterRegs.hop);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read blitter logical operation register.
|
|
*/
|
|
void Blitter_LogOp_ReadByte(void)
|
|
{
|
|
IoMem_WriteByte(REG_BLIT_LOP, BlitterRegs.lop);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read blitter control register.
|
|
*/
|
|
void Blitter_Control_ReadByte(void)
|
|
{
|
|
/* busy, hog/blit, smudge, n/a, 4bits for line number */
|
|
IoMem_WriteByte(REG_CONTROL, BlitterRegs.ctrl);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Read blitter skew register.
|
|
*/
|
|
void Blitter_Skew_ReadByte(void)
|
|
{
|
|
IoMem_WriteByte(REG_SKEW, BlitterRegs.skew);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Write to blitter halftone ram.
|
|
*/
|
|
static void Blitter_Halftone_WriteWord(int index)
|
|
{
|
|
BlitterHalftone[index] = IoMem_ReadWord(REG_HT_RAM + index + index);
|
|
}
|
|
|
|
void Blitter_Halftone00_WriteWord(void) { Blitter_Halftone_WriteWord(0); }
|
|
void Blitter_Halftone01_WriteWord(void) { Blitter_Halftone_WriteWord(1); }
|
|
void Blitter_Halftone02_WriteWord(void) { Blitter_Halftone_WriteWord(2); }
|
|
void Blitter_Halftone03_WriteWord(void) { Blitter_Halftone_WriteWord(3); }
|
|
void Blitter_Halftone04_WriteWord(void) { Blitter_Halftone_WriteWord(4); }
|
|
void Blitter_Halftone05_WriteWord(void) { Blitter_Halftone_WriteWord(5); }
|
|
void Blitter_Halftone06_WriteWord(void) { Blitter_Halftone_WriteWord(6); }
|
|
void Blitter_Halftone07_WriteWord(void) { Blitter_Halftone_WriteWord(7); }
|
|
void Blitter_Halftone08_WriteWord(void) { Blitter_Halftone_WriteWord(8); }
|
|
void Blitter_Halftone09_WriteWord(void) { Blitter_Halftone_WriteWord(9); }
|
|
void Blitter_Halftone10_WriteWord(void) { Blitter_Halftone_WriteWord(10); }
|
|
void Blitter_Halftone11_WriteWord(void) { Blitter_Halftone_WriteWord(11); }
|
|
void Blitter_Halftone12_WriteWord(void) { Blitter_Halftone_WriteWord(12); }
|
|
void Blitter_Halftone13_WriteWord(void) { Blitter_Halftone_WriteWord(13); }
|
|
void Blitter_Halftone14_WriteWord(void) { Blitter_Halftone_WriteWord(14); }
|
|
void Blitter_Halftone15_WriteWord(void) { Blitter_Halftone_WriteWord(15); }
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Write to blitter source x increment.
|
|
*/
|
|
void Blitter_SourceXInc_WriteWord(void)
|
|
{
|
|
BlitterRegs.src_x_incr = (short)(IoMem_ReadWord(REG_SRC_X_INC) & 0xFFFE);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Write to blitter source y increment.
|
|
*/
|
|
void Blitter_SourceYInc_WriteWord(void)
|
|
{
|
|
BlitterRegs.src_y_incr = (short)(IoMem_ReadWord(REG_SRC_Y_INC) & 0xFFFE);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Write to blitter source address register (0xff8a24).
|
|
*/
|
|
void Blitter_SourceAddr_WriteLong(void)
|
|
{
|
|
if ( ConfigureParams.System.bAddressSpace24 == true )
|
|
BlitterRegs.src_addr = IoMem_ReadLong(REG_SRC_ADDR) & 0x00FFFFFE; /* Normal STF/STE */
|
|
else
|
|
BlitterRegs.src_addr = IoMem_ReadLong(REG_SRC_ADDR) & 0xFFFFFFFE; /* Falcon with extra TT RAM */
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Write to blitter endmask 1.
|
|
*/
|
|
void Blitter_Endmask1_WriteWord(void)
|
|
{
|
|
BlitterRegs.end_mask_1 = IoMem_ReadWord(REG_END_MASK1);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Write to blitter endmask 2.
|
|
*/
|
|
void Blitter_Endmask2_WriteWord(void)
|
|
{
|
|
BlitterRegs.end_mask_2 = IoMem_ReadWord(REG_END_MASK2);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Write to blitter endmask 3.
|
|
*/
|
|
void Blitter_Endmask3_WriteWord(void)
|
|
{
|
|
BlitterRegs.end_mask_3 = IoMem_ReadWord(REG_END_MASK3);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Write to blitter destination x increment.
|
|
*/
|
|
void Blitter_DestXInc_WriteWord(void)
|
|
{
|
|
BlitterRegs.dst_x_incr = (short)(IoMem_ReadWord(REG_DST_X_INC) & 0xFFFE);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Write to blitter source y increment.
|
|
*/
|
|
void Blitter_DestYInc_WriteWord(void)
|
|
{
|
|
BlitterRegs.dst_y_incr = (short)(IoMem_ReadWord(REG_DST_Y_INC) & 0xFFFE);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Write to blitter destination address register.
|
|
*/
|
|
void Blitter_DestAddr_WriteLong(void)
|
|
{
|
|
if ( ConfigureParams.System.bAddressSpace24 == true )
|
|
BlitterRegs.dst_addr = IoMem_ReadLong(REG_DST_ADDR) & 0x00FFFFFE; /* Normal STF/STE */
|
|
else
|
|
BlitterRegs.dst_addr = IoMem_ReadLong(REG_DST_ADDR) & 0xFFFFFFFE; /* Falcon with extra TT RAM */
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Write to blitter words-per-line register.
|
|
*/
|
|
void Blitter_WordsPerLine_WriteWord(void)
|
|
{
|
|
Uint32 words = (Uint32)IoMem_ReadWord(REG_X_COUNT);
|
|
|
|
if (words == 0)
|
|
words = 65536;
|
|
|
|
BlitterRegs.words = words;
|
|
BlitterVars.dst_words_reset = words;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Write to blitter lines-per-bitblock register.
|
|
*/
|
|
void Blitter_LinesPerBitblock_WriteWord(void)
|
|
{
|
|
Uint32 lines = (Uint32)IoMem_ReadWord(REG_Y_COUNT);
|
|
|
|
if (lines == 0)
|
|
lines = 65536;
|
|
|
|
BlitterRegs.lines = lines;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Write to blitter halftone operation register.
|
|
*/
|
|
void Blitter_HalftoneOp_WriteByte(void)
|
|
{
|
|
/* h/ware reg masks out the top 6 bits! */
|
|
BlitterRegs.hop = IoMem_ReadByte(REG_BLIT_HOP) & 3;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Write to blitter logical operation register.
|
|
*/
|
|
void Blitter_LogOp_WriteByte(void)
|
|
{
|
|
/* h/ware reg masks out the top 4 bits! */
|
|
BlitterRegs.lop = IoMem_ReadByte(REG_BLIT_LOP) & 0xF;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Write to blitter control register.
|
|
*/
|
|
void Blitter_Control_WriteByte(void)
|
|
{
|
|
/* Control register bits:
|
|
* 0x80: busy bit
|
|
* - Turn on Blitter activity and stay "1" until copy finished
|
|
* 0x40: Blit-mode bit
|
|
* - 0: Blit mode, CPU and Blitter get 64 clockcycles in turns
|
|
* - 1: HOG Mode, Blitter reserves and hogs the bus for as long
|
|
* as the copy takes, CPU and DMA get no Bus access
|
|
* 0x20: Smudge mode
|
|
* - Which line of the halftone pattern to start with is
|
|
* read from the first source word when the copy starts
|
|
* 0x10: not used
|
|
* 0x0f
|
|
*
|
|
* The lowest 4 bits contain the Halftone pattern line number
|
|
*/
|
|
|
|
if (LOG_TRACE_LEVEL(TRACE_BLITTER))
|
|
{
|
|
int FrameCycles, HblCounterVideo, LineCycles;
|
|
|
|
Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
|
|
|
|
LOG_TRACE_PRINT("blitter write ctrl=%x video_cyc=%d %d@%d pc=%x instr_cyc=%d\n" ,
|
|
IoMem_ReadByte(REG_CONTROL) ,
|
|
FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles );
|
|
}
|
|
|
|
BlitterRegs.ctrl = IoMem_ReadByte(REG_CONTROL) & 0xEF;
|
|
|
|
BlitterVars.hog = BlitterRegs.ctrl & 0x40;
|
|
BlitterVars.smudge = BlitterRegs.ctrl & 0x20;
|
|
BlitterVars.line = BlitterRegs.ctrl & 0xF;
|
|
|
|
/* Remove old pending update interrupt */
|
|
CycInt_RemovePendingInterrupt(INTERRUPT_BLITTER);
|
|
|
|
/* Busy bit set? */
|
|
if (BlitterRegs.ctrl & 0x80)
|
|
{
|
|
if (BlitterRegs.lines == 0)
|
|
{
|
|
/* We're done, clear busy and hog bits */
|
|
BlitterRegs.ctrl &= ~(0x80|0x40);
|
|
}
|
|
else
|
|
{
|
|
/* Start blitting after some CPU cycles */
|
|
CycInt_AddRelativeInterrupt((CurrentInstrCycles+nWaitStateCycles)>>nCpuFreqShift,
|
|
INT_CPU_CYCLE, INTERRUPT_BLITTER);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Write to blitter skew register.
|
|
*/
|
|
void Blitter_Skew_WriteByte(void)
|
|
{
|
|
BlitterRegs.skew = IoMem_ReadByte(REG_SKEW);
|
|
BlitterVars.fxsr = (BlitterRegs.skew & 0x80)?1:0;
|
|
BlitterVars.nfsr = (BlitterRegs.skew & 0x40)?1:0;
|
|
BlitterVars.skew = BlitterRegs.skew & 0xF;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Handler which continues blitting after 64 bus cycles.
|
|
*/
|
|
void Blitter_InterruptHandler(void)
|
|
{
|
|
CycInt_AcknowledgeInterrupt();
|
|
|
|
if (BlitterRegs.ctrl & 0x80)
|
|
{
|
|
Blitter_Start();
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Save/Restore snapshot of Blitter variables.
|
|
*/
|
|
void Blitter_MemorySnapShot_Capture(bool bSave)
|
|
{
|
|
/* Save/Restore details */
|
|
MemorySnapShot_Store(&BlitterRegs, sizeof(BlitterRegs));
|
|
MemorySnapShot_Store(&BlitterVars, sizeof(BlitterVars));
|
|
MemorySnapShot_Store(&BlitterHalftone, sizeof(BlitterHalftone));
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Show Blitter register values.
|
|
*/
|
|
void Blitter_Info(FILE *fp, Uint32 dummy)
|
|
{
|
|
BLITTERREGS *regs = &BlitterRegs;
|
|
|
|
fprintf(fp, "src addr: 0x%06x\n", regs->src_addr);
|
|
fprintf(fp, "dst addr: 0x%06x\n", regs->dst_addr);
|
|
fprintf(fp, "words: %u\n", regs->words);
|
|
fprintf(fp, "lines: %u\n", regs->lines);
|
|
fprintf(fp, "src X-inc: %hd\n", regs->src_x_incr);
|
|
fprintf(fp, "src Y-inc: %hd\n", regs->src_y_incr);
|
|
fprintf(fp, "dst X-inc: %hd\n", regs->dst_x_incr);
|
|
fprintf(fp, "dst Y-inc: %hd\n", regs->dst_y_incr);
|
|
fprintf(fp, "end mask1: 0x%04x\n", regs->end_mask_1);
|
|
fprintf(fp, "end mask2: 0x%04x\n", regs->end_mask_2);
|
|
fprintf(fp, "end mask3: 0x%04x\n", regs->end_mask_3);
|
|
fprintf(fp, "HOP: 0x%02x\n", regs->hop);
|
|
fprintf(fp, "LOP: 0x%02x\n", regs->lop);
|
|
fprintf(fp, "control: 0x%02x\n", regs->ctrl);
|
|
fprintf(fp, "skew: 0x%02x\n", regs->skew);
|
|
fprintf(fp, "Note: internally changed register values aren't visible to breakpoints\nor in memdump output until emulated code reads or writes them!\n");
|
|
}
|