// ###########################################################################
// #
// # OSCAVideo - Implementation of OSCA Video Module
// # Copyright (C) 2009-2011 Tony Friery [DialTone]
// #
// # ALL RIGHTS RESERVED
// #
// ###########################################################################

#pragma hdrstop

#include "OSCAVideo.h"
#include "FormDiag.h"
#include "FormMain.h"
#include "FormDebug.h"

//---------------------------------------------------------------------------

// Helper macro for use when testing ;)
#define RANDOMCOLOR Color32(Random(255), Random(255), Random(255), 0xff)

//---------------------------------------------------------------------------

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCAVideo::Tick
//
//  PURPOSE:	Method called periodically to enable synching the
//					device clock against the CPU clock
//
//  INPUTS:		unsigned int		Number of cycles elapsed since last
//											time this method was called
//
//  OUTPUT:		bool					IRQ Status following tick being
//											processed
//
///////////////////////////////////////////////////////////////////////
bool __fastcall TOSCAVideo::Tick(unsigned int CycleCount)
{
	// TODO: Rewrite the Video TICK method to make it more correct and cycle accurate
	int _startScan, _endScan;

	_startScan = this->fScanLine - 8;

	this->fScanPrescaler += CycleCount;

	while (this->fScanPrescaler >= 1536)
	{
		this->fScanPrescaler -= 1536;
		int ramAddr;

//#if 0
		// Check if raster beam is outside the active drawable region
		if ((this->fScanLine < this->fWindowTop) || (this->fScanLine > this->fWindowBottom))
		{
			// If raster beam is inside the visible part of the display
			if ((this->fScanLine > 7) && (this->fScanLine < 280))
			{
				// Vertical Border
				outputImage->HorzLine(0, this->fScanLine - 8, 431, this->fVideoPalette[0]);
			}
		}
		else
		{
			int borderEnd = this->fWindowLeft + (this->fWideBorder ? 16 : 0);

			if (fScanLine > 7)
			{
				// TODO: Change this so that it handles the raster beam across the line correctly
				// handle case when raster beam is inside the left edge of the visible display
				if (this->fWindowLeft > 63)
				{
					// Left Border
					outputImage->HorzLine(0, this->fScanLine - 8, borderEnd - 65, this->fVideoPalette[0]);
				}

				// handle case when raster beam is inside the right edge of the visible display
				if (this->fWindowRight < 496)
				{
					// Right Border
					outputImage->HorzLine(this->fWindowRight - 64, this->fScanLine - 8, 431, this->fVideoPalette[0]);
				}

				// handle case when raster beam is inside the drawable (active) region
				switch(this->CurrentVidMode)
				{
					case BITMAP_PLANAR:
						ramAddr = ((this->fScanLine - this->fWindowTop) * ((this->fWindowRight - this->fWindowLeft) / 8));
						for (int i = this->fWindowLeft; i < this->fWindowRight; i++)
						{
							if (i > 63)
							{
								int adjustedRAM = ramAddr + ((i - this->fWindowLeft) >> 3);
								int paletteValue = 0x00;

								if (!fBGVideoInhibit)
								{
									for (int j = 0, bit = 1; j <= this->fPlaneCount; j++, bit <<= 1)
									{
										paletteValue |= (this->VideoRAM[adjustedRAM + this->fPlaneBaseAddr[j]] & (1 << (7 - ((i - this->fWindowLeft) % 8)))) ? bit : 0;
									}
								}
								else
								{
									paletteValue = 0;
								}

								TColor32 tempCol = this->fVideoPalette[paletteValue];
		//						outputImage->SetPixelTS(i - 64, this->fScanLine - 8, tempCol);
								outputImage->Pixel[i - 64][this->fScanLine - 8] = tempCol;
							}
						}
						break;
					case BITMAP_CHUNKY:
						{
							ramAddr = ((this->fScanLine - this->fWindowTop) * (this->fWindowRight - this->fWindowLeft));
							int adjustedRAM = ramAddr + this->fPlaneBaseAddr[0];

							for (int i = this->fWindowLeft; i < this->fWindowRight; i++)
							{
								int paletteValue = (this->VideoRAM[adjustedRAM++]);

								if (fBGVideoInhibit)
								{
									paletteValue = 0;
								}

								TColor32 tempCol = this->fVideoPalette[paletteValue];

								// TODO: Use this image for rendering instead of drawing to the screen
	//							outputImage->SetPixelTS(i - 64, this->fScanLine - 8, tempCol);
								outputImage->Pixel[i - 64][this->fScanLine - 8] = tempCol;
							}
						}
						break;
					case TILE_MAP:
						ramAddr = 0;

						for (int i = this->fWindowLeft; i < this->fWindowRight; i++)
						{
	//						MainForm->Screen->Bitmap->SetPixelTS(i - 64, this->fScanLine - 8, RANDOMCOLOR);
							outputImage->SetPixelTS(i - 64, this->fScanLine - 8, RANDOMCOLOR);
						}
						break;
				}
			}
		}
//#endif
		this->fScanLine++;
		_endScan = this->fScanLine - 8;

//		outputImage->DrawTo( MainForm->Screen->Bitmap, 0, 0);
//		Gr32::TRect updateRect = MakeRect(0, _startScan, 432, _endScan);
//		if ((_startScan >= 0) && (_endScan >= 0) && (_startScan < 272) && (_endScan < 272))
//			outputImage->DrawTo(MainForm->Screen->Bitmap, updateRect, updateRect);

		if (this->fScanLine == 312)
		{
			this->fScanLine = 0;
			//outputImage->DrawTo( MainForm->Screen->Bitmap, 0, 0);
			MainForm->Screen->Bitmap->Draw(0, 0, outputImage);
			//MainForm->Screen->Refresh();
			frames++;
		}

		// Check if raster IRQ needs to be triggered
		if (this->fScanLine == this->fRasterIRQTarget)
		{
			fRasterIRQ = true;
		}
	}

	return this->fRasterIRQ;
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCAVideo::WritePalette
//
//  PURPOSE:	Write to one of the palette entries in the table
//
//  INPUTS:		word					Address within palette to write
//					byte					Value to be written
//
//  OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCAVideo::WritePalette(word Address, byte Value)
{
	// Write new value to the palette storage
	this->PaletteRAM[Address & 0x1ff] = Value;

	// Extract new RGB value
	int g = (PaletteRAM[Address & 0x1fe] & 0xf0);
	int b = (PaletteRAM[Address & 0x1fe] & 0x0f) << 4;
	int r = (PaletteRAM[(Address & 0x1fe) | 1] & 0x0f) << 4;

	// Update actual palette used for UI rendering
	this->fVideoPalette[(Address >> 1)] = Color32(r, g, b, 0xff);

	if ((DiagForm) && (DiagForm->IsActive(DIAG_VIDEO)))
	{
		DiagForm->AddMessage(DIAG_VIDEO, UnicodeString::Format(
			L"Palette entry %03x set to %02x",
			OPENARRAY(TVarRec, ((Address & 0x1ff), Value))
		));
	}
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCAVideo::WriteBlitter
//
//  PURPOSE:	Write to one of the blitter registers
//
//  INPUTS:		word					Address of the blitter register to write
//					byte					Value to be written
//
//  OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCAVideo::WriteBlitter(byte Address, byte Value)
{
	switch (Address & 0x0f)
	{
		case 0x00:	// blit_src_loc (LSB)
			this->fBlitSrcLoc &= 0xff00;
			this->fBlitSrcLoc |= Value;
			break;
		case 0x01:	// blit_src_loc (MSB)
			this->fBlitSrcLoc &= 0xff;
			this->fBlitSrcLoc |= (Value << 8);
			break;
		case 0x02:	// blit_dst_loc (LSB)
			this->fBlitDestLoc &= 0xff00;
			this->fBlitDestLoc |= Value;
			break;
		case 0x03:	// blit_dst_loc (MSB)
			this->fBlitDestLoc &= 0xff;
			this->fBlitDestLoc |= (Value << 8);
			break;
		case 0x04:	// blit_src_mod
			this->fBlitSrcModulo &= 0x300;
			this->fBlitSrcModulo |= Value;
			break;
		case 0x05:	// blit_dst_mod
			this->fBlitDestModulo &= 0x300;
			this->fBlitDestModulo |= Value;
			break;
		case 0x06:	// blit_height
			this->fBlitHeight = Value;
			break;
		case 0x07:
			this->fBlitWidth = Value;

			if ((DiagForm) && (DiagForm->IsActive(DIAG_VIDEO)))
			{
				DiagForm->AddMessage(DIAG_VIDEO, UnicodeString::Format(
					L"Begin Blit from %06x to %06x",
					OPENARRAY(TVarRec, (
						((this->fBlitSrcMSB << 16) | ((this->fBlitMisc & 0x10) << 12)) + this->fBlitSrcLoc,
						((this->fBlitDestMSB << 16) | ((this->fBlitMisc & 0x20) << 11)) + this->fBlitDestLoc)
					)
				));

				DiagForm->AddMessage(DIAG_VIDEO, UnicodeString::Format(
					L"Blit W: %d, H: %d, Mod (s/d): %03x, %03x",
					OPENARRAY(TVarRec, (this->fBlitWidth, this->fBlitHeight,
					this->fBlitSrcModulo, this->fBlitDestModulo))
				));
			}

			// TODO: Move this blittery code to within the TICK method so it happens cycle accurately
			if (this->fBlitMisc & 0x40)
			{
				// Ascending Blit
				unsigned int blitFrom = ((this->fBlitSrcMSB << 16) | ((this->fBlitMisc & 0x10) << 12)) + this->fBlitSrcLoc;
				unsigned int blitTo = ((this->fBlitDestMSB << 16) | ((this->fBlitMisc & 0x20) << 11)) + this->fBlitDestLoc;

				for (int blitLines = 0; blitLines <= this->fBlitHeight; blitLines++)
				{

					for (int blitCols = 0; blitCols <= this->fBlitWidth; blitCols++)
					{
						if (((this->fBlitMisc & 0x80) == 0x00) || (this->VideoRAM[blitFrom] != 0x00))
						{
							this->VideoRAM[blitTo] = this->VideoRAM[blitFrom];
						}

						blitFrom++;
						blitTo++;
					}

					blitFrom += this->fBlitSrcModulo;
					blitTo += this->fBlitDestModulo;
				}
			}
			else
			{
				// Descending Blit
				if ((DiagForm) && (DiagForm->IsActive(DIAG_VIDEO)))
				{
					DiagForm->AddMessage(DIAG_VIDEO, L"!!! Descending Blit Unsupported");
				}
			}
			break;
		case 0x08:	// blit_misc
			this->fBlitMisc = Value;

			// Source Modulo Additional Bits
			this->fBlitSrcModulo &= 0xff;
			this->fBlitSrcModulo |= (this->fBlitMisc & 0x03) << 8;
			this->fBlitSrcModulo &= 0x3ff;
			if (this->fBlitSrcModulo & 0x200)
			{
				// Sign-Extend Source Modulo Value
				this->fBlitSrcModulo |= 0xfc00;
			}

			// Destination Modulo Additional Bits
			this->fBlitDestModulo &= 0xff;
			this->fBlitDestModulo |= (this->fBlitMisc & 0x0c) << 6;
			this->fBlitDestModulo &= 0x3ff;
			if (this->fBlitDestModulo & 0x200)
			{
				// Sign-Extend Destination Modulo Value
				this->fBlitDestModulo |= 0xfc00;
			}
			break;
		case 0x09:	// blit_src_msb
			this->fBlitSrcMSB = (Value & 0x07);
			break;
		case 0x0a:	// blit_dst_msb
			this->fBlitDestMSB = (Value & 0x07);
			break;
	}
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCAVideo::WriteRegister
//
//  PURPOSE:	Write to one of the video registers
//
//  INPUTS:		word					Address of the register to be written
//					byte					Value to be written
//
//  OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCAVideo::WriteRegister(word Address, byte Value)
{
	switch(Address & 0x70)
	{
		case 0x00:
			// Main Video Regs
			switch (Address & 0x0f)
			{
				case 0x00:	// vreg_xhws
					this->fXHWS = Value;
					break;
				case 0x01:	// vreg_vidctrl
					// ??? means that the behaviour varies depending on video mode
					// ??? | ??? | ??? | ??? | ??? | BG_VID_INHIBIT | WIDE_LEFT_BORDER | TILE_MAP_EN
					this->CurrentVidMode = (Value & 0x01) ? TILE_MAP :
						(Value & 0x80) ? BITMAP_CHUNKY : BITMAP_PLANAR;
					this->fWideBorder = (Value & 0x02) ? true : false;
					this->fBGVideoInhibit = (Value & 0x04) ? true : false;

					switch (this->CurrentVidMode)
					{
						case TILE_MAP:
						case BITMAP_CHUNKY:
							this->fBitmapFloodMode = (Value & 0x40) ? true : false;
							this->fPlaneSet = (Value & 0x20) ? true : false;
							break;
						case BITMAP_PLANAR:
							this->fPlaneSet = (Value & 0x20) ? true : false;
							break;
					}
					break;
				case 0x02:	// vreg_window
					if (this->fWindowXYSwitch)
					{
						// X Positions
						this->fWindowLeft = (Value & 0xf0);
						this->fWindowRight = 256 + ((Value & 0x0f) << 4);

						if ((DiagForm) && (DiagForm->IsActive(DIAG_VIDEO)))
						{
							DiagForm->AddMessage(DIAG_VIDEO, UnicodeString::Format(
								L"Display Width from %d to %d",
								OPENARRAY(TVarRec, (this->fWindowLeft, this->fWindowRight))
							));
						}
					}
					else
					{
						// Y Positions
						this->fWindowTop = ((Value & 0xf0) >> 1);
						this->fWindowBottom = 160 + ((Value & 0x0f) << 3);

						if ((DiagForm) && (DiagForm->IsActive(DIAG_VIDEO)))
						{
							DiagForm->AddMessage(DIAG_VIDEO, UnicodeString::Format(
								L"Display Height from %d to %d",
								OPENARRAY(TVarRec, (this->fWindowTop - 16, this->fWindowBottom - 16))
							));
						}
					}
					break;
				case 0x03:	// vreg_yhws_bplcount
					// TODO: Implement Hardware Scrolling

					if (this->CurrentVidMode == BITMAP_PLANAR)
					{
						this->fPlaneCount = Value & 0x07;
					}
					else if (this->CurrentVidMode == TILE_MAP)
					{
						// TODO: Implement TILEMAP mode
					}
					break;
				case 0x04:	// vreg_rasthi
					if (Value & 0x80)
					{
						if (!this->fRasterIRQ)
						{
							this->ClearIRQ();

							if ((DiagForm) && (DiagForm->IsActive(DIAG_VIDEO)))
							{
								DiagForm->AddMessage(DIAG_VIDEO, L"Raster IRQ Cleared");
							}
						}
					}
					else
					{
						this->fWindowXYSwitch = ((Value & 0x04) > 0);
						this->fRasterIRQTarget &= 0xff;
						this->fRasterIRQTarget |= ((Value & 0x03) << 8);

						if ((DiagForm) && (DiagForm->IsActive(DIAG_VIDEO)))
						{
							DiagForm->AddMessage(DIAG_VIDEO, UnicodeString::Format(
								L"Raster IRQ Target Set to %03x",
								OPENARRAY(TVarRec, (this->fRasterIRQTarget))
							));
						}
					}
					break;
				case 0x05:	// vreg_rastlo
					this->fRasterIRQTarget &= 0x300;
					this->fRasterIRQTarget |= Value;

					if ((DiagForm) && (DiagForm->IsActive(DIAG_VIDEO)))
					{
						DiagForm->AddMessage(DIAG_VIDEO, UnicodeString::Format(
							L"Raster IRQ Target Set to %03x",
							OPENARRAY(TVarRec, (this->fRasterIRQTarget))
						));
					}
					break;
				case 0x06:	// vreg_vidpage
					if (Value & 0x80)
					{
						// Sprite Page
						this->fSpritePage = (Value & 0x1f);

						if ((DiagForm) && (DiagForm->IsActive(DIAG_VIDEO)))
						{
							DiagForm->AddMessage(DIAG_VIDEO, UnicodeString::Format(
								L"Sprite Page set to %02x",
								OPENARRAY(TVarRec, (this->fSpritePage))
							));
						}
					}
					else
					{
						// Video Page
						this->fVideoPage = (Value & 0x3f);

						if ((DiagForm) && (DiagForm->IsActive(DIAG_VIDEO)))
						{
							DiagForm->AddMessage(DIAG_VIDEO, UnicodeString::Format(
								L"Video Page set to %02x",
								OPENARRAY(TVarRec, (this->fVideoPage))
							));
						}
					}
					break;
				case 0x0b:	// linedraw_colour
					this->fLineColour = Value;

					if ((DiagForm) && (DiagForm->IsActive(DIAG_VIDEO)))
					{
						DiagForm->AddMessage(DIAG_VIDEO, UnicodeString::Format(
							L"Linedraw Colour Index set to %02x",
							OPENARRAY(TVarRec, (this->fLineColour))
						));
					}
					break;
				default:
					if ((DiagForm) && (DiagForm->IsActive(DIAG_VIDEO)))
					{
						DiagForm->AddMessage(DIAG_VIDEO, UnicodeString::Format(
							L"Register %03x set to %02x",
							OPENARRAY(TVarRec, (Address, Value))
						));
					}
					break;
			}
			break;
		case 0x10:
			// Blitter
			if ((Address & 0x0f) < 0x0b)
			{
				this->WriteBlitter(Address & 0x0f, Value);
			}
			else
			{
				if ((DiagForm) && (DiagForm->IsActive(DIAG_VIDEO)))
				{
					DiagForm->AddMessage(DIAG_VIDEO, UnicodeString::Format(
						L"Unknown Blitter Register %03x set to %02x",
						OPENARRAY(TVarRec, (Address, Value))
					));
				}
			}
			break;
		case 0x20:
		case 0x30:
			// Line Draw
			// TODO: Implement Line Drawing Hardware
			if ((DiagForm) && (DiagForm->IsActive(DIAG_VIDEO)))
			{
				DiagForm->AddMessage(DIAG_VIDEO, UnicodeString::Format(
					L"(Line-Draw) Register %03x set to %02x",
					OPENARRAY(TVarRec, (Address, Value))
				));
			}
			break;
		default:
			// Bitplane Locations
			switch (Address & 0x03)
			{
				case 0x00:
					this->fPlaneBaseAddr[(Address & 0x3c) >> 2] &= 0x07ffff00;
					this->fPlaneBaseAddr[(Address & 0x3c) >> 2] |= Value;
					break;
				case 0x01:
					this->fPlaneBaseAddr[(Address & 0x3c) >> 2] &= 0x07ff00ff;
					this->fPlaneBaseAddr[(Address & 0x3c) >> 2] |= Value << 8;
					break;
				case 0x02:
					this->fPlaneBaseAddr[(Address & 0x3c) >> 2] &= 0x0700ffff;
					this->fPlaneBaseAddr[(Address & 0x3c) >> 2] |= Value << 16;
					break;
				case 0x03:
					this->fPlaneBaseAddr[(Address & 0x3c) >> 2] &= 0x00ffffff;
					this->fPlaneBaseAddr[(Address & 0x3c) >> 2] |= (Value & 0x07) << 24;
					break;
			}

			if ((DiagForm) && (DiagForm->IsActive(DIAG_VIDEO)))
			{
				DiagForm->AddMessage(DIAG_VIDEO, UnicodeString::Format(
					L"Plane Address %d set to %08x",
					OPENARRAY(TVarRec, (((Address & 0x3c) >> 2),
					this->fPlaneBaseAddr[(Address & 0x3c) >> 2]))
				));
			}
			break;
	}
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCAVideo::ReadVREG
//
//  PURPOSE:	Read the value of the VREG status register
//
//  INPUTS:		void
//
//  OUTPUT:		byte					Value of VREG register
//
///////////////////////////////////////////////////////////////////////
byte __fastcall TOSCAVideo::ReadVREG(void)
{
	byte retv = 0x00;

	retv |= 0x00;																					// Long Interlaced Field
	retv |= (this->fScanLine & 0x01) << 6;													// LSB of Scanline
	retv |= ((this->CurrentVidSystem == VIDEO_PAL_I)   								//	60Hz when set
		|| (this->CurrentVidSystem == VIDEO_PAL_P)) ? 0 : 0x20;
	retv |= 0x00; 																					// Blitter Busy
	retv |= this->GetRasterIRQ() ? 0x08 : 0x00;											// Raster IRQ
	retv |= ((this->fScanLine < this->fWindowTop) ||
		(this->fScanLine > this->fWindowBottom)) ? 0 : 0x04;							// Y Window (1 = Display)
	retv |= ((this->fScanPrescaler < (512 + this->fWindowLeft)) || 				// X Window (1 = Display)
		(this->fScanPrescaler > (512 + this->fWindowRight))) ? 0 : 0x02;
	retv |= ((this->fScanLine > 396) || (this->fScanLine < 8)) ? 0x01 : 0x00;	// Last Line VRT

	return retv;
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCAVideo::ReadMappedVRAM
//
//  PURPOSE:	Read from Video RAM (mapped as an 8k block)
//
//  INPUTS:		word					Address within Video RAM to read
//
//  OUTPUT:		byte					Value read from Video RAM
//
///////////////////////////////////////////////////////////////////////
byte __fastcall TOSCAVideo::ReadMappedVRAM(word Address)
{
	return this->VideoRAM[(Address & 0x1fff) + (this->fVideoPage * 8 * 1024)];
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCAVideo::ReadSpriteRAM
//
//  PURPOSE:	Read from Sprite Memory
//
//  INPUTS:		word					Address within Sprite RAM to read
//
//  OUTPUT:		byte					Byte read from Sprite Memory
//
///////////////////////////////////////////////////////////////////////
byte __fastcall TOSCAVideo::ReadSpriteRAM(word Address)
{
	return this->SpriteRAM[(Address & 0xfff) + (this->fSpritePage * 4 * 1024)];
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCAVideo::WriteDirectVRAM
//
//  PURPOSE:	Write to Video RAM (paged in as a full 64k block)
//
//  INPUTS:		word					Address within Video RAM to write
//					byte					Value to be written
//
//  OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCAVideo::WriteDirectVRAM(word Address, byte Value)
{

	this->VideoRAM[Address + ((this->fVideoPage & 0x38) << 13)] = Value;
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCAVideo::WriteMappedVRAM
//
//  PURPOSE:	Write to Video RAM (paged in as an 8K block)
//
//  INPUTS:		word					Address within Video RAM to write
//					byte					Value to be written
//
//  OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCAVideo::WriteMappedVRAM(word Address, byte Value)
{
	this->VideoRAM[(Address & 0x1fff) + (this->fVideoPage * 8 * 1024)] = Value;
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCAVideo::WriteSpriteRAM
//
//  PURPOSE:	Write to Sprite Memory
//
//  INPUTS:		word					Address within Sprite RAM to write
//					byte					Value to be written
//
//  OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCAVideo::WriteSpriteRAM(word Address, byte Value)
{
	this->SpriteRAM[(Address & 0xfff) + (this->fSpritePage * 4 * 1024)] = Value;
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCAVideo::GetRasterIRQ
//
//  PURPOSE:	Retrieve the current value of the Raster IRQ Flag
//
//  INPUTS:		void
//
//  OUTPUT:		bool					IRQ Flag Status (true = Flag set,
//											ie. Raster IRQ has Occurred)
//
///////////////////////////////////////////////////////////////////////
bool __fastcall TOSCAVideo::GetRasterIRQ(void)
{
	return this->fRasterIRQ;
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCAVideo::ClearIRQ
//
//  PURPOSE:	Clear down the Raster IRQ Flag
//
//  INPUTS:		void
//
//  OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCAVideo::ClearIRQ(void)
{
	if ((DiagForm) && (DiagForm->IsActive(DIAG_VIDEO)))
	{
		DiagForm->AddMessage(DIAG_VIDEO, L"Raster IRQ Cleared");
	}

	this->fRasterIRQ = false;
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TOSCAVideo::Reset
//
//  PURPOSE:	Clear down the Raster IRQ Flag
//
//  INPUTS:		void
//
//  OUTPUT:		void
//
///////////////////////////////////////////////////////////////////////
void __fastcall TOSCAVideo::Reset(void)
{
	memset(&this->VideoRAM, 0, (512 * 1024));		// Clear Video RAM
	memset(&this->SpriteRAM, 0, (128 * 1024));	// Clear Sprite RAM
	memset(&this->PaletteRAM, 0, 512);				// Clear Palette RAM
	this->fScanPrescaler 	= 0;
	this->fScanLine 			= 0;
	this->fRasterIRQ			= false;
	this->fWindowXYSwitch	= false;
	this->fRasterIRQTarget	= 0x0000;
	this->fWindowTop			= 0;						// Actually -16
	this->fWindowBottom		= 160;					// Actually 144
	this->fWindowLeft			= 0;
	this->fWindowRight		= 256;
	this->fVideoPage			= 0;
	this->fSpritePage			= 0;
	this->CurrentVidSystem	= VIDEO_VGA;
	this->CurrentVidMode		= BITMAP_PLANAR;
	this->fPlaneCount			= 0;
	this->fBlitDestMSB		= 0;
	this->fBlitSrcMSB			= 0;
	this->fBlitWidth			= 0;
	this->fBlitHeight			= 0;
	this->fBlitMisc			= 0;
	this->fBlitSrcModulo		= 0;
	this->fBlitDestModulo	= 0;
	this->fBlitSrcLoc			= 0;
	this->fBlitDestLoc		= 0;
	this->fBGVideoInhibit	= false;					// Enable Video by default
	this->fWideBorder			= false;
	this->fBitmapFloodMode	= false;

	this->fXHWS					= 0x00;

	for (int i = 0; i < 16; i++)
	{
		this->fPlaneBaseAddr[i] = 0x000000;
	}

	if (this->outputImage == NULL)
	{
		this->outputImage = new TBitmap32();
	}

	this->outputImage->SetSize(432, 272);
	this->outputImage->Clear(clBlack32);
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:  TOSCAVideo::TOSCAVideo [CONSTRUCTOR]
//
//  PURPOSE:	Constructor for the Video Module Class
//
//  INPUTS:		none
//
//  OUTPUT:		none
//
///////////////////////////////////////////////////////////////////////
__fastcall TOSCAVideo::TOSCAVideo() :
	outputImage(NULL)
{
	// Perform Initial Power-On-Reset
	this->Reset();
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:  TOSCAVideo::~TOSCAVideo [DESTRUCTOR]
//
//  PURPOSE:	Destructor for the Video Module Class
//
//  INPUTS:		none
//
//  OUTPUT:		none
//
///////////////////////////////////////////////////////////////////////
__fastcall TOSCAVideo::~TOSCAVideo()
{
	if (this->outputImage)
	{
		delete this->outputImage;
		this->outputImage = NULL;
	}
}

//---------------------------------------------------------------------------

#pragma package(smart_init)

