diff --git a/Makefile b/Makefile index 9e84bf3..04677b1 100755 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ include Makefile.cfg # Modules modules-gint = bopti clock core display events gray keyboard mmu rtc \ screen tales timer -modules-libc = setjmp stdio stdlib string time +modules-libc = ctype setjmp stdio stdlib string time # Targets target-lib = libgint.a diff --git a/TODO b/TODO index 45638bd..9134ce0 100644 --- a/TODO +++ b/TODO @@ -23,8 +23,8 @@ Larger improvements: * core: Allow return to menu - serial: Implement a driver - usb: Implement a driver -- esper: Cleaner playback, synthetizing -- clock: Handle overclocking (relaunch clocks when overclocking) +- esper: Cleaner playback, synthesizing +- clock: Handle overclock (relaunch clocks when overclocking) - project: Unify this hellish mess of register access! Things to investigate: diff --git a/demo/gintdemo.c b/demo/gintdemo.c index bbd1785..98d1ac5 100644 --- a/demo/gintdemo.c +++ b/demo/gintdemo.c @@ -288,7 +288,7 @@ void main_menu(int *category, int *app) // Quite a few things to declare... //--- - extern Image res_opt_menu; + extern image_t res_opt_menu; const char *mpu, *mpu_names[] = { "Unknown", @@ -332,7 +332,7 @@ void main_menu(int *category, int *app) int i; mpu = mpu_names[MPU_CURRENT < 5 ? MPU_CURRENT : 5]; - text_configure(NULL, Color_Black); + text_configure(NULL, color_black); while(1) { @@ -386,8 +386,8 @@ void main_menu(int *category, int *app) if(scroll > 0) locate(20, 2, "\x0d"); if(scroll + 6 < list_len) locate(20, 7, "\x0e"); - dreverse_area(0, 8 * (index - scroll) + 8, 127, - 8 * (index - scroll) + 15); + drect(0, 8 * (index - scroll) + 8, 127, + 8 * (index - scroll) + 15, color_invert); } dupdate(); @@ -495,8 +495,6 @@ int main(void) { int category, app; - sleep_ms(2000); - while(1) { main_menu(&category, &app); diff --git a/demo/test_bopti.c b/demo/test_bopti.c index 818bdb6..a908823 100644 --- a/demo/test_bopti.c +++ b/demo/test_bopti.c @@ -23,7 +23,7 @@ --------------------------------------------------------- */ -static void getwh(Image *img, int *width, int *height) +static void getwh(image_t *img, int *width, int *height) { const uint8_t *data; @@ -42,7 +42,7 @@ static void getwh(Image *img, int *width, int *height) *height = (data[2] << 8) | data[3]; } -static void getxy(Image *img, int *x, int *y) +static void getxy(image_t *img, int *x, int *y) { int width, height; @@ -51,10 +51,10 @@ static void getxy(Image *img, int *x, int *y) *y = 28 - (height >> 1); } -static Image *select(Image *current) +static image_t *select(image_t *current) { - extern Image res_bopti_thumbs; - extern Image + extern image_t res_bopti_thumbs; + extern image_t res_items, res_sprites, res_swords, @@ -62,7 +62,7 @@ static Image *select(Image *current) res_isometric; struct { - Image *img; + image_t *img; const char *name; const char *info; } images[] = { @@ -74,7 +74,7 @@ static Image *select(Image *current) { NULL, NULL, NULL } }; - Image *thumbs = &res_bopti_thumbs; + image_t *thumbs = &res_bopti_thumbs; int items = 0; static int row = 0; int leave = 1, i; @@ -106,7 +106,7 @@ static Image *select(Image *current) } } - greverse_area(0, 8 * row + 8, 128, 8 * row + 23); + grect(0, 8 * row + 8, 128, 8 * row + 23, color_invert); gupdate(); do @@ -138,8 +138,8 @@ static Image *select(Image *current) void test_bopti(void) { - extern Image res_opt_bitmap; - Image *img = NULL; + extern image_t res_opt_bitmap; + image_t *img = NULL; int leave = 1; int black_bg = 0; @@ -152,10 +152,10 @@ void test_bopti(void) gray_start(); gclear(); - if(black_bg) greverse_area(0, 0, 127, 63); + if(black_bg) grect(0, 0, 127, 63, color_invert); if(img) gimage(x, y, img); - gclear_area(0, 55, 127, 63); + grect(0, 55, 127, 63, color_white); gimage(0, 56, &res_opt_bitmap); gupdate(); } @@ -164,10 +164,10 @@ void test_bopti(void) gray_stop(); dclear(); - if(black_bg) dreverse_area(0, 0, 127, 63); + if(black_bg) drect(0, 0, 127, 63, color_invert); if(img) dimage(x, y, img); - dclear_area(0, 55, 127, 63); + drect(0, 55, 127, 63, color_white); dimage(0, 56, &res_opt_bitmap); dupdate(); } diff --git a/demo/test_gray.c b/demo/test_gray.c index b562f1f..4a1ac03 100644 --- a/demo/test_gray.c +++ b/demo/test_gray.c @@ -10,9 +10,9 @@ static void draw(int delay1, int delay2, int selected) { - extern Image res_opt_gray; - unsigned int *vl = gray_lightVRAM(); - unsigned int *vd = gray_darkVRAM(); + extern image_t res_opt_gray; + uint32_t *vl = gray_lightVRAM(); + uint32_t *vd = gray_darkVRAM(); gclear(); locate(1, 1, "Gray engine"); @@ -58,7 +58,7 @@ void test_gray(void) } changed = 0; - key = getkey_opt(Getkey_RepeatArrowKeys, 1); + key = getkey_opt(getkey_repeat_arrow_keys, 25); if(key == KEY_EXIT) break; changed = 1; diff --git a/demo/test_keyboard.c b/demo/test_keyboard.c index 87e3ead..2046118 100644 --- a/demo/test_keyboard.c +++ b/demo/test_keyboard.c @@ -31,24 +31,24 @@ static void draw_keyboard(volatile uint8_t *state) { 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); + dpixel(x + k, y + l, color_black); } // Drawing a square border otherwise. else { for(k = -1; k <= 1; k++) for(l = -1; l <= 1; l++) - if(k || l) dpixel(x + k, y + l, Color_Black); + if(k || l) dpixel(x + k, y + l, color_black); } } // Binding the arrow keys together for a more visual thing. - dpixel(28, 19, Color_Black); dpixel(29, 19, Color_Black); - dpixel(28, 24, Color_Black); dpixel(29, 24, Color_Black); - dpixel(26, 21, Color_Black); dpixel(26, 22, Color_Black); - dpixel(31, 21, Color_Black); dpixel(31, 22, Color_Black); + dpixel(28, 19, color_black); dpixel(29, 19, color_black); + dpixel(28, 24, color_black); dpixel(29, 24, color_black); + dpixel(26, 21, color_black); dpixel(26, 22, color_black); + dpixel(31, 21, color_black); dpixel(31, 22, color_black); // An horizontal line to separate parts of the keyboard. - dline(5, 28, 32, 28, Color_Black); + dline(5, 28, 32, 28, color_black); } typedef struct { @@ -64,7 +64,7 @@ static void push_history(enhanced_event_t *history, int size, event_t event) // Determining where the history ends. int length = 0; - while(length < size && history[length].type != ET_None) length++; + 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)) @@ -105,10 +105,10 @@ static void draw_events(enhanced_event_t *history, int size) "None ", "User ", "Press", "Rept.", "Rel. ", "Timer" }; - for(int i = 0; i < size && history[i].type != ET_None; i++) + for(int i = 0; i < size && history[i].type != event_none; i++) { print(8, 3 + i, "%s %s", event_names[history[i].type], - key_names[keyid(history[i].key)]); + key_names[key_id(history[i].key)]); if(history[i].repeats > 1) print(19, 3 + i, "%d", history[i].repeats); } @@ -124,18 +124,19 @@ void test_keyboard_events(void) int history_size = 5; event_t event; - for(int i = 0; i < history_size; i++) history[i].type = ET_None; + for(int i = 0; i < history_size; i++) history[i].type = event_none; while(1) { dclear(); locate(1, 1, "Keyboard and events"); - draw_keyboard(keystate()); + draw_keyboard(keyboard_stateBuffer()); draw_events(history, history_size); dupdate(); event = waitevent(); - if(event.type == ET_KeyPress && event.key == KEY_EXIT) break; + if(event.type == event_key_press && event.key == KEY_EXIT) + break; push_history(history, history_size, event); } } diff --git a/demo/test_rtc.c b/demo/test_rtc.c index 053e218..1211aed 100644 --- a/demo/test_rtc.c +++ b/demo/test_rtc.c @@ -18,7 +18,7 @@ static void draw(rtc_time_t time) { - extern Image res_rtc_segments; + extern image_t res_rtc_segments; const char *days[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" @@ -42,7 +42,7 @@ static void draw(rtc_time_t time) 12 * digits[i], 0, 11, 19); // Drawing ':' between pairs of digits. for(i = 0; i < 16; i++) dpixel(47 + 32 * (i >= 8) + (i & 1), - 14 + 5 * !!(i & 4) + !!(i & 2), Color_Black); + 14 + 5 * !!(i & 4) + !!(i & 2), color_black); // This should print time.year + 1900 but for the sake of this demo we // have tweaked the field so that it already contains time.year + 1900. @@ -52,7 +52,7 @@ static void draw(rtc_time_t time) static void callback(void) { - extern Image res_opt_rtc; + extern image_t res_opt_rtc; rtc_time_t time = rtc_getTime(); dclear(); @@ -115,8 +115,8 @@ static void set_region(rtc_time_t *time, int region, int value) static void set(void) { - extern Image res_opt_rtc; - Image *opt = &res_opt_rtc; + extern image_t res_opt_rtc; + image_t *opt = &res_opt_rtc; struct { int x, y; @@ -136,8 +136,8 @@ static void set(void) { dclear(); draw(time); - dreverse_area(regions[n].x, regions[n].y, regions[n].x - + regions[n].w - 1, regions[n].y + regions[n].h - 1); + drect(regions[n].x, regions[n].y, regions[n].x + regions[n].w + - 1, regions[n].y + regions[n].h - 1, color_invert); if(n == 6) dimage_part(0, 56, opt, 0, 9 * (1 + slide), 128, 8); if(n == 7) dimage_part(0, 56, opt, 0, 9 * (3 + slide), 128, 8); @@ -191,9 +191,9 @@ static void set(void) else leave = 0; } - else if(isdigit(keychar(key))) // Numbers + else if(isdigit(key_char(key))) // Numbers { - int val = keychar(key) - '0'; + int val = key_char(key) - '0'; int ok = 1; if(n == 0) ok = (val <= 2); diff --git a/demo/test_tales.c b/demo/test_tales.c index 644fc23..135925d 100644 --- a/demo/test_tales.c +++ b/demo/test_tales.c @@ -27,7 +27,7 @@ static Font *select(Font *current) while(1) { - text_configure(NULL, Color_Black); + text_configure(NULL, color_black); dclear(); locate(1, 1, "Select a font:"); @@ -39,18 +39,18 @@ static Font *select(Font *current) int height = fonts[i].font->line_height; int y = (i + 2) * 8 - 8 + ((7 - height) >> 1); - text_configure(fonts[i].font, Color_Black); + text_configure(fonts[i].font, color_black); dtext(7, y, fonts[i].name); } else { - text_configure(NULL, Color_Black); + text_configure(NULL, color_black); locate(2, i + 2, fonts[i].name); } } - dreverse_area(0, 8 * row + 8, 128, 8 * row + 15); + drect(0, 8 * row + 8, 128, 8 * row + 15, color_invert); dupdate(); do @@ -80,9 +80,9 @@ static Font *select(Font *current) void test_tales(void) { - enum Color colors[] = { Color_Black, Color_Dark, Color_Light, - Color_White, Color_Invert }; - extern Image res_opt_tales; + color_t colors[] = { color_black, color_dark, color_light, color_white, + color_invert }; + extern image_t res_opt_tales; Font *font = NULL; int black_bg = 0; @@ -94,7 +94,7 @@ void test_tales(void) while(1) { gclear(); - if(black_bg) greverse_area(0, 0, 127, 54); + if(black_bg) grect(0, 0, 127, 54, color_invert); if(font) { @@ -119,10 +119,10 @@ void test_tales(void) gimage(0, 56, &res_opt_tales); x = 45 + 8 * color; - gline(x, 57, x + 5, 57, Color_Black); - gline(x, 57, x, 62, Color_Black); - gline(x + 5, 57, x + 5, 62, Color_Black); - gline(x, 62, x + 5, 62, Color_Black); + gline(x, 57, x + 5, 57, color_black); + gline(x, 57, x, 62, color_black); + gline(x + 5, 57, x + 5, 62, color_black); + gline(x, 62, x + 5, 62, color_black); gupdate(); @@ -146,7 +146,7 @@ void test_tales(void) case KEY_EXIT: gray_stop(); - text_configure(NULL, Color_Black); + text_configure(NULL, color_black); return; default: leave = 0; diff --git a/demo/test_timer.c b/demo/test_timer.c index a830677..d64e6c0 100644 --- a/demo/test_timer.c +++ b/demo/test_timer.c @@ -36,7 +36,7 @@ static void timing_timer(void) static void timing_start(void) { - uint32_t delay = clock_setting(64, Clock_Hz); + uint32_t delay = clock_setting(64, clock_Hz); htimer = htimer_setup(timer_user, delay, timer_Po_4, 0); timer_attach(htimer, timing_timer, NULL); timer_start(htimer); @@ -59,8 +59,8 @@ static void timing_start(void) */ static void small_text(int x, int y, const char *text, int alignment) { - extern Image res_clock_chars; - Image *chars = &res_clock_chars; + extern image_t res_clock_chars; + image_t *chars = &res_clock_chars; const char *table = "0123456789kMHz*/"; if(alignment) x -= 2 * strlen(text) - 1, y -= 2; @@ -133,9 +133,9 @@ static void display_freq(int x, int y, int freq) */ static void draw(int tab) { - extern Image res_opt_timer; - extern Image res_clock_7705; - extern Image res_clock_7305; + extern image_t res_opt_timer; + extern image_t res_clock_7705; + extern image_t res_clock_7305; char buffer[16]; @@ -163,14 +163,14 @@ static void draw(int tab) small_text(84, 34, buffer, 1); if(conf.Iphi_div1 == 1) - dline(85, 43, 99, 43, Color_Black); + dline(85, 43, 99, 43, color_black); else { sprintf(buffer, "/%d", conf.Iphi_div1); small_text(89, 41, buffer, 0); } if(conf.Pphi_div1 == 1) - dline(85, 50, 99, 50, Color_Black); + dline(85, 50, 99, 50, color_black); else { sprintf(buffer, "/%d", conf.Pphi_div1); @@ -249,13 +249,13 @@ void test_timer(void) elapsed_rtc = -1; cb_id = rtc_cb_add(RTCFreq_64Hz, timing_start, 0); - text_configure(NULL, Color_Black); + text_configure(NULL, color_black); while(1) { draw(tab); - switch(getkey_opt(Getkey_NoOption, 1)) + switch(getkey_opt(getkey_none, 25)) { case KEY_EXIT: rtc_cb_end(cb_id); diff --git a/include/alloca.h b/include/alloca.h index 37c88e7..2011266 100644 --- a/include/alloca.h +++ b/include/alloca.h @@ -3,12 +3,14 @@ // standard library module: alloca // // Allows dynamic memory allocation on the stack. Memory is automatically -// freed when the calling function exits. +// freed when the calling function exits, but this function suffers from +// risks of stack overflow; make sure you don't inline functions that use +// alloca or allocate more than a few hundred bytes with it. // //--- #ifndef _ALLOCA_H -#define _ALLOCA_H 1 +#define _ALLOCA_H #include diff --git a/include/bopti.h b/include/bopti.h new file mode 100644 index 0000000..8889359 --- /dev/null +++ b/include/bopti.h @@ -0,0 +1,67 @@ +//--- +// +// gint drawing module: bopti +// +// This module is a powerful bitmap renderer. It *heavily* relies on the +// line-based structure of the video RAM as well as the high density of +// information. A single CPU access (longword operation) can affect 32 +// pixels at once, which is crucial for performance. The same goes for all +// other drawing modules, but this one typically has 350 lines of code +// just to wrap these longword accesses -- and it's blazingly fast. +// +//--- + +#ifndef _BOPTI_H +#define _BOPTI_H + +/* + image_t + This structure holds meta-data of a bitmap encoded with fxconv. Data is + accessed using longword operations for performance considerations, + which requires that the all fields of the structure be properly aligned + and of a correct size. +*/ +typedef struct +{ + uint8_t magic; + uint8_t format; + + uint8_t width; + uint8_t height; + + const uint32_t data[]; + +} __attribute__((packed, aligned(4))) image_t; + +/* + dimage() + Displays a monochrome image in the vram. This function does a real lot + of optimization. +*/ +void dimage(int x, int y, image_t *image); + +/* + dimage_part() + Draws a portion of an image, defined by its bounding rectangle. + Point (left, top) is included, but (left + width, top + height) is + excluded. +*/ +void dimage_part(int x, int y, image_t *img, int left, int top, int width, + int height); + +/* + gimage() + Displays a gray image in the dual-vram. +*/ +void gimage(int x, int y, image_t *image); + +/* + gimage_part() + Draws a portion of a gray image, defined by its bounding rectangle. + Point (left, top) is included, but (left + width, top + height) is + excluded. +*/ +void gimage_part(int x, int y, image_t *image, int left, int top, int width, + int height); + +#endif // _BOPTI_H diff --git a/include/clock.h b/include/clock.h index 00822e8..571b56c 100644 --- a/include/clock.h +++ b/include/clock.h @@ -2,8 +2,14 @@ // // gint core module: clock // -// Measures the frequency of the MPU clocks. This module assumes that the -// clock mode is 3 on SH7305 (as does FTune). +// This module interfaces with the MPU clocks and is used to measure the +// clock frequencies at the beginning of execution. At this stage, it +// assumes that clock mode 3 is used on SH7305 (as does FTune), because +// there doesn't seem to be a way of getting this information. +// +// It also provides some sleep and time conversion functions, and access +// to how the clocks are configured. In the future, it would be the module +// that supports overclock. // //--- @@ -18,7 +24,12 @@ /* sleep() - Puts the processor to sleep until an interrupt request is issued. + Puts the processor to sleep until an interrupt request is accepted. + This function should be called every time the program because idle + because it doesn't have anything to do -- between two game frames or + while waiting for a keyboard event. + This function is called by getkey_opt(), getkey(), waitevent(), this + module's sleep functions among others. */ void sleep(void); @@ -41,17 +52,29 @@ void sleep_us(int us_delay); // Clock management. //--- -enum ClockUnit +/* + clock_unit_t + Enumerated type used by the time conversion functions. It indicates the + type (delay or frequency) of a parameter. +*/ +typedef enum { - Clock_us = 0, - Clock_ms = 1, - Clock_s = 2, + clock_us = 0, + clock_ms = 1, + clock_s = 2, - Clock_Hz = 10, - Clock_kHz = 11, - Clock_MHz = 12, -}; + clock_Hz = 10, + clock_kHz = 11, + clock_MHz = 12, +} clock_unit_t; + +/* + clock_config_t + A copy of the Clock Pulse Generator (CPG) configuration. Be sure to + check which MPU the program is running on (using ) to access the + right fields. +*/ typedef struct { union @@ -75,9 +98,9 @@ typedef struct int RTCCLK_f; // SH7305 }; - int Bphi_f; - int Iphi_f; - int Pphi_f; + int Bphi_f; // Bus clock frequency + int Iphi_f; // Processor clock frequency + int Pphi_f; // Peripheral clock frequency } clock_config_t; @@ -86,10 +109,8 @@ typedef struct Returns the P_phi / 4 timer setting that will last for the given time. Several units can be used. Be aware that the result is approximate, and very high frequencies or very short delays will yield important errors. - Normally you need not use this function when setting up timers because - timer_start() handles this conversion for you. */ -uint32_t clock_setting(int duration, enum ClockUnit unit); +uint32_t clock_setting(int duration, clock_unit_t unit); /* clock_config() diff --git a/include/ctype.h b/include/ctype.h index 23ceac4..3037ccd 100644 --- a/include/ctype.h +++ b/include/ctype.h @@ -7,78 +7,29 @@ //--- #ifndef _CTYPE_H -#define _CTYPE_H 1 +#define _CTYPE_H #include -//--- -// Character classes. -//--- - extern uint8_t ctype_classes[0x80]; -__attribute__((always_inline)) static inline int isalnum(int c) { - return ctype_classes[c] & 0xf0; -} +// Character classes. +#define isalnum(c) (ctype_classes[(int)(c)] & 0xf0) +#define isalpha(c) (ctype_classes[(int)(c)] & 0x30) +#define iscntrl(c) (ctype_classes[(int)(c)] & 0x01) +#define isdigit(c) (ctype_classes[(int)(c)] & 0x40) +#define isgraph(c) (ctype_classes[(int)(c)] & 0xf4) +#define islower(c) (ctype_classes[(int)(c)] & 0x10) +#define isprint(c) (ctype_classes[(int)(c)] & 0x08) +#define ispunct(c) (ctype_classes[(int)(c)] & 0x04) +#define isspace(c) (ctype_classes[(int)(c)] & 0x02) +#define isupper(c) (ctype_classes[(int)(c)] & 0x20) +#define isxdigit(c) (ctype_classes[(int)(c)] & 0x80) +#define isascii(c) ((unsigned)c <= 0x7f) +#define isblank(c) (c == '\t' || c == ' ') -__attribute__((always_inline)) static inline int isalpha(int c) { - return ctype_classes[c] & 0x30; -} - -__attribute__((always_inline)) static inline int iscntrl(int c) { - return ctype_classes[c] & 0x01; -} - -__attribute__((always_inline)) static inline int isdigit(int c) { - return ctype_classes[c] & 0x40; -} - -__attribute__((always_inline)) static inline int isgraph(int c) { - return ctype_classes[c] & 0xf4; -} - -__attribute__((always_inline)) static inline int islower(int c) { - return ctype_classes[c] & 0x10; -} - -__attribute__((always_inline)) static inline int isprint(int c) { - return ctype_classes[c] & 0x08; -} - -__attribute__((always_inline)) static inline int ispunct(int c) { - return ctype_classes[c] & 0x04; -} - -__attribute__((always_inline)) static inline int isspace(int c) { - return ctype_classes[c] & 0x02; -} - -__attribute__((always_inline)) static inline int isupper(int c) { - return ctype_classes[c] & 0x20; -} - -__attribute__((always_inline)) static inline int isxdigit(int c) { - return ctype_classes[c] & 0x80; -} - -__attribute__((always_inline)) static inline int isascii(int c) { - return ((unsigned)c <= 0x7f); -} - -__attribute__((always_inline)) static inline int isblank(int c) { - return (c == '\t' || c == ' '); -} - -//--- -// Character manipulation. -//--- - -__attribute__((always_inline)) static inline int tolower(int c) { - return c | isupper(c); -} - -__attribute__((always_inline)) static inline int toupper(int c) { - return c & ~(islower(c) << 1); -} +// Character manipulation. +#define tolower(c) ((c) | isupper(c)) +#define toupper(c) ((c) & ~(islower(c) << 1)) #endif // _CTYPE_H diff --git a/include/display.h b/include/display.h index c5dba6e..924c6dc 100644 --- a/include/display.h +++ b/include/display.h @@ -2,94 +2,107 @@ // // gint drawing module: display // -// Handles vram manipulation and drawing for plain monochrome display. +// This module does most of the monochrome drawing. It manages the video +// memory although image rendering and text rendering, as complex tasks, +// are left to other modules (bopti and tales, respectively). // //--- #ifndef _DISPLAY_H -#define _DISPLAY_H 1 +#define _DISPLAY_H #include #include - //--- -// Heading declarations. +// Drawing-related types and constants. //--- -enum Color -{ - Color_White = 0, - Color_Light = 1, - Color_Dark = 2, - Color_Black = 3, - Color_None = 4, - Color_Invert = 5, -}; - -// This header needs enum Color to be defined. -#include +#define DWIDTH 128 /* Width of the screen */ +#define DHEIGHT 64 /* Height of the screen */ /* - struct Image - This structure holds information about a bitmap encoded with fxconv. - Data is accessed using longword operations, which *requires* many - sizes to be multiples of 4 (structure alignment, data alignment, layer - size, ...). + color_t + Defines all colors that the library knows about: + - white is exactly what you think it is; + - light is a light gray used by the gray module; + - dark is a dark gray, also used by the gray engine; + - black is nothing more than black; (sorry) + - none means transparent, but is shorter to write. + There are also some transformation-associated colors: + - invert reverses the intensity of the color (white -> black, dark -> + light, etc); + - lighten is some kind of partially-transparent white. It lightens the + color which it is drawn onto (black -> dark, light -> light); + - lighten2 is the same as lighten, except it lightens more (black -> + light, light -> white); + - darken is the exact opposite of lighten (light -> dark, black -> + black). + - darken2 is the same to darken as lighten2 to lighten (white -> dark, + dark -> black); + All transformations except invert only operate when the gray engine is + running. */ -struct Image +typedef enum { - uint8_t magic; - uint8_t format; + color_white = 0, + color_light = 1, + color_dark = 2, + color_black = 3, + color_none = 4, - uint8_t width; - uint8_t height; + color_invert = 5, + color_lighten = 6, + color_lighten2 = 7, + color_darken = 8, + color_darken2 = 9, - const uint32_t data[]; +} color_t; -} __attribute__((packed, aligned(4))); -// Useful shorthand for user code. -typedef struct Image Image; +// The bopti module provides bitmap rendering functions. +#include - - -// A few other constants. -#define DISPLAY_WIDTH 128 -#define DISPLAY_HEIGHT 64 +// The tales module provides text rendering functions but requires the color_t +// type definition. +#include //--- -// Generic functions. +// Video RAM management. //--- /* display_getLocalVRAM() - Returns the local video ram address. This function always return the - same address. - The buffer returned by this function should not be used directly when - running the gray engine. + Returns gint's local video RAM address. Gint does not use the system's + buffer because it is misaligned. This function always returns the same + address. Both the display and the gray module heavily use this buffer; + make sure you don't interfere with them if you access it. + This function does not necessarily returns the video ram that is + currently in use; call display_getCurrentVRAM() for this. */ -void *display_getLocalVRAM(void); +uint32_t *display_getLocalVRAM(void); /* display_getCurrentVRAM() - Returns the current video ram. This function usually returns the - parameter of the last call to display_useVRAM(), unless the gray engine - is running (in which case the result is undefined). Returns the local - vram address by default. + Returns the current monochrome video ram buffer. This function usually + returns the parameter of the last call to display_useVRAM(), or the + local vram address (which is default when the library starts). + The return value of this function is undefined if the gray engine is + running. */ -void *display_getCurrentVRAM(void); +uint32_t *display_getCurrentVRAM(void); /* display_useVRAM() - Changes the current video ram address. The argument MUST be a 4- - aligned 1024-byte buffer; otherwise any drawing operation will crash - the program. + Changes the current monochrome video ram address. The argument must be + a 4-aligned 1024-byte buffer because the library's design requires it. + This function refuses misaligned buffers but trusts that enough space + is available; failing to provide enough memory may crash the program. This function will most likely have no effect when running the gray engine. */ -void display_useVRAM(void *vram); +void display_useVRAM(uint32_t *vram); @@ -99,30 +112,24 @@ void display_useVRAM(void *vram); /* dupdate() - Displays the vram on the physical screen. Does nothing when the gray - engine is running. + Pushes the video RAM to the physical screen. This function also works + when the gray engine is running, but that's probably not what you want. */ void dupdate(void); /* dclear() - Clears the whole video ram. + Clears the whole video ram, making all pixels white. */ void dclear(void); /* - dclear_area() - Clears an area of the video ram. Both (x1, y1) and (x2, y2) are - cleared. + drect() + Draws a rectangle on the screen. This function can use any color which + is not associated with the gray engine, including the reverse operator. + Both end points (x1, y1) and (x2, y2) are affected as well. */ -void dclear_area(int x1, int y1, int x2, int y2); - -/* - dreverse_area() - Reverses an area of the vram. (x1, y1) and (x2, y2) are reversed as - well. -*/ -void dreverse_area(int x1, int y1, int x2, int y2); +void drect(int x1, int y1, int x2, int y2, color_t operator); @@ -132,9 +139,10 @@ void dreverse_area(int x1, int y1, int x2, int y2); /* dpixel() - Puts a pixel in the vram. + Changes a pixel's color in the video ram. The result may depend on the + current color of the pixel. */ -void dpixel(int x, int y, enum Color color); +void dpixel(size_t x, size_t y, color_t operator); /* dline() @@ -143,22 +151,6 @@ void dpixel(int x, int y, enum Color color); Uses an algorithm written by PierrotLL for MonochromeLib. */ -void dline(int x1, int y1, int x2, int y2, enum Color color); - -/* - dimage() - Displays a monochrome image in the vram. Does a real lot of - optimization. -*/ -void dimage(int x, int y, struct Image *image); - -/* - dimage_part() - Draws a portion of an image, defined by its bounding rectangle. - Point (left, top) is included, but (left + width, top + height) is - excluded. -*/ -void dimage_part(int x, int y, struct Image *img, int left, int top, - int width, int height); +void dline(int x1, int y1, int x2, int y2, color_t operator); #endif // _DISPLAY_H diff --git a/include/events.h b/include/events.h index 8c1f285..d88c98e 100644 --- a/include/events.h +++ b/include/events.h @@ -2,7 +2,17 @@ // // gint core module: events // -// Finally some user-friendly API. +// Finally some user-friendly API. This module is in charge of managing +// the event queue. The waitevent() function should be particularly useful +// in program main loops to record key presses and releases in real-time +// games. +// +// Other functions such as the getkey() of the keyboard module provide +// more advanced features such as SHIFT and ALPHA modifiers, backlight +// control for instance; these functions rely on this module and they +// ignore all events that they do not handle. If you want to catch several +// types of events (eg. keyboard and serial communication), then you need +// to use directly this module. // //--- @@ -17,30 +27,24 @@ */ typedef enum { - EventType_None = 0, - ET_None = EventType_None, + // Specific events. + event_none = 0, + event_user = 1, - EventType_User = 1, - ET_User = EventType_User, + // Keyboard events. + event_key_press = 2, + event_key_repeat = 3, + event_key_release = 4, - EventType_KeyPressed = 2, - ET_KeyPress = EventType_KeyPressed, - - EventType_KeyRepeated = 3, - ET_KeyRepeat = EventType_KeyRepeated, - - EventType_KeyReleased = 4, - ET_KeyRel = EventType_KeyReleased, - - EventType_TimerUnderflow = 5, - ET_Timer = EventType_TimerUnderflow, + // Other events. + event_timer_underflow = 5, } event_type_t; /* event_t Wake up, something's going on. The union member that holds information - about the event is implicitly defined by the type attribute. + about the event is specified by the type attribute. */ typedef struct { @@ -48,11 +52,11 @@ typedef struct union { - // For ET_User. + // For event_user. void *data; - // For ET_KeyPress, ET_KeyRepeat and ET_KeyRel. + // For event_key_press, event_key_repeat and event_key_release. int key; - // For ET_Timer. + // For event_timer_underflow. timer_t *timer; }; diff --git a/include/gint.h b/include/gint.h index 85fc84f..887b960 100644 --- a/include/gint.h +++ b/include/gint.h @@ -2,8 +2,10 @@ // // gint core module: interrupt handler // -// Central point of the library. Controls the interrupt handler and -// defines a few functions to configure callbacks for some interrupts. +// This module is the core of the gint library. It controls the interrupt +// handler, allows the user to customize interrupt management, provides +// peripheral register access and some information about the runtime +// environment. // //--- diff --git a/include/gray.h b/include/gray.h index bbae4f0..a4e3233 100644 --- a/include/gray.h +++ b/include/gray.h @@ -7,10 +7,14 @@ //--- #ifndef _GRAY_H -#define _GRAY_H 1 +#define _GRAY_H +#include #include +// This module provides bitmap rendering. +#include + //--- // Engine control. //--- @@ -39,13 +43,13 @@ void gray_stop(void); gray_lightVRAM() Returns the module's light gray vram address. */ -void *gray_lightVRAM(void); +uint32_t *gray_lightVRAM(void); /* gray_darkVRAM() Returns the module's dark gray vram address. */ -void *gray_darkVRAM(void); +uint32_t *gray_darkVRAM(void); /* gray_getDelays() @@ -81,29 +85,26 @@ void gray_setDelays(int light, int dark); /* gupdate() - Swaps the vram buffer sets. + Swaps the vram buffer sets. You need to call this function each time + you finish drawing something in the video ram. Unlike the monochrome + function dupdate(), gupdate() only does a quick operation indicating + that drawing and exposed buffers have been swapped, but nothing on the + screen will change until the gray timer fires. */ void gupdate(void); /* gclear() - Clears the video ram. + Clears the gray video ram, making all pixels white. */ void gclear(void); /* - gclear_area() - Clears an area of the video ram. End points (x1, y1) and (x2, y2) are - included. + grect() + Draws a rectangle in the gray video ram; this function accepts all + values of the color_t type, including gray operators. */ -void gclear_area(int x1, int y1, int x2, int y2); - -/* - greverse_area() - Reverses an area of the vram. End points (x1, y1) and (x2, y2) are - included. -*/ -void greverse_area(int x1, int y1, int x2, int y2); +void grect(int x1, int y1, int x2, int y2, color_t operator); @@ -113,29 +114,18 @@ void greverse_area(int x1, int y1, int x2, int y2); /* gpixel() - Puts a pixel in the vram. + Puts a pixel in the vram. This function accepts all values of the + color_t type, including gray operators. */ -void gpixel(int x, int y, enum Color color); +void gpixel(size_t x, size_t y, color_t operator); /* gline() - Draws a line in the vram. Automatically optimizes special cases. + Draws a line in the vram while automatically optimizing special cases. + This function supports all plain colors from the color_t type, but not + the gray operators. If you need them for horizontal or vertical lines, + you may want to use grect() as a replacement. */ -void gline(int x1, int y1, int x2, int y2, enum Color color); - -/* - gimage() - Displays a gray image in the vram. -*/ -void gimage(int x, int y, struct Image *image); - -/* - gimage_part() - Draws a portion of a gray image, defined by its bounding rectangle. - Point (left, top) is included, but (left + width, top + height) is - excluded. -*/ -void gimage_part(int x, int y, struct Image *image, int left, int top, - int width, int height); +void gline(int x1, int y1, int x2, int y2, color_t operator); #endif // _GRAY_H diff --git a/include/internals/bopti.h b/include/internals/bopti.h index d120db8..31a87e0 100644 --- a/include/internals/bopti.h +++ b/include/internals/bopti.h @@ -15,7 +15,7 @@ //--- #ifndef _INTERNALS_BOPTI_H -#define _INTERNALS_BOPTI_H 1 +#define _INTERNALS_BOPTI_H #include #include @@ -86,7 +86,7 @@ struct Command // The video ram addresses are set by the public functions and used internally // by the module. // Monochrome video ram, light and dark buffers (in this order). -extern int *bopti_vram, *bopti_v1, *bopti_v2; +extern uint32_t *bopti_vram, *bopti_v1, *bopti_v2; @@ -155,6 +155,6 @@ void bopti(const unsigned char *layer, struct Structure *s, struct Command *c); getStructure() Determines the image size and data pointer. */ -void getStructure(struct Image *img, struct Structure *structure); +void getStructure(image_t *img, struct Structure *structure); #endif // _INTERNALS_BOPTI_H diff --git a/include/internals/display.h b/include/internals/display.h index d862f58..8157b5b 100644 --- a/include/internals/display.h +++ b/include/internals/display.h @@ -1,47 +1,40 @@ -//--- -// -// gint drawing module: display -// -// Handles vram manipulation and drawing. -// -//--- - #ifndef _INTERNALS_DISPLAY_H -#define _INTERNALS_DISPLAY_H 1 +#define _INTERNALS_DISPLAY_H #include +#include -extern int *vram; +extern uint32_t *vram; //--- // Rectangle masks. // // The concept of 'rectangle masks' is used several times in this module. -// It is based on the fact that an operation that affects a rectangle acts -// the same on all its lines. Therefore the behavior of the operation is -// determined by its behavior on a single line, which is represented using -// 'masks' whose bits indicate whether a pixel is affected (1) or not (0). +// It relies on the fact that operations affecting a rectangle act the +// same for all lines, and line operation is very optimized. A rectangle +// mask is a set of integers, where each bit indicate whether a specific +// pixel is affected (1) by the operation, or not (0). // -// For example when clearing the screen rectangle (16, 16, 112, 48), the -// masks will represent information '16 to 112 on x-axis', and will hold -// the following values : 0000ffff, ffffffff, ffffffff and ffff0000. These -// masks can then be used by setting vram[offset] &= ~masks[i]. This -// appears to be very flexible : for instance, vram[offset] ^= masks[i] -// will reverse the pixels in the same rectangle. -// -// This technique can also be used in more subtle cases with more complex -// patterns, but within this module it is unlikely to happen. +// For example to clear a rectangle such as (14, 16, 112, 48), the masks +// will need to hold 0003ffff ffffffff ffffffff ffff0000. Bitwise- +// combining them with video ram long entries yields very good performance +// as compared to operation on single pixels. Each bitwise operation will +// produce different results, which is very flexible. // +// This technique can also be used in subtle cases with patterns more +// complicated than rectangles, but within this module this is unlikely to +// happen. //--- /* adjustRectangle() Adjusts the given rectangle coordinates to ensure that : - - the rectangle is entirely contained in the screen - - x1 < x2 - - y1 < y2 - which is needed when working with screen rectangles. - Returns non-zero if the rectangle is outside the screen. + - the rectangle is entirely contained in the screen; + - x1 < x2; + - y1 < y2, + which is needed when working with screen rectangles. Returns non-zero + if the rectangle is outside the screen, which usually means there is + nothing to do. */ int adjustRectangle(int *x1, int *y1, int *x2, int *y2); @@ -51,6 +44,6 @@ int adjustRectangle(int *x1, int *y1, int *x2, int *y2); and x2 (both included). The four masks are stored in the third argument (seen as an array). */ -void getMasks(int x1, int x2, uint32_t *masks); +void getMasks(size_t x1, size_t x2, uint32_t *masks); #endif // _INTERNALS_DISPLAY_H diff --git a/include/internals/gint.h b/include/internals/gint.h index 91a07a7..601ff70 100644 --- a/include/internals/gint.h +++ b/include/internals/gint.h @@ -1,11 +1,11 @@ #ifndef _INTERNALS_GINT_H -#define _INTERNALS_GINT_H 1 +#define _INTERNALS_GINT_H #include #include //--- -// Interrupt handlers +// Interrupt handlers. //--- // General exception handler. @@ -18,7 +18,7 @@ void gint_int(void); //--- -// Assembler-level VBR management +// Assembler-level VBR management. //--- /* @@ -37,7 +37,7 @@ void gint_setvbr(uint32_t vbr, void (*setup)(void)); //--- -// Initialization and termination routines +// Initialization and termination routines. //--- /* @@ -58,8 +58,8 @@ void gint_quit(void); Saves many registers into a buffer to ensure that the system is not upset by gint's configuration when the application ends. */ -//void gint_save_7705(gint_save_buffer_t *buffer); -//void gint_save_7305(gint_save_buffer_t *buffer); +// void gint_save_7705(gint_save_buffer_t *buffer); +// void gint_save_7305(gint_save_buffer_t *buffer); /* gint_setup() @@ -74,8 +74,8 @@ void gint_setup_7305(void); Restores the parameters saved in a save buffer to give back the interrupt control to the system. */ -//void gint_restore_7705(gint_save_buffer_t *buffer); -//void gint_restore_7305(gint_save_buffer_t *buffer); +// void gint_restore_7705(gint_save_buffer_t *buffer); +// void gint_restore_7305(gint_save_buffer_t *buffer); /* gint_reg() diff --git a/include/internals/interrupt_maps.h b/include/internals/interrupt_maps.h index ac66baf..a1d716b 100644 --- a/include/internals/interrupt_maps.h +++ b/include/internals/interrupt_maps.h @@ -5,7 +5,7 @@ #include //--- -// Interrupt handlers +// Interrupt handlers. //--- /* @@ -60,7 +60,7 @@ extern gint_interrupt_handler_t gint_handlers[]; //--- -// Interrupt maps +// Interrupt maps. //--- /* diff --git a/include/internals/keyboard.h b/include/internals/keyboard.h index 10bd564..738c1ee 100644 --- a/include/internals/keyboard.h +++ b/include/internals/keyboard.h @@ -2,6 +2,7 @@ #define _INTERNALS_KEYBOARD_H #include +#include #include // Keyboard variables. @@ -10,10 +11,10 @@ extern volatile int interrupt_flag; // Key statistics. extern int repeat_first, repeat_next; -extern int last_key, last_repeats, last_events; +extern int last_key, last_repeats, last_time; -// RTC callback id. -extern unsigned cb_id; +// Virtual timer object. +extern timer_t *vtimer; /* getPressedKey() diff --git a/include/internals/mmu.h b/include/internals/mmu.h index 23aa121..ced7211 100644 --- a/include/internals/mmu.h +++ b/include/internals/mmu.h @@ -8,8 +8,8 @@ // //--- -#ifndef _MMU_H -#define _MMU_H 1 +#ifndef _INTERNALS_MMU_H +#define _INTERNALS_MMU_H /* mmu_pseudoTLBInit() diff --git a/include/internals/stdio.h b/include/internals/stdio.h index bf68b81..67563d4 100644 --- a/include/internals/stdio.h +++ b/include/internals/stdio.h @@ -8,7 +8,7 @@ //--- #ifndef _INTERNALS_STDIO_H -#define _INTERNALS_STDIO_H 1 +#define _INTERNALS_STDIO_H #include #include diff --git a/include/internals/tales.h b/include/internals/tales.h index 40342c1..166f1ad 100644 --- a/include/internals/tales.h +++ b/include/internals/tales.h @@ -1,5 +1,5 @@ #ifndef _INTERNALS_TALES_H -#define _INTERNALS_TALES_H 1 +#define _INTERNALS_TALES_H #include #include @@ -7,13 +7,14 @@ #define OPERATE_ARGS uint32_t *operators, int height, int x, int y extern struct Font *font; -extern enum Color color; +extern color_t operator; /* tales_init() Configures tales with the default font (which is part of gint). */ -void tales_init(void) __attribute__((constructor)); +__attribute__((constructor)) +void tales_init(void); /* getCharacterIndex() diff --git a/include/internals/time.h b/include/internals/time.h index 236dcf6..aef910b 100644 --- a/include/internals/time.h +++ b/include/internals/time.h @@ -1,5 +1,5 @@ #ifndef _INTERNALS_TIME_H -#define _INTERNALS_TIME_H 1 +#define _INTERNALS_TIME_H /* isLeap() diff --git a/include/keyboard.h b/include/keyboard.h index ea27483..f788c13 100644 --- a/include/keyboard.h +++ b/include/keyboard.h @@ -11,7 +11,7 @@ //--- #ifndef _KEYBOARD_H -#define _KEYBOARD_H 1 +#define _KEYBOARD_H #include #include @@ -20,92 +20,87 @@ // Keycodes and related. //--- -// The following codes are gint matrix codes. They are not compatible with the -// system's. - -#define KEY_F1 0x69 -#define KEY_F2 0x59 -#define KEY_F3 0x49 -#define KEY_F4 0x39 -#define KEY_F4 0x39 -#define KEY_F5 0x29 -#define KEY_F6 0x19 - -#define KEY_SHIFT 0x68 -#define KEY_OPTN 0x58 -#define KEY_VARS 0x48 -#define KEY_MENU 0x38 -#define KEY_LEFT 0x28 -#define KEY_UP 0x18 - -#define KEY_ALPHA 0x67 -#define KEY_SQUARE 0x57 -#define KEY_POWER 0x47 -#define KEY_EXIT 0x37 -#define KEY_DOWN 0x27 -#define KEY_RIGHT 0x17 - -#define KEY_XOT 0x66 -#define KEY_LOG 0x56 -#define KEY_LN 0x46 -#define KEY_SIN 0x36 -#define KEY_COS 0x26 -#define KEY_TAN 0x16 - -#define KEY_FRAC 0x65 -#define KEY_FD 0x55 -#define KEY_LEFTP 0x45 -#define KEY_RIGHTP 0x35 -#define KEY_COMMA 0x25 -#define KEY_ARROW 0x15 - -#define KEY_7 0x64 -#define KEY_8 0x54 -#define KEY_9 0x44 -#define KEY_DEL 0x34 -#define KEY_AC_ON 0x24 - -#define KEY_4 0x63 -#define KEY_5 0x53 -#define KEY_6 0x43 -#define KEY_MUL 0x33 -#define KEY_DIV 0x23 - -#define KEY_1 0x62 -#define KEY_2 0x52 -#define KEY_3 0x42 -#define KEY_PLUS 0x32 -#define KEY_MINUS 0x22 - -#define KEY_0 0x61 -#define KEY_DOT 0x51 -#define KEY_EXP 0x41 -#define KEY_NEG 0x31 -#define KEY_EXE 0x21 - -// Key modifiers. -#define MOD_SHIFT 0x80 -#define MOD_ALPHA 0x100 -#define MOD_CLEAR ~(MOD_SHIFT | MOD_ALPHA) - -// Key events. -#define KEY_NONE 0x00 -#define KEY_NOEVENT 0xff - /* - enum KeyboardFrequency - Possible values for the keyboard frequency. + key_t + The following codes are gint matrix codes. They are not compatible with + the system's. Some keycodes are special event codes; all others are + made of a key identifier and possibly one or more modifiers. + Binary-and a keycode with MOD_CLEAR to remove the modifiers; this will + not work with special event codes. */ -enum KeyboardFrequency +typedef enum { - KeyboardFreq_500mHz = RTCFreq_500mHz, - KeyboardFreq_1Hz = RTCFreq_1Hz, - KeyboardFreq_2Hz = RTCFreq_2Hz, - KeyboardFreq_4Hz = RTCFreq_4Hz, - KeyboardFreq_16Hz = RTCFreq_16Hz, - KeyboardFreq_64Hz = RTCFreq_64Hz, - KeyboardFreq_256Hz = RTCFreq_256Hz, -}; + // Special events codes. + KEY_NONE = 0x00, + KEY_NOEVENT = 0xff, + + // Key modifiers. + MOD_SHIFT = 0x80, + MOD_ALPHA = 0x100, + MOD_CLEAR = ~(MOD_SHIFT | MOD_ALPHA), + + // Key identifiers. + + KEY_F1 = 0x69, + KEY_F2 = 0x59, + KEY_F3 = 0x49, + KEY_F4 = 0x39, + KEY_F5 = 0x29, + KEY_F6 = 0x19, + + KEY_SHIFT = 0x68, + KEY_OPTN = 0x58, + KEY_VARS = 0x48, + KEY_MENU = 0x38, + KEY_LEFT = 0x28, + KEY_UP = 0x18, + + KEY_ALPHA = 0x67, + KEY_SQUARE = 0x57, + KEY_POWER = 0x47, + KEY_EXIT = 0x37, + KEY_DOWN = 0x27, + KEY_RIGHT = 0x17, + + KEY_XOT = 0x66, + KEY_LOG = 0x56, + KEY_LN = 0x46, + KEY_SIN = 0x36, + KEY_COS = 0x26, + KEY_TAN = 0x16, + + KEY_FRAC = 0x65, + KEY_FD = 0x55, + KEY_LEFTP = 0x45, + KEY_RIGHTP = 0x35, + KEY_COMMA = 0x25, + KEY_ARROW = 0x15, + + KEY_7 = 0x64, + KEY_8 = 0x54, + KEY_9 = 0x44, + KEY_DEL = 0x34, + KEY_AC_ON = 0x24, + + KEY_4 = 0x63, + KEY_5 = 0x53, + KEY_6 = 0x43, + KEY_MUL = 0x33, + KEY_DIV = 0x23, + + KEY_1 = 0x62, + KEY_2 = 0x52, + KEY_3 = 0x42, + KEY_PLUS = 0x32, + KEY_MINUS = 0x22, + + KEY_0 = 0x61, + KEY_DOT = 0x51, + KEY_EXP = 0x41, + KEY_NEG = 0x31, + KEY_EXE = 0x21, + +} key_t; @@ -114,23 +109,31 @@ enum KeyboardFrequency //--- /* - keyboard_setFrequency() - Sets the keyboard frequency. The default frequency is 16 Hz. Very few - applications will need to change this setting. - At low frequencies, you will miss key hits. At high frequencies, you - may lose execution power. + keyboard_setAnalysisDelay() + Sets the keyboard analysis delay, that is, the delay (in ms) between + two keyboard analyzes. If a key is pressed then released in the lapse + between two analyzes, the program won't notice anything. On the other + hand, if the program spends too much time reading the keyboard, it will + lose a bit of execution power. + The default frequency is about 40 Hz; very few programs will need to + change this setting. Please note that the repeat delays should be + multiples of the analysis delay for better accuracy. */ -void keyboard_setFrequency(enum KeyboardFrequency frequency); +void keyboard_setAnalysisDelay(int analysis_delay_ms); /* keyboard_setRepeatRate() Sets the default repeat rate for key events. The delay before the first repeat may have a different value (usually longer). The unit for the - argument is the keyboard period. For example at 32 Hz, values of - (20, 4) will imitate the system default. - Set to 0 to disable repetition. If first = 0, no repetition will be - allowed. If first != 0 and next = 0, only one repetition will be - allowed. + argument is ms, but the repeat events themselves may only be fired when + a keyboard analysis is performed; which means that for better accuracy, + these delays should be a multiple of the keyboard period. The keyboard + period may be changed by calling keyboard_setAnalysisDelay(). + For instance, delays of (625 ms, 125 ms) will imitate the system's + default setting. + You can disable repetitions by passing 0 as arguments: + - if first = 0, no repetition will ever occur; + - if first != 0 and next = 0, only one repetition will occur. */ void keyboard_setRepeatRate(int first, int next); @@ -141,57 +144,74 @@ void keyboard_setRepeatRate(int first, int next); //--- /* - enum GetKeyOpt - Options available for use with getkey_opt(). + getkey_opt_t + Options available to customize the behavior of the getkey_opt() + function. */ -enum GetkeyOpt +typedef enum { - Getkey_NoOption = 0x00, + getkey_none = 0x00, - // Consider [SHIFT] and [ALPHA] as modifiers instead of returning - // KEY_SHIFT and KEY_ALPHA. - Getkey_ShiftModifier = 0x01, - Getkey_AlphaModifier = 0x02, + // Consider [SHIFT] and [ALPHA] as modifiers. Returns key identifiers + // with MOD_SHIFT and MOD_ALPHA flags instead of returning KEY_SHIFT + // and KEY_ALPHA. + getkey_shift_modifier = 0x01, + getkey_alpha_modifier = 0x02, - // Allow changing the backlight status on [SHIFT] + [OPTN]. - Getkey_ManageBacklight = 0x04, + // Allow changing the backlight status on [SHIFT] + [OPTN] on + // compatible models. + getkey_manage_backlight = 0x04, - // Key repetition. Notice that modifiers will never be repeated. - Getkey_RepeatArrowKeys = 0x10, - Getkey_RepeatCharKeys = 0x20, - Getkey_RepeatCtrlKeys = 0x40, - Getkey_RepeatFuncKeys = 0x80, + // 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 + // pressed continuously. + getkey_repeat_arrow_keys = 0x10, + getkey_repeat_char_keys = 0x20, + getkey_repeat_ctrl_keys = 0x40, + getkey_repeat_func_keys = 0x80, // Shorthand for the four previous properties. - Getkey_RepeatAllKeys = 0xf0, -}; + getkey_repeat_all_keys = 0xf0, + +} getkey_option_t; /* getkey() - Blocking function with auto-repeat and SHIFT modifying functionalities. - Reproduces the behavior of the system's GetKey(). Returns the matrix - code with a possible MOD_SHIFT bit. + Blocking function with auto-repeat that heeds for the SHIFT and ALPHA + modifiers. In short, this function reproduces the behavior of the + system's GetKey() function. It returns a matrix code, possibly with + modifier bits. + This function does not return until a key is pressed. */ int getkey(void); /* getkey_opt() - Enhances getkey() with most general functionalities. An OR-combination - of options may be given as first argument. - If max_cycles is non-zero and positive, getkey_opt() will return - KEY_NOEVENT if no event occurs during max_cycle analyzes. + Enhances getkey() with more general functionalities. An OR-combination + of options of type getkey_option_t may be given as first argument. + If delay is non-zero and positive, getkey_opt() will return KEY_NOEVENT + if no event occurs during the given delay. Please note that this + function can only ever return after a keyboard analysis is performed; + the actual delay may exceed the requested time if it's not a multiple + of the keyboard period (which can be changed by calling + keyboard_setAnalysisDelay()). Like getkey(), returns the pressed key matrix code, possibly with modifiers depending on the options. */ -int getkey_opt(enum GetkeyOpt options, int max_cycles); +int getkey_opt(getkey_option_t options, int delay_ms); /* multigetkey() Listens the keyboard for simultaneous key hits. This functions fills - array `keys` with `count` keycodes, adding KEY_NONE at the end if - less than `count` keys are pressed. - If `max_cycles` is non-zero and nothing happens after `max_cycles` - cycles, this function returns an array of KEY_NONE. + the 'keys' array with 'count' keycodes, padding with KEY_NONE values at + the end if less that 'count' keys are detected. + If 'delay_ms' is positive and nothing happens during this delay, this + function returns an array of KEY_NONE. Please note that the delay + detection suffers the same limitation as getkey_opt(). + + This function suffers from severe limitations and may not be very + convenient to use. For more accuracy, consider using the event system. WARNING: Because of hardware limitations, this function generally yields poor @@ -202,7 +222,7 @@ int getkey_opt(enum GetkeyOpt options, int max_cycles); The results are guaranteed to be exact if two keys or less are pressed. With three keys or more, column effects (on SH4) and rectangle effects (on both platforms) mess up the results by making this function think - that some keys, which are actually unpressed, are pressed. + that some keys, which are actually released, are pressed. This function is designed to make combinations of one or two arrow keys with another key as viable as possible. On SH4, this works pretty well @@ -215,25 +235,26 @@ int getkey_opt(enum GetkeyOpt options, int max_cycles); incorrect results. Please do not expect multigetkey() to work as an ideal multi-key analyzer. */ -void multigetkey(int *keys, int count, int max_cycles); +void multigetkey(int *keys, int count, int delay_ms); /* - keylast() - Returns the matrix code of the last pressed key. If repeat_count is - non-NULL, it is set to the number of repetitions. -*/ -int keylast(int *repeat_count); + keyboard_stateBuffer() -/* - keystate() Returns the address of the keyboard state array. The keyboard state consists in 10 bytes, in which every key is represented as a bit. The returned address is the original buffer address. You should avoid editing the array. It wouldn't influence the behavior of the keyboard - functions, but the buffer data is very volatile. Therefore, data - written to the buffer could be replaced anytime. + functions, but the buffer data is very volatile and any data written to + it could be replaced anytime without prior notice. + + If the user wishes to do really advanced keyboard management that they + can't achieve it using the library, they can access this buffer. + Updates of this buffer's contents can be detected by watching the + 'interrupt_flag' variable defined in internals/keyboard.h. However, the + library will continue firing events so the user needs to catch them and + ignore them. */ -volatile uint8_t *keystate(void); +volatile uint8_t *keyboard_stateBuffer(void); @@ -241,32 +262,61 @@ volatile uint8_t *keystate(void); // Key analysis. //--- -enum KeyType -{ - KeyType_Arrow = 1, - KeyType_Character = 2, - KeyType_Control = 4, - KeyType_Function = 8, -}; - /* keyid() - Returns a non-matrix key code that can be used for array subscript. - Ignores modifiers. + Transforms a key identifier and returns a key code that is more + convenient for array subscript that the original matrix codes. The new + codes are laid out the following way: + + +0 +1 +2 +3 +4 +5 + ------------------------------------ + +0 | F1 F2 F3 F4 F5 F6 + +6 | SHIFT OPTN VARS MENU Left Top + +12 | ALPHA x^2 ^ EXIT Down Right + +18 | X,O,T log ln sin cos tan + +24 | Frac F<>D ( ) , -> + +30 | 7 8 9 DEL AC/ON + +36 | 4 5 6 * / + +42 | 1 2 3 + - + +48 | 0 . x10^ (-) EXE + + The returned key code is the sum of the line and column headings. For + instance key_id(KEY_SIN) would be 18 + 3 = 21. Please note that there + are a few holes in the numbering. + This function ignores modifiers and returns -1 on error. */ -int keyid(int key); +int key_id(int matrix_key); /* - keychar() - Returns the ASCII character associated with a character key; 0 for - other keys. + key_char() + Returns the ASCII character associated with a character key, and 0 for + other keys. This function expects a matrix code and not a key_id() + code, and heeds for the ALPHA modifier. */ -int keychar(int key); +int key_char(int matrix_key); /* - keytype() - Returns a key's type. Ignores modifiers. + key_type_t + Categorizes the keyboard's keys into several types: + - Arrow keys only include the REPLAY pad; + - Function keys only include the F1 .. F6 keys; + - Character keys are those which input characters; + - Control characters are all others. */ -enum KeyType keytype(int key); +typedef enum +{ + key_type_arrow = 1, + key_type_character = 2, + key_type_control = 4, + key_type_function = 8, + +} key_type_t; + +/* + key_type() + Returns a key's type. This functions ignores modifiers and expects + matrix codes as argument, not key_id() codes. +*/ +key_type_t key_type(int matrix_key); #endif // _KEYBOARD_H diff --git a/include/mpu.h b/include/mpu.h index e789cdd..dd183bc 100644 --- a/include/mpu.h +++ b/include/mpu.h @@ -27,7 +27,7 @@ //--- #ifndef _MPU_H -#define _MPU_H 1 +#define _MPU_H /* mpu_t diff --git a/include/rtc.h b/include/rtc.h index e5ca263..771ac2c 100644 --- a/include/rtc.h +++ b/include/rtc.h @@ -7,7 +7,7 @@ //--- #ifndef _RTC_H -#define _RTC_H 1 +#define _RTC_H #include diff --git a/include/screen.h b/include/screen.h index 7f7e48c..ec459a5 100644 --- a/include/screen.h +++ b/include/screen.h @@ -12,7 +12,7 @@ //--- #ifndef _SCREEN_H -#define _SCREEN_H 1 +#define _SCREEN_H /* screen_display() diff --git a/include/setjmp.h b/include/setjmp.h index 54dceea..d9ad7d8 100644 --- a/include/setjmp.h +++ b/include/setjmp.h @@ -8,7 +8,7 @@ //--- #ifndef _SETJMP_H -#define _SETJMP_H 1 +#define _SETJMP_H // There are 16 CPU registers that *must* be saved to ensure a basically // safe jump. diff --git a/include/stdio.h b/include/stdio.h index f19ecdb..ba27de6 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -8,7 +8,7 @@ //--- #ifndef _STDIO_H -#define _STDIO_H 1 +#define _STDIO_H #include #include diff --git a/include/stdlib.h b/include/stdlib.h index 5904525..59b6272 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -8,7 +8,7 @@ //--- #ifndef _STDLIB_H -#define _STDLIB_H 1 +#define _STDLIB_H //--- // Common definitions. diff --git a/include/string.h b/include/string.h index 9fdbf2a..70ac65a 100644 --- a/include/string.h +++ b/include/string.h @@ -8,7 +8,7 @@ //--- #ifndef _STRING_H -#define _STRING_H 1 +#define _STRING_H #include diff --git a/include/tales.h b/include/tales.h index 501e263..42249ef 100644 --- a/include/tales.h +++ b/include/tales.h @@ -8,7 +8,7 @@ //--- #ifndef _TALES_H -#define _TALES_H 1 +#define _TALES_H #include #include @@ -95,7 +95,7 @@ typedef struct Font Font; Sets the font and color to use for subsequent text operations. Pass font = NULL to use the default font. */ -void text_configure(struct Font *font, enum Color color); +void text_configure(struct Font *font, color_t operator); /* dtext() diff --git a/include/time.h b/include/time.h index 107e4fc..8833524 100644 --- a/include/time.h +++ b/include/time.h @@ -7,7 +7,7 @@ //--- #ifndef _TIME_H -#define _TIME_H 1 +#define _TIME_H #include diff --git a/include/timer.h b/include/timer.h index 3028e62..f6cad24 100644 --- a/include/timer.h +++ b/include/timer.h @@ -8,7 +8,7 @@ //--- #ifndef _TIMER_H -#define _TIMER_H 1 +#define _TIMER_H #include #include @@ -49,6 +49,16 @@ typedef struct timer_t timer_t; */ timer_t *timer_create(int delay_ms, int repeats); +/* + timer_reload() + Changes a virtual timer's delay. The timer is not stopped nor started: + it keeps running or waiting. Events that were waiting to be handled are + dropped and the number of repeats left is not changed. The timer + restarts counting from 0 regardless of how much time had elapsed since + it last fired. +*/ +void timer_reload(timer_t *timer, int new_ms_delay); + /* timer_destroy() Destroys a virtual timer. This virtual timer pointer becomes invalid diff --git a/src/bopti/bopti_internals.c b/src/bopti/bopti_internals.c index 4b6503f..c42d128 100644 --- a/src/bopti/bopti_internals.c +++ b/src/bopti/bopti_internals.c @@ -1,7 +1,7 @@ #include // Monochrome video ram, light and dark buffers (in this order). -int *bopti_vram, *bopti_v1, *bopti_v2; +uint32_t *bopti_vram, *bopti_v1, *bopti_v2; /* bopti_op() @@ -285,7 +285,7 @@ void bopti(const unsigned char *layer, struct Structure *s, struct Command *c) getStructure() Determines the image size and data pointer. */ -void getStructure(struct Image *img, struct Structure *s) +void getStructure(image_t *img, struct Structure *s) { int column_count, end, end_bytes, layer; diff --git a/src/bopti/dimage.c b/src/bopti/dimage.c index 104aee8..3d05d72 100644 --- a/src/bopti/dimage.c +++ b/src/bopti/dimage.c @@ -7,8 +7,8 @@ Point (left, top) is included, but (left + width, top + height) is excluded. */ -void dimage_part(int x, int y, struct Image *img, int left, int top, - int width, int height) +void dimage_part(int x, int y, image_t *img, int left, int top, int width, + int height) { if(!img || img->magic != 0x01) return; @@ -70,7 +70,7 @@ void dimage_part(int x, int y, struct Image *img, int left, int top, dimage() Displays a monochrome image in the video ram. */ -void dimage(int x, int y, struct Image *img) +void dimage(int x, int y, image_t *img) { dimage_part(x, y, img, 0, 0, -1, -1); } diff --git a/src/bopti/gimage.c b/src/bopti/gimage.c index 1bd4b5c..c6c51c0 100644 --- a/src/bopti/gimage.c +++ b/src/bopti/gimage.c @@ -8,8 +8,8 @@ Point (left, top) is included, but (left + width, top + height) is excluded. */ -void gimage_part(int x, int y, struct Image *img, int left, int top, - int width, int height) +void gimage_part(int x, int y, image_t *img, int left, int top, int width, + int height) { if(!img || img->magic != 0x01) return; @@ -71,7 +71,7 @@ void gimage_part(int x, int y, struct Image *img, int left, int top, gimage() Displays a gray image in the video ram. */ -void gimage(int x, int y, struct Image *img) +void gimage(int x, int y, image_t *img) { gimage_part(x, y, img, 0, 0, -1, -1); } diff --git a/src/clock/clock.c b/src/clock/clock.c index 8a14274..9da10f8 100644 --- a/src/clock/clock.c +++ b/src/clock/clock.c @@ -19,7 +19,7 @@ static clock_config_t conf = { Several units can be used. Be aware that the result is approximate, and very high frequencies or very short delays will yield important errors. */ -uint32_t clock_setting(int duration, enum ClockUnit unit) +uint32_t clock_setting(int duration, clock_unit_t unit) { if(conf.Pphi_f <= 0) return 0xffffffff; uint64_t f = conf.Pphi_f >> 2; @@ -27,23 +27,23 @@ uint32_t clock_setting(int duration, enum ClockUnit unit) switch(unit) { - case Clock_us: + case clock_us: result = (duration * f) / 1000000; break; - case Clock_ms: + case clock_ms: result = (duration * f) / 1000; break; - case Clock_s: + case clock_s: result = (duration * f); break; - case Clock_Hz: + case clock_Hz: result = f / duration; break; - case Clock_kHz: + case clock_kHz: result = f / (duration * 1000); break; - case Clock_MHz: + case clock_MHz: result = f / (duration * 1000000); break; @@ -103,7 +103,7 @@ void sleep_ms(int ms_delay) void sleep_us(int us_delay) { volatile int sleep_done = 0; - const uint32_t constant = clock_setting(us_delay, Clock_us); + const uint32_t constant = clock_setting(us_delay, clock_us); timer_t *timer = htimer_setup(timer_user, constant, timer_Po_4, 1); timer_attach(timer, sleep_callback, (void *)&sleep_done); diff --git a/src/core/exceptions.c b/src/core/exceptions.c index ca57cc0..9491221 100644 --- a/src/core/exceptions.c +++ b/src/core/exceptions.c @@ -37,7 +37,7 @@ static void show_error(const char *name, uint32_t *access_mode, uint32_t *tea, __asm__("stc ssr, %0" : "=rm"(ssr)); dclear(); - text_configure(NULL, Color_Black); + text_configure(NULL, color_black); print(3, 1, "EXCEPTION RAISED!"); for(int i = 0; i < 36; i++) vram[i] = ~vram[i]; diff --git a/src/ctype/ctype_classes.c b/src/ctype/ctype_classes.c new file mode 100644 index 0000000..88ff792 --- /dev/null +++ b/src/ctype/ctype_classes.c @@ -0,0 +1,43 @@ +#include + +// Let's save up some space and readability (That's Cake's idea, its a bit of a +// preprocessor trick but a rather nice trick). +#define r4(x) (x), (x), (x), (x) +#define r5(x) r4(x), (x) +#define r6(x) r5(x), (x) +#define r7(x) r6(x), (x) +#define r9(x) r7(x), (x), (x) +#define r10(x) r6(x), r4(x) +#define r15(x) r10(x), r4(x), (x) +#define r18(x) r9(x), r9(x) +#define r20(x) r10(x), r10(x) + +enum { + cntrl = 0x01, + space = 0x02, + punct = 0x04, + print = 0x08, + upper = 0x20, + lower = 0x10, + digit = 0x40, + xdigt = 0x80, +}; + +uint8_t ctype_classes[0x80] = { + // Control characters. + r9(cntrl), r5(cntrl | space), r18(cntrl), + // Space and some punctuation. + space | print, r15(punct | print), + // Decimal digits. + r10(digit | xdigt | print), + // Some punctuation. + r7(punct | print), + // Uppercase alphabet. + r6(upper | xdigt | print), r20(upper | print), + // Other punctuation symbols. + r6(punct | print), + // Lowercase alphabet. + r6(lower | xdigt | print), r20(lower | print), + // Last punctuation characters and DEL. + r4(punct | print), cntrl, +}; diff --git a/src/ctype/ctype_functions.c b/src/ctype/ctype_functions.c new file mode 100644 index 0000000..f4697ac --- /dev/null +++ b/src/ctype/ctype_functions.c @@ -0,0 +1,71 @@ +//--- +// Character type functions. +// Normally this functions need not be linked because there are macros to +// optimize performance, but we still need them to get some pointers. +//--- + +// We don't want to include because it defines all the macros... +#include +extern uint8_t ctype_classes[0x80]; + +#define _inline __attribute__((always_inline)) inline + +_inline int isalnum(int c) { + return ctype_classes[c] & 0xf0; +} + +_inline int isalpha(int c) { + return ctype_classes[c] & 0x30; +} + +_inline int iscntrl(int c) { + return ctype_classes[c] & 0x01; +} + +_inline int isdigit(int c) { + return ctype_classes[c] & 0x40; +} + +_inline int isgraph(int c) { + return ctype_classes[c] & 0xf4; +} + +_inline int islower(int c) { + return ctype_classes[c] & 0x10; +} + +_inline int isprint(int c) { + return ctype_classes[c] & 0x08; +} + +_inline int ispunct(int c) { + return ctype_classes[c] & 0x04; +} + +_inline int isspace(int c) { + return ctype_classes[c] & 0x02; +} + +_inline int isupper(int c) { + return ctype_classes[c] & 0x20; +} + +_inline int isxdigit(int c) { + return ctype_classes[c] & 0x80; +} + +_inline int isascii(int c) { + return ((unsigned)c <= 0x7f); +} + +_inline int isblank(int c) { + return (c == '\t' || c == ' '); +} + +_inline int tolower(int c) { + return c | isupper(c); +} + +_inline int toupper(int c) { + return c & ~(islower(c) << 1); +} diff --git a/src/display/dclear.c b/src/display/dclear.c index ed45a6d..6fadb5a 100644 --- a/src/display/dclear.c +++ b/src/display/dclear.c @@ -3,10 +3,30 @@ /* dclear() - Clears the whole vram. + Clears the whole vram, making all pixels white. */ void dclear(void) { - int i; - for(i = 0; i < 256; i++) vram[i] = 0; + // I tend to use pre-decrement more than post-increment. + uint32_t *index = vram + 256; + + while(index > vram) + { + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + } } diff --git a/src/display/dclear_area.c b/src/display/dclear_area.c deleted file mode 100644 index 4cb4a70..0000000 --- a/src/display/dclear_area.c +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include - -/* - dclear_area() - Clears an area of the vram using rectangle masks. Both (x1, y1) and - (x2, y2) are cleared. -*/ -void dclear_area(int x1, int y1, int x2, int y2) -{ - uint32_t masks[4]; - if(adjustRectangle(&x1, &y1, &x2, &y2)) return; - getMasks(x1, x2, masks); - - int begin = y1 << 2; - int end = (y2 + 1) << 2; - int i; - - for(i = 0; i < 4; i++) masks[i] = ~masks[i]; - for(i = begin; i < end; i++) vram[i] &= masks[i & 3]; -} diff --git a/src/display/display_vram.c b/src/display/display_vram.c index 2dc3763..34ab168 100644 --- a/src/display/display_vram.c +++ b/src/display/display_vram.c @@ -1,43 +1,48 @@ #include -// Program video ram. It resides in .bss section, therefore it is cleared at +// Program video ram. It resides in BSS section, therefore it is cleared at // program initialization and stripped from the executable file. -static int local_vram[256]; -int *vram = local_vram; +static uint32_t local_vram[256]; +uint32_t *vram = local_vram; /* display_getLocalVRAM() - Returns the local video ram address. This function always return the - same address. - The buffer returned by this function should not be used directly when - running the gray engine. + Returns gint's local video RAM address. Gint does not use the system's + buffer because it is misaligned. This function always returns the same + address. Both the display and the gray module heavily use this buffer; + make sure you don't interfere with them if you access it. + This function does not necessarily returns the video ram that is + currently in use; call display_getCurrentVRAM() for this. */ -inline void *display_getLocalVRAM(void) +inline uint32_t *display_getLocalVRAM(void) { - return (void *)local_vram; + return local_vram; } /* display_getCurrentVRAM() - Returns the current video ram. This function usually returns the - parameter of the last call to display_useVRAM(), unless the gray engine - is running (in which case the result is undefined). Returns the local - vram address by default. + Returns the current monochrome video ram buffer. This function usually + returns the parameter of the last call to display_useVRAM(), or the + local vram address (which is default when the library starts). + The return value of this function is undefined if the gray engine is + running. */ -inline void *display_getCurrentVRAM(void) +inline uint32_t *display_getCurrentVRAM(void) { - return (void *)vram; + return vram; } /* display_useVRAM() - Changes the current video ram address. The argument MUST be a 4- - aligned 1024-byte buffer ; otherwise any drawing operation will crash - the program. + Changes the current monochrome video ram address. The argument must be + a 4-aligned 1024-byte buffer because the library's design requires it. + This function refuses misaligned buffers but trusts that enough space + is available; failing to provide enough memory may crash the program. This function will most likely have no effect when running the gray engine. */ -inline void display_useVRAM(void *ptr) +inline void display_useVRAM(uint32_t *ptr) { - vram = (int *)ptr; + if((intptr_t)ptr & 3) return; + vram = ptr; } diff --git a/src/display/dline.c b/src/display/dline.c index e6e1422..95d57b6 100644 --- a/src/display/dline.c +++ b/src/display/dline.c @@ -1,38 +1,41 @@ #include #include -#define sgn(x) ((x) < 0 ? -1 : 1) -#define abs(x) ((x) < 0 ? -(x) : (x)) -#define rnd(x) ((int)((x) + 0.5)) - /* - dline() - Draws a line on the screen. Automatically optimizes horizontal and - vertical lines. + dhline() + Optimized procedure for drawing an horizontal line. Uses a rectangle + mask. */ - -static void dhline(int x1, int x2, int y, enum Color color) +static void dhline(size_t x1, size_t x2, int y, color_t operator) { uint32_t masks[4]; - int offset = y << 2; - int i; + uint32_t *video = vram + (y << 2) + 4; // Swapping x1 and x2 if needed. if(x1 > x2) x1 ^= x2, x2 ^= x1, x1 ^= x2; getMasks(x1, x2, masks); - switch(color) + switch(operator) { - case Color_White: - for(i = 0; i < 4; i++) vram[offset + i] &= ~masks[i]; + case color_white: + *--video &= ~masks[3]; + *--video &= ~masks[2]; + *--video &= ~masks[1]; + *--video &= ~masks[0]; break; - case Color_Black: - for(i = 0; i < 4; i++) vram[offset + i] |= masks[i]; + case color_black: + *--video |= masks[3]; + *--video |= masks[2]; + *--video |= masks[1]; + *--video |= masks[0]; break; - case Color_Invert: - for(i = 0; i < 4; i++) vram[offset + i] ^= masks[i]; + case color_invert: + *--video ^= masks[3]; + *--video ^= masks[2]; + *--video ^= masks[1]; + *--video ^= masks[0]; break; default: @@ -40,24 +43,30 @@ static void dhline(int x1, int x2, int y, enum Color color) } } -static void dvline(int y1, int y2, int x, enum Color color) +/* + dvline() + Optimized procedure for drawing a vertical line. This one is far less + powerful than dhline() because the video ram is essentially line- + oriented. It also uses a mask. +*/ +static void dvline(int y1, int y2, int x, color_t operator) { - int offset = (y1 << 2) + (x >> 5); - int end = (y2 << 2) + (x >> 5); - int mask = 0x80000000 >> (x & 31); + uint32_t *base = vram + (y1 << 2) + (x >> 5); + uint32_t *video = vram + (y2 << 2) + (x >> 5) + 4; + uint32_t mask = 0x80000000 >> (x & 31); - switch(color) + switch(operator) { - case Color_White: - while(offset <= end) vram[offset] &= ~mask, offset += 4; + case color_white: + while(video > base) video -= 4, *video &= ~mask; break; - case Color_Black: - while(offset <= end) vram[offset] |= mask, offset += 4; + case color_black: + while(video > base) video -= 4, *video |= mask; break; - case Color_Invert: - while(offset <= end) vram[offset] ^= mask, offset += 4; + case color_invert: + while(video > base) video -= 4, *video ^= mask; break; default: @@ -65,19 +74,28 @@ static void dvline(int y1, int y2, int x, enum Color color) } } -void dline(int x1, int y1, int x2, int y2, enum Color color) +#define sgn(x) ((x) < 0 ? -1 : 1) +#define abs(x) ((x) < 0 ? -(x) : (x)) + +/* + dline() + Line drawing algorithm more or less directly taken for MonochromeLib. + Thanks PierrotLL for this. Relies on dhline() and dvline() for specific + cases. +*/ +void dline(int x1, int y1, int x2, int y2, color_t operator) { if(adjustRectangle(&x1, &y1, &x2, &y2)) return; // Possible optimizations. if(y1 == y2) { - dhline(x1, x2, y1, color); + dhline(x1, x2, y1, operator); return; } if(x1 == x2) { - dvline(y1, y2, x1, color); + dvline(y1, y2, x1, operator); return; } @@ -87,7 +105,7 @@ void dline(int x1, int y1, int x2, int y2, enum Color color) dx = abs(dx), dy = abs(dy); - dpixel(x1, y1, color); + dpixel(x1, y1, operator); if(dx >= dy) { @@ -97,7 +115,7 @@ void dline(int x1, int y1, int x2, int y2, enum Color color) x += sx; cumul += dy; if(cumul > dx) cumul -= dx, y += sy; - dpixel(x, y, color); + dpixel(x, y, operator); } } else @@ -108,9 +126,9 @@ void dline(int x1, int y1, int x2, int y2, enum Color color) y += sy; cumul += dx; if(cumul > dy) cumul -= dy, x += sx; - dpixel(x, y, color); + dpixel(x, y, operator); } } - dpixel(x2, y2, color); + dpixel(x2, y2, operator); } diff --git a/src/display/dpixel.c b/src/display/dpixel.c index 60c3a06..8423bff 100644 --- a/src/display/dpixel.c +++ b/src/display/dpixel.c @@ -3,30 +3,23 @@ /* dpixel() - Puts a pixel in the vram. + Changes a pixel's color in the video ram. The result may depend on the + current color of the pixel. */ -void dpixel(int x, int y, enum Color color) +void dpixel(size_t x, size_t y, color_t operator) { - if((unsigned int)x > 127 || (unsigned int)y > 63) return; + // Let's be honest, all this module's code *heavily* relies on the + // screen dimension in the end, so it's not that big a deal. + if(x > 127 || y > 63) return; - int offset = (y << 2) + (x >> 5); - int mask = 0x80000000 >> (x & 31); + uint32_t *video = vram + (y << 2) + (x >> 5); + uint32_t mask = 0x80000000 >> (x & 31); - switch(color) + switch(operator) { - case Color_White: - vram[offset] &= ~mask; - break; - - case Color_Black: - vram[offset] |= mask; - break; - - case Color_Invert: - vram[offset] ^= mask; - break; - - default: - break; + case color_white: *video &= ~mask; break; + case color_black: *video |= mask; break; + case color_invert: *video ^= mask; break; + default: return; } } diff --git a/src/display/drect.c b/src/display/drect.c new file mode 100644 index 0000000..5e4d02b --- /dev/null +++ b/src/display/drect.c @@ -0,0 +1,59 @@ +#include +#include + +/* + drect() + Draws a rectangle on the screen. This function can use any color which + is not associated with the gray engine, including the reverse operator. +*/ +void drect(int x1, int y1, int x2, int y2, color_t operator) +{ + // Avoid wasting time if the requested operation is invalid here. + if(operator != color_white && operator != color_black + && operator != color_invert) return; + // Make sure the coordinates are in the right order, and that the + // requested rectangle crosses the screen. + if(adjustRectangle(&x1, &y1, &x2, &y2)) return; + + uint32_t masks[4]; + getMasks(x1, x2, masks); + + uint32_t *base = vram + (y1 << 2); + uint32_t *video = vram + (y2 << 2) + 4; + + switch(operator) + { + case color_white: + while(video > base) + { + *--video &= ~masks[3]; + *--video &= ~masks[2]; + *--video &= ~masks[1]; + *--video &= ~masks[0]; + } + break; + + case color_black: + while(video > base) + { + *--video |= masks[3]; + *--video |= masks[2]; + *--video |= masks[1]; + *--video |= masks[0]; + } + break; + + case color_invert: + while(video > base) + { + *--video ^= masks[3]; + *--video ^= masks[2]; + *--video ^= masks[1]; + *--video ^= masks[0]; + } + break; + + // Avoid some warnings. + default: return; + } +} diff --git a/src/display/dreverse_area.c b/src/display/dreverse_area.c deleted file mode 100644 index 0288cbe..0000000 --- a/src/display/dreverse_area.c +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include - -/* - dreverse_area() - Reverses an area of the vram. This function is a simple application of - the rectangle masks concept. (x1, y1) and (x2, y2) are reversed as - well. -*/ -void dreverse_area(int x1, int y1, int x2, int y2) -{ - uint32_t masks[4]; - if(adjustRectangle(&x1, &y1, &x2, &y2)) return; - getMasks(x1, x2, masks); - - int begin = y1 << 2; - int end = (y2 + 1) << 2; - int i; - - for(i = begin; i < end; i++) vram[i] ^= masks[i & 3]; -} diff --git a/src/display/getMasks.c b/src/display/getMasks.c index 6f3091c..740d04b 100644 --- a/src/display/getMasks.c +++ b/src/display/getMasks.c @@ -6,18 +6,18 @@ and x2 (both included). The four masks are stored in the third argument (seen as an array). */ -void getMasks(int x1, int x2, uint32_t *masks) +void getMasks(size_t x1, size_t x2, uint32_t *masks) { - // Indexes of the first and last longs that are non-blank. - int l1 = x1 >> 5; - int l2 = x2 >> 5; - int i = 0; + // Indexes of the first and last longs that are not empty. + size_t l1 = x1 >> 5; + size_t l2 = x2 >> 5; + size_t i = 0; // Setting the base masks. Those are the final values, except for the - // longs with indexes l1 and l2, that still need to be adjusted. - while(i < l1) masks[i++] = 0x00000000; - while(i <= l2) masks[i++] = 0xffffffff; - while(i < 4) masks[i++] = 0x00000000; + // longs between indexes l1 and l2, that still need to be adjusted. + while(i < l1) masks[i++] = 0x00000000; + while(i <= l2) masks[i++] = 0xffffffff; + while(i < 4) masks[i++] = 0x00000000; // Removing the long number information in x1 and x2 (that is, the // multiples of 32) to keep only the interesting information -- the diff --git a/src/events/event_get.c b/src/events/event_get.c index 5b2e707..62b4c7f 100644 --- a/src/events/event_get.c +++ b/src/events/event_get.c @@ -10,7 +10,7 @@ event_t pollevent(void) { event_t event = { - .type = ET_None + .type = event_none, }; if(queue_size <= 0) return event; @@ -33,6 +33,6 @@ event_t waitevent(void) { event_t event; - while((event = pollevent()).type == ET_None) sleep(); + while((event = pollevent()).type == event_none) sleep(); return event; } diff --git a/src/events/event_push.c b/src/events/event_push.c index 0e39b2c..fc01a21 100644 --- a/src/events/event_push.c +++ b/src/events/event_push.c @@ -14,7 +14,7 @@ volatile int queue_size = 0; int event_push(event_t event) { if(queue_size >= EVENTS_QUEUE_SIZE) return 1; - if(event.type == ET_None) return 2; + if(event.type == event_none) return 2; int index = queue_start + queue_size; if(index >= EVENTS_QUEUE_SIZE) index -= EVENTS_QUEUE_SIZE; diff --git a/src/gray/gclear.c b/src/gray/gclear.c index e3beb21..60571ec 100644 --- a/src/gray/gclear.c +++ b/src/gray/gclear.c @@ -6,9 +6,28 @@ */ void gclear(void) { - int *v1 = gray_lightVRAM(); - int *v2 = gray_darkVRAM(); - int i; + uint32_t *lbase = gray_lightVRAM(); + uint32_t *v1 = lbase + 256; + uint32_t *v2 = gray_darkVRAM() + 256; - for(i = 0; i < 256; i++) v1[i] = v2[i] = 0; + while(v1 > lbase) + { + *--v1 = 0; + *--v1 = 0; + *--v1 = 0; + *--v1 = 0; + *--v1 = 0; + *--v1 = 0; + *--v1 = 0; + *--v1 = 0; + + *--v2 = 0; + *--v2 = 0; + *--v2 = 0; + *--v2 = 0; + *--v2 = 0; + *--v2 = 0; + *--v2 = 0; + *--v2 = 0; + } } diff --git a/src/gray/gclear_area.c b/src/gray/gclear_area.c deleted file mode 100644 index 42c3f29..0000000 --- a/src/gray/gclear_area.c +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include - -/* - gclear_area() - Clears an area of the video ram. End points (x1, y1) and (x2, y2) are - included. -*/ -void gclear_area(int x1, int y1, int x2, int y2) -{ - display_useVRAM(gray_lightVRAM()); - dclear_area(x1, y1, x2, y2); - display_useVRAM(gray_darkVRAM()); - dclear_area(x1, y1, x2, y2); -} diff --git a/src/gray/gline.c b/src/gray/gline.c index 9b31480..1599f58 100644 --- a/src/gray/gline.c +++ b/src/gray/gline.c @@ -3,22 +3,23 @@ /* gline() - Draws a line in the vram. Automatically optimizes special cases. + Draws a line in the vram. Automatically optimizes special cases. This + function does not support the light operators. */ -void gline(int x1, int y1, int x2, int y2, enum Color color) +void gline(int x1, int y1, int x2, int y2, color_t operator) { - enum Color c1, c2; + color_t op1, op2; - if(color == Color_None) return; - else if(color == Color_Invert) c1 = c2 = Color_Invert; + if(operator == color_invert) op1 = op2 = color_invert; + else if(operator >= color_none) return; else { - c1 = 3 * (color & 1); - c2 = 3 * (color >> 1); + op1 = 3 * (operator & 1); + op2 = 3 * (operator >> 1); } display_useVRAM(gray_lightVRAM()); - dline(x1, y1, x2, y2, c1); + dline(x1, y1, x2, y2, op1); display_useVRAM(gray_darkVRAM()); - dline(x1, y1, x2, y2, c2); + dline(x1, y1, x2, y2, op2); } diff --git a/src/gray/gpixel.c b/src/gray/gpixel.c index be44071..eae2460 100644 --- a/src/gray/gpixel.c +++ b/src/gray/gpixel.c @@ -4,44 +4,56 @@ gpixel() Puts a pixel in the vram. */ -void gpixel(int x, int y, enum Color color) +void gpixel(size_t x, size_t y, color_t operator) { - if((unsigned int)x > 127 || (unsigned int)y > 63) return; + if(x > 127 || y > 63) return; - int offset = (y << 2) + (x >> 5); - int mask = 0x80000000 >> (x & 31); + uint32_t *light = gray_lightVRAM() + (y << 2) + (x >> 5); + uint32_t *dark = gray_darkVRAM() + (y << 2) + (x >> 5); + uint32_t mask = 0x80000000 >> (x & 31); - int *v1 = gray_lightVRAM(); - int *v2 = gray_lightVRAM(); - - switch(color) + switch(operator) { - case Color_White: - v1[offset] &= ~mask; - v2[offset] &= ~mask; + case color_white: + *light &= ~mask; + *dark &= ~mask; break; - - case Color_Light: - v1[offset] |= mask; - v2[offset] &= ~mask; + case color_light: + *light |= mask; + *dark &= ~mask; break; - - case Color_Dark: - v1[offset] &= ~mask; - v2[offset] |= mask; + case color_dark: + *light &= ~mask; + *dark |= mask; break; - - case Color_Black: - v1[offset] |= mask; - v2[offset] |= mask; + case color_black: + *light |= mask; + *dark |= mask; break; + case color_none: + return; - case Color_Invert: - v1[offset] ^= mask; - v2[offset] ^= mask; + case color_invert: + *light ^= mask; + *dark ^= mask; break; - - default: + case color_lighten:; + uint32_t old_light_1 = *light; + *light &= *dark | ~mask; + *dark = (old_light_1 | ~mask) & (mask ^ *dark); + break; + case color_lighten2: + *dark &= *light | ~mask; + *light &= ~mask; + break; + case color_darken:; + uint32_t old_light_2 = *light; + *light |= *dark & mask; + *dark = (old_light_2 & mask) | (mask ^ *dark); + break; + case color_darken2: + *dark |= *light | mask; + *light |= mask; break; } } diff --git a/src/gray/gray_engine.c b/src/gray/gray_engine.c index 2e843f3..e79af37 100644 --- a/src/gray/gray_engine.c +++ b/src/gray/gray_engine.c @@ -11,8 +11,8 @@ #include #include -static int internal_vrams[3][256]; -static const void *vrams[4]; +static uint32_t internal_vrams[3][256]; +static uint32_t *vrams[4]; static int current = 0; static int delays[2]; @@ -45,10 +45,10 @@ void gray_interrupt(void) */ __attribute__((constructor)) void gray_init(void) { - vrams[0] = (const void *)display_getLocalVRAM(); - vrams[1] = (const void *)internal_vrams[0]; - vrams[2] = (const void *)internal_vrams[1]; - vrams[3] = (const void *)internal_vrams[2]; + vrams[0] = display_getLocalVRAM(); + vrams[1] = internal_vrams[0]; + vrams[2] = internal_vrams[1]; + vrams[3] = internal_vrams[2]; delays[0] = 912; delays[1] = 1343; @@ -119,18 +119,18 @@ inline int gray_runs(void) gray_lightVRAM() Returns the module's gray vram address. */ -void *gray_lightVRAM(void) +uint32_t *gray_lightVRAM(void) { - return (void *)vrams[~current & 2]; + return vrams[~current & 2]; } /* gray_lightVRAM() Returns the module's dark vram address. */ -void *gray_darkVRAM(void) +uint32_t *gray_darkVRAM(void) { - return (void *)vrams[(~current & 2) | 1]; + return vrams[(~current & 2) | 1]; } /* diff --git a/src/gray/grect.c b/src/gray/grect.c new file mode 100644 index 0000000..0edfb1d --- /dev/null +++ b/src/gray/grect.c @@ -0,0 +1,70 @@ +#include +#include + +/* + grect() + Draws a rectangle on the screen. This function can use all colors. +*/ +void grect(int x1, int y1, int x2, int y2, color_t operator) +{ + if(operator == color_none) return; + if(adjustRectangle(&x1, &y1, &x2, &y2)) return; + + uint32_t masks[4]; + getMasks(x1, x2, masks); + + uint32_t *lbase = gray_lightVRAM() + (y1 << 2); + uint32_t *lvideo = gray_lightVRAM() + (y2 << 2) + 4; + uint32_t *dvideo = gray_darkVRAM() + (y2 << 2) + 4; + + // Doing things in this order will be slower, but man, I can't stand + // writing that many lines of code for such a simple task. It will be + // terribly heavy in the binary file... + while(lvideo > lbase) for(int i = 0; i < 4; i++) switch(operator) + { + case color_white: + *--lvideo &= ~masks[i]; + *--dvideo &= ~masks[i]; + break; + case color_light: + *--lvideo |= masks[i]; + *--dvideo &= ~masks[i]; + break; + case color_dark: + *--lvideo &= ~masks[i]; + *--dvideo |= masks[i]; + break; + case color_black: + *--lvideo |= masks[i]; + *--dvideo |= masks[i]; + break; + case color_none: return; + + case color_invert: + *--lvideo ^= masks[i]; + *--dvideo ^= masks[i]; + break; + case color_lighten:; + uint32_t light_1 = *lvideo; + dvideo--; + *--lvideo &= *dvideo | ~masks[i]; + *dvideo = (light_1 | ~masks[i]) & (masks[i] ^ *dvideo); + break; + case color_lighten2: + lvideo--; + *--dvideo &= *lvideo | ~masks[i]; + *lvideo &= ~masks[i]; + break; + case color_darken:; + uint32_t light_2 = *lvideo; + dvideo--; + *--lvideo |= *dvideo & masks[i]; + *dvideo = (light_2 & masks[i]) | (masks[i] ^ *dvideo); + break; + case color_darken2: + lvideo--; + *--dvideo |= *lvideo | masks[i]; + *lvideo |= masks[i]; + break; + } +} diff --git a/src/gray/greverse_area.c b/src/gray/greverse_area.c deleted file mode 100644 index 7a3fccd..0000000 --- a/src/gray/greverse_area.c +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include - -/* - greverse_area() - Reverses an area of the vram. End points (x1, y1) and (x2, y2) are - included. -*/ -void greverse_area(int x1, int y1, int x2, int y2) -{ - display_useVRAM(gray_lightVRAM()); - dreverse_area(x1, y1, x2, y2); - display_useVRAM(gray_darkVRAM()); - dreverse_area(x1, y1, x2, y2); -} diff --git a/src/keyboard/getkey.c b/src/keyboard/getkey.c index 1cbfcad..5d3cc68 100644 --- a/src/keyboard/getkey.c +++ b/src/keyboard/getkey.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -11,10 +12,11 @@ int getkey(void) { return getkey_opt( - Getkey_ShiftModifier | - Getkey_AlphaModifier | - Getkey_ManageBacklight | - Getkey_RepeatArrowKeys, + getkey_shift_modifier | + getkey_alpha_modifier | + getkey_manage_backlight | + getkey_repeat_arrow_keys, + 0 ); } @@ -22,101 +24,92 @@ int getkey(void) /* getkey_opt() Enhances getkey() with more general functionalities. - If max_cycles is non-zero and positive, getkey_opt() will return - KEY_NOEVENT if no event occurs during max_cycle analysis. + If delay_ms is non-zero and positive, getkey_opt() will return + KEY_NOEVENT if no event occurs before delay_ms. */ -static void getkey_opt_wait(int *cycles) +static void getkey_opt_wait(int *delay_ms) { while(!interrupt_flag) sleep(); interrupt_flag = 0; - if(*cycles > 0) (*cycles)--; + if(*delay_ms > 0) + { + (*delay_ms) -= vtimer->ms_delay; + if(*delay_ms < 0) *delay_ms = 0; + } } -int getkey_opt(enum GetkeyOpt options, int cycles) +int getkey_opt(getkey_option_t options, int delay_ms) { event_t event; - int modifier = 0; - static int event_ref = 0; + int modifier = 0, key; + key_type_t type; - if(cycles <= 0) cycles = -1; - while(cycles != 0) + if(delay_ms <= 0) delay_ms = -1; + + while(delay_ms != 0) switch((event = pollevent()).type) { - event = pollevent(); - switch(event.type) + case event_none: + getkey_opt_wait(&delay_ms); + break; + + case event_key_press: + key = event.key; + + if(options & getkey_manage_backlight && key == KEY_OPTN + && (modifier & MOD_SHIFT)) { - case ET_None: - if(last_key == KEY_NONE) - { - getkey_opt_wait(&cycles); - continue; - } - - // Handling repetitions. - enum KeyType type = keytype(last_key); - if(!(options & (type << 4))) break; - - if(event_ref <= 0) - { - getkey_opt_wait(&cycles); - event_ref++; - continue; - } - - last_events++; - event_ref--; - - if(last_events >= (last_repeats ? repeat_next : - repeat_first)) - { - last_repeats++; - last_events = 0; - return last_key; - } - break; - - case ET_KeyPress: - ; - int key = event.key; - if(options & Getkey_ManageBacklight && key == KEY_OPTN - && modifier & MOD_SHIFT) - { - screen_toggleBacklight(); - modifier &= ~MOD_SHIFT; - continue; - } - if(options & Getkey_ShiftModifier && key == KEY_SHIFT) - { - modifier ^= MOD_SHIFT; - continue; - } - if(options & Getkey_AlphaModifier && key == KEY_ALPHA) - { - modifier ^= MOD_ALPHA; - continue; - } - - last_key = key; - last_repeats = 0; - last_events = 0; - event_ref = 0; - return key | modifier; - - case ET_KeyRel: - if(event.key != last_key) break; - last_key = KEY_NONE; - last_repeats = 0; - last_events = 0; - event_ref = 0; - break; - - default: - break; + screen_toggleBacklight(); + modifier &= ~MOD_SHIFT; + continue; } + if(options & getkey_shift_modifier && key == KEY_SHIFT) + { + modifier ^= MOD_SHIFT; + continue; + } + if(options & getkey_alpha_modifier && key == KEY_ALPHA) + { + modifier ^= MOD_ALPHA; + continue; + } + + last_key = key; + last_repeats = 0; + last_time = 0; + return key | modifier; + + case event_key_repeat: + key = event.key; + if(key != last_key) continue; + + // Checking that this type of repetition is allowed. + type = key_type(key); + if(!(options & (type << 4))) break; + + last_time += vtimer->ms_delay; + int cmp = last_repeats ? repeat_next : repeat_first; + + if(last_time >= cmp) + { + last_repeats++; + last_time -= cmp; + return last_key; + } + break; + + case event_key_release: + if(event.key != last_key) break; + last_key = KEY_NONE; + last_repeats = 0; + last_time = 0; + break; + + default: + break; } last_key = KEY_NONE; last_repeats = 0; - last_events = 0; - event_ref = 0; + last_time = 0; return KEY_NONE; } diff --git a/src/keyboard/keychar.c b/src/keyboard/key_char.c similarity index 67% rename from src/keyboard/keychar.c rename to src/keyboard/key_char.c index bd9e9cc..1e1ccd1 100644 --- a/src/keyboard/keychar.c +++ b/src/keyboard/key_char.c @@ -1,11 +1,12 @@ #include /* - keychar() - Returns the ASCII character associated with a key, or 0 for control - keys. + key_char() + Returns the ASCII character associated with a character key, and 0 for + other keys. This function expects a matrix code and not a key_id() + code, and heeds for the ALPHA modifier. */ -int keychar(int key) +int key_char(int matrix_key) { char flat[] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, @@ -30,7 +31,6 @@ int keychar(int key) 'Z', ' ', '"', 0x0, 0x0, 0x0 }; - int id = keyid(key); - - return (key & MOD_ALPHA) ? alpha[id] : flat[id]; + int id = key_id(matrix_key); + return (matrix_key & MOD_ALPHA) ? alpha[id] : flat[id]; } diff --git a/src/keyboard/key_id.c b/src/keyboard/key_id.c new file mode 100644 index 0000000..152208c --- /dev/null +++ b/src/keyboard/key_id.c @@ -0,0 +1,19 @@ +#include + +/* + keyid() + Transforms a key identifier and returns a key code that is more + convenient for array subscript that the original matrix codes. Please + note that there are a few holes in the numbering. + This function ignores modifiers and returns -1 on error. +*/ +int key_id(int matrix_key) +{ + if(matrix_key < 0) return -1; + matrix_key &= MOD_CLEAR; + + int row = 9 - (matrix_key & 0x0f); + int column = 6 - ((matrix_key & 0xf0) >> 4); + + return 6 * row + column; +} diff --git a/src/keyboard/key_type.c b/src/keyboard/key_type.c new file mode 100644 index 0000000..b29e617 --- /dev/null +++ b/src/keyboard/key_type.c @@ -0,0 +1,24 @@ +#include + +/* + key_type() + Returns a key's type. This functions ignores modifiers and expects + matrix codes as argument, not key_id() codes. +*/ +key_type_t key_type(int matrix_key) +{ + matrix_key &= MOD_CLEAR; + + // Arrow keys. + if(matrix_key == KEY_UP || matrix_key == KEY_RIGHT + || matrix_key == KEY_DOWN || matrix_key == KEY_LEFT) + { + return key_type_arrow; + } + + // Function keys (F1 .. F6) are ton only keys of the ninth row. + if((matrix_key & 0x0f) == 0x09) return key_type_function; + + // Then character keys are those that have an associated character. =p + return key_char(matrix_key) ? key_type_character : key_type_control; +} diff --git a/src/keyboard/keyboard_config.c b/src/keyboard/keyboard_config.c deleted file mode 100644 index e19347f..0000000 --- a/src/keyboard/keyboard_config.c +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include -#include -#include - -/* - keyboard_setRepeatRate() - Sets the default repeat rate for key events. The unit for the argument - is the keyboard period. - For example at 32 Hz, values of (20, 4) will imitate the system - default. -*/ -void keyboard_setRepeatRate(int first, int next) -{ - if(first < 0) first = 0; - if(next < 0) next = 0; - - repeat_first = first; - repeat_next = next; -} diff --git a/src/keyboard/keyboard_core.c b/src/keyboard/keyboard_core.c new file mode 100644 index 0000000..ea76d3c --- /dev/null +++ b/src/keyboard/keyboard_core.c @@ -0,0 +1,181 @@ +#include +#include +#include +#include +#include +#include + +//--- +// Keyboard variables. +//--- + +// Current keyboard state: each element represents a row, and each bit a key, +// but not all bits are used. +volatile uint8_t keyboard_state[10] = { 0 }; +// Interrupt flag: set when an interrupt occurs, and cleared by functions such +// as getkey() that watch it (because such functions will wake up every time an +// interrupt occurs, and they need to know whether it was the keyboard). +volatile int interrupt_flag = 0; + +// Delays, in milliseconds, before repeating keys (the first repetition may +// have a different delay). +int repeat_first = 625, repeat_next = 125; + +// Which key was pressed last, how many times it has been repeated, and how +// much time (in milliseconds) has elapsed since it was last repeated. +int last_key = KEY_NONE, last_repeats = 0, last_time = 0; + +// Virtual timer object. +timer_t *vtimer = NULL; + + + +//--- +// Interrupt management. +//--- + +static inline void push_press(int keycode) +{ + event_t event = { + .type = event_key_press, + .key = keycode, + }; + event_push(event); +} + +static inline void push_repeat(int keycode) +{ + event_t event = { + .type = event_key_repeat, + .key = keycode, + }; + event_push(event); +} + +static inline void push_release(int keycode) +{ + event_t event = { + .type = event_key_release, + .key = keycode, + }; + event_push(event); +} + +/* + keyboard_interrupt() + Callback function for keyboard update; called by the timer manager when + the keyboard's virtual timer fires. Ideally this function should be + interrupt-driven but the PINT interrupts seem to fire continuously, + which is annoying. +*/ +void keyboard_interrupt(void) +{ + 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]; + + if(pressed & 1) push_press(KEY_AC_ON); + if(released & 1) push_release(KEY_AC_ON); + } + 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]; + keyboard_state[row] = state[row]; + + // Make this a bit faster. + if(!(pressed | repeated | released)) continue; + + for(int column = 0; column < 8; column++) + { + if(pressed & 1) push_press ((column << 4) | row); + if(repeated & 1) push_repeat ((column << 4) | row); + if(released & 1) push_release((column << 4) | row); + + pressed >>= 1; + repeated >>= 1; + released >>= 1; + } + } + + // Signal the interrupt to the higher-level functions. + interrupt_flag = 1; +} + + + +//--- +// Keyboard configuration. +//--- + +/* + keyboard_init() + Starts the keyboard timer. +*/ +__attribute__((constructor)) void keyboard_init(void) +{ + keyboard_setRepeatRate(625, 125); + + vtimer = timer_create(25, 0); + timer_attach(vtimer, keyboard_interrupt, NULL); + timer_start(vtimer); +} + +/* + keyboard_setAnalysisDelay() + Sets the keyboard analysis delay, that is, the delay (in ms) between + two keyboard analyzes. Please note that the repeat delays should be + multiples of the analysis delay for better accuracy. +*/ +void keyboard_setAnalysisDelay(int analysis_delay_ms) +{ + if(analysis_delay_ms <= 0) return; + timer_reload(vtimer, analysis_delay_ms); +} + +/* + keyboard_setRepeatRate() + Sets the default repeat rate for key events. The delay before the first + repeat may have a different value (usually longer). The unit for the + argument is ms, but the repeat events themselves may only be fired when + a keyboard analysis is performed; which means that for better accuracy, + these delays should be a multiple of the keyboard period. + For instance, delays of (625 ms, 125 ms) will imitate the system's + default setting. +*/ +void keyboard_setRepeatRate(int first, int next) +{ + repeat_first = (first > 0) ? first : 0; + repeat_next = (next > 0) ? next : 0; +} + +/* + keyboard_quit() + Stops the keyboard timer. +*/ +__attribute__((destructor)) void keyboard_quit(void) +{ + timer_destroy(vtimer); + vtimer = NULL; +} + +/* + keyboard_stateBuffer() + Returns the address of the keyboard state array. The returned address + is the handler's buffer, therefore it contains volatile data. +*/ +volatile uint8_t *keyboard_stateBuffer(void) +{ + return keyboard_state; +} diff --git a/src/keyboard/keyboard_interrupt.c b/src/keyboard/keyboard_interrupt.c deleted file mode 100644 index 9cf854a..0000000 --- a/src/keyboard/keyboard_interrupt.c +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include -#include -#include -#include -#include - -//--- -// Keyboard variables. -//--- - -// These ones get modified by interrupts. -volatile uint8_t keyboard_state[10] = { 0 }; -volatile int interrupt_flag = 0; - -// Key statistics. -int repeat_first = 10, repeat_next = 2; -int last_key = KEY_NONE, last_repeats = 0, last_events = 0; - -// RTC callback id. -unsigned cb_id; - - - -//--- -// Interrupt management. -//--- - -static void push_press(int keycode) -{ - event_t event = { - .type = ET_KeyPress, - .key = keycode, - }; - event_push(event); -} - -static void push_repeat(int keycode) -{ - event_t event = { - .type = ET_KeyRepeat, - .key = keycode, - }; - event_push(event); -} - -static void push_release(int keycode) -{ - event_t event = { - .type = ET_KeyRel, - .key = keycode, - }; - event_push(event); -} - -/* - keyboard_interrupt() - Callback for keyboard update. Allows keyboard analysis functions to - wake only when keyboard interrupts happen. -*/ -void keyboard_interrupt(void) -{ - uint8_t state[10] = { 0 }; - - isSH3() ? keyboard_updateState_7705(state) - : keyboard_updateState_7305(state) - ; - - // Try to minimize number of operations in common cases... this 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]; - - if(pressed & 1) push_press(KEY_AC_ON); - if(released & 1) push_release(KEY_AC_ON); - } - 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]; - keyboard_state[row] = state[row]; - - // Fasten this a bit. - if(!(pressed | repeated | released)) continue; - - for(int column = 0; column < 8; column++) - { - if(pressed & 1) push_press ((column << 4) | row); - if(repeated & 1) push_repeat ((column << 4) | row); - if(released & 1) push_release((column << 4) | row); - - pressed >>= 1; - repeated >>= 1; - released >>= 1; - } - } - - interrupt_flag = 1; -} - -/* - keyboard_init() - Starts the keyboard timer. -*/ -__attribute__((constructor)) void keyboard_init(void) -{ - cb_id = rtc_cb_add(RTCFreq_16Hz, keyboard_interrupt, 0); -} - -/* - keyboard_setFrequency() - Sets the keyboard frequency. -*/ -void keyboard_setFrequency(enum KeyboardFrequency frequency) -{ - if(frequency < 1 || frequency > 7) return; - rtc_cb_edit(cb_id, frequency, keyboard_interrupt); -} - -/* - keyboard_quit() - Stops the keyboard timer. -*/ -__attribute__((destructor)) void keyboard_quit(void) -{ - rtc_cb_end(cb_id); -} diff --git a/src/keyboard/keyboard_misc.c b/src/keyboard/keyboard_misc.c deleted file mode 100644 index 6248a3d..0000000 --- a/src/keyboard/keyboard_misc.c +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include - -/* - keylast() - Returns the matrix code of the last pressed key. If repeat_count is - non-NULL, it is set to the number of repetitions. -*/ -int keylast(int *repeat_count) -{ - if(repeat_count) *repeat_count = last_repeats; - return last_key; -} - - - -/* - keystate() - Returns the address of the keyboard state array. The returned address - is the handler's buffer, therefore it contains volatile data. -*/ -volatile uint8_t *keystate(void) -{ - return keyboard_state; -} diff --git a/src/keyboard/keyboard_sh7305.c b/src/keyboard/keyboard_sh7305.c index 8eb0bca..cae413a 100644 --- a/src/keyboard/keyboard_sh7305.c +++ b/src/keyboard/keyboard_sh7305.c @@ -10,6 +10,7 @@ // //--- +#include #include #include <7305.h> @@ -61,20 +62,20 @@ static void kdelay(void) */ static int krow(int row) { - volatile unsigned short *injector1 = (unsigned short *)0xa4050116; - volatile unsigned char *data1 = (unsigned char *)0xa4050136; + volatile uint16_t *injector1 = (void *)0xa4050116; + volatile uint8_t *data1 = (void *)0xa4050136; - volatile unsigned short *injector2 = (unsigned short *)0xa4050118; - volatile unsigned char *data2 = (unsigned char *)0xa4050138; + volatile uint16_t *injector2 = (void *)0xa4050118; + volatile uint8_t *data2 = (void *)0xa4050138; - volatile unsigned short *detector = (unsigned short *)0xa405014c; - volatile unsigned char *keys = (unsigned char *)0xa405016c; + volatile uint16_t *detector = (void *)0xa405014c; + volatile uint8_t *keys = (void *)0xa405016c; - volatile unsigned char *key_register = (unsigned char *)0xa40501c6; -// volatile unsigned short *hizcrb = (unsigned short *)0xa405015a; + volatile uint8_t *key_register = (void *)0xa40501c6; +// volatile uint16_t *hizcrb = (void *)0xa405015a; - unsigned short smask; - unsigned char cmask; + uint16_t smask; + uint8_t cmask; int result = 0; if(row < 0 || row > 9) return 0; diff --git a/src/keyboard/keyid.c b/src/keyboard/keyid.c deleted file mode 100644 index c4ed014..0000000 --- a/src/keyboard/keyid.c +++ /dev/null @@ -1,17 +0,0 @@ -#include - -/* - keyid() - Returns a non-matrix key code that can be used for array subscript. - Ignores modifiers. -*/ -int keyid(int key) -{ - if(key < 0) return -1; - key &= MOD_CLEAR; - - int row = 9 - (key & 0x0f); - int column = 6 - ((key & 0xf0) >> 4); - - return 6 * row + column; -} diff --git a/src/keyboard/keytype.c b/src/keyboard/keytype.c deleted file mode 100644 index 9e3c0c9..0000000 --- a/src/keyboard/keytype.c +++ /dev/null @@ -1,17 +0,0 @@ -#include - -/* - keytype() - Returns a key's type. Ignores modifiers. -*/ -enum KeyType keytype(int key) -{ - key &= MOD_CLEAR; - - if(key == KEY_UP || key == KEY_RIGHT || key == KEY_DOWN || - key == KEY_LEFT) return KeyType_Arrow; - - if((key & 0x0f) == 0x09) return KeyType_Function; - - return keychar(key) ? KeyType_Character : KeyType_Control; -} diff --git a/src/keyboard/multigetkey.c b/src/keyboard/multigetkey.c index b1bc5e5..74f9b6c 100644 --- a/src/keyboard/multigetkey.c +++ b/src/keyboard/multigetkey.c @@ -40,7 +40,7 @@ void multigetkey(int *keys, int count, int cycles) { last_key = keys[0]; last_repeats = 0; - last_events = 0; + last_time = 0; } if(number) break; @@ -48,7 +48,7 @@ void multigetkey(int *keys, int count, int cycles) } do event = pollevent(); - while(event.type != ET_None); + while(event.type != event_none); return; } diff --git a/src/string/ctype.c b/src/string/ctype.c deleted file mode 100644 index 6b55781..0000000 --- a/src/string/ctype.c +++ /dev/null @@ -1,64 +0,0 @@ -#include - -enum { - cntrl = 0x01, - space = 0x02, - punct = 0x04, - print = 0x08, - upper = 0x20, - lower = 0x10, - digit = 0x40, - xdigt = 0x80, -}; - -uint8_t ctype_classes[0x80] = { - // Control characters. - cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, - cntrl | space, // Tabulation - cntrl | space, cntrl | space, cntrl | space, cntrl | space, - cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, - cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, - - // Space and some punctuation. - space | print, - punct | print, punct | print, punct | print, punct | print, - punct | print, punct | print, punct | print, punct | print, - punct | print, punct | print, punct | print, punct | print, - punct | print, punct | print, punct | print, - - // Decimal digits. - digit | xdigt | print, digit | xdigt | print, digit | xdigt | print, - digit | xdigt | print, digit | xdigt | print, digit | xdigt | print, - digit | xdigt | print, digit | xdigt | print, digit | xdigt | print, - digit | xdigt | print, - - // Some punctuation. - punct | print, punct | print, punct | print, punct | print, - punct | print, punct | print, punct | print, - - // Uppercase alphabet. - upper | xdigt | print, upper | xdigt | print, upper | xdigt | print, - upper | xdigt | print, upper | xdigt | print, upper | xdigt | print, - upper | print, upper | print, upper | print, upper | print, - upper | print, upper | print, upper | print, upper | print, - upper | print, upper | print, upper | print, upper | print, - upper | print, upper | print, upper | print, upper | print, - upper | print, upper | print, upper | print, upper | print, - - // Other punctuation symbols. - punct | print, punct | print, punct | print, punct | print, - punct | print, punct | print, - - // Lowercase alphabet. - lower | xdigt | print, lower | xdigt | print, lower | xdigt | print, - lower | xdigt | print, lower | xdigt | print, lower | xdigt | print, - lower | print, lower | print, lower | print, lower | print, - lower | print, lower | print, lower | print, lower | print, - lower | print, lower | print, lower | print, lower | print, - lower | print, lower | print, lower | print, lower | print, - lower | print, lower | print, lower | print, lower | print, - - // Last punctuation characters and DEL. - punct | print, punct | print, punct | print, punct | print, - cntrl -}; diff --git a/src/tales/tales_configuration.c b/src/tales/tales_configuration.c index 8b00345..81b9e32 100644 --- a/src/tales/tales_configuration.c +++ b/src/tales/tales_configuration.c @@ -5,11 +5,11 @@ text_configure() Sets the font and mode to use for the following print operations. */ -void text_configure(struct Font *next_font, enum Color next_color) +void text_configure(struct Font *next_font, color_t next_operator) { extern Font gint_font_system; if(next_font) font = next_font; else font = &gint_font_system; - color = next_color; + operator = next_operator; } diff --git a/src/tales/tales_gray.c b/src/tales/tales_gray.c index 4b9b02a..42918e8 100644 --- a/src/tales/tales_gray.c +++ b/src/tales/tales_gray.c @@ -4,42 +4,61 @@ void operate_gray(OPERATE_ARGS) { - int *vl = gray_lightVRAM(); - int *vd = gray_darkVRAM(); - int vram_offset = (x >> 5) + (y << 2); - uint32_t op; - int i; + size_t vram_offset = (x >> 5) + (y << 2); + uint32_t *light = gray_lightVRAM() + vram_offset; + uint32_t *dark = gray_darkVRAM() + vram_offset; + uint32_t op, old_light; - for(i = 0; i < height; i++) + for(int i = 0; i < height; i++) { op = operators[i]; - switch(color) + switch(operator) { - case Color_White: - vl[vram_offset] &= ~op; - vd[vram_offset] &= ~op; + case color_white: + *light &= ~op; + *dark &= ~op; break; - case Color_Light: - vl[vram_offset] |= op; - vd[vram_offset] &= ~op; + case color_light: + *light |= op; + *dark &= ~op; break; - case Color_Dark: - vl[vram_offset] &= ~op; - vd[vram_offset] |= op; + case color_dark: + *light &= ~op; + *dark |= op; break; - case Color_Black: - vl[vram_offset] |= op; - vd[vram_offset] |= op; + case color_black: + *light |= op; + *dark |= op; break; - case Color_Invert: - vl[vram_offset] ^= op; - vd[vram_offset] ^= op; + case color_none: + return; + + case color_invert: + *light ^= op; + *dark ^= op; break; - default: + case color_lighten: + old_light = *light; + *light &= *dark | ~op; + *dark = (old_light | ~op) & (op ^ *dark); + break; + case color_lighten2: + *dark &= *light | ~op; + *light &= ~op; + break; + case color_darken: + old_light = *light; + *light |= *dark & op; + *dark = (old_light & op) | (op ^ *dark); + break; + case color_darken2: + *dark |= *light | op; + *light |= op; break; } - vram_offset += 4; + light += 4; + dark += 4; } } diff --git a/src/tales/tales_internals.c b/src/tales/tales_internals.c index c12d2c5..e6dec37 100644 --- a/src/tales/tales_internals.c +++ b/src/tales/tales_internals.c @@ -5,7 +5,7 @@ #include struct Font *font; -enum Color color; +color_t operator; /* tales_init() @@ -13,7 +13,7 @@ enum Color color; */ void tales_init(void) { - text_configure(NULL, Color_Black); + text_configure(NULL, color_black); } /* @@ -93,31 +93,36 @@ int getCharacterIndex(int c) */ void operate_mono(OPERATE_ARGS) { - int *vram = display_getCurrentVRAM(); - int vram_offset = (x >> 5) + (y << 2); - uint32_t op; - int i; + uint32_t *vram = display_getCurrentVRAM(); + uint32_t *video = vram + (x >> 5) + (y << 2); - for(i = 0; i < height; i++) + switch(operator) { - op = operators[i]; - - switch(color) + case color_white: + for(int i = 0; i < height; i++) { - case Color_White: - vram[vram_offset] &= ~op; - break; - case Color_Black: - vram[vram_offset] |= op; - break; - case Color_Invert: - vram[vram_offset] ^= op; - break; - default: - break; + *video &= ~operators[i]; + video += 4; } + break; - vram_offset += 4; + case color_black: + for(int i = 0; i < height; i++) + { + *video |= operators[i]; + video += 4; + } + break; + + case color_invert: + for(int i = 0; i < height; i++) + { + *video ^= operators[i]; + video += 4; + } + break; + + default: return; } } diff --git a/src/timer/common_api.c b/src/timer/common_api.c index 46b39fa..d403e57 100644 --- a/src/timer/common_api.c +++ b/src/timer/common_api.c @@ -94,7 +94,7 @@ void timer_callback_event(timer_t *timer) else { event_t event = { - .type = ET_Timer, + .type = event_timer_underflow, .timer = timer }; event_push(event); diff --git a/src/timer/virtual_timers.c b/src/timer/virtual_timers.c index 518c5fd..b250065 100644 --- a/src/timer/virtual_timers.c +++ b/src/timer/virtual_timers.c @@ -38,6 +38,24 @@ timer_t *timer_create(int ms_delay, int repeats) return timer; } +/* + timer_reload() + Changes a virtual timer's delay. The timer is not stopped nor started: + it keeps running or waiting. Events that were waiting to be handled are + dropped and the number of repeats left is not changed. The timer + restarts counting from 0 regardless of how much time had elapsed since + it last fired. +*/ +void timer_reload(timer_t *timer, int new_ms_delay) +{ + if(!timer->virtual) return; + + timer->ms_delay = new_ms_delay; + timer->ms_elapsed = 0; + + vtimer_updateAll(); +} + /* timer_destroy() Destroys a virtual timer. This virtual timer pointer becomes invalid @@ -136,7 +154,7 @@ static void vtimer_update(int new_delay) timer_stop(timer); return; } - uint32_t new_constant = clock_setting(new_delay, Clock_ms); + uint32_t new_constant = clock_setting(new_delay, clock_ms); // The transition needs to be as smooth as possible. We have probably // spent a lot of time calculating this new GCD delay so we want to @@ -176,7 +194,7 @@ static void vtimer_update(int new_delay) /* gcd() - Well, the Euclidean algorithm. That's a O(ln(a)) & O(ln(b)) FWIW. + Well, the Euclidean algorithm. That's O(ln(max(a, b))) FWIW. */ static int gcd(int a, int b) {