@@ -58,6 +58,7 @@ void Mikey::Reset()
5858 ResetPalette ();
5959 ResetTimers ();
6060 ResetAudio ();
61+ ResetUART ();
6162
6263 m_debug_cycles = 0 ;
6364}
@@ -126,12 +127,241 @@ void Mikey::ResetAudio()
126127 }
127128}
128129
130+ void Mikey::ResetUART ()
131+ {
132+ m_state.uart .tx_int_en = false ;
133+ m_state.uart .rx_int_en = false ;
134+ m_state.uart .par_en = false ;
135+ m_state.uart .reset_err = false ;
136+ m_state.uart .tx_open = false ;
137+ m_state.uart .tx_brk = false ;
138+ m_state.uart .par_even = false ;
139+ m_state.uart .tx_ready = true ;
140+ m_state.uart .rx_ready = false ;
141+ m_state.uart .tx_empty = true ;
142+ m_state.uart .par_err = false ;
143+ m_state.uart .ovr_err = false ;
144+ m_state.uart .fram_err = false ;
145+ m_state.uart .rx_break = false ;
146+ m_state.uart .par_bit = false ;
147+ m_state.uart .tx_active = false ;
148+ m_state.uart .tx_hold_valid = false ;
149+ m_state.uart .tx_parbit = false ;
150+ m_state.uart .tx_hold_data = 0 ;
151+ m_state.uart .tx_data = 0 ;
152+ m_state.uart .rx_data = 0 ;
153+ m_state.uart .tx_bit_index = 0 ;
154+ }
155+
129156void Mikey::ResetPalette ()
130157{
131158 for (int address = 0xFDA0 ; address < 0xFDC0 ; address++)
132159 WriteColor (address, 0xFF );
133160}
134161
162+ void Mikey::UartBeginFrame (u8 data)
163+ {
164+ m_state.uart .tx_data = data;
165+ m_state.uart .tx_bit_index = 0 ;
166+
167+ if (m_state.uart .par_en )
168+ {
169+ bool odd = (parity8 (data) != 0 );
170+ bool want_even = m_state.uart .par_even ;
171+ m_state.uart .tx_parbit = (want_even ? !odd : odd);
172+ }
173+ else
174+ {
175+ m_state.uart .tx_parbit = m_state.uart .par_even ? 1 : 0 ;
176+ }
177+
178+ m_state.uart .tx_active = true ;
179+ m_state.uart .tx_empty = false ;
180+ m_state.uart .tx_ready = false ;
181+ }
182+
183+ void Mikey::UartClock ()
184+ {
185+ // If break is asserted, keep line busy and do not advance a normal frame
186+ if (m_state.uart .tx_brk )
187+ {
188+ m_state.uart .tx_active = true ;
189+ m_state.uart .tx_empty = false ;
190+ return ;
191+ }
192+
193+ if (!m_state.uart .tx_active )
194+ return ; // nothing to shift this tick
195+
196+ // Skip synthesizing the TX line level per bit for now
197+
198+ m_state.uart .tx_bit_index ++;
199+
200+ if (m_state.uart .tx_bit_index >= 11 )
201+ {
202+ // End of frame: deliver RX byte via loopback
203+ if (m_state.uart .rx_ready )
204+ {
205+ // RX overrun if previous byte wasn't read
206+ m_state.uart .ovr_err = true ;
207+ }
208+ m_state.uart .rx_data = m_state.uart .tx_data ;
209+ m_state.uart .par_bit = (m_state.uart .tx_parbit != 0 );
210+ m_state.uart .rx_ready = true ;
211+
212+ // Frame complete on TX side
213+ m_state.uart .tx_active = false ;
214+ // If there is a holding byte queued, start it now
215+ if (m_state.uart .tx_hold_valid )
216+ {
217+ u8 next = m_state.uart .tx_hold_data ;
218+ m_state.uart .tx_hold_valid = false ;
219+ UartBeginFrame (next);
220+ }
221+ else
222+ {
223+ m_state.uart .tx_empty = true ;
224+ m_state.uart .tx_ready = true ;
225+ }
226+
227+ UartRelevelIRQ ();
228+ }
229+ }
230+
231+ void Mikey::HorizontalBlank ()
232+ {
233+ u8 timer_2_counter = m_state.timers [2 ].counter ;
234+ u8 timer_2_backup = m_state.timers [2 ].backup ;
235+ int line = 101 - timer_2_counter;
236+
237+ if (line >= 0 && line < 102 )
238+ {
239+ // DebugMikey("===> Rendering line %d: DISPADR %04X. Timer 2 counter: %d. Cycles: %d", m_state.render_line, m_state.dispadr_latch, timer_2_counter, m_debug_cycles);
240+ LineDMA (line);
241+ }
242+ // else
243+ // {
244+ // DebugMikey("===> Skiping VBLANK line %d: DISPADR %04X. Timer 2 counter: %d. Cycles: %d", m_state.render_line, m_state.dispadr_latch, timer_2_counter, m_debug_cycles);
245+ // }
246+
247+ // Typically end of hcount 104
248+ if (timer_2_counter == timer_2_backup)
249+ {
250+ DebugMikey (" ===> Rest signal goes low" );
251+ m_state.rest = false ;
252+ }
253+ // Typically end of hcount 103, start of 3rd vblank line
254+ else if (timer_2_counter == (timer_2_backup - 1 ))
255+ {
256+ DebugMikey (" ===> Latching DISPADR %04X" , m_state.DISPADR .value );
257+ m_state.dispadr_latch = m_state.DISPADR .value & 0xFFFC ;
258+ }
259+ // Typically end of hcount 101
260+ else if (timer_2_counter == (timer_2_backup - 3 ))
261+ {
262+ DebugMikey (" ===> Rest signal goes high" );
263+ m_state.rest = true ;
264+ }
265+
266+ // At the end of the last vblank line
267+ if (timer_2_counter == (timer_2_backup - 2 ))
268+ {
269+ m_state.render_line = 0 ;
270+ }
271+ else
272+ m_state.render_line ++;
273+ }
274+
275+ void Mikey::VerticalBlank ()
276+ {
277+ DebugMikey (" ===> VBLANK. DISPADR %04X. Cycles: %d" , m_state.dispadr_latch , m_debug_cycles);
278+ m_state.frame_ready = true ;
279+ // m_state.render_line = 0;
280+ m_debug_cycles = 0 ;
281+
282+ GLYNX_Rotation rotation = m_media->GetRotation ();
283+ if (rotation != NO_ROTATION)
284+ RotateFrameBuffer (rotation);
285+ }
286+
287+ void Mikey::LineDMA (int line)
288+ {
289+ if (IS_SET_BIT (m_state.DISPCTL , 0 ))
290+ {
291+ if (m_pixel_format == GLYNX_PIXEL_RGB565)
292+ LineDMATemplate<2 >(line);
293+ else if (m_pixel_format == GLYNX_PIXEL_RGBA8888)
294+ LineDMATemplate<4 >(line);
295+ }
296+ else
297+ {
298+ if (m_pixel_format == GLYNX_PIXEL_RGB565)
299+ LineDMABlankTemplate<2 >(line);
300+ else if (m_pixel_format == GLYNX_PIXEL_RGBA8888)
301+ LineDMABlankTemplate<4 >(line);
302+ }
303+ }
304+
305+ template <int bytes_per_pixel>
306+ void Mikey::LineDMATemplate (int line)
307+ {
308+ assert (line >= 0 && line < GLYNX_SCREEN_HEIGHT);
309+
310+ u8 * ram = m_memory->GetRAM ();
311+ u16 line_offset = (u16 )(m_state.dispadr_latch + (line * (GLYNX_SCREEN_WIDTH / 2 )));
312+ u8 * src_line_ptr = ram + line_offset;
313+ u8 * dst_line_ptr = m_frame_buffer + (line * GLYNX_SCREEN_WIDTH * bytes_per_pixel);
314+ u16 * palette = m_host_palette;
315+ u8 * src = src_line_ptr;
316+
317+ // RGB565
318+ if (bytes_per_pixel == 2 )
319+ {
320+ u16 * dst16 = (u16 *)(dst_line_ptr);
321+
322+ for (int x = 0 ; x < GLYNX_SCREEN_WIDTH; x += 2 )
323+ {
324+ u8 byte = *src++;
325+ u8 color0 = byte >> 4 ;
326+ u8 color1 = byte & 0x0F ;
327+ u16 idx0 = palette[color0] & 0x0FFF ;
328+ u16 idx1 = palette[color1] & 0x0FFF ;
329+
330+ dst16[0 ] = m_rgb565_palette[idx0];
331+ dst16[1 ] = m_rgb565_palette[idx1];
332+ dst16 += 2 ;
333+ }
334+ }
335+ // RGBA8888
336+ else
337+ {
338+ u32 * dst32 = (u32 *)(dst_line_ptr);
339+
340+ for (int x = 0 ; x < GLYNX_SCREEN_WIDTH; x += 2 )
341+ {
342+ u8 byte = *src++;
343+ u8 color0 = byte >> 4 ;
344+ u8 color1 = byte & 0x0F ;
345+ u16 idx0 = palette[color0] & 0x0FFF ;
346+ u16 idx1 = palette[color1] & 0x0FFF ;
347+
348+ dst32[0 ] = m_rgba8888_palette[idx0];
349+ dst32[1 ] = m_rgba8888_palette[idx1];
350+ dst32 += 2 ;
351+ }
352+ }
353+ }
354+
355+ template <int bytes_per_pixel>
356+ void Mikey::LineDMABlankTemplate (int line)
357+ {
358+ assert (line >= 0 && line < GLYNX_SCREEN_HEIGHT);
359+
360+ u8 * dst_line_ptr = m_frame_buffer + (line * GLYNX_SCREEN_WIDTH * bytes_per_pixel);
361+ size_t line_size = GLYNX_SCREEN_WIDTH * bytes_per_pixel;
362+ memset (dst_line_ptr, 0 , line_size);
363+ }
364+
135365void Mikey::RotateFrameBuffer (GLYNX_Rotation rotation)
136366{
137367 if (rotation == NO_ROTATION)
@@ -238,6 +468,29 @@ void Mikey::Serialize(StateSerializer& s)
238468 G_SERIALIZE (s, m_state.audio [i].internal_mix );
239469 }
240470
471+ G_SERIALIZE (s, m_state.uart .tx_int_en );
472+ G_SERIALIZE (s, m_state.uart .rx_int_en );
473+ G_SERIALIZE (s, m_state.uart .par_en );
474+ G_SERIALIZE (s, m_state.uart .reset_err );
475+ G_SERIALIZE (s, m_state.uart .tx_open );
476+ G_SERIALIZE (s, m_state.uart .tx_brk );
477+ G_SERIALIZE (s, m_state.uart .par_even );
478+ G_SERIALIZE (s, m_state.uart .tx_ready );
479+ G_SERIALIZE (s, m_state.uart .rx_ready );
480+ G_SERIALIZE (s, m_state.uart .tx_empty );
481+ G_SERIALIZE (s, m_state.uart .par_err );
482+ G_SERIALIZE (s, m_state.uart .ovr_err );
483+ G_SERIALIZE (s, m_state.uart .fram_err );
484+ G_SERIALIZE (s, m_state.uart .rx_break );
485+ G_SERIALIZE (s, m_state.uart .par_bit );
486+ G_SERIALIZE (s, m_state.uart .tx_active );
487+ G_SERIALIZE (s, m_state.uart .tx_hold_valid );
488+ G_SERIALIZE (s, m_state.uart .tx_parbit );
489+ G_SERIALIZE (s, m_state.uart .tx_hold_data );
490+ G_SERIALIZE (s, m_state.uart .tx_data );
491+ G_SERIALIZE (s, m_state.uart .rx_data );
492+ G_SERIALIZE (s, m_state.uart .tx_bit_index );
493+
241494 G_SERIALIZE (s, m_state.ATTEN_A );
242495 G_SERIALIZE (s, m_state.ATTEN_B );
243496 G_SERIALIZE (s, m_state.ATTEN_C );
0 commit comments