Skip to content

Commit c85741c

Browse files
committed
Improve UART
1 parent 4045dda commit c85741c

File tree

6 files changed

+390
-182
lines changed

6 files changed

+390
-182
lines changed

src/bit_ops.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,19 @@ INLINE u32 l_zero16(u16 value)
5656
#endif
5757
}
5858

59-
INLINE u8 parity16(u16 x) {
59+
INLINE u8 parity8(u8 x)
60+
{
61+
#if defined(__GNUC__) || defined(__clang__)
62+
return (u8)__builtin_parity((unsigned int)x);
63+
#else
64+
x ^= x >> 4;
65+
x &= 0x0F;
66+
return (u8)((0x6996 >> x) & 1);
67+
#endif
68+
}
69+
70+
INLINE u8 parity16(u16 x)
71+
{
6072
#if defined(__GNUC__) || defined(__clang__)
6173
return (u8)__builtin_parity((unsigned int)x);
6274
#else

src/defines.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151

5252
#define GLYNX_BIOS_SIZE 0x200
5353

54-
#define GLYNX_SAVESTATE_VERSION 1
54+
#define GLYNX_SAVESTATE_VERSION 2
5555
#define GLYNX_SAVESTATE_MAGIC 0x56191212
5656

5757
#if !defined(NULL)

src/mikey.cpp

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
129156
void 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+
135365
void 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);

src/mikey.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class Mikey
3939
GLYNX_Mikey_Timer timers[8];
4040
GLYNX_Mikey_Color colors[16];
4141
GLYNX_Mikey_Audio audio[4];
42+
GLYNX_Uart uart;
4243
u8 ATTEN_A;
4344
u8 ATTEN_B;
4445
u8 ATTEN_C;
@@ -85,6 +86,7 @@ class Mikey
8586
void InitPalettes();
8687
void ResetTimers();
8788
void ResetAudio();
89+
void ResetUART();
8890
void ResetPalette();
8991
u8 ReadColor(u16 address);
9092
void WriteColor(u16 address, u8 value);
@@ -103,6 +105,9 @@ class Mikey
103105
void RebuildLFSR(GLYNX_Mikey_Audio* channel);
104106
void CalculateCutoff(u8 channel);
105107
void UpdateIRQs();
108+
void UartRelevelIRQ();
109+
void UartBeginFrame(u8 data);
110+
void UartClock();
106111
void HorizontalBlank();
107112
void VerticalBlank();
108113
void LineDMA(int line);

0 commit comments

Comments
 (0)