Preliminary VBL support sourced from AppleWin

This commit is contained in:
Aaron Culliney 2015-01-10 09:53:38 -08:00
parent a7cd281037
commit 381d97c485
6 changed files with 186 additions and 66 deletions

View File

@ -1693,24 +1693,7 @@ void MB_Reset()
//-----------------------------------------------------------------------------
#ifdef APPLE2IX
// TODO FIXME!!!
// From AppleWin ... We don't do anything clever (yet) with video emulation ...
// References to Jim Sather's books are given as eg:
// UTAIIe:5-7,P3 (Understanding the Apple IIe, chapter 5, page 7, Paragraph 3)
/*
WORD VideoGetScannerAddress(bool* pbVblBar_OUT, const DWORD uExecutedCycles)
{
...;
}
*/
static uint8_t MemReadFloatingBus(const unsigned long uExecutedCycles)
{
//return*(LPBYTE)(mem + VideoGetScannerAddress(NULL, uExecutedCycles));
// HUGE HACK FIXME TODO
return c_read_rand(0x0);
}
#define MemReadFloatingBus floating_bus
#define nCyclesLeft cpu65_cycle_count
#define nAddr ea
GLUE_C_READ(MB_Read)

View File

@ -464,23 +464,6 @@ static bool save_track_data(void) {
return true;
}
static uint8_t disk_io_pseudo_random(uint8_t hibit) {
// AppleWin algorithm ... unsure of the source or whether it fixes issues with particular images
static const uint8_t ret[16] = {
0x00, 0x2D, 0x2D, 0x30, 0x30, 0x32, 0x32, 0x34,
0x35, 0x39, 0x43, 0x43, 0x43, 0x60, 0x7F, 0x7F
};
if (hibit) {
hibit = 0x80;
}
uint8_t r = c_read_rand(/*ignored*/0x0);
if (r <= 0xAA) {
return 0x20 | hibit;
} else {
return ret[r&0x0f] | hibit;
}
}
// ----------------------------------------------------------------------------
// Emulator hooks
@ -622,31 +605,31 @@ GLUE_C_READ(disk_read_phase)
#endif
}
return ea == 0xE0 ? 0xFF : disk_io_pseudo_random(1);
return ea == 0xE0 ? 0xFF : floating_bus_hibit(1, cpu65_cycle_count);
}
GLUE_C_READ(disk_read_motor_off)
{
disk6.motor = 1;
return disk_io_pseudo_random(1);
return floating_bus_hibit(1, cpu65_cycle_count);
}
GLUE_C_READ(disk_read_motor_on)
{
disk6.motor = 0;
return disk_io_pseudo_random(1);
return floating_bus_hibit(1, cpu65_cycle_count);
}
GLUE_C_READ(disk_read_select_a)
{
disk6.drive = 0;
return 0;
return floating_bus(cpu65_cycle_count);
}
GLUE_C_READ(disk_read_select_b)
{
disk6.drive = 1;
return 0;
return floating_bus(cpu65_cycle_count);
}
GLUE_C_READ(disk_read_latch)
@ -657,18 +640,19 @@ GLUE_C_READ(disk_read_latch)
GLUE_C_READ(disk_read_prepare_in)
{
disk6.ddrw = 0;
return disk_io_pseudo_random(disk6.disk[disk6.drive].is_protected);
return floating_bus_hibit(disk6.disk[disk6.drive].is_protected, cpu65_cycle_count);
}
GLUE_C_READ(disk_read_prepare_out)
{
disk6.ddrw = 1;
return disk_io_pseudo_random(1);
return floating_bus_hibit(1, cpu65_cycle_count);
}
GLUE_C_WRITE(disk_write_latch)
{
disk6.disk_byte = b;
//return b;
}
// ----------------------------------------------------------------------------

View File

@ -53,6 +53,25 @@ static uint8_t video__int_font[3][0x4000];
int video__current_page; // current visual page
int video__strictcolors = 1;// refactor : should be static
// Video constants -- sourced from AppleWin
static const bool bVideoScannerNTSC = true;
static const int kHBurstClock = 53; // clock when Color Burst starts
static const int kHBurstClocks = 4; // clocks per Color Burst duration
static const int kHClock0State = 0x18; // H[543210] = 011000
static const int kHClocks = 65; // clocks per horizontal scan (including HBL)
static const int kHPEClock = 40; // clock when HPE (horizontal preset enable) goes low
static const int kHPresetClock = 41; // clock when H state presets
static const int kHSyncClock = 49; // clock when HSync starts
static const int kHSyncClocks = 4; // clocks per HSync duration
static const int kNTSCScanLines = 262; // total scan lines including VBL (NTSC)
static const int kNTSCVSyncLine = 224; // line when VSync starts (NTSC)
static const int kPALScanLines = 312; // total scan lines including VBL (PAL)
static const int kPALVSyncLine = 264; // line when VSync starts (PAL)
static const int kVLine0State = 0x100; // V[543210CBA] = 100000000
static const int kVPresetLine = 256; // line when V state presets
static const int kVSyncLines = 4; // lines per VSync duration
void video_loadfont(int first, int quantity, const uint8_t *data, int mode) {
uint8_t fg = 0;
uint8_t bg = 0;
@ -1115,3 +1134,124 @@ void video_redraw(void) {
softswitches = softswitches_save;
}
// References to Jim Sather's books are given as eg:
// UTAIIe:5-7,P3 (Understanding the Apple IIe, chapter 5, page 7, Paragraph 3)
extern unsigned int CpuGetCyclesThisVideoFrame(const unsigned int nExecutedCycles);
uint16_t video_scanner_get_address(bool *vblBarOut, const unsigned int executedCycles) {
const bool SW_HIRES = (softswitches & SS_HIRES);
const bool SW_TEXT = (softswitches & SS_TEXT);
const bool SW_PAGE2 = (softswitches & SS_PAGE2);
const bool SW_80STORE = (softswitches & SS_80STORE);
const bool SW_MIXED = (softswitches & SS_MIXED);
// get video scanner position
unsigned int nCycles = CpuGetCyclesThisVideoFrame(executedCycles);
// machine state switches
int nHires = (SW_HIRES && !SW_TEXT) ? 1 : 0;
int nPage2 = SW_PAGE2 ? 1 : 0;
int n80Store = SW_80STORE ? 1 : 0;
// calculate video parameters according to display standard
int nScanLines = bVideoScannerNTSC ? kNTSCScanLines : kPALScanLines;
int nVSyncLine = bVideoScannerNTSC ? kNTSCVSyncLine : kPALVSyncLine;
int nScanCycles = nScanLines * kHClocks;
// calculate horizontal scanning state
int nHClock = (nCycles + kHPEClock) % kHClocks; // which horizontal scanning clock
int nHState = kHClock0State + nHClock; // H state bits
if (nHClock >= kHPresetClock) // check for horizontal preset
{
nHState -= 1; // correct for state preset (two 0 states)
}
int h_0 = (nHState >> 0) & 1; // get horizontal state bits
int h_1 = (nHState >> 1) & 1;
int h_2 = (nHState >> 2) & 1;
int h_3 = (nHState >> 3) & 1;
int h_4 = (nHState >> 4) & 1;
int h_5 = (nHState >> 5) & 1;
// calculate vertical scanning state
int nVLine = nCycles / kHClocks; // which vertical scanning line
int nVState = kVLine0State + nVLine; // V state bits
if ((nVLine >= kVPresetLine)) // check for previous vertical state preset
{
nVState -= nScanLines; // compensate for preset
}
int v_A = (nVState >> 0) & 1; // get vertical state bits
int v_B = (nVState >> 1) & 1;
int v_C = (nVState >> 2) & 1;
int v_0 = (nVState >> 3) & 1;
int v_1 = (nVState >> 4) & 1;
int v_2 = (nVState >> 5) & 1;
int v_3 = (nVState >> 6) & 1;
int v_4 = (nVState >> 7) & 1;
int v_5 = (nVState >> 8) & 1;
// calculate scanning memory address
if (nHires && SW_MIXED && v_4 && v_2) // HIRES TIME signal (UTAIIe:5-7,P3)
{
nHires = 0; // address is in text memory for mixed hires
}
int nAddend0 = 0x0D; // 1 1 0 1
int nAddend1 = (h_5 << 2) | (h_4 << 1) | (h_3 << 0);
int nAddend2 = (v_4 << 3) | (v_3 << 2) | (v_4 << 1) | (v_3 << 0);
int nSum = (nAddend0 + nAddend1 + nAddend2) & 0x0F; // SUM (UTAIIe:5-9)
unsigned int nAddress = 0; // build address from video scanner equations (UTAIIe:5-8,T5.1)
nAddress |= h_0 << 0; // a0
nAddress |= h_1 << 1; // a1
nAddress |= h_2 << 2; // a2
nAddress |= nSum << 3; // a3 - a6
nAddress |= v_0 << 7; // a7
nAddress |= v_1 << 8; // a8
nAddress |= v_2 << 9; // a9
int p2a = !(nPage2 && !n80Store);
int p2b = nPage2 && !n80Store;
if (nHires) // hires?
{
// Y: insert hires-only address bits
nAddress |= v_A << 10; // a10
nAddress |= v_B << 11; // a11
nAddress |= v_C << 12; // a12
nAddress |= p2a << 13; // a13
nAddress |= p2b << 14; // a14
}
else
{
// N: insert text-only address bits
nAddress |= p2a << 10; // a10
nAddress |= p2b << 11; // a11
// Apple ][ (not //e) and HBL?
if (false/*IS_APPLE2*/ && // Apple II only (UTAIIe:I-4,#5)
!h_5 && (!h_4 || !h_3)) // HBL (UTAIIe:8-10,F8.5)
{
nAddress |= 1 << 12; // Y: a12 (add $1000 to address!)
}
}
// update VBL' state
if (vblBarOut != NULL)
{
*vblBarOut = !v_4 || !v_3; // VBL' = (v_4 & v_3)' (UTAIIe:5-10,#3)
}
return (uint16_t)nAddress;
}
uint8_t floating_bus(const unsigned int executedCycles) {
uint16_t scanner_addr = video_scanner_get_address(NULL, executedCycles);
return apple_ii_64k[0][scanner_addr];
}
uint8_t floating_bus_hibit(const bool hibit, const unsigned int executedCycles) {
uint16_t scanner_addr = video_scanner_get_address(NULL, executedCycles);
uint8_t b = apple_ii_64k[0][scanner_addr];
return (b & ~0x80) | (hibit ? 0x80 : 0);
}

View File

@ -23,10 +23,16 @@
#define EXECUTION_PERIOD_NSECS 1000000 // AppleWin: nExecutionPeriodUsec
const unsigned int uCyclesPerLine = 65; // 25 cycles of HBL & 40 cycles of HBL'
const unsigned int uVisibleLinesPerFrame = 64*3; // 192
const unsigned int uLinesPerFrame = 262; // 64 in each third of the screen & 70 in VBL
const unsigned int dwClksPerFrame = uCyclesPerLine * uLinesPerFrame; // 17030
double g_fCurrentCLK6502 = CLK_6502;
bool g_bFullSpeed = false; // HACK TODO FIXME : prolly shouldn't be global anymore -- don't think it's necessary for speaker/soundcore/etc anymore ...
uint64_t g_nCumulativeCycles = 0; // cumulative cycles since emulator (re)start
int g_nCpuCyclesFeedback = 0;
static unsigned int g_dwCyclesThisFrame = 0;
static bool alt_speed_enabled = false;
double cpu_scale_factor = 1.0;
@ -253,23 +259,30 @@ void *cpu_thread(void *dummyptr) {
dbg_cycles_executed += cpu65_cycle_count;
#endif
#ifdef AUDIO_ENABLED
unsigned int uExecutedCycles = cpu65_cycle_count;
unsigned int uActualCyclesExecuted = cpu65_cycle_count;
g_dwCyclesThisFrame += uActualCyclesExecuted;
MB_UpdateCycles(uExecutedCycles); // Update 6522s (NB. Do this before updating g_nCumulativeCycles below)
MB_UpdateCycles(uActualCyclesExecuted); // Update 6522s (NB. Do this before updating g_nCumulativeCycles below)
// N.B.: IO calls that depend on accurate timing will update g_nCyclesExecuted
const unsigned int nRemainingCycles = uExecutedCycles - g_nCyclesExecuted;
const unsigned int nRemainingCycles = uActualCyclesExecuted - g_nCyclesExecuted;
g_nCumulativeCycles += nRemainingCycles;
if (!g_bFullSpeed)
{
SpkrUpdate(uExecutedCycles); // play audio
SpkrUpdate(uActualCyclesExecuted); // play audio
}
// N.B.: technically this is not the end of the video frame...
MB_EndOfVideoFrame();
#endif
if (g_dwCyclesThisFrame >= dwClksPerFrame)
{
g_dwCyclesThisFrame -= dwClksPerFrame;
//VideoEndOfVideoFrame();
#ifdef AUDIO_ENABLED
MB_EndOfVideoFrame();
#endif
}
clock_gettime(CLOCK_MONOTONIC, &tj);
pthread_mutex_unlock(&interface_mutex);
// -UNLOCK--------------------------------------------------------------------------------------- SAMPLE tj
@ -330,9 +343,14 @@ void *cpu_thread(void *dummyptr) {
}
// From AppleWin...
unsigned int CpuGetCyclesThisVideoFrame(const unsigned int nExecutedCycles) {
CpuCalcCycles(nExecutedCycles);
return g_dwCyclesThisFrame + g_nCyclesExecuted;
}
// Called when an IO-reg is accessed & accurate cycle info is needed
void CpuCalcCycles(const unsigned long nExecutedCycles)
{
void CpuCalcCycles(const unsigned long nExecutedCycles) {
// Calc # of cycles executed since this func was last called
const long nCycles = nExecutedCycles - g_nCyclesExecuted;
assert(nCycles >= 0);

View File

@ -116,6 +116,13 @@ void video_plotchar(int row, int col, int color, uint8_t code);
*/
const uint8_t * const video_current_framebuffer();
/*
* VBL routines
*/
uint16_t video_scanner_get_address(bool *vblBarOut, const unsigned int executedCycles);
uint8_t floating_bus(const unsigned int executedCycles);
uint8_t floating_bus_hibit(const bool hibit, const unsigned int executedCycles);
#endif /* !__ASSEMBLER__ */
/**** Private stuff follows *****/

View File

@ -122,12 +122,6 @@ typedef struct vm_trace_range_t {
} vm_trace_range_t;
#endif
static uint16_t video_scanner_get_address(uint8_t *vbl_bar /*, current_executed_cycles*/) {
// HACK HACK HACK of course this is wrong ...
*vbl_bar = (c_read_rand(0x0) < 0x40) ? 0x80 : 0x0;
return 0x000;
}
GLUE_C_READ(ram_nop)
{
return 0x0;
@ -892,16 +886,10 @@ GLUE_C_READ(iie_check_dhires)
GLUE_C_READ(iie_check_vbl)
{
uint8_t vbl_bar = 0;
video_scanner_get_address(&vbl_bar /*, current_executed_cycles*/);
bool vbl_bar = false;
video_scanner_get_address(&vbl_bar, cpu65_cycle_count);
uint8_t key = c_read_keyboard(0xC000);
if (vbl_bar) {
vbl_bar = 0x80;
}
return (key & ~0x80) | vbl_bar;
return (key & ~0x80) | (vbl_bar ? 0x80 : 0x00);
}
GLUE_C_READ(iie_c3rom_peripheral)