Mercurial > repos > blastem
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 } |