Skip to content

Commit 9ad800d

Browse files
committed
[core] Initial implementation of hardware cursor
1 parent 8f35ca0 commit 9ad800d

File tree

3 files changed

+133
-63
lines changed

3 files changed

+133
-63
lines changed

core/keypad.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,19 @@ void emu_set_keypad_ghosting(int enable) {
2929
keypad_atomics.ghosting = enable;
3030
}
3131

32-
static inline void keypad_intrpt_check() {
32+
static inline void keypad_intrpt_check(void) {
3333
intrpt_set(INT_KEYPAD, keypad.status & keypad.enable);
3434
}
3535

36-
static inline uint8_t keypad_row_limit() {
36+
static inline uint8_t keypad_row_limit(void) {
3737
return keypad.rows >= KEYPAD_MAX_ROWS ? KEYPAD_MAX_ROWS : keypad.rows;
3838
}
3939

40-
static inline uint8_t keypad_actual_row_limit() {
40+
static inline uint8_t keypad_actual_row_limit(void) {
4141
return keypad.rows >= KEYPAD_ACTUAL_ROWS ? KEYPAD_ACTUAL_ROWS : keypad.rows;
4242
}
4343

44-
static inline uint16_t keypad_data_mask() {
44+
static inline uint16_t keypad_data_mask(void) {
4545
uint8_t colLimit = keypad.cols >= KEYPAD_ACTUAL_COLS ? KEYPAD_ACTUAL_COLS : keypad.cols;
4646
return (1 << colLimit) - 1;
4747
}

core/lcd.c

Lines changed: 113 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,39 @@ static inline uint32_t lcd_rgb888out(uint32_t rgb888) {
5454
return lcd_argb8888out(UINT32_C(0xFF000000) | (rgb888 & 0xF8FCF8));
5555
}
5656

57+
static inline void lcd_intrpt_check(void) {
58+
intrpt_set(INT_LCD, lcd.ris & lcd.imsc);
59+
}
60+
61+
static inline void lcd_crsr_update(void) {
62+
if (lcd.compare != LCD_FRONT_PORCH) {
63+
return;
64+
}
65+
lcd_crsr_state_t *crsrRegs = &lcd.crsrRegs[lcd.crsrConfig >> 1 & 1];
66+
uint32_t crsrSize = ((lcd.crsrConfig & 1) + 1) * 32;
67+
uint32_t crsrClipX = crsrRegs->crsrClip & 0xFF;
68+
uint32_t crsrClipY = crsrRegs->crsrClip >> 8;
69+
uint32_t crsrRow = crsrRegs->crsrXY >> 16;
70+
uint32_t crsrRowUnclipped = crsrRow - crsrClipY;
71+
/* This value is treated as a signed 12-bit value for the cursor interrupt check */
72+
crsrRowUnclipped = (int32_t)(crsrRowUnclipped << 20) >> 20;
73+
uint32_t crsrRowOffset = lcd.curRow - crsrRowUnclipped;
74+
if (likely((int32_t)crsrRowOffset >= (int32_t)crsrSize)) {
75+
if (!likely(lcd.crsrIntrptd)) {
76+
lcd.crsrIntrptd = true;
77+
lcd.ris |= 1 << 0;
78+
lcd_intrpt_check();
79+
}
80+
lcd.curCrsrWidth = 0;
81+
} else if (!unlikely(lcd.crsrControl) || likely(lcd.curRow < crsrRow) || crsrClipX >= crsrSize) {
82+
lcd.curCrsrWidth = 0;
83+
} else {
84+
lcd.curCrsrOffset = ((crsrRegs->crsrImage * crsrSize + crsrRowOffset) * crsrSize + crsrClipX) & 0xFFF;
85+
lcd.curCrsrCol = crsrRegs->crsrXY & 0xFFFF;
86+
lcd.curCrsrWidth = crsrSize - crsrClipX;
87+
}
88+
}
89+
5790
void emu_set_lcd_callback(bool (*callback)(void*), void *data) {
5891
lcd.gui_callback = callback;
5992
lcd.gui_callback_data = data;
@@ -187,15 +220,22 @@ static uint32_t lcd_process_pixel(uint32_t ticks, uint16_t bgr565) {
187220
}
188221
panel_hsync();
189222
panel_clock_porch(lcd.HSW + lcd.HBP);
223+
lcd_crsr_update();
190224
}
191225

192226
if (unlikely(sched_active(SCHED_PANEL))) {
193227
panel_scan_until(sched_ticks_remaining_relative(SCHED_PANEL, SCHED_LCD_DMA, ticks));
194228
}
195-
assert(lcd.curCol < lcd.PPL);
229+
assert(lcd.curCol < lcd.CPL);
230+
uint32_t crsrColOffset = lcd.curCol - lcd.curCrsrCol;
231+
if (unlikely(crsrColOffset < lcd.curCrsrWidth)) {
232+
uint32_t crsrOffset = lcd.curCrsrOffset + crsrColOffset;
233+
uint8_t crsrPixel = lcd.crsrImageBytes[crsrOffset >> 2] >> ((~crsrOffset & 3) * 2) & 3;
234+
bgr565 = (crsrPixel & 2 ? bgr565 : 0) ^ lcd.crsrPalette[crsrPixel];
235+
}
196236
panel.clock_pixel(bgr565);
197237

198-
if (unlikely(++lcd.curCol >= lcd.PPL)) {
238+
if (unlikely(++lcd.curCol >= lcd.CPL)) {
199239
panel_clock_porch(lcd.HFP);
200240
lcd.curCol = 0;
201241
lcd.curRow++;
@@ -301,6 +341,7 @@ static void lcd_event(enum sched_item_id id) {
301341
enum lcd_comp compare = lcd.control >> 12 & 3;
302342
switch (lcd.compare) {
303343
case LCD_FRONT_PORCH:
344+
lcd.ris |= !lcd.crsrIntrptd << 0;
304345
if (lcd.VFP) {
305346
if (compare == LCD_FRONT_PORCH) {
306347
lcd.ris |= 1 << 3;
@@ -348,6 +389,8 @@ static void lcd_event(enum sched_item_id id) {
348389
duration = ((lcd.VSW - 1) * (lcd.HSW + lcd.HBP + lcd.CPL + lcd.HFP) +
349390
lcd.HSW) * lcd.PCD + 1;
350391
lcd.prefill = true;
392+
lcd.crsrIntrptd = false;
393+
lcd.crsrRegs[1] = lcd.crsrRegs[0];
351394
if (lcd.useDma) {
352395
lcd.pos = 0;
353396
lcd.curRow = lcd.curCol = 0;
@@ -385,7 +428,7 @@ static void lcd_event(enum sched_item_id id) {
385428
lcd.compare = LCD_FRONT_PORCH;
386429
break;
387430
}
388-
intrpt_set(INT_LCD, lcd.ris & lcd.imsc);
431+
lcd_intrpt_check();
389432
sched_repeat(id, duration);
390433
}
391434

@@ -418,6 +461,7 @@ static void lcd_init_events(void) {
418461

419462
void lcd_reset(void) {
420463
memset(&lcd, 0, offsetof(lcd_state_t, useDma));
464+
lcd.crsrPalette[3] = 0xFFFF;
421465
lcd_update();
422466
lcd_init_events();
423467
gui_console_printf("[CEmu] LCD reset.\n");
@@ -432,9 +476,9 @@ static uint8_t lcd_read(const uint16_t pio, bool peek) {
432476
if (index < 0x014 && index >= 0x010) { return read8(lcd.upbase, bit_offset); }
433477
if (index < 0x018 && index >= 0x014) { return read8(lcd.lpbase, bit_offset); }
434478
if (index < 0x01C && index >= 0x018) { return read8(lcd.control, bit_offset); }
435-
if (index < 0x020 && index >= 0x01C) { return read8(lcd.imsc, bit_offset); }
436-
if (index < 0x024 && index >= 0x020) { return read8(lcd.ris, bit_offset); }
437-
if (index < 0x028 && index >= 0x024) { return read8(lcd.imsc & lcd.ris, bit_offset); }
479+
if (index == 0x01C) { return lcd.imsc & ~1; }
480+
if (index == 0x020) { return lcd.ris & ~1; }
481+
if (index == 0x024) { return lcd.imsc & lcd.ris & ~1; }
438482
if (index < 0x030 && index >= 0x02C) {
439483
if (!peek) {
440484
sched_process_pending_dma(0);
@@ -453,15 +497,15 @@ static uint8_t lcd_read(const uint16_t pio, bool peek) {
453497
if (!peek) {
454498
cpu.cycles--;
455499
}
456-
if (index == 0xC00) { return read8(lcd.crsrControl, bit_offset); }
457-
if (index == 0xC04) { return read8(lcd.crsrConfig, bit_offset); }
500+
if (index == 0xC00) { return lcd.crsrControl | (lcd.crsrRegs[0].crsrImage << 4); }
501+
if (index == 0xC04) { return lcd.crsrConfig; }
458502
if (index < 0xC0C && index >= 0xC08) { return read8(lcd.crsrPalette0, bit_offset); }
459503
if (index < 0xC10 && index >= 0xC0C) { return read8(lcd.crsrPalette1, bit_offset); }
460-
if (index < 0xC14 && index >= 0xC10) { return read8(lcd.crsrXY, bit_offset); }
461-
if (index < 0xC16 && index >= 0xC14) { return read8(lcd.crsrClip, bit_offset); }
462-
if (index == 0xC20) { return read8(lcd.crsrImsc, bit_offset); }
463-
if (index == 0xC28) { return read8(lcd.crsrRis, bit_offset); }
464-
if (index == 0xC2C) { return read8(lcd.crsrRis & lcd.crsrImsc, bit_offset); }
504+
if (index < 0xC14 && index >= 0xC10) { return read8(lcd.crsrRegs[0].crsrXY, bit_offset); }
505+
if (index < 0xC16 && index >= 0xC14) { return read8(lcd.crsrRegs[0].crsrClip, bit_offset); }
506+
if (index == 0xC20) { return lcd.imsc & 1; }
507+
if (index == 0xC28) { return lcd.ris & 1; }
508+
if (index == 0xC2C) { return lcd.ris & lcd.imsc & 1; }
465509
} else if (index >= 0xFE0) {
466510
static const uint8_t id[1][8] = {
467511
{ 0x11, 0x11, 0x14, 0x00, 0x0D, 0xF0, 0x05, 0xB1 }
@@ -620,12 +664,16 @@ static void lcd_write(const uint16_t pio, const uint8_t value, bool poke) {
620664
}
621665
}
622666
} else if (index == 0x01C) {
623-
write8(lcd.imsc, bit_offset, value);
624-
lcd.imsc &= 0x1E;
625-
intrpt_set(INT_LCD, lcd.ris & lcd.imsc);
667+
if (likely(bit_offset == 0)) {
668+
lcd.imsc &= ~0x1E;
669+
lcd.imsc |= value & 0x1E;
670+
lcd_intrpt_check();
671+
}
626672
} else if (index == 0x028) {
627-
lcd.ris &= ~(value << bit_offset);
628-
intrpt_set(INT_LCD, lcd.ris & lcd.imsc);
673+
if (likely(bit_offset == 0)) {
674+
lcd.ris &= ~(value & 0x1E);
675+
lcd_intrpt_check();
676+
}
629677
}
630678
lcd_update();
631679
} else if (index < 0x400) {
@@ -643,40 +691,58 @@ static void lcd_write(const uint16_t pio, const uint8_t value, bool poke) {
643691
}
644692
} else if (index < 0xC00) {
645693
if (index >= 0x800) {
694+
if (!poke && unlikely(lcd.crsrControl)) {
695+
sched_process_pending_dma(0);
696+
}
646697
lcd.crsrImageBytes[pio - 0x800] = value;
647698
}
648699
} else if (index < 0xE00) {
649-
if (index == 0xC00) {
650-
write8(lcd.crsrControl, bit_offset, value);
651-
}
652-
if (index == 0xC04) {
653-
write8(lcd.crsrConfig, bit_offset, value);
654-
lcd.crsrConfig &= 0xF;
655-
}
656-
if (index == 0xC08) {
657-
write8(lcd.crsrPalette0, bit_offset, value);
658-
}
659-
if (index == 0xC0C) {
660-
write8(lcd.crsrPalette1, bit_offset, value);
661-
}
662-
if (index == 0xC10) {
663-
write8(lcd.crsrXY, bit_offset, value);
664-
lcd.crsrXY &= (0xFFF | (0xFFF << 16));
665-
}
666-
if (index == 0xC14) {
667-
write8(lcd.crsrClip, bit_offset, value);
668-
lcd.crsrClip &= (0x3F | (0x3F << 8));
669-
}
670-
if (index == 0xC20) {
671-
write8(lcd.crsrImsc, bit_offset, value);
672-
lcd.crsrImsc &= 0xF;
673-
}
674-
if (index == 0xC24) {
675-
lcd.crsrRis &= ~(value << bit_offset);
676-
lcd.crsrRis &= 0xF;
677-
}
678700
if (!poke) {
679701
lcd_write_crsr_delay();
702+
if (index < 0xC18) {
703+
sched_process_pending_dma(0);
704+
}
705+
}
706+
if (index == 0xC00) {
707+
if (likely(bit_offset == 0)) {
708+
lcd.crsrControl = value & 1;
709+
lcd.crsrRegs[0].crsrImage = value >> 4 & 3;
710+
lcd_crsr_update();
711+
}
712+
} else if (index == 0xC04) {
713+
if (likely(bit_offset == 0)) {
714+
lcd.crsrConfig = value & 3;
715+
lcd_crsr_update();
716+
}
717+
} else if (index == 0xC08) {
718+
if (likely(bit_offset < 24)) {
719+
write8(lcd.crsrPalette0, bit_offset, value);
720+
lcd.crsrPalette[0] = c888(lcd.crsrPalette0);
721+
}
722+
} else if (index == 0xC0C) {
723+
if (likely(bit_offset < 24)) {
724+
write8(lcd.crsrPalette1, bit_offset, value);
725+
lcd.crsrPalette[1] = c888(lcd.crsrPalette1);
726+
}
727+
} else if (index == 0xC10) {
728+
write8(lcd.crsrRegs[0].crsrXY, bit_offset, value);
729+
lcd.crsrRegs[0].crsrXY &= (0xFFF | (0xFFF << 16));
730+
lcd_crsr_update();
731+
} else if (index == 0xC14) {
732+
write8(lcd.crsrRegs[0].crsrClip, bit_offset, value);
733+
lcd.crsrRegs[0].crsrClip &= (0x3F | (0x3F << 8));
734+
lcd_crsr_update();
735+
} else if (index == 0xC20) {
736+
if (likely(bit_offset == 0)) {
737+
lcd.imsc &= ~1;
738+
lcd.imsc |= value & 1;
739+
lcd_intrpt_check();
740+
}
741+
} else if (index == 0xC24) {
742+
if (likely(bit_offset == 0)) {
743+
lcd.ris &= ~(value & 1);
744+
lcd_intrpt_check();
745+
}
680746
}
681747
}
682748
}

core/lcd.h

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,20 @@ enum lcd_comp {
2626
LCD_LNBU
2727
};
2828

29+
typedef struct lcd_crsr_state {
30+
uint32_t crsrXY; /* Cursor XY position register */
31+
uint32_t crsrClip; /* Cursor clip position register */
32+
uint32_t crsrImage; /* Cursor image index */
33+
} lcd_crsr_state_t;
34+
2935
typedef struct lcd_state {
3036
uint32_t timing[4];
3137

32-
uint32_t control; /* Control register */
33-
uint32_t imsc; /* Interrupt mask set/clear register */
34-
uint32_t ris;
38+
uint32_t control; /* Control register */
39+
uint8_t imsc; /* Interrupt mask set/clear register, plus cursor interrupt bit */
40+
uint8_t ris; /* Interrupt raw status register, plus cursor interrupt bit */
41+
uint8_t crsrControl; /* Cursor control register */
42+
uint8_t crsrConfig; /* Cursor configuration register */
3543

3644
uint32_t upbase; /* Upper panel frame base address register */
3745
uint32_t lpbase; /* Lower panel frame base address register */
@@ -51,23 +59,19 @@ typedef struct lcd_state {
5159
uint32_t crsrImageWords[0x100]; /* For alignment */
5260
uint8_t crsrImageBytes[0x400];
5361
};
54-
uint32_t crsrControl; /* Cursor control register */
55-
uint32_t crsrConfig; /* Cursor configuration register */
62+
5663
uint32_t crsrPalette0; /* Cursor palette registers */
5764
uint32_t crsrPalette1;
58-
uint32_t crsrXY; /* Cursor XY position register */
59-
uint32_t crsrClip; /* Cursor clip position register */
60-
uint32_t crsrImsc; /* Cursor interrupt mask set/clear register */
61-
uint32_t crsrIcr; /* Cursor interrupt clear register */
62-
uint32_t crsrRis; /* Cursor raw interrupt status register - const */
65+
lcd_crsr_state_t crsrRegs[2]; /* Async and frame sync cursor registers */
6366

6467
/* Internal */
65-
bool prefill;
68+
bool prefill, crsrIntrptd;
6669
uint8_t pos;
67-
uint32_t curCol, curRow, fifo[64];
70+
uint32_t curCol, curRow, curCrsrCol, curCrsrWidth, curCrsrOffset, fifo[64];
6871
enum lcd_comp compare;
6972
uint32_t PPL, HSW, HFP, HBP, LPP, VSW, VFP, VBP, PCD, ACB, CPL, LED, LCDBPP, BPP, PPF;
7073
bool CLKSEL, IVS, IHS, IPC, IOE, LEE, BGR, BEBO, BEPO, WTRMRK;
74+
uint16_t crsrPalette[4]; /* Palette stored as RGB565 or BGR565, and transparency/inversion masks */
7175
uint16_t palette[0x100]; /* Palette stored as RGB565 in native endianness */
7276

7377
/* Everything above here goes into the state */

0 commit comments

Comments
 (0)