Interrupt controller module description (WIP), task switch and return to menu.

This commit is contained in:
lephe 2017-07-01 16:07:14 +02:00
parent cd6bd5a1a4
commit 5e66efc560
20 changed files with 297 additions and 130 deletions

3
TODO
View File

@ -6,11 +6,10 @@ Bugs to fix:
Things to do before 1.0:
- bopti: Test partial transparency
* core: Allow return to menu
- demo: Try 284x124 at (-60, -28) (all disadvantages)
- project: Check size of *all* library structures
- project: Clean headers that have some internal definitions
- project: Unify this hellish mess of register access!
- project: Get rid of 7705.h (keyboard) and 7305.h (keyboard, gint)
- time: Compute CLOCKS_PER_SEC
Things to do later:

View File

@ -277,6 +277,9 @@ void tlb_debug(void)
}
*/
#include <modules/rtc.h>
#include <modules/interrupts.h>
/*
main_menu()
Displays the main menu and returns user's choice: 0 for [EXIT],
@ -301,7 +304,7 @@ void main_menu(int *category, int *app)
};
const char *list_tests[] = {
"Keyboard and events",
"Keyboard & events",
"Gray engine",
"Image rendering",
"Text rendering",
@ -353,9 +356,10 @@ void main_menu(int *category, int *app)
{
case 0:
print(1, 1, "gint %s", gint_version);
print(2, 4, "handler size: %5d", gint_size);
print(2, 5, "mpu type: %7s", mpu);
print(2, 6, "romdata: %08x", &romdata);
print(2, 3, "MPU type %7s", mpu);
print(2, 4, "Add-in size %3dk",
((uint32_t)&romdata - 0x00300000) >> 10);
print(2, 5, "gint size %5do", gint_size);
list = NULL;
break;
@ -408,9 +412,6 @@ void main_menu(int *category, int *app)
switch(getkey())
{
// case KEY_7:
// crt0_system_menu();
// break;
case KEY_F1:
if(!tab) break;
tab = 0;
@ -434,6 +435,9 @@ void main_menu(int *category, int *app)
index = 0;
scroll = 0;
break;
case KEY_F5:
gint_switch();
break;
case KEY_F6:;
void screen(void);
screen();
@ -480,11 +484,11 @@ void main_menu(int *category, int *app)
if(app) *app = index + 1;
return;
case KEY_MENU:
/* case KEY_MENU:
if(category) *category = 0;
if(app) *app = 0;
return;
*/
default:
leave = 0;
}

View File

@ -3,7 +3,7 @@
output. Note how symbols romdata, bbss, ebss, bdata and edata are used
in the initialization routine (crt0.c) to initialize the application.
Two ram areas are specified. The "real ram" is accessed direcly while
Two ram areas are specified. The "real ram" is accessed directly while
the other area is virtualized. It is not possible to execute code in
virtualized ram.
*/

View File

@ -4,8 +4,9 @@
#include <stdlib.h>
#include <events.h>
static void draw_keyboard(volatile uint8_t *state)
static int draw_keyboard(volatile uint8_t *state)
{
int pressed_keys = 0;
int i, j, k, l;
int x, y;
@ -29,6 +30,7 @@ static void draw_keyboard(volatile uint8_t *state)
// Drawing a filled shape when the key is pressed.
if(state[i] & (0x80 >> j))
{
pressed_keys++;
for(k = -2; k <= 2; k++) for(l = -2; l <= 2; l++)
if(abs(k) + abs(l) <= 2)
dpixel(x + k, y + l, color_black);
@ -49,47 +51,61 @@ static void draw_keyboard(volatile uint8_t *state)
// An horizontal line to separate parts of the keyboard.
dline(5, 28, 32, 28, color_black);
return pressed_keys;
}
typedef struct {
event_type_t type;
uint32_t key;
int repeats;
} history_key_t;
} enhanced_event_t;
typedef struct {
history_key_t *data;
int size;
int pressed;
} history_t;
static void push_history(enhanced_event_t *history, int size, event_t event)
static int pressed_keys = -1;
static int releases = 0;
static void history_push(history_t *h, event_t event)
{
#define event_eq(x, y) \
((x).type == (y).type && (x).key == (y).key.code)
// Only reset the number of pressed keys in the history when the actual
// number of pressed keys reaches 0.
// Determining where the history ends.
int length = 0;
while(length < size && history[length].type != event_none) length++;
// Checking if the previous event is being repeated.
if(length > 0 && event_eq(history[length - 1], event))
if(event.type == event_key_release)
{
history[length - 1].repeats++;
pressed_keys--;
if(!pressed_keys) h->pressed = 0;
return;
}
// Making up some space if required.
if(length == size)
// Now we're sure a key was pressed or repeated. Check if it's in the
// last pressed elements of the history array, if so, update it.
// Otherwise make space for a new entry and add it.
if(event.type == event_key_press) pressed_keys++;
for(int i = h->size - h->pressed; i < h->size; i++)
{
for(int i = 0; i < size - 1; i++) history[i] = history[i + 1];
length = size - 1;
if(h->data[i].key == event.key.code)
{
h->data[i].repeats++;
return;
}
}
// Adding a new entry to the history.
history[length].type = event.type;
history[length].key = event.key.code;
history[length].repeats = 1;
// If there are already h->size keys pressed, do not add more.
if(event.type == event_key_press) h->pressed++;
if(h->pressed > h->size) return;
#undef event_eq
for(int i = 0; i < h->size - 1; i++) h->data[i] = h->data[i + 1];
h->data[h->size - 1].key = event.key.code;
h->data[h->size - 1].repeats = 1;
}
static void draw_events(enhanced_event_t *history, int size)
static void draw_events(history_t *h)
{
const char *key_names[] = {
"F1", "F2", "F3", "F4", "F5", "F6",
@ -102,17 +118,21 @@ static void draw_events(enhanced_event_t *history, int size)
"1", "2", "3", "+", "-", NULL,
"0", ".", "\x08", "(-)", "EXE", NULL
};
const char *event_names[] = {
"None ", "User ", "Press", "Rept.", "Rel. ", "Timer"
};
for(int i = 0; i < size && history[i].type != event_none; i++)
int y = 3;
print(8, 2, "%d %d %d", pressed_keys, h->pressed, releases);
for(int i = 0; i < h->size; i++)
{
print(8, 3 + i, "%s %s", event_names[history[i].type],
key_names[key_id(history[i].key)]);
if(history[i].repeats > 1)
print(19, 3 + i, "%d", history[i].repeats);
if(!h->data[i].key) continue;
print(8, y, "%s", key_names[key_id(h->data[i].key)]);
if(h->data[i].repeats > 1)
print(18, y, "%d", h->data[i].repeats);
y++;
}
if(h->pressed > h->size) print(8, 8, "(more)");
}
/*
@ -121,23 +141,31 @@ static void draw_events(enhanced_event_t *history, int size)
*/
void test_keyboard_events(void)
{
enhanced_event_t history[5];
int history_size = 5;
history_key_t history_data[5] = { 0 };
history_t history = {
.data = history_data,
.size = 5,
.pressed = 0,
};
event_t event;
for(int i = 0; i < history_size; i++) history[i].type = event_none;
while(1)
{
dclear();
locate(1, 1, "Keyboard and events");
draw_keyboard(keyboard_stateBuffer());
draw_events(history, history_size);
// There may be more than zero keys pressed when this test
// starts. We need to detect this count automatically.
int x = draw_keyboard(keyboard_stateBuffer());
if(pressed_keys < 0) pressed_keys = x;
draw_events(&history);
dupdate();
event = waitevent();
if(event.type == event_key_release) releases++;
if(event.type == event_key_press && event.key.code == KEY_EXIT)
break;
push_history(history, history_size, event);
history_push(&history, event);
}
}

View File

@ -236,4 +236,16 @@ typedef enum
*/
volatile void *gint_reg(gint_register_t reg);
//---
// Other functions
//---
/*
gint_switch()
Temporarily returns to the system's main menu.
*/
void gint_switch(void);
#endif // _GINT_H

View File

@ -51,6 +51,14 @@ uint32_t *gray_lightVRAM(void);
*/
uint32_t *gray_darkVRAM(void);
/*
gray_currentVRAM()
Returns the currently displayed video ram (if the engine runs). Used
internally, but has no interest for the user. You don't want to draw to
this vram.
*/
uint32_t *gray_currentVRAM(void);
/*
gray_getDelays()
Returns the gray engine delays. Pointers are not set if NULL.

View File

@ -162,6 +162,10 @@ typedef enum
// compatible models.
getkey_manage_backlight = 0x04,
// Allow returning to menu using the [MENU] key. (This operation is not
// absolutely safe.)
getkey_task_switch = 0x08,
// Allow key repetition. This option does not control the generation of
// repeat events (use keyboard_setRepeatRate() for this) but filters
// them. Please note that modifiers will never be repeated, even when

View File

@ -590,7 +590,7 @@ typedef struct
uint8_t IMR12;
char gap2[15];
} __attribute__((packed)) mod_intc_masks_7305_t;
} __attribute__((packed, aligned(4))) mod_intc_masks_7305_t;
/*
mod_intc_userimask_7305_t
@ -605,7 +605,7 @@ typedef struct
void set_user_imask(int new_level)
{
mod_intc_userimask_7305_t mask = *(INTC._7305.USERIMASK);
mask._a5 = 0xa5;
mask._0xa5 = 0xa5;
mask.UIMASK = new_level & 0x0f;
*(INTC._7305.USERIMASK) = mask;
}
@ -613,7 +613,7 @@ typedef struct
typedef struct
{
lword_union(,
uint _a5 :8; /* Always set to 0xa5 before writing */
uint _0xa5 :8; /* Always set to 0xa5 before writing */
uint :16;
uint UIMASK :4; /* User Interrupt Mask Level */
uint :4;
@ -628,7 +628,7 @@ typedef struct
*/
typedef struct
{
byte_union(,
word_union(,
uint const NMIL :1; /* NMI Interupt Level */
uint :14;
uint NMIFL :1; /* NMI Interrupt Request Flag */
@ -682,4 +682,7 @@ typedef union
} __attribute__((packed)) mod_intc_t;
// Here's what you'll need to use.
extern mod_intc_t INTC;
#endif // _MODULE_INTERRUPTS

View File

@ -39,7 +39,7 @@ typedef unsigned uint;
uint16_t word; \
struct { fields } \
__attribute__((packed)); \
} __attribute__((packed)) name
} __attribute__((packed, aligned(2))) name
/*
lword_union()
@ -52,6 +52,6 @@ typedef unsigned uint;
uint32_t lword; \
struct { fields } \
__attribute__((packed)); \
} __attribute__((packed)) name
} __attribute__((packed, aligned(4))) name
#endif // _MODULES_MACROS_H

View File

@ -140,8 +140,6 @@ __attribute__((section(".pretext.entry"))) int start(void)
dg->stage = stage_running;
#endif
// Saving the execution state there.
int x = setjmp(env);
// If the program has just started, executing main(). Otherwise, the
@ -253,13 +251,3 @@ int atexit(void (*function)(void))
atexit_handlers[atexit_index++] = function;
return 0;
}
void crt0_system_menu(void)
{
fini();
gint_quit();
__system_menu();
mmu_pseudoTLBInit();
gint_init();
init();
}

View File

@ -153,7 +153,7 @@ void gint_install(gint_interrupt_type_t type, void *function)
/*
gint_uninstall()
Uninstalls the exception or interrupt handler that was used for the
Un-installs the exception or interrupt handler that was used for the
given interrupt, falling back to gint's default handler.
*/
void gint_uninstall(gint_interrupt_type_t type)

View File

@ -4,7 +4,6 @@
#include <gint.h>
#include <timer.h>
#include <rtc.h>
#include <7705.h>
/*
gint_reg()
@ -34,15 +33,18 @@ volatile void *gint_reg_7705(gint_register_t reg)
void gint_save_7705(environment_7705_t *e)
{
mod_intc_7705_t *intc = &INTC._7705;
mod_intc_ipc_7705_t *ipc = &intc->iprs;
// Saving the interrupt masks from registers IPRA to IPRH.
e->IPR[0] = INTC.IPRA.WORD;
e->IPR[1] = INTC.IPRB.WORD;
e->IPR[2] = INTX.IPRC.WORD;
e->IPR[3] = INTX.IPRD.WORD;
e->IPR[4] = INTX.IPRE.WORD;
e->IPR[5] = INTX.IPRF.WORD;
e->IPR[6] = INTX.IPRG.WORD;
e->IPR[7] = INTX.IPRH.WORD;
e->IPR[0] = ipc->IPRA->word;
e->IPR[1] = ipc->IPRB->word;
e->IPR[2] = ipc->IPRC->word;
e->IPR[3] = ipc->IPRD->word;
e->IPR[4] = ipc->IPRE->word;
e->IPR[5] = ipc->IPRF->word;
e->IPR[6] = ipc->IPRG->word;
e->IPR[7] = ipc->IPRH->word;
// Saving RTC registers.
e->RCR1 = RTC.RCR1->byte;
@ -54,35 +56,39 @@ void gint_save_7705(environment_7705_t *e)
e->TMU2 = *(TMU.timers[2]);
e->TSTR = TMU.TSTR->byte;
// Saving port data used to access the keyboard.
/* // Saving port data used to access the keyboard.
e->PACR = PFC.PACR.WORD;
e->PADR = PA.DR.BYTE;
e->PBCR = PFC.PBCR.WORD;
e->PBDR = PB.DR.BYTE;
e->PMCR = PFC.PMCR.WORD;
e->PMDR = PM.DR.BYTE;
*/
}
void gint_lock_and_setup_7705(void)
{
mod_intc_7705_t *intc = &INTC._7705;
mod_intc_ipc_7705_t *ipc = &intc->iprs;
// Disabling everything by default to avoid receiving an interrupt that
// the handler doesn't handle, which would cause the user program to
// freeze.
INTC.IPRA.WORD = 0x0000;
INTC.IPRB.WORD = 0x0000;
INTX.IPRC.WORD = 0x0000;
INTX.IPRD.WORD = 0x0000;
INTX.IPRE.WORD = 0x0000;
INTX.IPRF.WORD = 0x0000;
INTX.IPRG.WORD = 0x0000;
INTX.IPRH.WORD = 0x0000;
ipc->IPRA->word = 0x0000;
ipc->IPRB->word = 0x0000;
ipc->IPRC->word = 0x0000;
ipc->IPRD->word = 0x0000;
ipc->IPRE->word = 0x0000;
ipc->IPRF->word = 0x0000;
ipc->IPRG->word = 0x0000;
ipc->IPRH->word = 0x0000;
// Allowing RTC and timer (which handles keyboard and a whole bunch of
// other things).
INTC.IPRA.BIT._RTC = 10;
INTC.IPRA.BIT._TMU0 = 12;
INTC.IPRA.BIT._TMU1 = 12;
INTC.IPRA.BIT._TMU2 = 12;
ipc->IPRA->RTC = 10;
ipc->IPRA->TMU0 = 12;
ipc->IPRA->TMU1 = 12;
ipc->IPRA->TMU2 = 12;
// Don't enable RTC periodic signals by default.
RTC.RCR2->byte = 0x09;
@ -90,15 +96,18 @@ void gint_lock_and_setup_7705(void)
void gint_restore_and_unlock_7705(environment_7705_t *e)
{
mod_intc_7705_t *intc = &INTC._7705;
mod_intc_ipc_7705_t *ipc = &intc->iprs;
// Restoring the saved states.
INTC.IPRA.WORD = e->IPR[0];
INTC.IPRB.WORD = e->IPR[1];
INTX.IPRC.WORD = e->IPR[2];
INTX.IPRD.WORD = e->IPR[3];
INTX.IPRE.WORD = e->IPR[4];
INTX.IPRF.WORD = e->IPR[5];
INTX.IPRG.WORD = e->IPR[6];
INTX.IPRH.WORD = e->IPR[7];
ipc->IPRA->word = e->IPR[0];
ipc->IPRB->word = e->IPR[1];
ipc->IPRC->word = e->IPR[2];
ipc->IPRD->word = e->IPR[3];
ipc->IPRE->word = e->IPR[4];
ipc->IPRF->word = e->IPR[5];
ipc->IPRG->word = e->IPR[6];
ipc->IPRH->word = e->IPR[7];
// Restoring RTC registers.
RTC.RCR1->byte = e->RCR1;
@ -110,11 +119,12 @@ void gint_restore_and_unlock_7705(environment_7705_t *e)
*(TMU.timers[2]) = e->TMU2;
TMU.TSTR->byte = e->TSTR;
// Restoring keyboard-related I/O port registers.
/* // Restoring keyboard-related I/O port registers.
PFC.PACR.WORD = e->PACR;
PA.DR.BYTE = e->PADR;
PFC.PBCR.WORD = e->PBCR;
PB.DR.BYTE = e->PBDR;
PFC.PMCR.WORD = e->PMCR;
PM.DR.BYTE = e->PMDR;
*/
}

View File

@ -79,3 +79,46 @@ void gint_quit(void)
// Restoring the system's VBR.
gint_setvbr(gint.system_vbr, stop);
}
//---
// System-switch technique
//---
#include <display.h>
#include <gray.h>
/*
gint_switch()
Temporarily returns to the system's main menu.
*/
static environment_t switch_env;
static void switch_(void)
{
isSH3() ? gint_restore_and_unlock_7705(&switch_env.env_7705)
: gint_restore_and_unlock_7305(&switch_env.env_7305);
}
void gint_switch(void)
{
// Save the current environment in a special buffer, so that we can
// restore it if we ever come back to gint.
isSH3() ? gint_save_7705(&switch_env.env_7705)
: gint_save_7305(&switch_env.env_7305);
// Give the control back to the system.
gint_setvbr(gint.system_vbr, stop);
// When returning to the add-in from the menu, the system displays the
// add-in's video ram again, but it's often blank. We thus need to copy
// the contents of the gint video ram to the system's.
void *vram = gray_runs()
? gray_currentVRAM()
: display_getCurrentVRAM();
// Use system calls to put KEY_MENU in the key buffer and invoke
// GetKeyWait(), triggering the main menu.
__system_menu(vram);
// If the user came back, restore the gint working environment.
gint_setvbr(gint.gint_vbr, switch_);
}

View File

@ -4,7 +4,6 @@
All the system calls used by the library. Somehow "the less, the
better".
We have finally gotten rid of every obscure system-related syscalls!
Looks like an important step towards being completely free-standing :)
*/
.global ___malloc
@ -45,6 +44,18 @@ ___system_menu:
sts.l pr, @-r15
add #-4, r15
/* Copying gint's VRAM into the system's. */
mov.l syscall_table, r1
mov.l .syscall_vram, r0
jsr @r1
mov.l r4, @r15 /* gint video ram */
mov r0, r4
mov.l @r15, r5
mov.w .vram_size, r6
mov.l .memcpy, r1
jsr @r1
nop
/* Putting the matrix code in the key buffer. */
mov r15, r4
@ -60,14 +71,14 @@ ___system_menu:
mov r15, r4 /* column pointer */
add #-4, r15
mov r15, r5 /* row pointer */
mov #-5, r15
mov r15, r1
add #-4, r15
mov r15, r1 /* keycode pointer */
mov #2, r6 /* type of waiting */
mov #0, r7 /* timeout period */
mov.l r1, @-r15 /* keycode pointer */
mov #0, r2
mov.l r2, @-r15 /* allow return to menu */
mov.l r1, @-r15
mov #0, r2 /* allow return to menu */
mov.l r2, @-r15
mov.l syscall_table, r1
mov.l .syscall_getkeywait, r0
@ -88,8 +99,14 @@ ___system_menu:
.long 0x0247
.syscall_putcode:
.long 0x024f
.syscall_vram:
.long 0x0135
.matrix_menu:
.word 0x0308
.vram_size:
.word 1024
.memcpy:
.long _memcpy

View File

@ -69,3 +69,5 @@ _inline int tolower(int c) {
_inline int toupper(int c) {
return c & ~(islower(c) << 1);
}
#undef _inline

View File

@ -140,6 +140,17 @@ uint32_t *gray_darkVRAM(void)
return vrams[(~current & 2) | 1];
}
/*
gray_currentVRAM()
Returns the currently displayed video ram (if the engine runs). Used
internally, but has no interest for the user. You don't want to draw to
this vram.
*/
uint32_t *gray_currentVRAM(void)
{
return vrams[current ^ 1];
}
/*
gray_getDelays()
Returns the gray engine delays. Pointers are not set if NULL.

View File

@ -3,6 +3,7 @@
#include <keyboard.h>
#include <events.h>
#include <screen.h>
#include <gint.h>
/*
getkey()
@ -15,6 +16,7 @@ int getkey(void)
getkey_shift_modifier |
getkey_alpha_modifier |
getkey_manage_backlight |
getkey_task_switch |
getkey_repeat_arrow_keys,
0
@ -62,6 +64,11 @@ int getkey_opt(getkey_option_t options, int delay_ms)
modifier &= ~MOD_SHIFT;
continue;
}
if((options & getkey_task_switch) && key == KEY_MENU
&& !modifier)
{
continue;
}
if(options & getkey_shift_modifier && key == KEY_SHIFT)
{
modifier ^= MOD_SHIFT;
@ -98,6 +105,13 @@ int getkey_opt(getkey_option_t options, int delay_ms)
break;
case event_key_release:
if((options & getkey_task_switch) && event.key.code == KEY_MENU
&& !modifier)
{
gint_switch();
continue;
}
if((int)event.key.code != last_key) break;
last_key = KEY_NONE;
last_repeats = 0;

View File

@ -82,42 +82,66 @@ static inline void push_release(int keycode)
*/
void keyboard_interrupt(void)
{
uint8_t state[10] = { 0 };
// This procedure is critical for speed. If there's anything that could
// be optimized, please tell me.
// New keyboard state.
uint8_t state[10] = { 0 };
isSH3() ? keyboard_updateState_7705(state)
: keyboard_updateState_7305(state);
// This procedure really needs to be speed-optimized... and it's hard
// because of this bit manipulation. This condition handles AC/ON.
if(keyboard_state[0] ^ state[0])
{
uint8_t pressed = ~keyboard_state[0] & state[0];
uint8_t released = keyboard_state[0] & ~state[0];
// Event types associated with each old/new state pair (see later).
uint8_t events[4] = {
event_none,
event_key_release,
event_key_press,
event_key_repeat,
};
if(pressed & 1) push_press(KEY_AC_ON);
if(released & 1) push_release(KEY_AC_ON);
// AC/ON has not a matrix code that corresponds to its location in the
// buffer, so we need to check it independently.
if(keyboard_state[0] | state[0])
{
int kind = (state[0] << 1) | keyboard_state[0];
if(kind)
{
event_t event = {
.type = events[kind],
.key.code = KEY_AC_ON,
.key.id = key_id(KEY_AC_ON),
.key.character = key_char(KEY_AC_ON)
};
event_push(event);
}
}
keyboard_state[0] = state[0];
for(int row = 1; row <= 9; row++)
{
uint8_t pressed = ~keyboard_state[row] & state[row];
uint8_t repeated = keyboard_state[row] & state[row];
uint8_t released = keyboard_state[row] & ~state[row];
// Shifting the new state will allow us to make up a 2-bit
// value for each key more easily, improving efficiency.
uint16_t old = keyboard_state[row];
uint16_t new = state[row] << 1;
if(!new && !old) continue;
keyboard_state[row] = state[row];
// Make this a bit faster.
if(!(pressed | repeated | released)) continue;
for(int column = 0; column < 8; column++)
for(uint8_t code = row; code < (row | 0x80); code += 0x10)
{
if(pressed & 1) push_press ((column << 4) | row);
if(repeated & 1) push_repeat ((column << 4) | row);
if(released & 1) push_release((column << 4) | row);
int kind = (new & 2) | (old & 1);
old >>= 1;
new >>= 1;
pressed >>= 1;
repeated >>= 1;
released >>= 1;
if(!kind) continue;
event_t event = {
.type = events[kind],
.key.code = code,
.key.id = key_id(code),
.key.character = key_char(code)
};
event_push(event);
}
}

View File

@ -44,7 +44,7 @@ static void rtc_cb_update(void)
-1 Array is full
-2 Invalid parameter
The number of repeats may be set to 0, in which case the callback is
called indefinitely unless the user calls rtc_cb_end().
called indefinitely until the user calls rtc_cb_end().
*/
int rtc_cb_add(rtc_frequency_t freq, void (*function)(void), int repeats)
{

View File

@ -1 +1 @@
beta-0.9-354
beta-0.9-410