// Commander X16 Emulator // Copyright (c) 2019 Michael Steil // All rights reserved. License: 2-clause BSD #ifndef _VIDEO_H_ #define _VIDEO_H_ #include #include #include #include #include "glue.h" #ifdef __cplusplus extern "C" { #endif bool video_init(int window_scale, float screen_x_scale, char *quality, bool fullscreen, float opacity); void video_reset(void); bool video_step(float mhz, float steps, bool midline); bool video_update(void); void video_present_no_input(void); void video_repaint_only(void); // The ImGui debugger window, in builds configured with -DENABLE_IMGUI. // Both are always defined: without the UI, available() is false and the pump // reports "stay paused", so call sites need no #ifdef. bool video_debug_ui_available(void); int video_debug_ui_pump_paused(void); bool video_is_debug_ui_window(Uint32 window_id); bool video_event_targets_debug_ui(const SDL_Event *ev); void video_debug_ui_shortcut_key(const SDL_Event *ev); void video_debug_ui_feed_event(const SDL_Event *ev); // ─── Debug view accessors ────────────────────────────────────────────────── // Per-scanline history of the layer registers each display line was actually // rendered with. Programs rewrite MAPBASE/TILEBASE/scroll part-way down a frame // from a line IRQ ("raster split"), so one register snapshot describes only one // band of the screen. // // out_regs receives the 7 layer registers L?_CONFIG..L?_VSCROLL_H; out_eff_y is // the composer's effective layer Y for that line, before the layer's own // VSCROLL; out_layer_row is the row of the layer image the line displayed, // recorded rather than derivable because the renderer mixes two register // generations; out_enabled is false for a line the layer did not reach the // screen on, including border lines outside the active display window. Returns // false if the line is out of range or has not been rendered yet. Every // out-parameter is optional. // // Accurate for the common shape of a raster split -- a program rewriting // MAPBASE/TILEBASE/scroll from a line IRQ so that horizontal bands differ -- // and approximate in narrower ones. Known limits: out_regs is the layout // generation, one behind the generation the row was computed from, so a scroll // change at the split misleads anything derived from out_regs alone; bitmap // mode takes its palette offset from the live registers, so a palette change at // a split reports up to two lines late; there is one snapshot per scanline and // the last render_line() call wins, so a mid-line change is not represented; // settlement is tracked per line rather than per layer; and a split that // changes tile geometry falls back to the live registers for those rows. // Getting these exact means having each render_layer_line_* publish the state // it actually used, rather than reconstructing it from outside as this does. bool video_get_layer_line_state(uint8_t layer, uint16_t line, uint8_t out_regs[7], uint16_t *out_eff_y, bool *out_enabled, uint16_t *out_layer_row); uint16_t video_get_scanline_count(void); // The frame period video_step() actually raises a new frame on, expressed in // VERA's 25 MHz dot clock and in scanlines. Together with the CPU clock these // give the cycles a program has between vsyncs, which is what a performance // budget is measured against. // // NTSC reports a FIELD, not a full interlaced frame, because a field is what // video_step() signals a new frame at -- and it is also what a program syncing // to VSYNC actually gets. uint32_t video_frame_dot_clocks(void); uint16_t video_frame_scanlines(void); // Size in layer pixels of the image the composer is actually displaying: the // active window (DC_HSTART/HSTOP, DC_VSTART/VSTOP) scaled by DC_HSCALE/VSCALE. // Viewers use this to size themselves to the current video mode. void video_get_active_layer_size(int *out_w, int *out_h); // Cycles until the next enabled VERA interrupt and the ISR bit that will cause // it (1 = VSYNC, 2 = LINE); false when neither is enabled. bool video_next_irq(float mhz, uint32_t *out_cycles, uint8_t *out_source); void video_get_irq_state(uint8_t *out_ien, uint8_t *out_isr, uint16_t *out_irq_line); void video_end(void); bool video_get_irq_out(void); // For the control port (control_backend.c): the last whole frame, BGRA8888, // and the count of frames completed. const uint8_t *video_get_framebuffer(int *width, int *height); uint32_t video_get_frame_count(void); void video_save(SDL_RWops *f); uint8_t video_read(uint8_t reg, bool debugOn); void video_write(uint8_t reg, uint8_t value); // Announce that the next video_write() is the DEBUGGER's, not the guest's, so // its data-port traffic is not charged to VERA's bandwidth accounting. // video_read() is told the same thing directly, as its debugOn argument. void video_set_debug_write(bool on); void video_update_title(const char* window_title); uint8_t via1_read(uint8_t reg, bool debug); void via1_write(uint8_t reg, uint8_t value); // For debugging purposes only: uint8_t video_space_read(uint32_t address); void video_space_write(uint32_t address, uint8_t value); bool video_is_tilemap_address(int addr); bool video_is_tiledata_address(int addr); bool video_is_special_address(int addr); uint32_t video_get_address(uint8_t sel); uint32_t video_get_fx_accum(void); uint8_t video_get_dc_value(uint8_t reg); #ifdef __cplusplus } #endif #endif