Re: [hatari-devel] Falcon raster emulation |
[ Thread Index |
Date Index
| More lists.tuxfamily.org/hatari-devel Archives
]
Hi,
I've given a closer look at my old Videl code.
To understand what follows, I strongly suggest to read the Videl part of Mikro's site (in the documentation area)
Here is what I did 2 years ago :
- I replaced all the video.c code by videl code for VBL, HBL, ... (see below)
- I added some Videl interrupts.
The videl doesn't work like the ST Shifter.
In the videl registers, you can read the number of "VIDEL cycles" for a HBL.
You can get also the number of HBL for a VBL.
The VIDEL can work at 25 or 32 Mhz.
The display is composed of 2 HBL per line (except in some special cases).
So, I generated a half_HBL interrupt. Every other half_HBL interrupt, I send a HBL interrupt
When I reach the correct number of HBL, I send a VBL interrupt.
I've put here my old code (Videl part only).
I think it could be used as a start to write something better.
If someone wants the complete code, I can zip it.
It's based on hatari V1.5 or V1.6 I guess, but the falcon Videl part didn't move a lot.
I'm pretty sure this is the good way to proceed, but I may have done some things wrong here (or maybe I encountered timings problems, I don't remenber).
Maybe I should include this into the current hatari version with a compiler define to able/disable it.
The define could be removed when it works well.
Don't hesitate to ask if you have questions.
Reading this code again, I think some datas should be float, not int32 (cycles mainly).
Regards
Laurent
[added at the end of the videl.c code, after the rendering code]
/**
* Start internal halfFrame Interrupt (25 or 32 Mhz)
*/
static void VIDEL_Start_Interrupt_HalfLine(void)
{
Uint32 videlFreq = IoMem_ReadWord(0xff82c0) & 0x4;
Uint32 hht_cycles = IoMem_ReadWord(0xff8282) + 2;
Uint32 cycles;
if (videlFreq) {
/* 25 Mzh Frequency */
cycles = ((hht_cycles * 25) / 32) / 2;
}
else {
/* 32 Mzh Frequency */
cycles = hht_cycles / 2;
}
VIDEL_updateInternalValues();
videl.saveCycle = cycles;
// fprintf(stderr ,"VIDEL HHT_cycles : %d\n", cycles);
CycInt_AddRelativeInterrupt(cycles, INT_CPU_CYCLE, INTERRUPT_VIDEL_HALFLINE);
}
/**
* Start internal HBL Interrupt (25 or 32 Mhz)
*/
static void VIDEL_Start_Interrupt_HBL(void)
{
Uint32 hht_cycles = IoMem_ReadWord(0xff8282) + 2;
Uint32 hhs_cycles = IoMem_ReadWord(0xff828c);
Uint32 cycles = (hhs_cycles * videl.saveCycle) / hht_cycles;
// fprintf(stderr ,"VIDEL HBL_cycles : %d\n", cycles);
CycInt_AddRelativeInterrupt(cycles, INT_CPU_CYCLE, INTERRUPT_VIDEL_HBL);
}
void VIDEL_InterruptHandler_HBL(void)
{
/* Remove this interrupt from list and re-order */
CycInt_AcknowledgeInterrupt();
/* "normal" case, this HBL doesn't occur during the processing of a pending HBL */
M68000_Exception(EXCEPTION_HBLANK , M68000_EXC_SRC_AUTOVEC); /* Horizontal blank interrupt, level 2! */
}
void VIDEL_InterruptHandler_HalfLine(void)
{
Uint32 curr_HSS, curr_HHT, curr_VFC, curr_VFT, curr_VSS, curr_VBB, curr_VBE;
int PendingCyclesOver;
/* How many cycle was this HBL delayed (>= 0) */
PendingCyclesOver = -INT_CONVERT_FROM_INTERNAL ( PendingInterruptCount , INT_CPU_CYCLE );
/* Remove this interrupt from list and re-order */
CycInt_AcknowledgeInterrupt();
curr_HSS = IoMem_ReadWord(0xff828c);
curr_HHT = IoMem_ReadWord(0xff8282);
curr_VFC = IoMem_ReadWord(0xff820a) + 1;
curr_VFT = IoMem_ReadWord(0xff82a2);
curr_VSS = IoMem_ReadWord(0xff82ac);
curr_VBB = IoMem_ReadWord(0xff82a4);
curr_VBE = IoMem_ReadWord(0xff82a6);
IoMem_WriteWord(0xff820a, curr_VFC);
/* second half line starts, start a new Hsync timer */
if ((curr_VFC & 1) == 1) {
if (curr_HSS <= curr_HHT + 1)
VIDEL_Start_Interrupt_HBL();
}
else {
/* Timer B occurs at END of first visible screen line in Event Count mode */
if ( (curr_VFC > curr_VBE) && (curr_VFC < curr_VBB) ) {
/* Handle Timer B when using Event Count mode */
if (MFP_TBCR == 0x08) /* Is timer in Event Count mode ? */
MFP_TimerB_EventCount_Interrupt(); /* we have a valid timer B interrupt */
}
}
/* VSync reached ? */
if (curr_VFC == curr_VSS + 1)
VIDEL_Handler_VBL();
/* End of screen frame reached ? */
if (curr_VFC >= curr_VFT)
IoMem_WriteWord(0xff820a, 0);
/* Start the next half line timer interrupt */
VIDEL_Start_Interrupt_HalfLine();
}
static void VIDEL_Handler_VBL(void)
{
/* Clear any key presses which are due to be de-bounced (held for one ST frame) */
Keymap_DebounceAllKeys();
Videl_DrawScreen();
/* Check printer status */
Printer_CheckIdleStatus();
/* Update counter for number of screen refreshes per second */
videl.vbl_count ++;
/* Set video registers for frame */
Videl_ClearOnVBL();
/* Since we don't execute HBL functions in VDI mode, we've got to
* initialize the first HBL palette here when VDI mode is enabled. */
// if (bUseVDIRes)
//lolo Videl_StoreFirstLinePalette();
/* Act on shortcut keys */
ShortCut_ActKey();
/* Record video frame is necessary */
if ( bRecordingAvi )
Avi_RecordVideoStream ();
/* Store off PSG registers for YM file, is enabled */
YMFormat_UpdateRecording();
/* Generate 1/50th second of sound sample data, to be played by sound thread */
Sound_Update_VBL();
// LOG_TRACE(TRACE_VIDEO_VBL , "VBL %d video_cyc=%d pending_cyc=%d jitter=%d\n" ,
// nVBLs , Cycles_GetCounter(CYCLES_COUNTER_VIDEO) , PendingCyclesOver , VblJitterArray[ VblJitterIndex ] );
/* Vertical blank interrupt, level 4! */
M68000_Exception(EXCEPTION_VBLANK, M68000_EXC_SRC_AUTOVEC);
/* Host computer synchro */
Main_WaitOnVbl();
}
/**
* Called on each VBL, set registers ready for frame
*/
static void Videl_ClearOnVBL(void)
{
/* New screen, so VFC = 0 */
IoMem_WriteWord(0xff820a, 0);
/* Get screen address pointer */
videl.videoBaseAddr = VIDEL_getVideoramAddress();
}
/**
* Draw screen
*/
static void Videl_DrawScreen(void)
{
bool bScreenChanged;
/* Skip frame if need to */
if (videl.vbl_count % (nFrameSkips+1))
return;
/* Use extended VDI resolution? */
if (bUseVDIRes) {
/* If so, just copy whole screen on VBL rather than per HBL */
/* (don't care about being exact as for GEM only) */
//lolo memcpy(pSTScreen, pVideoRaster, ((VDIWidth*VDIPlanes)/8)*VDIHeight);
}
else {
/* Draw the screen with Videl */
bScreenChanged = VIDEL_renderScreen();
}
}
static void VIDEL_updateInternalValues(void)
{
int i;
Uint32 curr_VFC = IoMem_ReadWord(0xff820a) + 1;
videl.internal_regs[curr_VFC].video_base = VIDEL_getVideoramAddress();
videl.internal_regs[curr_VFC].video_count = 0; /* Todo */
videl.internal_regs[curr_VFC].sync_mode = IoMem_ReadByte(0xff820a);
videl.internal_regs[curr_VFC].offset_next_line = IoMem_ReadWord(0xff820e);
videl.internal_regs[curr_VFC].line_width = IoMem_ReadWord(0xff8210);
for (i=0; i<16; i++)
videl.internal_regs[curr_VFC].st_palette[i] = IoMem_ReadWord(0xff8240 + (i*2));
videl.internal_regs[curr_VFC].st_shift_mode = IoMem_ReadByte(0xff8260);
videl.internal_regs[curr_VFC].h_scroll = IoMem_ReadWord(0xff8264);
videl.internal_regs[curr_VFC].falcon_shift_mode = IoMem_ReadWord(0xff8266);
videl.internal_regs[curr_VFC].HHC = IoMem_ReadWord(0xff8280);
videl.internal_regs[curr_VFC].HHT = IoMem_ReadWord(0xff8282);
videl.internal_regs[curr_VFC].HBB = IoMem_ReadWord(0xff8284);
videl.internal_regs[curr_VFC].HBE = IoMem_ReadWord(0xff8286);
videl.internal_regs[curr_VFC].HDB = IoMem_ReadWord(0xff8288);
videl.internal_regs[curr_VFC].HDE = IoMem_ReadWord(0xff828a);
videl.internal_regs[curr_VFC].HSS = IoMem_ReadWord(0xff828c);
videl.internal_regs[curr_VFC].HFS = IoMem_ReadWord(0xff828e);
videl.internal_regs[curr_VFC].HEE = IoMem_ReadWord(0xff8290);
videl.internal_regs[curr_VFC].VFC = IoMem_ReadWord(0xff82a0);
videl.internal_regs[curr_VFC].VFT = IoMem_ReadWord(0xff82a2);
videl.internal_regs[curr_VFC].VBB = IoMem_ReadWord(0xff82a4);
videl.internal_regs[curr_VFC].VBE = IoMem_ReadWord(0xff82a6);
videl.internal_regs[curr_VFC].VDB = IoMem_ReadWord(0xff82a8);
videl.internal_regs[curr_VFC].VDE = IoMem_ReadWord(0xff82aa);
videl.internal_regs[curr_VFC].VSS = IoMem_ReadWord(0xff82ac);
videl.internal_regs[curr_VFC].VCO = IoMem_ReadWord(0xff82c0);
videl.internal_regs[curr_VFC].VMD = IoMem_ReadWord(0xff82c2);
for (i=0; i<256; i++)
videl.internal_regs[curr_VFC].falcon_palette[i] = IoMem_ReadWord(0xff9800 + (i*2));
}