diff --git a/src/audio/mockingboard.c b/src/audio/mockingboard.c index ffaef0fa..c3b2b5d2 100644 --- a/src/audio/mockingboard.c +++ b/src/audio/mockingboard.c @@ -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) diff --git a/src/disk.c b/src/disk.c index 54183768..bfc7d518 100644 --- a/src/disk.c +++ b/src/disk.c @@ -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; } // ---------------------------------------------------------------------------- diff --git a/src/display.c b/src/display.c index 677ad977..6347074f 100644 --- a/src/display.c +++ b/src/display.c @@ -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); +} + diff --git a/src/timing.c b/src/timing.c index 0113fcdc..b7d5fb3d 100644 --- a/src/timing.c +++ b/src/timing.c @@ -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); diff --git a/src/video/video.h b/src/video/video.h index 649ae8b0..b728274b 100644 --- a/src/video/video.h +++ b/src/video/video.h @@ -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 *****/ diff --git a/src/vm.c b/src/vm.c index 0b91f55b..8d5b92b8 100644 --- a/src/vm.c +++ b/src/vm.c @@ -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)