A project to implement a console using the Mbed using VGA for video output and a PS/2 keyboard for the input. The eventual goal is to also include tools for managing SD cards, and a semi-self-hosting programming environment.

Dependencies:   PS2_MbedConsole fastlib SDFileSystem vga640x480g_mbedconsole lightvm mbed

MbedConsole is a cool little project to have a self-contained computer all on an Mbed. So far it has VGA and PS/2 support and can stand alone without a computer powering it. Next planned features are SD card support and a lightweight programmable VM complete with a file editor and self-hosted assembler.

You can view additional details about it at http://earlz.net/tags/mbedconsole

Committer:
earlz
Date:
Fri Sep 28 04:03:54 2012 +0000
Revision:
11:fede136943a9
Parent:
10:bda85442b674
Child:
12:3ee3062cc11c
Finally have keyboard support, AND it plays nice with VGA

Who changed what in which revision?

UserRevisionLine numberNew contents of line
earlz 10:bda85442b674 1 #include "mbedconsole.h"
earlz 10:bda85442b674 2 /**This is basically a straight rip off of my x86 OS project AlloyOS. I just ported the keyboard driver from it cause it always treated me well**/
earlz 10:bda85442b674 3
earlz 10:bda85442b674 4
earlz 10:bda85442b674 5 #define KEYBOARD_DATAPIN p11
earlz 10:bda85442b674 6 #define KEYBOARD_CLOCKPIN p12
earlz 10:bda85442b674 7
earlz 10:bda85442b674 8 //how many keys the buffer can hold
earlz 10:bda85442b674 9 #define KBD_BUFFER_SIZE 128
earlz 10:bda85442b674 10
earlz 10:bda85442b674 11 //key defines
earlz 11:fede136943a9 12 #define LSHIFT_KEY 0x12
earlz 11:fede136943a9 13 #define RSHIFT_KEY 0x59
earlz 11:fede136943a9 14
earlz 11:fede136943a9 15 #define CTRL_KEY 0xF3
earlz 11:fede136943a9 16 #define ALT_KEY 0xF4
earlz 11:fede136943a9 17 #define CAPS_KEY 0x58
earlz 11:fede136943a9 18 #define NUM_KEY 0x77
earlz 11:fede136943a9 19 #define SCROLL_KEY 0xF7
earlz 11:fede136943a9 20 //#define F_BASE_KEY 0xFF //59 is F1, 60 is F2, and so on until F10
earlz 11:fede136943a9 21 #define HOME_KEY 0xFF
earlz 11:fede136943a9 22 #define UP_KEY 0xFF
earlz 11:fede136943a9 23 #define PAGE_UP_KEY 0xFF
earlz 11:fede136943a9 24 #define LEFT_KEY 0xFF
earlz 11:fede136943a9 25 #define RIGHT_KEY 0xFF
earlz 11:fede136943a9 26 #define END_KEY 0xFF
earlz 11:fede136943a9 27 #define DOWN_KEY 0xFF
earlz 11:fede136943a9 28 #define PAGE_DOWN_KEY 0xFF
earlz 11:fede136943a9 29 #define INSERT_KEY 0xFF
earlz 11:fede136943a9 30 #define DEL_KEY 0xFF
earlz 11:fede136943a9 31 #define F11_KEY 0xFF
earlz 11:fede136943a9 32 #define F12_KEY 0xFF
earlz 10:bda85442b674 33
earlz 10:bda85442b674 34 #define SCROLL_LED 1
earlz 10:bda85442b674 35 #define NUM_LED 2
earlz 10:bda85442b674 36 #define CAPS_LED 4
earlz 10:bda85442b674 37
earlz 10:bda85442b674 38
earlz 11:fede136943a9 39 const char kbdus[0x84] =
earlz 10:bda85442b674 40 {
earlz 11:fede136943a9 41 0,
earlz 11:fede136943a9 42 0, //1 F9
earlz 11:fede136943a9 43 0, //2 ?
earlz 11:fede136943a9 44 0, //3 f5
earlz 11:fede136943a9 45 0, //4 f3
earlz 11:fede136943a9 46 0, //5 F1
earlz 11:fede136943a9 47 0, //6 F2
earlz 11:fede136943a9 48 0, //7 F12
earlz 11:fede136943a9 49 0, //8 ?
earlz 11:fede136943a9 50 0, //9 F10
earlz 11:fede136943a9 51 0, //A F8
earlz 11:fede136943a9 52 0, //B F6
earlz 11:fede136943a9 53 0, //C F4
earlz 11:fede136943a9 54 '\t',
earlz 11:fede136943a9 55 '`',
earlz 11:fede136943a9 56 0, //F ?
earlz 11:fede136943a9 57 0, //10 ?
earlz 11:fede136943a9 58 0, //11 L alt
earlz 11:fede136943a9 59 0, //L shift
earlz 11:fede136943a9 60 0, //13 ?
earlz 11:fede136943a9 61 0, //14 L CTRL
earlz 11:fede136943a9 62 'q', '1',
earlz 11:fede136943a9 63 0, 0, 0, //17-19 ?
earlz 11:fede136943a9 64 'z', 's', 'a', 'w', '2',
earlz 11:fede136943a9 65 0, 0, //1F-20 ?
earlz 11:fede136943a9 66 'c', 'x', 'd', 'e', '4', '3',
earlz 11:fede136943a9 67 0, 0, //27-28 ?
earlz 11:fede136943a9 68 ' ', 'v', 'f', 't', 'r', '5',
earlz 11:fede136943a9 69 0, 0, //2F-30 ?
earlz 11:fede136943a9 70 'n', 'b', 'h', 'g', 'y', '6',
earlz 11:fede136943a9 71 0, 0, 0, //37-39 ?
earlz 11:fede136943a9 72 'm','j', 'u', '7', '8',
earlz 11:fede136943a9 73 0, 0, //3F-40 ?
earlz 11:fede136943a9 74 ',', 'k', 'i', 'o', '0', '9',
earlz 11:fede136943a9 75 0, 0, //47-48 ?
earlz 11:fede136943a9 76 '.', '/', 'l', ';', 'p', '-',
earlz 11:fede136943a9 77 0, 0, 0, //4f-51 ?
earlz 11:fede136943a9 78 '\'',
earlz 11:fede136943a9 79 0, //53 ?
earlz 11:fede136943a9 80 '[', '=',
earlz 11:fede136943a9 81 0, 0, //56-57 ?
earlz 11:fede136943a9 82 0, //58 caplock
earlz 11:fede136943a9 83 0, //59 R shift
earlz 11:fede136943a9 84 '\n', //5A enter
earlz 11:fede136943a9 85 ']',
earlz 11:fede136943a9 86 0, //5C ?
earlz 11:fede136943a9 87 '\\',
earlz 11:fede136943a9 88 0, 0, 0, 0, 0, 0, 0, 0, //5E-65 ?
earlz 11:fede136943a9 89 '\b', //backspace
earlz 11:fede136943a9 90 0, 0, //67-68
earlz 11:fede136943a9 91 0, //69 End 1
earlz 11:fede136943a9 92 0, //6a ?
earlz 11:fede136943a9 93 '4', //6b left 4
earlz 11:fede136943a9 94 0, //6c home 7
earlz 11:fede136943a9 95 0, 0, 0, //6d-6f ?
earlz 11:fede136943a9 96 '0', //70 ins 0
earlz 11:fede136943a9 97 0, //71 del .
earlz 11:fede136943a9 98 '2', //72 down 2
earlz 11:fede136943a9 99 '5', //73 5
earlz 11:fede136943a9 100 '6', //74 right 6
earlz 11:fede136943a9 101 '8', //75 up 8
earlz 11:fede136943a9 102 27, //76 esc
earlz 11:fede136943a9 103 0, //77 numlock
earlz 11:fede136943a9 104 0, //78 f11
earlz 11:fede136943a9 105 '+', //79 +
earlz 11:fede136943a9 106 '3', //7A pagedown 3
earlz 11:fede136943a9 107 '-', //7B
earlz 11:fede136943a9 108 '*', //7C
earlz 11:fede136943a9 109 '9', //7D pageup 9
earlz 11:fede136943a9 110 '0', //7E scroll lock
earlz 11:fede136943a9 111 0, 0, 0, 0, //7F-82 ?
earlz 11:fede136943a9 112 0, //83 F7
earlz 10:bda85442b674 113 };
earlz 10:bda85442b674 114
earlz 11:fede136943a9 115
earlz 11:fede136943a9 116 const char kbdus_caps[0x84] =
earlz 10:bda85442b674 117 {
earlz 11:fede136943a9 118 0,
earlz 11:fede136943a9 119 0, //1 F9
earlz 11:fede136943a9 120 0, //2 ?
earlz 11:fede136943a9 121 0, //3 f5
earlz 11:fede136943a9 122 0, //4 f3
earlz 11:fede136943a9 123 0, //5 F1
earlz 11:fede136943a9 124 0, //6 F2
earlz 11:fede136943a9 125 0, //7 F12
earlz 11:fede136943a9 126 0, //8 ?
earlz 11:fede136943a9 127 0, //9 F10
earlz 11:fede136943a9 128 0, //A F8
earlz 11:fede136943a9 129 0, //B F6
earlz 11:fede136943a9 130 0, //C F4
earlz 11:fede136943a9 131 '\t',
earlz 11:fede136943a9 132 '~',
earlz 11:fede136943a9 133 0, //F ?
earlz 11:fede136943a9 134 0, //10 ?
earlz 11:fede136943a9 135 0, //11 L alt
earlz 11:fede136943a9 136 0, //L shift
earlz 11:fede136943a9 137 0, //13 ?
earlz 11:fede136943a9 138 0, //14 L CTRL
earlz 11:fede136943a9 139 'Q', '!',
earlz 11:fede136943a9 140 0, 0, 0, //17-19 ?
earlz 11:fede136943a9 141 'Z', 'S', 'A', 'W', '@',
earlz 11:fede136943a9 142 0, 0, //1F-20 ?
earlz 11:fede136943a9 143 'C', 'X', 'D', 'E', '#', '$',
earlz 11:fede136943a9 144 0, 0, //27-28 ?
earlz 11:fede136943a9 145 ' ', 'V', 'F', 'T', 'R', '%',
earlz 11:fede136943a9 146 0, 0, //2F-30 ?
earlz 11:fede136943a9 147 'N', 'B', 'H', 'G', 'Y', '^',
earlz 11:fede136943a9 148 0, 0, 0, //37-39 ?
earlz 11:fede136943a9 149 'M','J', 'U', '&', '*',
earlz 11:fede136943a9 150 0, 0, //3F-40 ?
earlz 11:fede136943a9 151 '<', 'K', 'I', 'O', ')', '(',
earlz 11:fede136943a9 152 0, 0, //47-48 ?
earlz 11:fede136943a9 153 '>', '?', 'L', ':', 'P', '_',
earlz 11:fede136943a9 154 0, 0, 0, //4f-51 ?
earlz 11:fede136943a9 155 '\"',
earlz 11:fede136943a9 156 0, //53 ?
earlz 11:fede136943a9 157 '{', '+',
earlz 11:fede136943a9 158 0, 0, //56-57 ?
earlz 11:fede136943a9 159 0, //58 caplock
earlz 11:fede136943a9 160 0, //59 R shift
earlz 11:fede136943a9 161 '\n', //5A enter
earlz 11:fede136943a9 162 '}',
earlz 11:fede136943a9 163 0, //5C ?
earlz 11:fede136943a9 164 '|',
earlz 11:fede136943a9 165 0, 0, 0, 0, 0, 0, 0, 0, //5E-65 ?
earlz 11:fede136943a9 166 '\b', //backspace
earlz 11:fede136943a9 167 0, 0, //67-68
earlz 11:fede136943a9 168 0, //69 End 1
earlz 11:fede136943a9 169 0, //6a ?
earlz 11:fede136943a9 170 '4', //6b left 4
earlz 11:fede136943a9 171 0, //6c home 7
earlz 11:fede136943a9 172 0, 0, 0, //6d-6f ?
earlz 11:fede136943a9 173 '0', //70 ins 0
earlz 11:fede136943a9 174 0, //71 del .
earlz 11:fede136943a9 175 '2', //72 down 2
earlz 11:fede136943a9 176 '5', //73 5
earlz 11:fede136943a9 177 '6', //74 right 6
earlz 11:fede136943a9 178 '8', //75 up 8
earlz 11:fede136943a9 179 27, //76 esc
earlz 11:fede136943a9 180 0, //77 numlock
earlz 11:fede136943a9 181 0, //78 f11
earlz 11:fede136943a9 182 '+', //79 +
earlz 11:fede136943a9 183 '3', //7A pagedown 3
earlz 11:fede136943a9 184 '-', //7B
earlz 11:fede136943a9 185 '*', //7C
earlz 11:fede136943a9 186 '9', //7D pageup 9
earlz 11:fede136943a9 187 '0', //7E scroll lock
earlz 11:fede136943a9 188 0, 0, 0, 0, //7F-82 ?
earlz 11:fede136943a9 189 0, //83 F7
earlz 10:bda85442b674 190 };
earlz 10:bda85442b674 191
earlz 10:bda85442b674 192
earlz 10:bda85442b674 193 typedef struct {
earlz 11:fede136943a9 194 unsigned char caps;
earlz 11:fede136943a9 195 unsigned char shift;
earlz 11:fede136943a9 196 unsigned char scroll;
earlz 11:fede136943a9 197 unsigned char num;
earlz 11:fede136943a9 198 unsigned char ctrl;
earlz 11:fede136943a9 199 unsigned char alt;
earlz 10:bda85442b674 200 }
earlz 10:bda85442b674 201 shift_locks; /*for simplicity and speed*/
earlz 10:bda85442b674 202
earlz 10:bda85442b674 203 extern volatile shift_locks kbd_shifts;
earlz 10:bda85442b674 204
earlz 10:bda85442b674 205
earlz 10:bda85442b674 206 typedef struct {
earlz 11:fede136943a9 207 uint16_t scancode;
earlz 11:fede136943a9 208 uint8_t asci;
earlz 10:bda85442b674 209 }kbd_key;
earlz 10:bda85442b674 210
earlz 10:bda85442b674 211
earlz 10:bda85442b674 212 PS2KB_INIT *ps2_kb_init;
earlz 10:bda85442b674 213 PS2KB *ps2_kb;
earlz 10:bda85442b674 214
earlz 10:bda85442b674 215 volatile kbd_key *keys;
earlz 10:bda85442b674 216 volatile uint8_t current_key=0;
earlz 10:bda85442b674 217 volatile uint8_t key_got=0;
earlz 10:bda85442b674 218 volatile char pending_key=0;
earlz 10:bda85442b674 219 volatile uint8_t led_status=0;
earlz 10:bda85442b674 220
earlz 10:bda85442b674 221 void keyboard_callback(PS2KB kb, uint8_t val);
earlz 10:bda85442b674 222
earlz 10:bda85442b674 223 void keyboard_init()
earlz 10:bda85442b674 224 {
earlz 10:bda85442b674 225 keys=(kbd_key*)malloc(256*sizeof(kbd_key));
earlz 11:fede136943a9 226 kbd_shifts.shift=0;
earlz 11:fede136943a9 227 kbd_shifts.caps=0;
earlz 11:fede136943a9 228 kbd_shifts.num=0;
earlz 11:fede136943a9 229 kbd_shifts.scroll=0;
earlz 11:fede136943a9 230 kbd_shifts.ctrl=0;
earlz 11:fede136943a9 231 kbd_shifts.alt=0;
earlz 10:bda85442b674 232 ps2_kb_init=new PS2KB_INIT(KEYBOARD_CLOCKPIN, KEYBOARD_DATAPIN);
earlz 10:bda85442b674 233 ps2_kb=new PS2KB(KEYBOARD_CLOCKPIN, KEYBOARD_DATAPIN, (KeyboardCallback) &keyboard_callback);
earlz 10:bda85442b674 234 }
earlz 10:bda85442b674 235
earlz 10:bda85442b674 236
earlz 10:bda85442b674 237
earlz 10:bda85442b674 238 volatile shift_locks kbd_shifts;
earlz 10:bda85442b674 239
earlz 10:bda85442b674 240
earlz 10:bda85442b674 241
earlz 10:bda85442b674 242 /**The way the keyboard driver works is this:
earlz 10:bda85442b674 243 the buffer is 256 elements big. key_got and current_key are both
earlz 10:bda85442b674 244 bytes. Rather than setting current_key to 0, the entire buffer is constantly
earlz 10:bda85442b674 245 in use. when a key is pushed, current key is incremented. when a key is got(with safety checks)
earlz 10:bda85442b674 246 key_got is incremented. Also, when it reaches the end of the buffer, it simply wraps around
earlz 10:bda85442b674 247 to the beginning due to byte overflow. The worst that can happen is if 256 bytes get
earlz 10:bda85442b674 248 built up, in which case everything will be screwed up.. I should eventually controlt he capture
earlz 10:bda85442b674 249 of these keys to where not all of them are stored, and they are only stored when needed**/
earlz 10:bda85442b674 250
earlz 10:bda85442b674 251 int kbd_PutBuffer(uint16_t scan,uint8_t asci){
earlz 11:fede136943a9 252 keys[current_key].scancode=scan;
earlz 11:fede136943a9 253 keys[current_key].asci=asci;
earlz 11:fede136943a9 254 current_key++;
earlz 11:fede136943a9 255 pending_key++;;
earlz 11:fede136943a9 256 return 0;
earlz 10:bda85442b674 257 }
earlz 10:bda85442b674 258
earlz 10:bda85442b674 259 kbd_key kbd_PopBuffer(){
earlz 11:fede136943a9 260 kbd_key k;
earlz 11:fede136943a9 261 while(pending_key==0){
earlz 11:fede136943a9 262 //hlt();
earlz 11:fede136943a9 263 //fill in busy code here
earlz 11:fede136943a9 264 }
earlz 11:fede136943a9 265
earlz 11:fede136943a9 266 k.scancode=keys[key_got].scancode;
earlz 11:fede136943a9 267 k.asci=keys[key_got].asci;
earlz 11:fede136943a9 268 pending_key--;
earlz 11:fede136943a9 269 key_got++;
earlz 11:fede136943a9 270 return k;
earlz 10:bda85442b674 271 }
earlz 10:bda85442b674 272
earlz 10:bda85442b674 273 uint8_t kbd_GetKey(){ //this will just return the asci code
earlz 11:fede136943a9 274 kbd_key k;
earlz 11:fede136943a9 275 k.asci=0;
earlz 11:fede136943a9 276 while(k.asci==0){
earlz 11:fede136943a9 277 k=kbd_PopBuffer();
earlz 11:fede136943a9 278 }
earlz 11:fede136943a9 279 return k.asci;
earlz 10:bda85442b674 280 }
earlz 10:bda85442b674 281
earlz 10:bda85442b674 282
earlz 10:bda85442b674 283 void kbd_update_leds(uint8_t status){
earlz 11:fede136943a9 284 uint8_t tmp;
earlz 11:fede136943a9 285 //TODO
earlz 11:fede136943a9 286 /*while((inportb(0x64)&2)!=0){}
earlz 11:fede136943a9 287 outportb(0x60,0xED);
earlz 11:fede136943a9 288
earlz 11:fede136943a9 289 while((inportb(0x64)&2)!=0){}
earlz 11:fede136943a9 290 outportb(0x60,status);
earlz 11:fede136943a9 291 */
earlz 10:bda85442b674 292 }
earlz 10:bda85442b674 293
earlz 10:bda85442b674 294
earlz 10:bda85442b674 295
earlz 10:bda85442b674 296 int kbd_DoShifts(uint8_t scan){
earlz 11:fede136943a9 297 switch(scan){
earlz 11:fede136943a9 298 case RSHIFT_KEY:
earlz 11:fede136943a9 299 kbd_shifts.shift++;
earlz 11:fede136943a9 300 break;
earlz 11:fede136943a9 301 case LSHIFT_KEY:
earlz 11:fede136943a9 302 kbd_shifts.shift++;
earlz 11:fede136943a9 303 break;
earlz 11:fede136943a9 304 case CTRL_KEY:
earlz 11:fede136943a9 305 kbd_shifts.ctrl++;
earlz 11:fede136943a9 306 break;
earlz 11:fede136943a9 307 case ALT_KEY:
earlz 11:fede136943a9 308 kbd_shifts.alt++;
earlz 11:fede136943a9 309 break;
earlz 11:fede136943a9 310 case CAPS_KEY:
earlz 11:fede136943a9 311 led_status^=CAPS_LED;
earlz 11:fede136943a9 312 kbd_update_leds(led_status);
earlz 11:fede136943a9 313 kbd_shifts.caps^=1;
earlz 11:fede136943a9 314 break;
earlz 11:fede136943a9 315 case NUM_KEY:
earlz 11:fede136943a9 316 led_status^=NUM_LED;
earlz 11:fede136943a9 317 kbd_update_leds(led_status);
earlz 11:fede136943a9 318 kbd_shifts.num^=1;
earlz 11:fede136943a9 319 break;
earlz 11:fede136943a9 320 case SCROLL_KEY:
earlz 11:fede136943a9 321 led_status^=SCROLL_LED;
earlz 11:fede136943a9 322 kbd_update_leds(led_status);
earlz 11:fede136943a9 323 kbd_shifts.scroll^=1;
earlz 11:fede136943a9 324 break;
earlz 11:fede136943a9 325 default:
earlz 11:fede136943a9 326 return 0;
earlz 11:fede136943a9 327 break;
earlz 11:fede136943a9 328 }
earlz 11:fede136943a9 329 return 1;
earlz 10:bda85442b674 330 }
earlz 10:bda85442b674 331
earlz 10:bda85442b674 332 int kbd_DoUnshifts(uint8_t scan){
earlz 11:fede136943a9 333 switch(scan){
earlz 11:fede136943a9 334 case RSHIFT_KEY:
earlz 11:fede136943a9 335 kbd_shifts.shift--;
earlz 11:fede136943a9 336 break;
earlz 11:fede136943a9 337 case LSHIFT_KEY:
earlz 11:fede136943a9 338 kbd_shifts.shift--;
earlz 11:fede136943a9 339 break;
earlz 11:fede136943a9 340 case CTRL_KEY:
earlz 11:fede136943a9 341 kbd_shifts.ctrl--;
earlz 11:fede136943a9 342 break;
earlz 11:fede136943a9 343 case ALT_KEY:
earlz 11:fede136943a9 344 kbd_shifts.alt--;
earlz 11:fede136943a9 345 break;
earlz 11:fede136943a9 346 case CAPS_KEY:
earlz 11:fede136943a9 347 //kbd_shifts.caps=0;
earlz 11:fede136943a9 348 break;
earlz 11:fede136943a9 349 case NUM_KEY:
earlz 11:fede136943a9 350 //kbd_shifts.num=0;
earlz 11:fede136943a9 351 break;
earlz 11:fede136943a9 352 case SCROLL_KEY:
earlz 11:fede136943a9 353 //kbd_shifts.scroll=0;
earlz 11:fede136943a9 354 break;
earlz 11:fede136943a9 355 default:
earlz 11:fede136943a9 356 return 0;
earlz 11:fede136943a9 357 break;
earlz 11:fede136943a9 358 }
earlz 11:fede136943a9 359 return 1;
earlz 10:bda85442b674 360 }
earlz 10:bda85442b674 361 volatile bool expecting_break=false;
earlz 10:bda85442b674 362 void keyboard_callback(PS2KB kb, uint8_t val) //this is called from interrupt! Must be fast
earlz 10:bda85442b674 363 {
earlz 11:fede136943a9 364 //stopints();
earlz 10:bda85442b674 365 if(expecting_break){
earlz 11:fede136943a9 366 //val^=0xF0;
earlz 10:bda85442b674 367 kbd_DoUnshifts(val);
earlz 10:bda85442b674 368 expecting_break=false;
earlz 10:bda85442b674 369 }else{
earlz 11:fede136943a9 370 if(val>=0xF0){
earlz 11:fede136943a9 371 expecting_break=true;
earlz 11:fede136943a9 372 }else{
earlz 11:fede136943a9 373 //add check for 0x84 to make sure we don't go over our keymap
earlz 11:fede136943a9 374 if(val<=0x84 && kbd_DoShifts(val)==0){ //if not a shift-type key
earlz 11:fede136943a9 375
earlz 11:fede136943a9 376 if ((kbd_shifts.caps^kbd_shifts.shift)==1) {
earlz 11:fede136943a9 377 kbd_PutBuffer(val,kbdus_caps[val]);
earlz 11:fede136943a9 378 }else{
earlz 11:fede136943a9 379 kbd_PutBuffer(val,kbdus[val]);
earlz 11:fede136943a9 380 }
earlz 11:fede136943a9 381 }
earlz 11:fede136943a9 382 }
earlz 11:fede136943a9 383 }
earlz 10:bda85442b674 384 }
earlz 10:bda85442b674 385
earlz 10:bda85442b674 386
earlz 11:fede136943a9 387
earlz 11:fede136943a9 388