fork of PCE focusing on macplus, supporting DaynaPort SCSI network emulation
at master 871 lines 15 kB view raw
1/***************************************************************************** 2 * pce * 3 *****************************************************************************/ 4 5/***************************************************************************** 6 * File name: src/devices/cassette.c * 7 * Created: 2020-04-30 by Hampa Hug <hampa@hampa.ch> * 8 * Copyright: (C) 2020-2025 Hampa Hug <hampa@hampa.ch> * 9 *****************************************************************************/ 10 11/***************************************************************************** 12 * This program is free software. You can redistribute it and / or modify it * 13 * under the terms of the GNU General Public License version 2 as published * 14 * by the Free Software Foundation. * 15 * * 16 * This program is distributed in the hope that it will be useful, but * 17 * WITHOUT ANY WARRANTY, without even the implied warranty of * 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * 19 * Public License for more details. * 20 *****************************************************************************/ 21 22 23#include <config.h> 24 25#include <devices/cassette.h> 26 27#include <stdlib.h> 28#include <string.h> 29 30#include <drivers/pti/pti.h> 31#include <drivers/pti/pti-io.h> 32 33#include <lib/console.h> 34#include <lib/msg.h> 35#include <lib/string.h> 36#include <lib/sysdep.h> 37 38 39typedef struct { 40 const char *msg; 41 42 int (*set_msg) (cassette_t *cas, const char *msg, const char *val); 43} cas_msg_list_t; 44 45 46static 47int cas_set_msg_commit (cassette_t *cas, const char *msg, const char *val) 48{ 49 if (cas_commit (cas)) { 50 return (1); 51 } 52 53 return (0); 54} 55 56static 57int cas_set_msg_create (cassette_t *cas, const char *msg, const char *val) 58{ 59 if (*val == 0) { 60 return (1); 61 } 62 63 remove (val); 64 65 if (cas_set_write_name (cas, val, 1)) { 66 return (1); 67 } 68 69 if (cas->auto_play) { 70 cas_press_keys (cas, 1, 1); 71 } 72 else { 73 cas_press_keys (cas, 0, 0); 74 } 75 76 cas_print_state (cas); 77 78 return (0); 79} 80 81static 82int cas_set_msg_open (cassette_t *cas, const char *msg, const char *val) 83{ 84 if (*val == 0) { 85 val = NULL; 86 } 87 88 if (cas_set_read_name (cas, NULL)) { 89 return (1); 90 } 91 92 if (cas_set_write_name (cas, val, 0)) { 93 return (1); 94 } 95 96 cas_press_keys (cas, 0, 0); 97 cas_print_state (cas); 98 99 return (0); 100} 101 102static 103int cas_set_msg_play (cassette_t *cas, const char *msg, const char *val) 104{ 105 cas_press_keys (cas, 1, 0); 106 cas_print_state (cas); 107 108 return (0); 109} 110 111static 112int cas_set_msg_load (cassette_t *cas, const char *msg, const char *val) 113{ 114 double v; 115 116 cas_press_keys (cas, 0, 0); 117 118 if (*val != 0) { 119 if (msg_get_double (val, &v)) { 120 return (1); 121 } 122 123 cas_set_position (cas, v); 124 } 125 126 cas_print_state (cas); 127 128 return (0); 129} 130 131static 132int cas_set_msg_read (cassette_t *cas, const char *msg, const char *val) 133{ 134 if (*val == 0) { 135 val = NULL; 136 } 137 138 if (cas_set_read_name (cas, val)) { 139 return (1); 140 } 141 142 cas_press_keys (cas, 0, 0); 143 cas_print_state (cas); 144 145 return (0); 146} 147 148static 149int cas_set_msg_record (cassette_t *cas, const char *msg, const char *val) 150{ 151 cas_press_keys (cas, 1, 1); 152 cas_print_state (cas); 153 154 return (0); 155} 156 157static 158int cas_set_msg_space (cassette_t *cas, const char *msg, const char *val) 159{ 160 double sec; 161 162 if (*val != 0) { 163 if (msg_get_double (val, &sec)) { 164 return (1); 165 } 166 } 167 else { 168 sec = 1.0; 169 } 170 171 if (cas_space (cas, sec)) { 172 return (1); 173 } 174 175 cas_print_state (cas); 176 177 return (0); 178} 179 180static 181int cas_set_msg_state (cassette_t *cas, const char *msg, const char *val) 182{ 183 cas_print_state (cas); 184 185 return (0); 186} 187 188static 189int cas_set_msg_stop (cassette_t *cas, const char *msg, const char *val) 190{ 191 cas_press_keys (cas, 0, 0); 192 cas_print_state (cas); 193 194 return (0); 195} 196 197static 198int cas_set_msg_truncate (cassette_t *cas, const char *msg, const char *val) 199{ 200 double v; 201 202 if (*val == 0) { 203 return (1); 204 } 205 206 if (msg_get_double (val, &v)) { 207 return (1); 208 } 209 210 if (cas_truncate (cas, v)) { 211 return (1); 212 } 213 214 cas_print_state (cas); 215 216 return (0); 217} 218 219static 220int cas_set_msg_write (cassette_t *cas, const char *msg, const char *val) 221{ 222 if (*val == 0) { 223 val = NULL; 224 } 225 226 if (cas_set_write_name (cas, val, 1)) { 227 return (1); 228 } 229 230 if (cas->auto_play) { 231 cas_press_keys (cas, 1, 1); 232 } 233 else { 234 cas_press_keys (cas, 0, 0); 235 } 236 237 cas_print_state (cas); 238 239 return (0); 240} 241 242static cas_msg_list_t set_msg_list[] = { 243 { "emu.cas.commit", cas_set_msg_commit }, 244 { "emu.cas.create", cas_set_msg_create }, 245 { "emu.cas.c", cas_set_msg_create }, 246 { "emu.cas.open", cas_set_msg_open }, 247 { "emu.cas.play", cas_set_msg_play }, 248 { "emu.cas.load", cas_set_msg_load }, 249 { "emu.cas.read", cas_set_msg_read }, 250 { "emu.cas.record", cas_set_msg_record }, 251 { "emu.cas.space", cas_set_msg_space }, 252 { "emu.cas.state", cas_set_msg_state }, 253 { "emu.cas.stop", cas_set_msg_stop }, 254 { "emu.cas.truncate", cas_set_msg_truncate }, 255 { "emu.cas.write", cas_set_msg_write }, 256 { NULL, NULL } 257}; 258 259 260int cas_set_msg (cassette_t *cas, const char *msg, const char *val) 261{ 262 cas_msg_list_t *lst; 263 264 if (val == NULL) { 265 val = ""; 266 } 267 268 lst = set_msg_list; 269 270 while (lst->msg != NULL) { 271 if (msg_is_message (lst->msg, msg)) { 272 return (lst->set_msg (cas, msg, val)); 273 } 274 275 lst += 1; 276 } 277 278 return (-1); 279} 280 281 282void cas_init (cassette_t *cas) 283{ 284 cas->set_inp = NULL; 285 cas->set_inp_ext = NULL; 286 287 cas->set_play = NULL; 288 cas->set_play_ext = NULL; 289 290 cas->set_run = NULL; 291 cas->set_run_ext = NULL; 292 293 cas->read_img = NULL; 294 cas->read_name = NULL; 295 296 cas->write_img = NULL; 297 cas->write_name = NULL; 298 299 cas->modified = 0; 300 cas->eof = 0; 301 302 cas->run = 0; 303 cas->motor = 0; 304 cas->play = 0; 305 cas->record = 0; 306 307 cas->auto_play = 0; 308 cas->auto_motor = 0; 309 310 cas->out_val = 0; 311 cas->inp_val = 0; 312 313 cas->clock = 0; 314 cas->remainder = 0; 315 cas->position = 0; 316 317 cas->motor_delay = 0; 318 cas->motor_delay_count = 0; 319 320 cas->counter = 0; 321} 322 323void cas_free (cassette_t *cas) 324{ 325 cas_commit (cas); 326 327 pti_img_del (cas->write_img); 328 pti_img_del (cas->read_img); 329 330 free (cas->write_name); 331 free (cas->read_name); 332} 333 334cassette_t *cas_new (void) 335{ 336 cassette_t *cas; 337 338 if ((cas = malloc (sizeof (cassette_t))) == NULL) { 339 return (NULL); 340 } 341 342 cas_init (cas); 343 344 return (cas); 345} 346 347void cas_del (cassette_t *cas) 348{ 349 if (cas != NULL) { 350 cas_free (cas); 351 } 352 353 free (cas); 354} 355 356void cas_set_inp_fct (cassette_t *cas, void *ext, void *fct) 357{ 358 cas->set_inp_ext = ext; 359 cas->set_inp = fct; 360} 361 362void cas_set_play_fct (cassette_t *cas, void *ext, void *fct) 363{ 364 cas->set_play_ext = ext; 365 cas->set_play = fct; 366} 367 368void cas_set_run_fct (cassette_t *cas, void *ext, void *fct) 369{ 370 cas->set_run_ext = ext; 371 cas->set_run = fct; 372} 373 374void cas_set_clock (cassette_t *cas, unsigned long val) 375{ 376 cas->clock = val; 377 cas->remainder = 0; 378} 379 380void cas_set_auto_play (cassette_t *cas, int val) 381{ 382 cas->auto_play = (val != 0); 383} 384 385void cas_set_auto_motor (cassette_t *cas, int val) 386{ 387 cas->auto_motor = (val != 0); 388} 389 390void cas_set_motor_delay (cassette_t *cas, unsigned long val) 391{ 392 cas->motor_delay = val; 393} 394 395static 396pti_img_t *cas_get_read_img (const cassette_t *cas) 397{ 398 if (cas->read_img != NULL) { 399 return (cas->read_img); 400 } 401 else if (cas->write_img != NULL) { 402 return (cas->write_img); 403 } 404 405 return (NULL); 406} 407 408int cas_set_read_name (cassette_t *cas, const char *fname) 409{ 410 cas_set_record (cas, 0); 411 cas_set_play (cas, 0); 412 413 pti_img_del (cas->read_img); 414 cas->read_img = NULL; 415 416 free (cas->read_name); 417 cas->read_name = NULL; 418 419 cas->position = 0; 420 cas->remainder = 0; 421 422 if (fname == NULL) { 423 return (0); 424 } 425 426 if ((cas->read_name = str_copy_alloc (fname)) == NULL) { 427 return (1); 428 } 429 430 if ((cas->read_img = pti_img_load (cas->read_name, PTI_FORMAT_NONE)) == NULL) { 431 return (1); 432 } 433 434 cas->eof = 0; 435 436 return (0); 437} 438 439int cas_set_write_name (cassette_t *cas, const char *fname, int create) 440{ 441 cas_set_record (cas, 0); 442 cas_set_play (cas, 0); 443 444 if (cas_commit (cas)) { 445 return (1); 446 } 447 448 pti_img_del (cas->write_img); 449 cas->write_img = NULL; 450 451 free (cas->write_name); 452 cas->write_name = NULL; 453 454 cas->modified = 0; 455 cas->position = 0; 456 cas->remainder = 0; 457 458 if (fname == NULL) { 459 return (0); 460 } 461 462 if ((cas->write_name = str_copy_alloc (fname)) == NULL) { 463 return (1); 464 } 465 466 if (create) { 467 if (pce_file_exists (fname) == 0) { 468 if ((cas->write_img = pti_img_new()) == NULL) { 469 return (1); 470 } 471 472 pti_img_set_clock (cas->write_img, cas->clock); 473 474 return (0); 475 } 476 } 477 478 if ((cas->write_img = pti_img_load (cas->write_name, PTI_FORMAT_NONE)) == NULL) { 479 return (1); 480 } 481 482 return (0); 483} 484 485int cas_commit (cassette_t *cas) 486{ 487 if (cas->modified == 0) { 488 return (0); 489 } 490 491 if ((cas->write_img == NULL) || (cas->write_name == NULL)) { 492 return (1); 493 } 494 495 pce_printf ("CASSETTE writing back %s\n", cas->write_name); 496 497 if (pti_img_save (cas->write_name, cas->write_img, PTI_FORMAT_NONE)) { 498 return (1); 499 } 500 501 cas->modified = 0; 502 503 return (0); 504} 505 506int cas_get_position (const cassette_t *cas, double *tm) 507{ 508 unsigned long pos, sec, clk; 509 pti_img_t *img; 510 511 if (cas->record) { 512 if ((img = cas->write_img) == NULL) { 513 return (1); 514 } 515 516 pos = img->pulse_cnt; 517 } 518 else { 519 if ((img = cas_get_read_img (cas)) == NULL) { 520 return (1); 521 } 522 523 pos = cas->position; 524 } 525 526 pti_img_get_time (img, pos, &sec, &clk); 527 pti_time_get (sec, clk, tm, img->clock); 528 529 return (0); 530} 531 532int cas_set_position (cassette_t *cas, double tm) 533{ 534 unsigned long pos, sec, clk; 535 pti_img_t *img; 536 537 if ((img = cas_get_read_img (cas)) == NULL) { 538 return (1); 539 } 540 541 pti_time_set (&sec, &clk, tm, img->clock); 542 pti_img_get_index (img, &pos, sec, clk); 543 544 cas->position = pos; 545 546 cas->remainder = 0; 547 cas->eof = 0; 548 549 return (0); 550} 551 552int cas_truncate (cassette_t *cas, double tm) 553{ 554 unsigned long pos, sec, clk; 555 556 if (cas->write_img == NULL) { 557 return (1); 558 } 559 560 pti_time_set (&sec, &clk, tm, cas->write_img->clock); 561 pti_img_get_index (cas->write_img, &pos, sec, clk); 562 563 if (pos < cas->write_img->pulse_cnt) { 564 cas->write_img->pulse_cnt = pos; 565 cas->modified = 1; 566 } 567 568 return (0); 569} 570 571int cas_space (cassette_t *cas, double tm) 572{ 573 unsigned long sec, clk; 574 575 if (cas->write_img == NULL) { 576 return (1); 577 } 578 579 pti_time_set (&sec, &clk, tm, cas->write_img->clock); 580 581 if (pti_img_add_pulse (cas->write_img, clk, 0)) { 582 return (1); 583 } 584 585 cas->modified = 1; 586 587 return (0); 588} 589 590static 591int cas_read_pulse (cassette_t *cas, unsigned long *clk, int *level) 592{ 593 pti_img_t *img; 594 595 if ((img = cas->read_img) == NULL) { 596 if ((img = cas->write_img) == NULL) { 597 return (1); 598 } 599 } 600 601 if (cas->position >= img->pulse_cnt) { 602 return (1); 603 } 604 605 pti_pulse_get (img->pulse[cas->position], clk, level); 606 607 *clk = pti_pulse_convert (*clk, cas->clock, img->clock, &cas->remainder); 608 609 cas->position += 1; 610 611 return (0); 612} 613 614static 615int cas_write_pulse (cassette_t *cas, unsigned long clk, int level) 616{ 617 pti_img_t *img; 618 619 if ((img = cas->write_img) == NULL) { 620 return (1); 621 } 622 623 clk = pti_pulse_convert (clk, img->clock, cas->clock, &cas->remainder); 624 625 if (pti_img_add_pulse (img, clk, level)) { 626 return (1); 627 } 628 629 cas->modified = 1; 630 631 return (0); 632} 633 634static 635void cas_write_pulse_flush (cassette_t *cas) 636{ 637 if ((cas->run == 0) || (cas->record == 0)) { 638 return; 639 } 640 641 if (cas->counter > 0) { 642 cas_write_pulse (cas, cas->counter, cas->out_val ? 1 : -1); 643 cas->counter = 0; 644 } 645} 646 647static 648void cas_run_stop (cassette_t *cas) 649{ 650 int run; 651 652 run = (cas->motor && cas->play) || (cas->motor_delay_count > 0); 653 654 if (cas->run == run) { 655 return; 656 } 657 658 if (cas->run && cas->record) { 659 cas_write_pulse_flush (cas); 660 } 661 662 cas->run = run; 663 664 if (cas->set_run != NULL) { 665 cas->set_run (cas->set_run_ext, run); 666 } 667 668 if (cas->record) { 669 cas->eof = 0; 670 } 671 672 cas_print_state (cas); 673 674 if (cas->run) { 675 cas->counter = 0; 676 cas->remainder = 0; 677 } 678} 679 680void cas_set_play (cassette_t *cas, int val) 681{ 682 val = (val != 0) || cas->auto_play; 683 684 if (cas->play == val) { 685 return; 686 } 687 688 cas->play = val; 689 cas->motor_delay_count = 0; 690 691 if (cas->auto_motor) { 692 cas->motor = cas->play || cas->record; 693 } 694 695 if (cas->set_play != NULL) { 696 cas->set_play (cas->set_play_ext, cas->play); 697 } 698 699 cas_run_stop (cas); 700} 701 702void cas_set_record (cassette_t *cas, int val) 703{ 704 val = (val != 0); 705 706 if (cas->record == val) { 707 return; 708 } 709 710 if (cas->run && cas->record) { 711 cas_write_pulse_flush (cas); 712 } 713 714 cas->record = val; 715 cas->motor_delay_count = 0; 716 717 if (cas->auto_motor) { 718 cas->motor = cas->play || cas->record; 719 } 720 721 cas_run_stop (cas); 722} 723 724void cas_press_keys (cassette_t *cas, int play, int record) 725{ 726 cas_set_record (cas, record); 727 cas_set_play (cas, play); 728} 729 730void cas_print_state (cassette_t *cas) 731{ 732 pti_img_t *img1, *img2; 733 const char *state; 734 unsigned long sec, clk; 735 double tm1, tm2; 736 737 img1 = cas_get_read_img (cas); 738 img2 = cas->write_img; 739 740 tm1 = 0.0; 741 tm2 = 0.0; 742 743 if (img1 != NULL) { 744 pti_img_get_time (img1, cas->position, &sec, &clk); 745 pti_time_get (sec, clk, &tm1, img1->clock); 746 } 747 748 if (img2 != NULL) { 749 pti_img_get_length (img2, &sec, &clk); 750 pti_time_get (sec, clk, &tm2, img2->clock); 751 } 752 753 if (cas->eof) { 754 state = "EOF"; 755 } 756 else if (cas->run) { 757 state = cas->record ? "SAVE" : "LOAD"; 758 } 759 else { 760 state = ""; 761 } 762 763 pce_printf ("CASSETTE [%c%c%c] [%05.1f] [%05.1f] %s\n", 764 cas->play ? 'P' : '-', 765 cas->record ? 'R' : '-', 766 cas->motor ? 'M' : '-', 767 tm1, tm2, 768 state 769 ); 770} 771 772void cas_set_motor (cassette_t *cas, int val) 773{ 774 val = (val != 0); 775 776 if (cas->auto_motor) { 777 val |= cas->play || cas->record; 778 } 779 780 if (cas->motor == val) { 781 return; 782 } 783 784 cas->motor = val; 785 786 if (cas->run && (cas->motor == 0)) { 787 cas->motor_delay_count = cas->motor_delay; 788 } 789 else { 790 cas->motor_delay_count = 0; 791 } 792 793 cas_run_stop (cas); 794} 795 796static 797void cas_set_inp (cassette_t *cas, int val) 798{ 799 val = (val != 0); 800 801 if (cas->inp_val == val) { 802 return; 803 } 804 805 if (cas->set_inp != NULL) { 806 cas->set_inp (cas->set_inp_ext, val); 807 } 808 809 cas->inp_val = val; 810} 811 812int cas_get_inp (const cassette_t *cas) 813{ 814 return (cas->inp_val); 815} 816 817void cas_set_out (cassette_t *cas, int val) 818{ 819 val = (val != 0); 820 821 if (cas->out_val == val) { 822 return; 823 } 824 825 if ((cas->run == 0) || (cas->record == 0)) { 826 cas->out_val = val; 827 cas->inp_val = val; 828 return; 829 } 830 831 cas_write_pulse (cas, cas->counter, cas->out_val ? 1 : -1); 832 833 cas->counter = 0; 834 835 cas->out_val = val; 836} 837 838void cas_clock (cassette_t *cas) 839{ 840 int v; 841 842 if (cas->run == 0) { 843 return; 844 } 845 846 if (cas->motor_delay_count > 0) { 847 cas->motor_delay_count -= 1; 848 849 if (cas->motor_delay_count == 0) { 850 cas_run_stop (cas); 851 } 852 } 853 854 if (cas->record) { 855 cas->counter += 1; 856 return; 857 } 858 859 if (cas->counter > 1) { 860 cas->counter -= 1; 861 return; 862 } 863 864 if (cas_read_pulse (cas, &cas->counter, &v)) { 865 cas->eof = 1; 866 cas_press_keys (cas, 0, 0); 867 return; 868 } 869 870 cas_set_inp (cas, v >= 0); 871}