comparison sms.c @ 2535:0c6519125a28

Implement paste support for box drawing chars and Latin-1 character Æ
author Michael Pavone <pavone@retrodev.com>
date Wed, 27 Nov 2024 20:04:28 -0800
parents 35dbe1873c8f
children 0d8e3e65327f
comparison
equal deleted inserted replaced
2534:35dbe1873c8f 2535:0c6519125a28
236 uint8_t mod; 236 uint8_t mod;
237 uint8_t before; 237 uint8_t before;
238 uint8_t after; 238 uint8_t after;
239 } cp_keys; 239 } cp_keys;
240 240
241 #define SIMPLE(cp, sc) case cp: return (cp_keys){sc}
242 #define MAYBE_SHIFT(cp, sc) case cp: return (cp_keys){sc, shift}
243 #define SHIFTED(cp, sc) case cp: return (cp_keys){sc, 0x12}
244 #define ACCENTED(cp, sc) case cp: return (cp_keys){sc, 0x81}
245 #define GRAPHIC(cp, sc) case cp: return (cp_keys){sc, .before=0x11, .after=0x11}
246 #define SHIFTED_GRAPHIC(cp, sc) case cp: return (cp_keys){sc, 0x12, .before=0x11, .after=0x11}
247
241 static cp_keys cp_to_keys(int cp) 248 static cp_keys cp_to_keys(int cp)
242 { 249 {
243 uint8_t shift = 0; 250 uint8_t shift = 0;
244 if (cp >= 'a' && cp <= 'z') { 251 if (cp >= 'a' && cp <= 'z') {
245 shift = 0x12; 252 shift = 0x12;
251 //accented latin letters only have a single case 258 //accented latin letters only have a single case
252 cp -= 0xE0 - 0xC0; 259 cp -= 0xE0 - 0xC0;
253 } 260 }
254 switch (cp) 261 switch (cp)
255 { 262 {
256 case '0': return (cp_keys){0x45}; 263 SIMPLE('0', 0x45);
257 case '1': return (cp_keys){0x16, shift}; 264 MAYBE_SHIFT('1', 0x16);
258 case '2': return (cp_keys){0x1E, shift}; 265 MAYBE_SHIFT('2', 0x1E);
259 case '3': return (cp_keys){0x26, shift}; 266 MAYBE_SHIFT('3', 0x26);
260 case '4': return (cp_keys){0x25, shift}; 267 MAYBE_SHIFT('4', 0x25);
261 case '5': return (cp_keys){0x2E, shift}; 268 MAYBE_SHIFT('5', 0x2E);
262 case '6': return (cp_keys){0x36, shift}; 269 MAYBE_SHIFT('6', 0x36);
263 case '7': return (cp_keys){0x3D, shift}; 270 MAYBE_SHIFT('7', 0x3D);
264 case '8': return (cp_keys){0x3E, shift}; 271 MAYBE_SHIFT('8', 0x3E);
265 case '9': return (cp_keys){0x46, shift}; 272 MAYBE_SHIFT('9', 0x46);
266 case 'A': return (cp_keys){0x1C, shift}; 273 MAYBE_SHIFT('A', 0x1C);
267 case 'B': return (cp_keys){0x32, shift}; 274 MAYBE_SHIFT('B', 0x32);
268 case 'C': return (cp_keys){0x21, shift}; 275 MAYBE_SHIFT('C', 0x21);
269 case 'D': return (cp_keys){0x23, shift}; 276 MAYBE_SHIFT('D', 0x23);
270 case 'E': return (cp_keys){0x24, shift}; 277 MAYBE_SHIFT('E', 0x24);
271 case 'F': return (cp_keys){0x2B, shift}; 278 MAYBE_SHIFT('F', 0x2B);
272 case 'G': return (cp_keys){0x34, shift}; 279 MAYBE_SHIFT('G', 0x34);
273 case 'H': return (cp_keys){0x33, shift}; 280 MAYBE_SHIFT('H', 0x33);
274 case 'I': return (cp_keys){0x43, shift}; 281 MAYBE_SHIFT('I', 0x43);
275 case 'J': return (cp_keys){0x3B, shift}; 282 MAYBE_SHIFT('J', 0x3B);
276 case 'K': return (cp_keys){0x42, shift}; 283 MAYBE_SHIFT('K', 0x42);
277 case 'L': return (cp_keys){0x4B, shift}; 284 MAYBE_SHIFT('L', 0x4B);
278 case 'M': return (cp_keys){0x3A, shift}; 285 MAYBE_SHIFT('M', 0x3A);
279 case 'N': return (cp_keys){0x31, shift}; 286 MAYBE_SHIFT('N', 0x31);
280 case 'O': return (cp_keys){0x44, shift}; 287 MAYBE_SHIFT('O', 0x44);
281 case 'P': return (cp_keys){0x4D, shift}; 288 MAYBE_SHIFT('P', 0x4D);
282 case 'Q': return (cp_keys){0x15, shift}; 289 MAYBE_SHIFT('Q', 0x15);
283 case 'R': return (cp_keys){0x2D, shift}; 290 MAYBE_SHIFT('R', 0x2D);
284 case 'S': return (cp_keys){0x1B, shift}; 291 MAYBE_SHIFT('S', 0x1B);
285 case 'T': return (cp_keys){0x2C, shift}; 292 MAYBE_SHIFT('T', 0x2C);
286 case 'U': return (cp_keys){0x3C, shift}; 293 MAYBE_SHIFT('U', 0x3C);
287 case 'V': return (cp_keys){0x2A, shift}; 294 MAYBE_SHIFT('V', 0x2A);
288 case 'W': return (cp_keys){0x1D, shift}; 295 MAYBE_SHIFT('W', 0x1D);
289 case 'X': return (cp_keys){0x22, shift}; 296 MAYBE_SHIFT('X', 0x22);
290 case 'Y': return (cp_keys){0x35, shift}; 297 MAYBE_SHIFT('Y', 0x35);
291 case 'Z': return (cp_keys){0x1A, shift}; 298 MAYBE_SHIFT('Z', 0x1A);
292 case '-': return (cp_keys){0x4E}; 299 SIMPLE('-', 0x4E);
293 case '=': return (cp_keys){0x4E, 0x12}; 300 SHIFTED('=', 0x4E);
294 case ';': return (cp_keys){0x4C}; 301 SIMPLE(';', 0x4C);
295 case '+': return (cp_keys){0x4C, 0x12}; 302 SHIFTED('+', 0x4C);
296 case ':': return (cp_keys){0x52}; 303 SIMPLE(':', 0x52);
297 case '*': return (cp_keys){0x52, 0x12}; 304 SHIFTED('*', 0x52);
298 case ',': return (cp_keys){0x41}; 305 SIMPLE(',', 0x41);
299 case '<': return (cp_keys){0x41, 0x12}; 306 SHIFTED('<', 0x41);
300 case '.': return (cp_keys){0x49}; 307 SIMPLE('.', 0x49);
301 case '>': return (cp_keys){0x49, 0x12}; 308 SHIFTED('>', 0x49);
302 case '/': return (cp_keys){0x4A}; 309 SIMPLE('/', 0x4A);
303 case '?': return (cp_keys){0x4A, 0x12}; 310 SHIFTED('?', 0x4A);
304 case '^': return (cp_keys){0x55}; 311 SIMPLE('^', 0x55);
305 case '~': return (cp_keys){0x55, 0x12}; 312 SHIFTED('~', 0x55);
306 case '[': return (cp_keys){0x54}; 313 SIMPLE('[', 0x54);
307 case '{': return (cp_keys){0x54, 0x12}; 314 SHIFTED('{', 0x54);
308 case ']': return (cp_keys){0x5B}; 315 SIMPLE(']', 0x5B);
309 case '}': return (cp_keys){0x5B, 0x12}; 316 SHIFTED('}', 0x5B);
310 case '@': return (cp_keys){0x85}; 317 SIMPLE('@', 0x85);
311 case '`': return (cp_keys){0x85, 0x12}; 318 SHIFTED('`', 0x85);
312 case '\n': return (cp_keys){0x5A}; 319 SIMPLE('\n', 0x5A);
313 case ' ': return (cp_keys){0x29}; 320 SIMPLE(' ', 0x29);
314 case 0xA5: return (cp_keys){0x5D};//¥ 321 SIMPLE(0xA5, 0x5D);//¥
315 //Accented latin letters will only work right with export BASIC 322 //Accented latin letters will only work right with export BASIC
316 case 0xA1: return (cp_keys){0x32, 0x81};//¡ 323 ACCENTED(0xA1, 0x32);//¡
317 case 0xA3: return (cp_keys){0x5D, 0x81};//£ 324 ACCENTED(0xA3, 0x5D);//£
318 case 0xBF: return (cp_keys){0x2A, 0x81};//¿ 325 ACCENTED(0xBF, 0x2A);//¿
319 case 0xC0: return (cp_keys){0x1D, 0x81};//À 326 ACCENTED(0xC0, 0x1D);//À
320 case 0xC1: return (cp_keys){0x15, 0x81};//Á 327 ACCENTED(0xC1, 0x15);//Á
321 case 0xC2: return (cp_keys){0x16, 0x81};//Â 328 ACCENTED(0xC2, 0x16);//Â
322 case 0xC3: return (cp_keys){0x23, 0x81};//Ã 329 ACCENTED(0xC3, 0x23);//Ã
323 case 0xC4: return (cp_keys){0x1C, 0x81};//Ä 330 ACCENTED(0xC4, 0x1C);//Ä
324 case 0xC5: return (cp_keys){0x1B, 0x81};//Å 331 ACCENTED(0xC5, 0x1B);//Å
325 case 0xC7: return (cp_keys){0x21, 0x81};//Ç 332 ACCENTED(0xC7, 0x21);//Ç
326 case 0xC8: return (cp_keys){0x2D, 0x81};//È 333 case 0xC6: return (cp_keys){0x31, 0x81, .after=0x24};//Æ
327 case 0xC9: return (cp_keys){0x24, 0x81};//É 334 ACCENTED(0xC8, 0x2D);//È
328 case 0xCA: return (cp_keys){0x26, 0x81};//Ê 335 ACCENTED(0xC9, 0x24);//É
329 case 0xCB: return (cp_keys){0x2E, 0x81};//Ë 336 ACCENTED(0xCA, 0x26);//Ê
330 case 0xCC: return (cp_keys){0x44, 0x81};//Ì 337 ACCENTED(0xCB, 0x2E);//Ë
331 case 0xCD: return (cp_keys){0x4B, 0x81};//Í 338 ACCENTED(0xCC, 0x44);//Ì
332 case 0xCE: return (cp_keys){0x49, 0x81};//Î 339 ACCENTED(0xCD, 0x4B);//Í
333 case 0xCF: return (cp_keys){0x4C, 0x81};//Ï 340 ACCENTED(0xCE, 0x49);//Î
334 case 0xD1: return (cp_keys){0x2C, 0x81};//Ñ 341 ACCENTED(0xCF, 0x4C);//Ï
335 case 0xD2: return (cp_keys){0x85, 0x81};//Ò 342 ACCENTED(0xD1, 0x2C);//Ñ
336 case 0xD3: return (cp_keys){0x4D, 0x81};//Ó 343 ACCENTED(0xD2, 0x85);//Ò
337 case 0xD4: return (cp_keys){0x45, 0x81};//Ô 344 ACCENTED(0xD3, 0x4D);//Ó
338 case 0xD5: return (cp_keys){0x0E, 0x81};//Õ 345 ACCENTED(0xD4, 0x45);//Ô
339 case 0xD6: return (cp_keys){0x52, 0x81};//Ö 346 ACCENTED(0xD5, 0x0E);//Õ
347 ACCENTED(0xD6, 0x52);//Ö
340 //character in font doesn't really look like a phi to me 348 //character in font doesn't really look like a phi to me
341 //but Wikipedia lists it as such 349 //but Wikipedia lists it as such
342 case 0x3A6: //Φ 350 case 0x3A6: //Φ
343 case 0xD8: return (cp_keys){0x54, 0x81};//Ø 351 ACCENTED(0xD8, 0x54);//Ø
344 case 0xD9: return (cp_keys){0x43, 0x81};//Ù 352 ACCENTED(0xD9, 0x43);//Ù
345 case 0xDA: return (cp_keys){0x3C, 0x81};//Ú 353 ACCENTED(0xDA, 0x3C);//Ú
346 case 0xDB: return (cp_keys){0x3D, 0x81};//Û 354 ACCENTED(0xDB, 0x3D);//Û
347 case 0xDC: return (cp_keys){0x42, 0x81};//Ü 355 ACCENTED(0xDC, 0x42);//Ü
348 case 0x3A3: return (cp_keys){0x5B, 0x81};//Σ 356 ACCENTED(0x3A3, 0x5B);//Σ
349 case 0x3A9: return (cp_keys){0x3A, 0x81};//Ω 357 ACCENTED(0x3A9, 0x3A);//Ω
350 case 0x3B1: return (cp_keys){0x34, 0x81};//α 358 ACCENTED(0x3B1, 0x34);//α
351 case 0x3B2: return (cp_keys){0x33, 0x81};//β 359 ACCENTED(0x3B2, 0x33);//β
352 case 0x3B8: return (cp_keys){0x3B, 0x81};//θ 360 ACCENTED(0x3B8, 0x3B);//θ
353 case 0x3BB: return (cp_keys){0x22, 0x81};//λ 361 ACCENTED(0x3BB, 0x22);//λ
354 case 0xB5://µ 362 case 0xB5://µ
355 case 0x3BC: return (cp_keys){0x1A, 0x81};//μ 363 ACCENTED(0x3BC, 0x1A);//μ
356 case 0x3C0: return (cp_keys){0x0E, 0x12};//π 364 SHIFTED(0x3C0, 0x0E);//π
365 //Box drawing
366 GRAPHIC(0x2500, 0x1E);//─
367 GRAPHIC(0x2501, 0x34);//━
368 GRAPHIC(0x2502, 0x26);//│
369 GRAPHIC(0x2503, 0x2C);//┃
370 GRAPHIC(0x250C, 0x15);//┌
371 GRAPHIC(0x2510, 0x1D);//┐
372 GRAPHIC(0x2514, 0x1C);//└
373 GRAPHIC(0x2518, 0x1B);//┘
374 SHIFTED_GRAPHIC(0x251C, 0x15);//├
375 SHIFTED_GRAPHIC(0x2524, 0x1B);//┤
376 SHIFTED_GRAPHIC(0x252C, 0x1D);//┬
377 SHIFTED_GRAPHIC(0x2534, 0x1C);//┴
378 GRAPHIC(0x253C, 0x16);//┼
379 GRAPHIC(0x2571, 0x4E);//╱
380 GRAPHIC(0x2572, 0x5D);//╲
381 GRAPHIC(0x2573, 0x55);//╳
357 default: return (cp_keys){0}; 382 default: return (cp_keys){0};
358 } 383 }
359 } 384 }
360 385
361 static void advance_paste_buffer(sms_context *sms, const char *paste) 386 static void advance_paste_buffer(sms_context *sms, const char *paste)
365 sms->header.paste_buffer = NULL; 390 sms->header.paste_buffer = NULL;
366 } else { 391 } else {
367 sms->header.paste_cur_char = paste - sms->header.paste_buffer; 392 sms->header.paste_cur_char = paste - sms->header.paste_buffer;
368 } 393 }
369 } 394 }
395
396 enum {
397 PASTE_BEFORE,
398 PASTE_MAIN,
399 PASTE_AFTER,
400 PASTE_BEFORE_UP,
401 PASTE_MAIN_UP,
402 PASTE_AFTER_UP,
403 PASTE_TOGGLE_UP
404 };
370 405
371 406
372 static uint8_t paste_internal(sms_context *sms, uint8_t prev_key) 407 static uint8_t paste_internal(sms_context *sms, uint8_t prev_key)
373 { 408 {
374 const char *paste = sms->header.paste_buffer + sms->header.paste_cur_char; 409 const char *paste = sms->header.paste_buffer + sms->header.paste_cur_char;
376 cp_keys keys = cp_to_keys(cp); 411 cp_keys keys = cp_to_keys(cp);
377 if (!keys.main) { 412 if (!keys.main) {
378 advance_paste_buffer(sms, paste); 413 advance_paste_buffer(sms, paste);
379 return 0; 414 return 0;
380 } 415 }
381 if (sms->paste_toggle) { 416 switch (sms->paste_state)
382 //key up 417 {
418 default:
419 case PASTE_BEFORE:
420 if (sms->paste_toggle != keys.before) {
421 if (sms->paste_toggle) {
422 sms->header.keyboard_down(&sms->header, sms->paste_toggle);
423 sms->paste_state = PASTE_TOGGLE_UP;
424 return sms->paste_toggle;
425 } else {
426 if (prev_key == keys.before) {
427 return 0;
428 }
429 sms->header.keyboard_down(&sms->header, keys.before);
430 sms->paste_state = PASTE_BEFORE_UP;
431 return keys.before;
432 }
433 }
434 case PASTE_MAIN:
435 if (prev_key == keys.main) {
436 // we're pressing the key that was just released, we need to wait to the next scan
437 return 0;
438 }
439 sms->header.keyboard_down(&sms->header, keys.main);
440 if (keys.mod) {
441 sms->header.keyboard_down(&sms->header, keys.mod);
442 }
443 sms->paste_state = PASTE_MAIN_UP;
444 return keys.main;
445 case PASTE_AFTER:
446 if (prev_key == keys.after) {
447 return 0;
448 }
449 sms->header.keyboard_down(&sms->header, keys.after);
450 sms->paste_state = PASTE_AFTER_UP;
451 return keys.after;
452 case PASTE_BEFORE_UP:
453 sms->header.keyboard_up(&sms->header, keys.before);
454 sms->paste_state = PASTE_MAIN;
455 return keys.before;
456 case PASTE_MAIN_UP:
383 sms->header.keyboard_up(&sms->header, keys.main); 457 sms->header.keyboard_up(&sms->header, keys.main);
384 if (keys.mod) { 458 if (keys.mod) {
385 sms->header.keyboard_up(&sms->header, keys.mod); 459 sms->header.keyboard_up(&sms->header, keys.mod);
386 } 460 }
461 if (keys.after && keys.after != keys.before) {
462 sms->paste_state = PASTE_AFTER;
463 } else {
464 sms->paste_toggle = keys.after;
465 sms->paste_state = PASTE_BEFORE;
466 advance_paste_buffer(sms, paste);
467 }
468 return keys.main;
469 case PASTE_AFTER_UP:
470 sms->header.keyboard_up(&sms->header, keys.after);
471 sms->paste_state = PASTE_BEFORE;
387 advance_paste_buffer(sms, paste); 472 advance_paste_buffer(sms, paste);
388 } else { 473 return keys.after;
389 //key down 474 case PASTE_TOGGLE_UP:
390 if (prev_key == keys.main) { 475 sms->header.keyboard_up(&sms->header, sms->paste_toggle);
391 // we're pressing the key that was just released, we need to wait to the next scan 476 sms->paste_state = PASTE_BEFORE;
392 return 0; 477 return sms->paste_toggle;
393 } 478 }
394 sms->header.keyboard_down(&sms->header, keys.main);
395 if (keys.mod) {
396 sms->header.keyboard_down(&sms->header, keys.mod);
397 }
398 }
399 sms->paste_toggle = !sms->paste_toggle;
400 return keys.main;
401 } 479 }
402 480
403 static void process_paste(sms_context *sms, uint32_t cycle) 481 static void process_paste(sms_context *sms, uint32_t cycle)
404 { 482 {
405 if (sms->header.paste_buffer && cycle > sms->last_paste_cycle && cycle - sms->last_paste_cycle >= PASTE_DELAY) { 483 if (sms->header.paste_buffer && cycle > sms->last_paste_cycle && cycle - sms->last_paste_cycle >= PASTE_DELAY) {
406 484
407 uint8_t main_key; 485 uint8_t main_key;
408 if ((main_key = paste_internal(sms, 0))) { 486 if ((main_key = paste_internal(sms, 0))) {
409 sms->last_paste_cycle = cycle; 487 sms->last_paste_cycle = cycle;
410 if (sms->header.paste_buffer && !sms->paste_toggle) { 488 if (sms->header.paste_buffer && !sms->paste_state) {
411 paste_internal(sms, main_key); 489 paste_internal(sms, main_key);
412 } 490 }
413 } 491 }
414 } 492 }
415 } 493 }