Add video testing program for fun

This commit is contained in:
Mark
2020-03-27 22:51:32 +02:00
parent d4e78ec6c4
commit 815aa5c2a3
13 changed files with 1076 additions and 29 deletions
+17 -2
View File
@@ -7,7 +7,8 @@ CC=x86_64-elf-yggdrasil-gcc
DIRS=$(O) \
$(STAGE) \
$(O)/sh \
$(O)/ase
$(O)/ase \
$(O)/vsh
HDRS=$(shell find $(S) -type f -name "*.h")
STAGE_BIN=$(STAGE)/init \
$(STAGE)/bin/hexd \
@@ -24,7 +25,8 @@ STAGE_BIN=$(STAGE)/init \
$(STAGE)/bin/netctl \
$(STAGE)/bin/netmeow \
$(STAGE)/bin/netdump \
$(STAGE)/bin/video
$(STAGE)/bin/mouse \
$(STAGE)/bin/vsh
# $(STAGE)/bin/com \
# $(STAGE)/bin/ase \
@@ -35,6 +37,11 @@ sh_OBJS=$(O)/sh/sh.o \
$(O)/sh/builtin.o \
$(O)/sh/cmd.o
ase_OBJS=$(O)/ase/ase.o
vsh_OBJS=$(O)/vsh/vsh.o \
$(O)/vsh/input.o \
$(O)/vsh/video.o \
$(O)/vsh/font.o \
$(O)/vsh/logo.o
usr_CFLAGS=-ggdb \
-msse \
@@ -75,9 +82,17 @@ $(STAGE)/bin/%: core/bin/%.c
$(STAGE)/bin/sh: $(sh_OBJS)
$(CC) -o $@ $(usr_LDFLAGS) $(sh_OBJS)
$(STAGE)/bin/vsh: $(vsh_OBJS)
$(CC) -o $@ $(usr_LDFLAGS) $(vsh_OBJS)
$(STAGE)/bin/ase: $(ase_OBJS)
$(CC) -o $@ $(usr_LDFLAGS) $(ase_OBJS)
$(O)/vsh/%.o: vsh/%.c $(shell find vsh -name "*.h")
$(CC) -c -o $@ $(usr_CFLAGS) $<
$(O)/vsh/%.o: vsh/%.S
$(CC) -c -o $@ $(usr_CFLAGS) $<
$(O)/sh/%.o: sh/%.c $(shell find sh -name "*.h")
$(CC) -c -o $@ $(usr_CFLAGS) $<
+129
View File
@@ -0,0 +1,129 @@
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
static int running = 1;
static int mx = 40;
static int my = 12;
static double mx_d = 40, my_d = 12;
static int con_width, con_height;
static void signal_handle(int signum) {
running = 0;
}
static void kb_handle(char *buf, size_t len) {
if (buf[0] == 'q') {
running = 0;
}
}
static void ms_handle(char *buf, size_t len) {
for (size_t i = 0; i < len / 5; ++i) {
char type = buf[i * 5 + 0];
switch (type) {
case 'd': {
int16_t dx = *(int16_t *) &buf[i * 5 + 1];
int16_t dy = *(int16_t *) &buf[i * 5 + 3];
mx_d += (double) dx / 4.0;
my_d -= (double) dy / 12.0;
if (mx_d < 0) {
mx_d = 0;
}
if (mx_d >= con_width - 1) {
mx_d = con_width - 2;
}
if (my_d < 0) {
my_d = 0;
}
if (my_d >= con_height - 1) {
my_d = con_height - 2;
}
mx = mx_d;
my = my_d;
puts2("\033[2J\033[1;1f");
printf("\033[%d;%dfX\033[%d;%df", my + 1, mx + 1, my + 1, mx + 1);
}
break;
}
}
}
int main(int argc, char **argv) {
fd_set fds;
struct timeval tv;
struct termios old_ts, ts;
struct winsize winsz;
int kb_fd = STDIN_FILENO, ms_fd;
char buf[512];
ssize_t len;
signal(SIGINT, signal_handle);
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &winsz) != 0) {
perror("ioctl()");
return -1;
}
printf("Terminal is %dx%d\n", winsz.ws_col, winsz.ws_row);
con_width = winsz.ws_col;
con_height = winsz.ws_row;
if (tcgetattr(kb_fd, &old_ts) != 0) {
perror("tcgetattr()");
return -1;
}
memcpy(&ts, &old_ts, sizeof(struct termios));
ts.c_iflag &= ~ICANON;
ts.c_lflag = 0;
if (tcsetattr(kb_fd, TCSANOW, &ts) != 0) {
perror("tcsetattr()");
return -1;
}
ms_fd = open("/dev/ps2aux", O_RDONLY, 0);
while (running) {
FD_ZERO(&fds);
FD_SET(ms_fd, &fds);
FD_SET(kb_fd, &fds);
tv.tv_sec = 1;
tv.tv_usec = 0;
int res = select(ms_fd + 1, &fds, NULL, NULL, &tv);
if (res < 0) {
perror("select()");
break;
}
if (res != 0) {
if (FD_ISSET(ms_fd, &fds)) {
len = read(ms_fd, buf, sizeof(buf));
ms_handle(buf, len);
}
if (FD_ISSET(kb_fd, &fds)) {
len = read(kb_fd, buf, sizeof(buf));
kb_handle(buf, len);
}
}
}
if (tcsetattr(kb_fd, TCSANOW, &old_ts) != 0) {
perror("tcsetattr()");
return -1;
}
printf("Stopped\n");
return 0;
}
+175 -27
View File
@@ -1,6 +1,8 @@
#include <ygg/video.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/select.h>
#include <termios.h>
#include <assert.h>
#include <signal.h>
#include <string.h>
@@ -8,16 +10,119 @@
#include <stdio.h>
#include <time.h>
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define MAX(x, y) ((x) > (y) ? (x) : (y))
static int running = 1;
static double mx_d = 40, my_d = 12;
static uint8_t cursor[8] = {
0b11111111,
0b11111110,
0b11111100,
0b11111000,
0b11110000,
0b11100000,
0b11000000,
0b10000000,
};
static uint32_t *vmem;
static size_t vsize;
static void signal_handler(int signum) {
running = 0;
}
static void ms_handle(char *buf, size_t len) {
for (size_t i = 0; i < len / 5; ++i) {
char type = buf[i * 5 + 0];
switch (type) {
case 'd': {
int16_t dx = *(int16_t *) &buf[i * 5 + 1];
int16_t dy = *(int16_t *) &buf[i * 5 + 3];
mx_d += (double) dx / 4.0;
my_d -= (double) dy / 6.0;
if (mx_d < 0) {
mx_d = 0;
}
if (mx_d >= mode.width - 1) {
mx_d = mode.width - 2;
}
if (my_d < 0) {
my_d = 0;
}
if (my_d >= mode.height - 1) {
my_d = mode.height - 2;
}
}
break;
}
}
}
static void rect(int x0, int y0, int x1, int y1, uint32_t col) {
for (int x = MAX(x0, 0); x <= MIN(x1, mode.width - 1); ++x) {
for (int y = MAX(y0, 0); y <= MIN(y1, mode.height - 1); ++y) {
vmem[y * mode.width + x] = col;
}
}
}
static void draw_cursor(int x, int y, uint32_t col) {
for (int i = 0; i < 8; ++i) {
if (i + x >= mode.width) {
break;
}
for (int j = 0; j < 8; ++j) {
if (j + y >= mode.height) {
break;
}
if (!(cursor[j] & (1 << (7 - i)))) {
continue;
}
vmem[(j + y) * mode.width + x + i] = col;
}
}
}
static void render(void) {
static double px, py;
rect(px, py, px + 8, py + 8, 0);
px = mx_d;
py = my_d;
draw_cursor(px, py, 0xFF0000);
}
int main(int argc, char **argv) {
struct ioc_vmode mode;
int fd = open("/dev/fb0", O_RDONLY, 0);
if (fd < 0) {
int mouse_fd = open("/dev/ps2aux", O_RDONLY, 0);
struct termios tc, tc_old;
struct timeval tv;
fd_set fds;
char buf[512];
ssize_t len;
int no = 0;
tc.c_iflag = 0;
tc.c_lflag = 0;
tc.c_oflag = 0;
if (tcgetattr(STDIN_FILENO, &tc_old) != 0) {
perror("tcgetattr()");
goto end;
}
if (fd < 0 || mouse_fd < 0) {
perror("open()");
return -1;
}
@@ -27,55 +132,98 @@ int main(int argc, char **argv) {
goto end;
}
if (tcsetattr(STDIN_FILENO, TCSANOW, &tc) != 0) {
perror("tcsetattr()");
goto end;
}
signal(SIGINT, signal_handler);
printf("Video mode: %dx%d\n", mode.width, mode.height);
size_t size = mode.width * mode.height * 4;
size = (size + 0xFFF) & ~0xFFF;
printf("%u bytes\n", size);
vsize = mode.width * mode.height * 4;
vsize = (vsize + 0xFFF) & ~0xFFF;
printf("%u bytes\n", vsize);
printf("You can exit by pressing ^C\n");
printf("Starting video in 3 secs\n");
usleep(3000000);
printf("Starting video in 1 sec\n");
usleep(1000000);
if (!running) {
// Interrupt happened
close(mouse_fd);
close(fd);
if (tcsetattr(STDIN_FILENO, TCSANOW, &tc_old) != 0) {
perror("tcsetattr()");
goto end;
}
puts2("\033[2J\033[1;1f");
printf("Goodbye\n");
return 0;
}
int no = 0;
if (ioctl(fd, IOC_FBCON, &no) != 0) {
perror("ioctl()");
goto end;
}
uint32_t *data = mmap(NULL, size, 0, MAP_PRIVATE, fd, 0);
assert(data);
vmem = mmap(NULL, vsize, 0, MAP_PRIVATE, fd, 0);
assert(vmem);
struct timeval tv;
int c = 0;
int s = 0;
memset(vmem, 0, vsize);
while (running) {
usleep(10000);
gettimeofday(&tv, NULL);
if (s == 0) {
c += 2;
} else {
c -= 2;
tv.tv_sec = 0;
tv.tv_usec = 10000;
FD_ZERO(&fds);
FD_SET(mouse_fd, &fds);
FD_SET(STDIN_FILENO, &fds);
int res = select(mouse_fd + 1, &fds, NULL, NULL, &tv);
if (res < 0) {
no = 1;
if (ioctl(fd, IOC_FBCON, &no) != 0) {
perror("ioctl()");
goto end;
}
printf("select() failed\n");
break;
}
if (c < 0) {
s = 0;
c = 0;
} else if (c >= 256) {
s = 1;
c = 255;
if (res > 0) {
if (FD_ISSET(mouse_fd, &fds)) {
len = read(mouse_fd, buf, sizeof(buf));
ms_handle(buf, len);
}
if (FD_ISSET(STDIN_FILENO, &fds)) {
read(STDIN_FILENO, buf, sizeof(buf));
if (buf[0] == 'q') {
break;
}
}
}
memset(data, c, size);
render();
}
munmap(data, size);
munmap(vmem, vsize);
no = 1;
if (ioctl(fd, IOC_FBCON, &no) != 0) {
perror("ioctl()");
goto end;
}
if (tcsetattr(STDIN_FILENO, TCSANOW, &tc_old) != 0) {
perror("tcsetattr()");
goto end;
}
puts2("\033[2J\033[1;1f");
printf("Goodbye\n");
end:
+8
View File
@@ -0,0 +1,8 @@
.section .text
.global font_start
.global font_end
.align 4
font_start:
.incbin "vsh/font.psfu"
font_end:
BIN
View File
Binary file not shown.
+127
View File
@@ -0,0 +1,127 @@
#include <sys/select.h>
#include <termios.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <time.h>
#include "video.h"
#include "input.h"
static fd_set input_fds;
static struct termios tc_old, tc;
static struct timeval input_tv;
static int keyboard_fd = STDIN_FILENO;
static int mouse_fd;
int cursor_x = 0, cursor_y = 0;
void cursor_move(int16_t dx, int16_t dy) {
cursor_x += dx;
cursor_y -= dy;
if (cursor_x < 0) {
cursor_x = 0;
} else if (cursor_x >= fb_mode.width) {
cursor_x = fb_mode.width - 1;
}
if (cursor_y < 0) {
cursor_y = 0;
} else if (cursor_y >= fb_mode.height) {
cursor_y = fb_mode.height - 1;
}
}
int input_wait_event(struct input_event *ev) {
int res;
ssize_t len;
char buf[128];
FD_ZERO(&input_fds);
FD_SET(keyboard_fd, &input_fds);
FD_SET(mouse_fd, &input_fds);
input_tv.tv_sec = 0;
input_tv.tv_usec = 10000;
res = select(mouse_fd + 1, &input_fds, NULL, NULL, &input_tv);
if (res < 0) {
return -1;
}
if (res > 0) {
if (FD_ISSET(keyboard_fd, &input_fds)) {
// Read a single key event
len = read(keyboard_fd, buf, 1);
if (len < 0) {
return -1;
}
ev->type = IN_KEY_DOWN;
ev->key = buf[0];
return 1;
}
if (FD_ISSET(mouse_fd, &input_fds)) {
len = read(mouse_fd, buf, 5);
if (len != 5) {
return 0;
}
char type = buf[0];
switch (type) {
case 'd':
ev->type = IN_MOUSE_MOVE;
ev->mouse_move.dx = *(int16_t *) &buf[1];
ev->mouse_move.dy = *(int16_t *) &buf[3];
cursor_move(ev->mouse_move.dx, ev->mouse_move.dy);
return 1;
case 'b':
ev->type = buf[3] ? IN_BUTTON_DOWN : IN_BUTTON_UP;
ev->button = buf[1];
return 1;
default:
// Skip unknown events
return 0;
}
}
}
return 0;
}
int input_init(void) {
if (tcgetattr(keyboard_fd, &tc_old)) {
perror("tcgetattr()");
return -1;
}
memcpy(&tc, &tc_old, sizeof(struct termios));
// No canonical mode
tc.c_iflag &= ~ICANON;
// No echo
tc.c_lflag = 0;
if (tcsetattr(keyboard_fd, TCSANOW, &tc) != 0) {
perror("tcsetattr()");
return -1;
}
mouse_fd = open("/dev/ps2aux", O_RDONLY, 0);
if (mouse_fd < 0) {
perror("open(/dev/ps2aux)");
// Restore old terminal behavior
tcsetattr(keyboard_fd, TCSANOW, &tc_old);
return -1;
}
return 0;
}
void input_close(void) {
tcsetattr(keyboard_fd, TCSANOW, &tc_old);
close(mouse_fd);
}
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#define BUTTON_LEFT 1
struct input_event {
enum {
IN_KEY_DOWN,
IN_BUTTON_DOWN,
IN_BUTTON_UP,
IN_MOUSE_MOVE,
} type;
union {
char key;
struct {
int16_t dx, dy;
} mouse_move;
int button;
};
};
extern int cursor_x, cursor_y;
int input_wait_event(struct input_event *ev);
int input_init(void);
void input_close(void);
+261
View File
@@ -0,0 +1,261 @@
/* GIMP header image file format (RGB): /home/alnyan/Documents/logo.h */
char *font_logo_header_data =
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!:76F:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
":76F:76F:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
":76F:76F:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
":76F:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F"
":76F:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F"
":76F:76F:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F"
":76F:76F:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F"
":76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F"
":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F"
":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F"
":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F"
":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F"
":76F:76F:76F````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F"
":76F````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F"
":76F:76F````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F"
"````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F"
":76F:76F````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F"
"````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F"
":76F````````````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F````"
"````````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F"
"````````````````````````````:76F:76F:76F:76F:76F:76F:76F````````"
"````````````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!"
"!!!!!!!!`Q!!`Q!!`Q!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F"
"````````````````````````````:76F:76F:76F:76F:76F:76F:76F````````"
"````````````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F`Q!!"
"`Q!!`Q!!`Q!!`Q!!C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F"
"````````!!!!!!!!````````````:76F:76F:76F:76F:76F:76F:76F````````"
"!!!!!!!!````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F`Q!!"
"`Q!!`Q!!C!!!C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F````"
"````````!!!!!!!!````````````````:76F:76F:76F:76F:76F````````````"
"!!!!!!!!````````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F"
"C!!!C!!!`Q!!C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F````````"
"````````!!!!!!!!````````````````````:76F:76F:76F````````````````"
"!!!!!!!!````````````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F"
"`Q!!`Q!!C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F````````"
"````````!!!!!!!!````````````````````:76F:76F:76F````````````````"
"!!!!!!!!````````````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F"
"`Q!!`Q!!C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F````````"
"````````!!!!!!!!````````````````````:76F:76F:76F````````````````"
"!!!!!!!!````````````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F"
"C!!!C!!!!!!!`Q!!`Q!!`Q!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F:76F````"
"````````````````````````````````:76F:76F:76F:76F:76F````````````"
"````````````````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F"
"`Q!!`Q!!`Q!!`Q!!`Q!!C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F:76F:76F:76F"
"````````````````````````````:76F:76F:76F:76F:76F:76F:76F````````"
"````````````````````:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F"
"`Q!!`Q!!C!!!`Q!!C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F"
":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F"
":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F`Q!!"
"C!!!C!!!`Q!!`Q!!C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F:76F:76F:76F"
":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F"
":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76FC!!!"
"`Q!!`Q!!`Q!!C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F:76F:76F"
":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F"
":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F`Q!!`Q!!"
"`Q!!`Q!!C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F:76F"
":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F"
":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F`Q!!`Q!!"
"`Q!!C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F:76F"
":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F"
":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76FC!!!C!!!C!!!"
"C!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F:76F"
":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F"
":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F:76F:76F:76F"
":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F"
":76F:76F:76F:76F:76F:76F:76F:76F:76F`Q!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!:76F"
":76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F"
":76F:76F:76F:76F:76F:76F:76F`Q!!`Q!!`Q!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`Q!!"
"C!!!C!!!C!!!:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F:76F"
":76F:76F:76FC!!!C!!!C!!!`Q!!`Q!!`Q!!`Q!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`Q!!"
"C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!"
"C!!!C!!!C!!!C!!!C!!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`Q!!`Q!!"
"`Q!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!"
"C!!!C!!!C!!!C!!!`Q!!`Q!!`Q!!`Q!!C!!!`Q!!`Q!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`Q!!`Q!!"
"`Q!!`Q!!`Q!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!"
"C!!!C!!!C!!!`Q!!`Q!!`Q!!`Q!!C!!!`Q!!`Q!!`Q!!`Q!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`Q!!`Q!!C!!!"
"`Q!!`Q!!`Q!!`Q!!`Q!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!C!!!"
"C!!!`Q!!`Q!!`Q!!`Q!!`Q!!C!!!`Q!!`Q!!`Q!!`Q!!`Q!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`Q!!`Q!!`Q!!"
"C!!!C!!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!"
"`Q!!`Q!!`Q!!`Q!!`Q!!C!!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`Q!!`Q!!`Q!!"
"`Q!!`Q!!C!!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!"
"`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!`Q!!!!!!!!!!!!!!"
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"";
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#define FONT_LOGO_WIDTH 64
#define FONT_LOGO_HEIGHT 64
#define FONT_LOGO_HEADER_PIXEL(data,pixel) {\
pixel[0] = (((data[0] - 33) << 2) | ((data[1] - 33) >> 4)); \
pixel[1] = ((((data[1] - 33) & 0xF) << 4) | ((data[2] - 33) >> 2)); \
pixel[2] = ((((data[2] - 33) & 0x3) << 6) | ((data[3] - 33))); \
data += 4; \
}
extern char *font_logo_header_data;
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
#define PSF_FONT_MAGIC 0x864ab572
struct psf_font {
uint32_t magic;
uint32_t version;
uint32_t headersize;
uint32_t flags;
uint32_t numglyph;
uint32_t bytesperglyph;
uint32_t height;
uint32_t width;
};
+130
View File
@@ -0,0 +1,130 @@
#include "video.h"
#include "psf.h"
#include <sys/ioctl.h>
#include <ygg/video.h>
#include <sys/mman.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
// Framebuffer
static int fb;
static size_t fb_size;
static uint32_t *fb_mem;
static uint16_t fb_charw, fb_charh;
struct ioc_vmode fb_mode;
// PSF font
static struct psf_font *font;
static uintptr_t psf_fb_addr, psf_fb_pitch;
int psf_init(uintptr_t addr, uintptr_t pitch, uint16_t *charw, uint16_t *charh);
void video_putpixel(size_t x, size_t y, uint32_t v) {
fb_mem[y * fb_mode.width + x] = v;
}
int video_start(void) {
int off = 0;
if ((fb = open("/dev/fb0", O_RDONLY, 0)) < 0) {
perror("open(/dev/fb0)");
return -1;
}
// Get framebuffer size
if (ioctl(fb, IOC_GETVMODE, &fb_mode) != 0) {
perror("ioctl(IOC_GETVMODE)");
close(fb);
return -1;
}
fb_size = (fb_mode.width * fb_mode.height * 4 + 0xFFF) & ~0xFFF;
// Lock framebuffer
if (ioctl(fb, IOC_FBCON, &off) != 0) {
perror("ioctl(IOC_FBCON = off)");
close(fb);
return -1;
}
// Map framebuffer into memory
if ((fb_mem = mmap(NULL, fb_size, 0, MAP_PRIVATE, fb, 0)) == MAP_FAILED) {
perror("mmap()");
// Unlock framebuffer for cleanup
off = 1;
if (ioctl(fb, IOC_FBCON, &off) != 0) {
perror("ioctl(IOC_FBCON = off)");
close(fb);
return -1;
}
close(fb);
return -1;
}
psf_init((uintptr_t) fb_mem, fb_mode.width * 4, &fb_charw, &fb_charh);
memset(fb_mem, 0, fb_size);
return 0;
}
void video_stop(void) {
int on = 1;
ioctl(fb, IOC_FBCON, &on);
munmap(fb_mem, fb_size);
close(fb);
}
extern char font_start;
int psf_init(uintptr_t addr, uintptr_t pitch, uint16_t *charw, uint16_t *charh) {
font = (struct psf_font *) &font_start;
if (font->magic != PSF_FONT_MAGIC) {
return -1;
}
psf_fb_addr = addr;
psf_fb_pitch = pitch;
*charw = font->width;
*charh = font->height;
return 0;
}
void video_draw_string(size_t x, size_t y, const char *str, uint32_t fg) {
for (; *str; x += fb_charw, ++str) {
video_draw_glyph(y, x, *str, fg, 0);
}
}
void video_draw_glyph(uint16_t row, uint16_t col, uint8_t c, uint32_t fg, uint32_t bg) {
if (c >= font->numglyph) {
c = 0;
}
int bytesperline = (font->width + 7) / 8;
uint8_t *glyph = (uint8_t *) &font_start + font->headersize + c * font->bytesperglyph;
// XXX: Assuming BPP is 32
/* calculate the upper left corner on screen where we want to display.
we only do this once, and adjust the offset later. This is faster. */
uintptr_t offs = (row * psf_fb_pitch) + (col * 4);
/* finally display pixels according to the bitmap */
uint32_t x, y, line, mask;
for (y = 0; y < font->height; ++y) {
/* save the starting position of the line */
line = offs;
mask = 1 << (font->width - 1);
/* display a row */
for (x = 0; x < font->width; ++x) {
*((uint32_t *)(psf_fb_addr + line)) = ((int) *glyph) & (mask) ? fg : bg;
/* adjust to the next pixel */
mask >>= 1;
line += 4;
}
/* adjust to the next line */
glyph += bytesperline;
offs += psf_fb_pitch;
}
}
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <ygg/video.h>
extern struct ioc_vmode fb_mode;
void video_putpixel(size_t x, size_t y, uint32_t v);
void video_draw_glyph(uint16_t row, uint16_t col, uint8_t c, uint32_t fg, uint32_t bg);
void video_draw_string(size_t x, size_t y, const char *str, uint32_t fg);
int video_start(void);
void video_stop(void);
+165
View File
@@ -0,0 +1,165 @@
#include "video.h"
#include "input.h"
#include "logo.h"
#include <signal.h>
#include <errno.h>
#include <stdio.h>
#include <time.h>
#define MIN(x, y) ((x) < (y) ? (x) : (y))
static int running = 1;
static void signal_handler(int signum) {
running = 0;
}
static void draw_logo(int x, int y) {
char pixel[4];
char *data = font_logo_header_data;
uint32_t v;
for (uint32_t j = y; j < y + FONT_LOGO_HEIGHT; ++j) {
for (uint32_t i = x; i < x + FONT_LOGO_WIDTH; ++i) {
FONT_LOGO_HEADER_PIXEL(data, pixel);
v = pixel[2] | ((uint32_t) pixel[1] << 8) | ((uint32_t) pixel[0] << 16);
if (v) {
video_putpixel(i, j, v);
}
}
}
}
static void draw_cursor(int x, int y) {
for (size_t i = 0; i < 8; ++i) {
for (size_t j = 0; j < 8; ++j) {
if (x + i >= fb_mode.width ||
y + j >= fb_mode.height) {
continue;
}
if (7 - j > i) {
video_putpixel(x + i, y + j, 0xFFFFFF);
}
}
}
}
static uint64_t get_time(void) {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
static void draw_progress(size_t count, size_t total) {
size_t w = count * fb_mode.width / total;
for (size_t i = 0; i < fb_mode.width; ++i) {
for (size_t j = fb_mode.height - 16; j < fb_mode.height; ++j) {
video_putpixel(i, j, i <= w ? 0x00FF00 : 0xCCCCCC);
}
}
}
#define CLICK_COUNT_EXIT 20
int main(int argc, char **argv) {
struct input_event ev;
int prev_x = 0, prev_y = 0;
int logo_vel_x = 1, logo_vel_y = 1;
double logo_x = 0, logo_y = 0;
int click_count = 0;
int err = 0;
int res;
signal(SIGINT, signal_handler);
if (input_init() != 0) {
return -1;
}
if (video_start() != 0) {
input_close();
return -1;
}
uint64_t t0, t, dt;
t0 = get_time();
while (running) {
t = get_time();
dt = t - t0;
res = input_wait_event(&ev);
if (res < 0) {
err = errno;
break;
}
if (res == 1) {
// An event ocurred
if (ev.type == IN_KEY_DOWN && ev.key == 'q') {
break;
}
if (ev.type == IN_KEY_DOWN || ev.type == IN_BUTTON_UP) {
++click_count;
if (click_count == CLICK_COUNT_EXIT) {
break;
}
}
}
for (size_t x = (int) logo_x; x < (int) logo_x + FONT_LOGO_WIDTH; ++x) {
for (size_t y = (int) logo_y; y < (int) logo_y + FONT_LOGO_HEIGHT; ++y) {
video_putpixel(x, y, 0);
}
}
t0 = t;
logo_x += logo_vel_x * ((double) dt / 10);
logo_y += logo_vel_y * ((double) dt / 10);
if (logo_x < 0) {
logo_vel_x = 1;
logo_x = 0;
} else if (logo_x + FONT_LOGO_WIDTH >= fb_mode.width) {
logo_vel_x = -1;
logo_x = fb_mode.width - FONT_LOGO_WIDTH - 1;
}
if (logo_y < 0) {
logo_vel_y = 1;
logo_y = 0;
} else if (logo_y + FONT_LOGO_HEIGHT >= fb_mode.height) {
logo_vel_y = -1;
logo_y = fb_mode.height - FONT_LOGO_HEIGHT - 1;
}
draw_logo(logo_x, logo_y);
draw_progress(click_count, CLICK_COUNT_EXIT);
video_draw_string(100, 100, "Test?", 0xFF0000);
for (size_t x = prev_x; x < MIN(prev_x + 8, fb_mode.width); ++x) {
for (size_t y = prev_y; y < MIN(prev_y + 8, fb_mode.height); ++y) {
video_putpixel(x, y, 0);
}
}
draw_cursor(cursor_x, cursor_y);
prev_x = cursor_x;
prev_y = cursor_y;
}
video_stop();
input_close();
puts2("\033[2J\033[1;1f");
if (err) {
printf("An error ocurred: %s\n", strerror(err));
} else {
printf("Execution finished normally\n");
}
return 0;
}