2 Skelton for retropc emulator
4 Author : Kyuma Ohta <whatisthis.sowhat _at_ gmail.com>
8 History: 2016.12.28 Initial from HD46505 .
11 #include "towns_crtc.h"
15 EVENT_CRTC_VSTART = 0,
20 EVENT_CRTC_HSTART = 10,
26 void TOWNS_CRTC::initialize()
28 memset(regs, 0, sizeof(regs));
29 memset(regs_written, 0, sizeof(regs_written));
31 line_count[0] = line_count[1] = 0;
32 for(int i = 0; i < TOWNS_CRTC_MAX_LINES; i++) {
33 line_changed[0][i] = true;
34 line_changed[1][i] = true;
45 //register_frame_event(this);
46 //register_vline_event(this);
50 void TOWNS_CRTC::reset()
54 vblank = vsync = hsync = true;
56 // memset(regs, 0, sizeof(regs));
59 // initial settings for 1st frame
61 hz_total = (CHARS_PER_LINE > 54) ? CHARS_PER_LINE : 54;
65 hz_disp = (hz_total > 80) ? 80 : 40;
66 hs_start = hz_disp + 4;
67 hs_end = hs_start + 4;
69 vt_total = LINES_PER_FRAME;
70 vt_disp = (SCREEN_HEIGHT > LINES_PER_FRAME) ? (SCREEN_HEIGHT >> 1) : SCREEN_HEIGHT;
71 vs_start = vt_disp + 16;
72 vs_end = vs_start + 16;
74 timing_changed = false;
77 #if defined(TOWNS_CRTC_CHAR_CLOCK)
79 next_char_clock = TOWNS_CRTC_CHAR_CLOCK;
80 #elif defined(TOWNS_CRTC_HORIZ_FREQ)
82 next_horiz_freq = TOWNS_CRTC_HORIZ_FREQ;
85 line_count[0] = line_count[1] = 0;
86 for(int i = 0; i < TOWNS_CRTC_MAX_LINES; i++) {
87 line_changed[0][i] = true;
88 line_rendered[0][i] = false;
89 line_changed[1][i] = true;
90 line_rendered[1][i] = false;
92 if(event_id_hsync >= 0) cancel_event(this, event_id_hsync);
93 if(event_id_hsw >= 0) cancel_event(this, event_id_hsw);
94 if(event_id_vsync >= 0) cancel_event(this, event_id_vsync);
95 if(event_id_vstart >= 0) cancel_event(this, event_id_vstart);
96 if(event_id_vst1 >= 0) cancel_event(this, event_id_vst1);
97 if(event_id_vst2 >= 0) cancel_event(this, event_id_vst2);
98 if(event_id_vblank >= 0) cancel_event(this, event_id_vblank);
102 event_id_vstart = -1;
105 event_id_vblank = -1;
108 register_event(this, EVENT_CRTC_VSTART, vstart_us, false, &event_id_vstart);
111 void TOWNS_CRTC::set_crtc_clock(uint16_t val)
113 scsel = (val & 0x0c) >> 2;
115 const double clocks[] = {
116 28.6363e6, 24.5454e6, 25.175e6, 21.0525e6
118 if(crtc_clock[clksel] != crtc_clock) {
119 crtc_clock = crtc_clock[clksel];
120 force_recalc_crtc_param(-1);
124 void TOWNS_CRTC::set_crtc_hsw1(void)
126 int val = (int)(regs[ch + TOWNS_CRTC_REG_HSW1] & 0x0fff);
127 horiz_width_posi_us = horiz_us * (double)val;
130 void TOWNS_CRTC::set_crtc_hsw2(void)
132 int val = (int)(regs[TOWNS_CRTC_REG_HSW2] & 0x0fff);
133 horiz_width_nega_us = horiz_us * (double)val;
137 void TOWNS_CRTC::set_crtc_hst(void)
139 int val = (int)(regs[TOWNS_CRTC_REG_HST] & 0x07ff) | 1;
140 next_horiz_us = (double)(val + 1) * 1.0 / crtc_clock;
144 void TOWNS_CRTC::set_crtc_vst1(void)
146 int val = (int)regs[TOWNS_CRTC_REG_VST1];
147 vert_sync_pre_us = ((double)val * horiz_us) / 2.0;
150 void TOWNS_CRTC::set_crtc_vst2(void)
152 int val = (int)regs[TOWNS_CRTC_REG_VST2];
153 vert_sync_end_us = ((double)val * horiz_us) / 2.0;
156 void TOWNS_CRTC::set_crtc_vst2(void)
158 int val = (int)regs[TOWNS_CRTC_REG_VST];
159 next_vert_us = ((double)val * horiz_us) / 2.0;
162 void TOWNS_CRTC::write_io8(uint32_t addr, uint32_t data)
166 if(ch < 16 && regs[ch] != data) {
167 timing_changed = true;
170 regs_written[ch] = true;
177 uint32_t TOWNS_CRTC::read_io8(uint32_t addr)
180 return (12 <= ch && ch < 18) ? regs[ch] : 0xff;
187 void TOWNS_CRTC::event_pre_frame()
190 if(regs_written[0] && regs_written[1] && regs_written[2] && regs_written[3] && regs_written[4] && regs_written[5] && regs_written[6] && regs_written[7] && regs_written[9]) {
191 int ch_height = (regs[9] & 0x1f) + 1;
193 hz_total = regs[0] + 1;
196 hs_end = hs_start + (regs[3] & 0x0f);
198 int new_vt_total = ((regs[4] & 0x7f) + 1) * ch_height + (regs[5] & 0x1f);
199 vt_disp = (regs[6] & 0x7f) * ch_height;
200 vs_start = ((regs[7] & 0x7f) + 1) * ch_height;
201 vs_end = vs_start + ((regs[3] & 0xf0) ? (regs[3] >> 4) : 16);
203 if(vt_total != new_vt_total) {
204 vt_total = new_vt_total;
205 set_lines_per_frame(vt_total);
207 timing_changed = false;
209 #if defined(TOWNS_CRTC_CHAR_CLOCK)
211 #elif defined(TOWNS_CRTC_HORIZ_FREQ)
216 #if defined(TOWNS_CRTC_CHAR_CLOCK)
217 if(char_clock != next_char_clock) {
218 char_clock = next_char_clock;
219 frames_per_sec = char_clock / (double)vt_total / (double)hz_total;
221 frames_per_sec *= 2; // interlace mode
223 set_frames_per_sec(frames_per_sec);
225 #elif defined(TOWNS_CRTC_HORIZ_FREQ)
226 if(horiz_freq != next_horiz_freq) {
227 horiz_freq = next_horiz_freq;
228 frames_per_sec = horiz_freq / (double)vt_total;
230 frames_per_sec *= 2; // interlace mode
232 set_frames_per_sec(frames_per_sec);
237 void TOWNS_CRTC::update_timing(int new_clocks, double new_frames_per_sec, int new_lines_per_frame)
239 cpu_clocks = new_clocks;
240 #if !defined(TOWNS_CRTC_CHAR_CLOCK) && !defined(TOWNS_CRTC_HORIZ_FREQ)
241 frames_per_sec = new_frames_per_sec;
244 // update event clocks
248 void TOWNS_CRTC::event_callback(int event_id, int err)
251 * Related CRTC registers:
252 * HST, HSW1, HSW2 : HSYNC
253 * VST, VST1, VST2 : VSYNC
254 * (EET: for interlace : still not implement)
255 * VDS0, VDE0, HDS0, HDE0 : Display timing for Layer0
256 * VDS1, VDE1, HDS1, HDE1 : Display timing for Layer1
257 * FA0, HAJ0, LO0, FO0 : ToDo (For calculating address)
258 * FA1, HAJ1, LO1, FO1 : ToDo (For calculating address)
261 int eid2 = (event_id / 2) * 2;
262 if(eid2 == EVENT_CRTC_VSTART) {
263 line_count[0] = line_count[1] = 0;
264 if((horiz_us != next_horiz_us) || (vert_us != next_vert_us)) {
265 horiz_us = next_horiz_us;
266 vert_us = next_vert_us;
267 force_recalc_crtc_param(-1);
268 if(event_id_vsync >= 0) cancel_event(this, event_id_vsync);
269 if(event_id_hsw >= 0) cancel_event(this, event_id_hsw);
271 register_event(this, EVENT_CRTC_HSTART + 0, vert_sync_pre_us, false, &event_id_hsw); // HSW = HSYNC
272 register_event(this, EVENT_CRTC_VSTART + 0, vert_us, true, &event_id_vsync); // VST
274 frame_in[0] = frame_in[1] = false;
275 line_count[0] = line_count[1] = 0;
276 line_count_mod[0] = line_count_mod[1] = 0;
282 major_line_count = -1;
283 register_event(this, EVENT_CRTC_VST1 + 0, vert_sync_pre_us, false, &event_id_vstn); // VST1
284 register_event(this, EVENT_CRTC_VDS + 0, vert_start_us[0], false, &event_id_vstart[0]); // VDS0 : Layer1
285 register_event(this, EVENT_CRTC_VDS + 1, vert_start_us[1], false, &event_id_vstart[1]); // VDS1 : Layer2
286 } else if(eid2 == EVENT_CRTC_VST1) {
288 if(vert_sync_end_us > 0.0) {
289 register_event(this, EVENT_CRTC_VST2 + 0, vert_sync_end_us, false, &event_id_vstn); // VST2 - VST1.
293 } else if (eid2 == EVENT_CRTC_VST2) {
297 } else if(eid2 == EVENT_CRTC_VDS) { // Display start
298 int layer = event_id & 1;
299 frame_in[layer] = true;
301 if(event_id_vend[layer] >= 0) cancel_event(this, event_id_vend[layer]);
302 register_event(this, EVENT_CRTC_VDE + layer, vert_start_us[0], false, &event_id_vend[layer]); // VDEx
303 event_id_vstart[layer] = -1;
304 } else if(eid2 == EVENT_CRTC_VDE) { // Display end
305 int layer = event_id & 1;
306 frame_in[layer] = false;
307 if(event_id_vstart[layer] >= 0) cancel_event(this, event_id_vstart[layer]);
308 event_id_vstart[layer] = -1;
309 event_id_vend[layer] = -1;
311 } else if(eid2 == EVENT_CRTC_HSTART) {
316 if(vsync) { // in VSYNC. Not Display.
317 if(event_id_hswn >= 0) cancel_event(this, event_id_hswn);
318 register_event(this, EVENT_CRTC_HSW + 0, hsw2_us, false, &event_id_hswn); // Indicate HSYNC
319 } else { // Not in VSYNC. May be displaied.
320 for(layer = 0; layer < 2; layer++) {
321 if(frame_in[layer] && (major_line_count >= 0) && (major_line_count < TOWNS_CRTC_MAX_LINES)) {
322 if((vstart_lines[layer] <= major_line_count) && (vend_lines[layer] => major_line_count)) {
323 // Proccessing zooming by ZOOM register(reg #27).
325 // If zoom is not supported by hardware, if first line : render.
326 // Not first: copy (put here?)
328 if(line_changed[layer][line_count[layer]]) {
331 line_changed[layer][line_count[layer]] = false;
332 line_rendered[layer][line_count[layer]] = true;
337 // If zoom by hardware
338 line_count_mod[layer] += line_count_factor[layer]; // line_count_factor[n] = 2048 / VZOOM_FACTOR[01]
339 if(line_count_mod[layer] >= 2048) {
341 line_count_mod[layer] -= 2048;
346 if(event_id_hswn >= 0) cancel_event(this, event_id_hswn);
347 register_event(this, EVENT_CRTC_HSW + 0, hsw1_us, false, &event_id_hswn); // Indicate HSYNC
349 } else if(eid2 == EVENT_CRTC_HSW) {
358 void TOWNS_CRTC::set_display(bool val)
361 write_signals(&outputs_disp, val ? 0xffffffff : 0);
366 void TOWNS_CRTC::set_vblank(bool val)
369 write_signals(&outputs_vblank, val ? 0xffffffff : 0);
376 #define STATE_VERSION 1
378 void TOWNS_CRTC::save_state(FILEIO* state_fio)
380 state_fio->FputUint32(STATE_VERSION);
381 state_fio->FputInt32(this_device_id);
382 state_fio->Fwrite(regs, sizeof(regs), 1);
383 state_fio->Fwrite(regs_written, sizeof(regs_written), 1);
384 state_fio->FputInt32(ch);
385 state_fio->FputBool(timing_changed);
386 state_fio->FputInt32(cpu_clocks);
387 #if defined(TOWNS_CRTC_CHAR_CLOCK)
388 state_fio->FputDouble(char_clock);
389 state_fio->FputDouble(next_char_clock);
390 #elif defined(TOWNS_CRTC_HORIZ_FREQ)
391 state_fio->FputDouble(horiz_freq);
392 state_fio->FputDouble(next_horiz_freq);
394 state_fio->FputDouble(frames_per_sec);
395 state_fio->FputInt32(hz_total);
396 state_fio->FputInt32(hz_disp);
397 state_fio->FputInt32(hs_start);
398 state_fio->FputInt32(hs_end);
399 state_fio->FputInt32(vt_total);
400 state_fio->FputInt32(vt_disp);
401 state_fio->FputInt32(vs_start);
402 state_fio->FputInt32(vs_end);
403 state_fio->FputInt32(disp_end_clock);
404 state_fio->FputInt32(hs_start_clock);
405 state_fio->FputInt32(hs_end_clock);
406 state_fio->FputBool(display);
407 state_fio->FputBool(vblank);
408 state_fio->FputBool(vsync);
409 state_fio->FputBool(hsync);
412 bool TOWNS_CRTC::load_state(FILEIO* state_fio)
414 if(state_fio->FgetUint32() != STATE_VERSION) {
417 if(state_fio->FgetInt32() != this_device_id) {
420 state_fio->Fread(regs, sizeof(regs), 1);
421 state_fio->Fread(regs_written, sizeof(regs_written), 1);
422 ch = state_fio->FgetInt32();
423 timing_changed = state_fio->FgetBool();
424 cpu_clocks = state_fio->FgetInt32();
425 #if defined(TOWNS_CRTC_CHAR_CLOCK)
426 char_clock = state_fio->FgetDouble();
427 next_char_clock = state_fio->FgetDouble();
428 #elif defined(TOWNS_CRTC_HORIZ_FREQ)
429 horiz_freq = state_fio->FgetDouble();
430 next_horiz_freq = state_fio->FgetDouble();
432 frames_per_sec = state_fio->FgetDouble();
433 hz_total = state_fio->FgetInt32();
434 hz_disp = state_fio->FgetInt32();
435 hs_start = state_fio->FgetInt32();
436 hs_end = state_fio->FgetInt32();
437 vt_total = state_fio->FgetInt32();
438 vt_disp = state_fio->FgetInt32();
439 vs_start = state_fio->FgetInt32();
440 vs_end = state_fio->FgetInt32();
441 disp_end_clock = state_fio->FgetInt32();
442 hs_start_clock = state_fio->FgetInt32();
443 hs_end_clock = state_fio->FgetInt32();
444 display = state_fio->FgetBool();
445 vblank = state_fio->FgetBool();
446 vsync = state_fio->FgetBool();
447 hsync = state_fio->FgetBool();