OSDN Git Service

[DOC] For release 2017-01-24.
[csp-qt/common_source_project-fm7.git] / source / src / vm / hd46505.cpp
1 /*
2         Skelton for retropc emulator
3
4         Author : Takeda.Toshiya
5         Date   : 2006.09.20 -
6
7         [ HD46505 ]
8 */
9
10 #include "hd46505.h"
11
12 #define EVENT_DISPLAY   0
13 #define EVENT_HSYNC_S   1
14 #define EVENT_HSYNC_E   2
15
16 void HD46505::initialize()
17 {
18         memset(regs, 0, sizeof(regs));
19         memset(regs_written, 0, sizeof(regs_written));
20         
21         // register events
22         register_frame_event(this);
23         register_vline_event(this);
24 }
25
26 void HD46505::reset()
27 {
28         // initialize
29         display = false;
30         vblank = vsync = hsync = true;
31         
32 //      memset(regs, 0, sizeof(regs));
33         ch = 0;
34         
35         // initial settings for 1st frame
36 #ifdef CHARS_PER_LINE
37         hz_total = (CHARS_PER_LINE > 54) ? CHARS_PER_LINE : 54;
38 #else
39         hz_total = 54;
40 #endif
41         hz_disp = (hz_total > 80) ? 80 : 40;
42         hs_start = hz_disp + 4;
43         hs_end = hs_start + 4;
44         
45         vt_total = LINES_PER_FRAME;
46         vt_disp = (SCREEN_HEIGHT > LINES_PER_FRAME) ? (SCREEN_HEIGHT >> 1) : SCREEN_HEIGHT;
47         vs_start = vt_disp + 16;
48         vs_end = vs_start + 16;
49         
50         timing_changed = false;
51         disp_end_clock = 0;
52         
53 #if defined(HD46505_CHAR_CLOCK)
54         char_clock = 0;
55         next_char_clock = HD46505_CHAR_CLOCK;
56 #elif defined(HD46505_HORIZ_FREQ)
57         horiz_freq = 0;
58         next_horiz_freq = HD46505_HORIZ_FREQ;
59 #endif
60 }
61
62 void HD46505::write_io8(uint32_t addr, uint32_t data)
63 {
64         if(addr & 1) {
65                 if(ch < 18) {
66                         if(ch < 10 && regs[ch] != data) {
67                                 timing_changed = true;
68                         }
69                         regs[ch] = data;
70                         regs_written[ch] = true;
71                 }
72         } else {
73                 ch = data;
74         }
75 }
76
77 uint32_t HD46505::read_io8(uint32_t addr)
78 {
79         if(addr & 1) {
80                 return (12 <= ch && ch < 18) ? regs[ch] : 0xff;
81         } else {
82                 return ch;
83         }
84 }
85
86 void HD46505::event_pre_frame()
87 {
88         if(timing_changed) {
89                 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]) {
90                         int ch_height = (regs[9] & 0x1f) + 1;
91                         
92                         hz_total = regs[0] + 1;
93                         hz_disp = regs[1];
94                         hs_start = regs[2];
95                         hs_end = hs_start + (regs[3] & 0x0f);
96                         
97                         int new_vt_total = ((regs[4] & 0x7f) + 1) * ch_height + (regs[5] & 0x1f);
98                         vt_disp = (regs[6] & 0x7f) * ch_height;
99                         vs_start = ((regs[7] & 0x7f) + 1) * ch_height;
100                         vs_end = vs_start + ((regs[3] & 0xf0) ? (regs[3] >> 4) : 16);
101                         
102                         if(vt_total != new_vt_total) {
103                                 vt_total = new_vt_total;
104                                 set_lines_per_frame(vt_total);
105                         }
106                         timing_changed = false;
107                         disp_end_clock = 0;
108 #if defined(HD46505_CHAR_CLOCK)
109                         char_clock = 0;
110 #elif defined(HD46505_HORIZ_FREQ)
111                         horiz_freq = 0;
112 #endif
113                 }
114         }
115 #if defined(HD46505_CHAR_CLOCK)
116         if(char_clock != next_char_clock) {
117                 char_clock = next_char_clock;
118                 frames_per_sec = char_clock / (double)vt_total / (double)hz_total;
119                 if(regs[8] & 1) {
120                         frames_per_sec *= 2; // interlace mode
121                 }
122                 set_frames_per_sec(frames_per_sec);
123         }
124 #elif defined(HD46505_HORIZ_FREQ)
125         if(horiz_freq != next_horiz_freq) {
126                 horiz_freq = next_horiz_freq;
127                 frames_per_sec = horiz_freq / (double)vt_total;
128                 if(regs[8] & 1) {
129                         frames_per_sec *= 2; // interlace mode
130                 }
131                 set_frames_per_sec(frames_per_sec);
132         }
133 #endif
134 }
135
136 void HD46505::update_timing(int new_clocks, double new_frames_per_sec, int new_lines_per_frame)
137 {
138         cpu_clocks = new_clocks;
139 #if !defined(HD46505_CHAR_CLOCK) && !defined(HD46505_HORIZ_FREQ)
140         frames_per_sec = new_frames_per_sec;
141 #endif
142         
143         // update event clocks
144         disp_end_clock = 0;
145 }
146
147 void HD46505::event_frame()
148 {
149         // update envet clocks after update_timing() is called
150         if(disp_end_clock == 0 && vt_total != 0) {
151                 disp_end_clock = (int)((double)cpu_clocks * (double)hz_disp / frames_per_sec / (double)vt_total / (double)hz_total);
152                 hs_start_clock = (int)((double)cpu_clocks * (double)hs_start / frames_per_sec / (double)vt_total / (double)hz_total);
153                 hs_end_clock = (int)((double)cpu_clocks * (double)hs_end / frames_per_sec / (double)vt_total / (double)hz_total);
154         }
155 }
156
157 void HD46505::event_vline(int v, int clock)
158 {
159         // if vt_disp == 0, raise vblank for one line
160         bool new_vblank = ((v < vt_disp) || (v == 0 && vt_disp == 0));
161         
162         // display
163         if(outputs_disp.count) {
164                 set_display(new_vblank);
165                 if(new_vblank && hz_disp < hz_total) {
166                         register_event_by_clock(this, EVENT_DISPLAY, disp_end_clock, false, NULL);
167                 }
168         }
169         
170         // vblank
171         set_vblank(new_vblank); // active low
172         
173         // vsync
174         set_vsync(vs_start <= v && v < vs_end);
175         
176         // hsync
177         if(outputs_hsync.count && hs_start < hs_end && hs_end < hz_total) {
178                 set_hsync(false);
179                 register_event_by_clock(this, EVENT_HSYNC_S, hs_start_clock, false, NULL);
180                 register_event_by_clock(this, EVENT_HSYNC_E, hs_end_clock, false, NULL);
181         }
182 }
183
184 void HD46505::event_callback(int event_id, int err)
185 {
186         if(event_id == EVENT_DISPLAY) {
187                 set_display(false);
188         } else if(event_id == EVENT_HSYNC_S) {
189                 set_hsync(true);
190         } else if(event_id == EVENT_HSYNC_E) {
191                 set_hsync(false);
192         }
193 }
194
195 void HD46505::set_display(bool val)
196 {
197         if(display != val) {
198                 write_signals(&outputs_disp, val ? 0xffffffff : 0);
199                 display = val;
200         }
201 }
202
203 void HD46505::set_vblank(bool val)
204 {
205         if(vblank != val) {
206                 write_signals(&outputs_vblank, val ? 0xffffffff : 0);
207                 vblank = val;
208         }
209 }
210
211 void HD46505::set_vsync(bool val)
212 {
213         if(vsync != val) {
214                 write_signals(&outputs_vsync, val ? 0xffffffff : 0);
215                 vsync = val;
216         }
217 }
218
219 void HD46505::set_hsync(bool val)
220 {
221         if(hsync != val) {
222                 write_signals(&outputs_hsync, val ? 0xffffffff : 0);
223                 hsync = val;
224         }
225 }
226
227 #define STATE_VERSION   3
228
229 void HD46505::save_state(FILEIO* state_fio)
230 {
231         state_fio->FputUint32(STATE_VERSION);
232         state_fio->FputInt32(this_device_id);
233         
234         state_fio->Fwrite(regs, sizeof(regs), 1);
235         state_fio->Fwrite(regs_written, sizeof(regs_written), 1);
236         state_fio->FputInt32(ch);
237         state_fio->FputBool(timing_changed);
238         state_fio->FputInt32(cpu_clocks);
239 #if defined(HD46505_CHAR_CLOCK)
240         state_fio->FputDouble(char_clock);
241         state_fio->FputDouble(next_char_clock);
242 #elif defined(HD46505_HORIZ_FREQ)
243         state_fio->FputDouble(horiz_freq);
244         state_fio->FputDouble(next_horiz_freq);
245 #endif
246         state_fio->FputDouble(frames_per_sec);
247         state_fio->FputInt32(hz_total);
248         state_fio->FputInt32(hz_disp);
249         state_fio->FputInt32(hs_start);
250         state_fio->FputInt32(hs_end);
251         state_fio->FputInt32(vt_total);
252         state_fio->FputInt32(vt_disp);
253         state_fio->FputInt32(vs_start);
254         state_fio->FputInt32(vs_end);
255         state_fio->FputInt32(disp_end_clock);
256         state_fio->FputInt32(hs_start_clock);
257         state_fio->FputInt32(hs_end_clock);
258         state_fio->FputBool(display);
259         state_fio->FputBool(vblank);
260         state_fio->FputBool(vsync);
261         state_fio->FputBool(hsync);
262 }
263
264 bool HD46505::load_state(FILEIO* state_fio)
265 {
266         if(state_fio->FgetUint32() != STATE_VERSION) {
267                 return false;
268         }
269         if(state_fio->FgetInt32() != this_device_id) {
270                 return false;
271         }
272         state_fio->Fread(regs, sizeof(regs), 1);
273         state_fio->Fread(regs_written, sizeof(regs_written), 1);
274         ch = state_fio->FgetInt32();
275         timing_changed = state_fio->FgetBool();
276         cpu_clocks = state_fio->FgetInt32();
277 #if defined(HD46505_CHAR_CLOCK)
278         char_clock = state_fio->FgetDouble();
279         next_char_clock = state_fio->FgetDouble();
280 #elif defined(HD46505_HORIZ_FREQ)
281         horiz_freq = state_fio->FgetDouble();
282         next_horiz_freq = state_fio->FgetDouble();
283 #endif
284         frames_per_sec = state_fio->FgetDouble();
285         hz_total = state_fio->FgetInt32();
286         hz_disp = state_fio->FgetInt32();
287         hs_start = state_fio->FgetInt32();
288         hs_end = state_fio->FgetInt32();
289         vt_total = state_fio->FgetInt32();
290         vt_disp = state_fio->FgetInt32();
291         vs_start = state_fio->FgetInt32();
292         vs_end = state_fio->FgetInt32();
293         disp_end_clock = state_fio->FgetInt32();
294         hs_start_clock = state_fio->FgetInt32();
295         hs_end_clock = state_fio->FgetInt32();
296         display = state_fio->FgetBool();
297         vblank = state_fio->FgetBool();
298         vsync = state_fio->FgetBool();
299         hsync = state_fio->FgetBool();
300         return true;
301 }
302