OSDN Git Service

31a67a0a2341230f13c171f3f98f5859d9cd8bb9
[csp-qt/common_source_project-fm7.git] / source / src / vm / fmtowns / towns_crtc.cpp
1 /*
2         Skelton for retropc emulator
3
4         Author : Kyuma Ohta <whatisthis.sowhat _at_ gmail.com>
5         Date   : 2016.12.28 -
6
7         [ FM-Towns CRTC ]
8         History: 2016.12.28 Initial from HD46505 .
9 */
10
11 #include "towns_crtc.h"
12
13 namespace FMTOWNS {
14 enum {
15         EVENT_CRTC_VSTART = 0,
16         EVENT_CRTC_VST1   = 2,
17         EVENT_CRTC_VST2   = 4,
18         EVENT_CRTC_VDS    = 6,
19         EVENT_CRTC_VDE    = 8,
20         EVENT_CRTC_HSTART = 10,
21         EVENT_CRTC_HEND   = 12,
22         EVENT_CRTC_HSW    = 14,
23 };
24
25
26 void TOWNS_CRTC::initialize()
27 {
28         memset(regs, 0, sizeof(regs));
29         memset(regs_written, 0, sizeof(regs_written));
30
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;
35         }
36         event_id_hsync = -1;
37         event_id_hsw = -1;
38         event_id_vsync = -1;
39         event_id_vstart = -1;
40         event_id_vst1 = -1;
41         event_id_vst2 = -1;
42         event_id_vblank = -1;
43         
44         // register events
45         //register_frame_event(this);
46         //register_vline_event(this);
47         
48 }
49
50 void TOWNS_CRTC::reset()
51 {
52         // initialize
53         display = false;
54         vblank = vsync = hsync = true;
55         
56 //      memset(regs, 0, sizeof(regs));
57         ch = 0;
58         
59         // initial settings for 1st frame
60 #ifdef CHARS_PER_LINE
61         hz_total = (CHARS_PER_LINE > 54) ? CHARS_PER_LINE : 54;
62 #else
63         hz_total = 54;
64 #endif
65         hz_disp = (hz_total > 80) ? 80 : 40;
66         hs_start = hz_disp + 4;
67         hs_end = hs_start + 4;
68         
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;
73         
74         timing_changed = false;
75         disp_end_clock = 0;
76         
77 #if defined(TOWNS_CRTC_CHAR_CLOCK)
78         char_clock = 0;
79         next_char_clock = TOWNS_CRTC_CHAR_CLOCK;
80 #elif defined(TOWNS_CRTC_HORIZ_FREQ)
81         horiz_freq = 0;
82         next_horiz_freq = TOWNS_CRTC_HORIZ_FREQ;
83 #endif
84
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;
91         }
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);
99         
100         event_id_hsw = -1;
101         event_id_vsync = -1;
102         event_id_vstart = -1;
103         event_id_vst1 = -1;
104         event_id_vst2 = -1;
105         event_id_vblank = -1;
106
107         // Register vstart
108         register_event(this, EVENT_CRTC_VSTART, vstart_us, false, &event_id_vstart);
109 }
110 // CRTC register #29
111 void TOWNS_CRTC::set_crtc_clock(uint16_t val)
112 {
113         scsel = (val & 0x0c) >> 2;
114         clksel = val & 0x03;
115         const double clocks[] = {
116                 28.6363e6, 24.5454e6, 25.175e6, 21.0525e6
117         };
118         if(crtc_clock[clksel] != crtc_clock) {
119                 crtc_clock = crtc_clock[clksel];
120                 force_recalc_crtc_param(-1);
121         }
122 }
123
124 void TOWNS_CRTC::set_crtc_hsw1(void)
125 {
126         int val = (int)(regs[ch + TOWNS_CRTC_REG_HSW1] & 0x0fff);
127         horiz_width_posi_us = horiz_us * (double)val;
128         // Update
129 }
130 void TOWNS_CRTC::set_crtc_hsw2(void)
131 {
132         int val = (int)(regs[TOWNS_CRTC_REG_HSW2] & 0x0fff);
133         horiz_width_nega_us = horiz_us * (double)val;
134         // Update
135 }
136
137 void TOWNS_CRTC::set_crtc_hst(void)
138 {
139         int val = (int)(regs[TOWNS_CRTC_REG_HST] & 0x07ff) | 1;
140         next_horiz_us = (double)(val + 1) * 1.0 / crtc_clock;
141         // Update
142 }
143
144 void TOWNS_CRTC::set_crtc_vst1(void)
145 {
146         int val = (int)regs[TOWNS_CRTC_REG_VST1];
147         vert_sync_pre_us = ((double)val * horiz_us) / 2.0;
148 }
149
150 void TOWNS_CRTC::set_crtc_vst2(void)
151 {
152         int val = (int)regs[TOWNS_CRTC_REG_VST2];
153         vert_sync_end_us = ((double)val * horiz_us) / 2.0;
154 }
155
156 void TOWNS_CRTC::set_crtc_vst2(void)
157 {
158         int val = (int)regs[TOWNS_CRTC_REG_VST];
159         next_vert_us = ((double)val * horiz_us) / 2.0;
160 }
161
162 void TOWNS_CRTC::write_io8(uint32_t addr, uint32_t data)
163 {
164         if(addr & 1) {
165                 if(ch < 32) {
166                         if(ch < 16 && regs[ch] != data) {
167                                 timing_changed = true;
168                         }
169                         regs[ch] = data;
170                         regs_written[ch] = true;
171                 }
172         } else {
173                 ch = data;
174         }
175 }
176
177 uint32_t TOWNS_CRTC::read_io8(uint32_t addr)
178 {
179         if(addr & 1) {
180                 return (12 <= ch && ch < 18) ? regs[ch] : 0xff;
181         } else {
182                 return ch;
183         }
184 }
185
186
187 void TOWNS_CRTC::event_pre_frame()
188 {
189         if(timing_changed) {
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;
192                         
193                         hz_total = regs[0] + 1;
194                         hz_disp = regs[1];
195                         hs_start = regs[2];
196                         hs_end = hs_start + (regs[3] & 0x0f);
197                         
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);
202                         
203                         if(vt_total != new_vt_total) {
204                                 vt_total = new_vt_total;
205                                 set_lines_per_frame(vt_total);
206                         }
207                         timing_changed = false;
208                         disp_end_clock = 0;
209 #if defined(TOWNS_CRTC_CHAR_CLOCK)
210                         char_clock = 0;
211 #elif defined(TOWNS_CRTC_HORIZ_FREQ)
212                         horiz_freq = 0;
213 #endif
214                 }
215         }
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;
220                 if(regs[8] & 1) {
221                         frames_per_sec *= 2; // interlace mode
222                 }
223                 set_frames_per_sec(frames_per_sec);
224         }
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;
229                 if(regs[8] & 1) {
230                         frames_per_sec *= 2; // interlace mode
231                 }
232                 set_frames_per_sec(frames_per_sec);
233         }
234 #endif
235 }
236
237 void TOWNS_CRTC::update_timing(int new_clocks, double new_frames_per_sec, int new_lines_per_frame)
238 {
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;
242 #endif
243         
244         // update event clocks
245         disp_end_clock = 0;
246 }
247
248 void TOWNS_CRTC::event_callback(int event_id, int err)
249 {
250         /*
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)
259          * ZOOM (#27) : ToDo
260          */
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);
270                         
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
273                 }
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;
277                 hsync = false;
278                 hdisp = false;
279                 vdisp = true;
280                 vblank = true;
281                 vsync = false;
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) {
287                 vsync = true;
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.
290                 } else {
291                         vsync = false;
292                 }
293         } else if (eid2 == EVENT_CRTC_VST2) {
294                 vsync = false;
295                 vblank = false;
296                 event_id_vstn = -1;
297         } else if(eid2 == EVENT_CRTC_VDS) { // Display start
298                 int layer = event_id & 1;
299                 frame_in[layer] = true;
300                 // DO ofset line?
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;
310                 // DO ofset line?
311         } else if(eid2 == EVENT_CRTC_HSTART) {
312                 // Do render
313                 hsync = true;
314                 hdisp = false;
315                 major_line_count++;
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).
324                                                 {
325                                                         // If zoom is not supported by hardware, if first line : render.
326                                                         // Not first: copy (put here?)
327                                                 } // else {
328                                                 if(line_changed[layer][line_count[layer]]) {
329                                                         {
330                                                                 // Do rendering.
331                                                                 line_changed[layer][line_count[layer]] = false;
332                                                                 line_rendered[layer][line_count[layer]] = true;
333                                                         }
334                                                 }
335                                         }
336                                         {
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) {
340                                                         line_count[layer]++;
341                                                         line_count_mod[layer] -= 2048;
342                                                 }
343                                         }
344                                 }
345                         }
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
348                 }
349         } else if(eid2 == EVENT_CRTC_HSW) {
350                 if(!vsync) {
351                         hdisp = true;
352                 }
353                 hsync = false;
354         }
355
356 }
357
358 void TOWNS_CRTC::set_display(bool val)
359 {
360         if(display != val) {
361                 write_signals(&outputs_disp, val ? 0xffffffff : 0);
362                 display = val;
363         }
364 }
365
366 void TOWNS_CRTC::set_vblank(bool val)
367 {
368         if(vblank != val) {
369                 write_signals(&outputs_vblank, val ? 0xffffffff : 0);
370                 vblank = val;
371         }
372 }
373
374
375
376 #define STATE_VERSION   1
377
378 void TOWNS_CRTC::save_state(FILEIO* state_fio)
379 {
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);
393 #endif
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);
410 }
411
412 bool TOWNS_CRTC::load_state(FILEIO* state_fio)
413 {
414         if(state_fio->FgetUint32() != STATE_VERSION) {
415                 return false;
416         }
417         if(state_fio->FgetInt32() != this_device_id) {
418                 return false;
419         }
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();
431 #endif
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();
448
449         return true;
450 }
451
452 }