void PPU::enter() { while(true) { if(scheduler.sync == Scheduler::SynchronizeMode::All) { scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); } scanline(); if(vcounter() < display.height && vcounter()) { add_clocks(512); render_scanline(); add_clocks(lineclocks() - 512); } else { add_clocks(lineclocks()); } } }
void gpu_step(Word ticks) { gpu.modeclock += ticks; switch (gpu.mode) { // OAM read mode, scanline active case OAM_MODE: if (gpu.modeclock >= 20) { set_mode(VRAM_MODE); } break; // VRAM read mode, scanline active // Treat end of mode as end of scanline case VRAM_MODE: if (gpu.modeclock >= 43) { render_scanline(); set_mode(HBLANK_MODE); } break; // After last hblank, update the display case HBLANK_MODE: if (gpu.modeclock >= 51) { if (gpu.regs.line == (DISPLAY_HEIGHT - 1)) { // Update screen when scanner reaches end of last line SDL_Flip(gpu.screen); set_mode(VBLANK_MODE); mmu.iflag |= INT_VBLANK; } else { set_mode(OAM_MODE); } next_line(); } break; // Vblank (10 lines) case VBLANK_MODE: if (gpu.modeclock >= 114) { if (gpu.regs.line == (DISPLAY_HEIGHT + 9)) { // Restart scan gpu.regs.line = 0; set_mode(OAM_MODE); } else { next_line(); gpu.modeclock = 0; } } break; default: assert(false); } }
static void do_hs_fall(void *data) { struct MC6847_private *vdg = data; // Finish rendering previous scanline if (vdg->frame == 0) { if (vdg->scanline < VDG_ACTIVE_AREA_START) { if (vdg->scanline == 0) { memset(vdg->pixel_data + VDG_LEFT_BORDER_START, vdg->border_colour, VDG_tAVB); } video_module->render_scanline(vdg->pixel_data); } else if (vdg->scanline >= VDG_ACTIVE_AREA_START && vdg->scanline < VDG_ACTIVE_AREA_END) { render_scanline(vdg); vdg->row++; if (vdg->row > 11) vdg->row = 0; video_module->render_scanline(vdg->pixel_data); vdg->pixel = vdg->pixel_data + VDG_LEFT_BORDER_START; } else if (vdg->scanline >= VDG_ACTIVE_AREA_END) { if (vdg->scanline == VDG_ACTIVE_AREA_END) { memset(vdg->pixel_data + VDG_LEFT_BORDER_START, vdg->border_colour, VDG_tAVB); } video_module->render_scanline(vdg->pixel_data); } } // HS falling edge. DELEGATE_CALL1(vdg->public.signal_hs, 0); vdg->scanline_start = vdg->hs_fall_event.at_tick; // Next HS rise and fall vdg->hs_rise_event.at_tick = vdg->scanline_start + VDG_CYCLES(VDG_HS_RISING_EDGE); vdg->hs_fall_event.at_tick = vdg->scanline_start + VDG_CYCLES(VDG_LINE_DURATION); /* On PAL machines, the clock to the VDG is interrupted at two points * in every frame to fake up some extra scanlines, padding the signal * from 262 lines to 312 lines. Dragons do not generate an HS-related * interrupt signal during this time, CoCos do. The positioning and * duration of each interruption differs also. */ if (IS_PAL && IS_COCO) { if (vdg->scanline == SCANLINE(VDG_ACTIVE_AREA_END + 25)) { vdg->pal_padding = 26; vdg->hs_fall_event.delegate.func = do_hs_fall_pal_coco; } else if (vdg->scanline == SCANLINE(VDG_ACTIVE_AREA_END + 47)) { vdg->pal_padding = 24; vdg->hs_fall_event.delegate.func = do_hs_fall_pal_coco; } } else if (IS_PAL && IS_DRAGON) { if (vdg->scanline == SCANLINE(VDG_ACTIVE_AREA_END + 24) || vdg->scanline == SCANLINE(VDG_ACTIVE_AREA_END + 32)) { vdg->hs_rise_event.at_tick += 25 * VDG_CYCLES(VDG_PAL_PADDING_LINE); vdg->hs_fall_event.at_tick += 25 * VDG_CYCLES(VDG_PAL_PADDING_LINE); } } event_queue(&MACHINE_EVENT_LIST, &vdg->hs_rise_event); event_queue(&MACHINE_EVENT_LIST, &vdg->hs_fall_event); // Next scanline vdg->scanline = SCANLINE(vdg->scanline + 1); vdg->beam_pos = 0; vdg->vram_nbytes = 0; vdg->vram_ptr = vdg->vram; vdg->vram_bit = 0; vdg->lborder_remaining = VDG_tLB; vdg->vram_remaining = vdg->is_32byte ? 32 : 16; vdg->rborder_remaining = VDG_tRB; if (vdg->scanline == VDG_ACTIVE_AREA_START) { vdg->row = 0; } if (vdg->scanline == VDG_ACTIVE_AREA_END) { // FS falling edge DELEGATE_CALL1(vdg->public.signal_fs, 0); }
static void do_hs_fall(void) { /* Finish rendering previous scanline */ #ifdef HAVE_GP32 /* GP32 renders 4 scanlines at once */ if (frame == 0 && scanline >= VDG_ACTIVE_AREA_START && scanline < VDG_ACTIVE_AREA_END && (scanline & 3) == ((VDG_ACTIVE_AREA_START+3)&3) ) { video_module->render_scanline(); } #elif defined (HAVE_NDS) if (scanline >= VDG_ACTIVE_AREA_START && scanline < VDG_ACTIVE_AREA_END) { render_scanline(); sam_vdg_hsync(); } #elif !defined(HAVE_NDS) /* NDS video module does its own thing */ /* Normal code */ if (frame == 0 && scanline >= (VDG_TOP_BORDER_START + 1)) { if (scanline < VDG_ACTIVE_AREA_START) { video_module->render_border(); } else if (scanline < VDG_ACTIVE_AREA_END) { render_scanline(); sam_vdg_hsync(); video_module->hsync(); } else if (scanline < (VDG_BOTTOM_BORDER_END - 2)) { video_module->render_border(); } } #endif /* Next scanline */ scanline = (scanline + 1) % VDG_FRAME_DURATION; scanline_start = hs_fall_event.at_cycle; SET_BEAM_POS(0); PIA_RESET_Cx1(PIA0.a); #ifdef FAST_VDG /* Faster, less accurate timing for GP32/NDS */ PIA_SET_Cx1(PIA0.a); #else /* Everything else schedule HS rise for later */ hs_rise_event.at_cycle = scanline_start + VDG_HS_RISING_EDGE; event_queue(&MACHINE_EVENT_LIST, &hs_rise_event); #endif hs_fall_event.at_cycle = scanline_start + VDG_LINE_DURATION; /* Frame sync */ if (scanline == SCANLINE(VDG_VBLANK_START)) { sam_vdg_fsync(); #ifndef HAVE_NDS frame--; if (frame < 0) frame = xroar_frameskip; if (frame == 0) video_module->vdg_vsync(); #else scanline_data_ptr = scanline_data; #endif } #ifndef FAST_VDG /* Enable mode changes at beginning of active area */ if (scanline == SCANLINE(VDG_ACTIVE_AREA_START)) { inhibit_mode_change = 0; vdg_set_mode(); } #endif /* FS falling edge at end of this scanline */ if (scanline == SCANLINE(VDG_ACTIVE_AREA_END - 1)) { #ifdef HAVE_NDS nds_update_screen = 1; #endif fs_fall_event.at_cycle = scanline_start + VDG_LINE_DURATION; event_queue(&MACHINE_EVENT_LIST, &fs_fall_event); } #ifndef FAST_VDG /* Disable mode changes after end of active area */ if (scanline == SCANLINE(VDG_ACTIVE_AREA_END)) { inhibit_mode_change = 1; } #endif /* PAL delay 24 lines after FS falling edge */ if (IS_PAL && (scanline == SCANLINE(VDG_ACTIVE_AREA_END + 23))) { hs_fall_event.at_cycle += 25 * VDG_PAL_PADDING_LINE; } /* FS rising edge at end of this scanline */ if (scanline == SCANLINE(VDG_ACTIVE_AREA_END + 31)) { /* Fig. 8, VDG data sheet: tWFS = 32 * (227.5 * 1/f) */ fs_rise_event.at_cycle = scanline_start + VDG_LINE_DURATION; event_queue(&MACHINE_EVENT_LIST, &fs_rise_event); /* PAL delay after FS rising edge */ if (IS_PAL) { hs_fall_event.at_cycle += 25 * VDG_PAL_PADDING_LINE; } } event_queue(&MACHINE_EVENT_LIST, &hs_fall_event); }
//------------------------------------------------------------------------ void outline::render_line(int x1, int y1, int x2, int y2) { int ey1 = y1 >> poly_base_shift; int ey2 = y2 >> poly_base_shift; int fy1 = y1 & poly_base_mask; int fy2 = y2 & poly_base_mask; int dx, dy, x_from, x_to; int p, rem, mod, lift, delta, first, incr; if(ey1 < m_min_y) m_min_y = ey1; if(ey1+1 > m_max_y) m_max_y = ey1+1; if(ey2 < m_min_y) m_min_y = ey2; if(ey2+1 > m_max_y) m_max_y = ey2+1; dx = x2 - x1; dy = y2 - y1; //everything is on a single scanline if(ey1 == ey2) { render_scanline(ey1, x1, fy1, x2, fy2); return; } //Vertical line - we have to calculate start and end cells, //and then - the common values of the area and coverage for //all cells of the line. We know exactly there's only one //cell, so, we don't have to call render_scanline(). incr = 1; if(dx == 0) { int ex = x1 >> poly_base_shift; int two_fx = (x1 - (ex << poly_base_shift)) << 1; int area; first = poly_base_size; if(dy < 0) { first = 0; incr = -1; } x_from = x1; //render_scanline(ey1, x_from, fy1, x_from, first); delta = first - fy1; m_cur_cell.add_cover(delta, two_fx * delta); ey1 += incr; set_cur_cell(ex, ey1); delta = first + first - poly_base_size; area = two_fx * delta; while(ey1 != ey2) { //render_scanline(ey1, x_from, poly_base_size - first, x_from, first); m_cur_cell.set_cover(delta, area); ey1 += incr; set_cur_cell(ex, ey1); } //render_scanline(ey1, x_from, poly_base_size - first, x_from, fy2); delta = fy2 - poly_base_size + first; m_cur_cell.add_cover(delta, two_fx * delta); return; }